Browse Source

Refactor Virtual Machines

master
Joshua Rubingh 12 months ago
parent
commit
155edafe4f
  1. 6
      VRE/VRE/settings.py
  2. 11
      VRE/apps/api/urls.py
  3. 23
      VRE/apps/study/migrations/0008_auto_20211014_0853.py
  4. 25
      VRE/apps/virtual_machine/admin.py
  5. 4
      VRE/apps/virtual_machine/apps.py
  6. 410
      VRE/apps/virtual_machine/fixtures/virtual_machine_initial_data.json
  7. 147
      VRE/apps/virtual_machine/migrations/0002_auto_20211014_0853.py
  8. 43
      VRE/apps/virtual_machine/models.py
  9. 1
      VRE/apps/virtual_machine/providers/openstack/__init__.py
  10. 12
      VRE/apps/virtual_machine/providers/openstack/admin.py
  11. 32
      VRE/apps/virtual_machine/providers/openstack/apps.py
  12. 48
      VRE/apps/virtual_machine/providers/openstack/migrations/0001_initial.py
  13. 23
      VRE/apps/virtual_machine/providers/openstack/migrations/0002_auto_20210625_0900.py
  14. 16
      VRE/apps/virtual_machine/providers/openstack/migrations/0003_delete_workspacepart.py
  15. 0
      VRE/apps/virtual_machine/providers/openstack/migrations/__init__.py
  16. 82
      VRE/apps/virtual_machine/providers/openstack/models.py
  17. 51
      VRE/apps/virtual_machine/providers/openstack/signals.py
  18. 169
      VRE/apps/virtual_machine/providers/openstack/tasks.py
  19. 0
      VRE/apps/virtual_machine/providers/openstack/tests.py
  20. 1
      VRE/apps/virtual_machine/providers/vrw/__init__.py
  21. 22
      VRE/apps/virtual_machine/providers/vrw/admin.py
  22. 10
      VRE/apps/virtual_machine/providers/vrw/apps.py
  23. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0001_initial.py
  24. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0002_auto_20210625_0900.py
  25. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0003_alter_workspace_status.py
  26. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0004_workspacequeue.py
  27. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0005_workspacequeue_status.py
  28. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0006_remove_workspacequeue_status.py
  29. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/0007_alter_workspacequeue_options.py
  30. 16
      VRE/apps/virtual_machine/providers/vrw/migrations/0008_delete_workspacepart.py
  31. 0
      VRE/apps/virtual_machine/providers/vrw/migrations/__init__.py
  32. 61
      VRE/apps/virtual_machine/providers/vrw/models.py
  33. 0
      VRE/apps/virtual_machine/providers/vrw/permissions.py
  34. 3
      VRE/apps/virtual_machine/providers/vrw/serializers.py
  35. 81
      VRE/apps/virtual_machine/providers/vrw/signals.py
  36. 3
      VRE/apps/virtual_machine/providers/vrw/tests.py
  37. 0
      VRE/apps/virtual_machine/providers/vrw/urls.py
  38. 0
      VRE/apps/virtual_machine/providers/vrw/views.py
  39. 103
      VRE/apps/virtual_machine/serializers.py
  40. 18
      VRE/apps/virtual_machine/urls.py
  41. 158
      VRE/apps/virtual_machine/views.py
  42. 1
      VRE/apps/vrw/__init__.py
  43. 38
      VRE/apps/vrw/fixtures/vrw_initial_data.json
  44. 62
      VRE/apps/vrw/signals.py
  45. 2
      docker/entrypoint.api.sh

6
VRE/VRE/settings.py

@ -66,8 +66,10 @@ INSTALLED_APPS = [ @@ -66,8 +66,10 @@ INSTALLED_APPS = [
'apps.storage',
'apps.study',
'apps.virtual_machine',
'apps.vrw',
'apps.openstack',
'apps.virtual_machine.providers.vrw',
'apps.virtual_machine.providers.openstack',
# 'apps.openstack',
'apps.university',
'djoser',

11
VRE/apps/api/urls.py

@ -15,8 +15,7 @@ from apps.virtual_machine.views import (VirtualMachineViewSet, @@ -15,8 +15,7 @@ from apps.virtual_machine.views import (VirtualMachineViewSet,
VirtualMachineMemoryViewSet,
VirtualMachineNetworkViewSet,
VirtualMachineStorageViewSet,
VirtualMachineGPUViewSet,
VirtualMachineAccessViewSet)
VirtualMachineGPUViewSet)
schema_view = get_schema_view(
openapi.Info(
@ -42,7 +41,7 @@ api_router_v1.register(r'studies', Studies) @@ -42,7 +41,7 @@ api_router_v1.register(r'studies', Studies)
# Order is important for virtual machines. Longest match first
api_router_v1.register(r'virtualmachines/profiles', VirtualMachineProfileViewSet)
api_router_v1.register(r'virtualmachines/storage', VirtualMachineStorageViewSet)
api_router_v1.register(r'virtualmachines/access', VirtualMachineAccessViewSet)
#api_router_v1.register(r'virtualmachines/access', VirtualMachineAccessViewSet)
api_router_v1.register(r'virtualmachines/memory', VirtualMachineMemoryViewSet)
api_router_v1.register(r'virtualmachines/network', VirtualMachineNetworkViewSet)
api_router_v1.register(r'virtualmachines/gpu', VirtualMachineGPUViewSet)
@ -53,7 +52,11 @@ api_router_v1.register(r'virtualmachines', VirtualMachineViewSet) @@ -53,7 +52,11 @@ api_router_v1.register(r'virtualmachines', VirtualMachineViewSet)
api_router_v1.urls.append(path('researchers/', include('apps.researcher.urls')))
api_router_v1.urls.append(path('studies/', include('apps.study.urls')))
api_router_v1.urls.append(path('universities/', include('apps.university.urls')))
api_router_v1.urls.append(path('vrw/', include('apps.vrw.urls')))
api_router_v1.urls.append(path('virtual_machines/', include('apps.virtual_machine.urls')))
#api_router_v1.urls.append(path('vrw/', include('apps.vrw.urls')))
# Main namespace for the API urls
app_name = 'api'

23
VRE/apps/study/migrations/0008_auto_20211014_0853.py

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2021-10-14 08:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('study', '0007_alter_studyrole_active'),
]
operations = [
migrations.AlterField(
model_name='studyrole',
name='active',
field=models.BooleanField(default=True, help_text='', verbose_name='Active'),
),
migrations.AlterField(
model_name='studyrole',
name='role',
field=models.CharField(choices=[('ADMIN', 'Administrator'), ('RESEARCHER', 'Researcher'), ('MEMBER', 'Member')], default='MEMBER', help_text='The role withing this research study.', max_length=10, verbose_name='Role'),
),
]

25
VRE/apps/virtual_machine/admin.py

@ -16,8 +16,9 @@ from .models import (VirtualMachineOperatingSystem, @@ -16,8 +16,9 @@ from .models import (VirtualMachineOperatingSystem,
@admin.register(VirtualMachine)
class VirtualMachineAdmin(admin.ModelAdmin):
list_display = ('name', 'researcher', 'total_memory', 'total_storage', 'created_at')
search_fields = ('name', 'researcher')
list_display = ('name', 'researcher', 'profile', 'provider', 'total_memory', 'total_storage', 'created_at')
list_filter = ('provider', 'profile')
search_fields = ('name', 'researcher', 'profile', 'provider', )
ordering = ('-created_at', 'name', 'researcher', )
readonly_fields = ('created_at', 'updated_at')
@ -29,10 +30,12 @@ class VirtualMachineAdmin(admin.ModelAdmin): @@ -29,10 +30,12 @@ class VirtualMachineAdmin(admin.ModelAdmin):
class VirtualMachinePartAdmin(admin.ModelAdmin):
list_display = ('name', 'is_available', 'created_at')
search_fields = ('name', )
ordering = ('-created_at', 'name',)
readonly_fields = ('created_at', 'updated_at')
list_display = ('name', 'provider', 'is_available', 'created_at')
list_filter = ('provider', 'is_available')
search_fields = ('name', 'provider', )
ordering = ('provider', 'name', '-created_at', )
readonly_fields = ('created_at', 'updated_at', )
fields = ('name', 'provider', 'cloud_id', 'is_available', 'created_at', 'updated_at')
@admin.register(VirtualMachineOperatingSystem)
@ -62,7 +65,13 @@ class VirtualMachineGPUAdmin(VirtualMachinePartAdmin): @@ -62,7 +65,13 @@ class VirtualMachineGPUAdmin(VirtualMachinePartAdmin):
@admin.register(VirtualMachineProfile)
class VirtualMachineProfileAdmin(VirtualMachinePartAdmin):
pass
list_display = ('name', 'provider', 'is_available', 'created_at')
list_filter = ('provider', 'is_available')
search_fields = ('name', 'provider', )
ordering = ('provider', 'name', '-created_at', )
readonly_fields = ('created_at', 'updated_at', )
fields = ('name', 'provider', 'os', 'networks', 'memory_type', 'memory_amount', 'storage_type', 'storage_amount', 'gpu_type', 'gpu_amount', 'is_available', 'created_at', 'updated_at')
@admin.register(VirtualMachineAccess)
@ -70,4 +79,4 @@ class VirtualMachineAccessAdmin(admin.ModelAdmin): @@ -70,4 +79,4 @@ class VirtualMachineAccessAdmin(admin.ModelAdmin):
list_display = ('researcher', 'virtual_machine', 'created_at')
search_fields = ('researcher', 'virtual_machine')
ordering = ('-created_at', 'researcher',)
readonly_fields = ('user', 'created_at', 'updated_at')
readonly_fields = ('username', 'created_at', 'updated_at')

4
VRE/apps/virtual_machine/apps.py

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
from django.apps import AppConfig
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
@ -7,3 +8,6 @@ class VirtualMachineConfig(AppConfig): @@ -7,3 +8,6 @@ class VirtualMachineConfig(AppConfig):
label = 'virtual_machine'
verbose_name = _('Virtual Machine')
verbose_name_plural = _('Virtual Machines')
# Other cloud providers will register themselfs in this setting
settings.CLOUD_PROVIDERS = {}

410
VRE/apps/virtual_machine/fixtures/virtual_machine_initial_data.json

@ -1,144 +1,270 @@ @@ -1,144 +1,270 @@
[
{
"model": "virtual_machine.virtualmachineoperatingsystem",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:45:41.308Z",
"updated_at": "2020-10-26T09:47:19.425Z",
"name": "Ubuntu 18.04 LTS",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachineoperatingsystem",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:45:54.979Z",
"updated_at": "2020-03-31T08:46:14.343Z",
"name": "Windows 10 standaard",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:44:54.925Z",
"updated_at": "2020-03-31T08:44:54.925Z",
"name": "1 GB Standaard",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:45:15.985Z",
"updated_at": "2020-03-31T08:46:35.124Z",
"name": "1 GB High performance",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinenetwork",
"pk": 1,
"fields": {
"created_at": "2020-10-26T09:49:36.730Z",
"updated_at": "2020-10-26T09:50:13.440Z",
"name": "Private network (internal)",
"is_available": true,
"network_type": "PRIVATE"
}
},
{
"model": "virtual_machine.virtualmachinenetwork",
"pk": 2,
"fields": {
"created_at": "2020-10-26T09:50:08.083Z",
"updated_at": "2020-10-26T09:50:08.083Z",
"name": "Public network (vlan16)",
"is_available": true,
"network_type": "PUBLIC"
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:47:03.785Z",
"updated_at": "2020-10-26T09:53:18.052Z",
"name": "1GB Standaard",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:47:28.233Z",
"updated_at": "2020-10-26T09:53:10.606Z",
"name": "1GB NVMe",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 3,
"fields": {
"created_at": "2020-03-31T08:47:44.444Z",
"updated_at": "2020-10-26T09:53:04.073Z",
"name": "1GB SSD",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinegpu",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:44:23.755Z",
"updated_at": "2020-03-31T08:44:23.755Z",
"name": "NVIDIA GTX1080",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 1,
"fields": {
"created_at": "2020-03-31T09:17:11.143Z",
"updated_at": "2020-10-26T09:52:50.679Z",
"is_available": true,
"name": "Basic",
"memory_type": 1,
"memory_amount": 4,
"storage_type": 1,
"storage_amount": 20,
"gpu_type": null,
"gpu_amount": 0,
"networks": [
1,
2
]
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 2,
"fields": {
"created_at": "2020-03-31T09:17:35.444Z",
"updated_at": "2020-10-26T09:53:40.396Z",
"is_available": true,
"name": "Premium",
"memory_type": 2,
"memory_amount": 12,
"storage_type": 2,
"storage_amount": 100,
"gpu_type": 1,
"gpu_amount": 1,
"networks": [
1,
2
]
}
}
{
"model": "virtual_machine.virtualmachineoperatingsystem",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:45:41.308Z",
"updated_at": "2021-10-14T09:05:39.024Z",
"cloud_id": "fd0e4ec4-8df3-4e42-adbb-394a16e900fc",
"name": "Ubuntu 18.04 LTS",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachineoperatingsystem",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:45:54.979Z",
"updated_at": "2020-03-31T08:46:14.343Z",
"cloud_id": null,
"name": "Windows 10 standaard",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:44:54.925Z",
"updated_at": "2020-03-31T08:44:54.925Z",
"cloud_id": null,
"name": "1 GB Standaard",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:45:15.985Z",
"updated_at": "2020-03-31T08:46:35.124Z",
"cloud_id": null,
"name": "1 GB High performance",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 3,
"fields": {
"created_at": "2021-10-14T09:04:40.483Z",
"updated_at": "2021-10-14T09:04:40.483Z",
"cloud_id": null,
"name": "1 GB High performance",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinememory",
"pk": 4,
"fields": {
"created_at": "2021-10-14T09:04:53.822Z",
"updated_at": "2021-10-14T09:04:53.822Z",
"cloud_id": null,
"name": "1 GB Standaard",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinenetwork",
"pk": 1,
"fields": {
"created_at": "2020-10-26T09:49:36.730Z",
"updated_at": "2021-10-14T09:03:48.114Z",
"cloud_id": "637562ae-25ab-4a7a-8a6c-ed48c924be26",
"name": "Private network (internal)",
"provider": "openstack",
"is_available": true,
"network_type": "PRIVATE"
}
},
{
"model": "virtual_machine.virtualmachinenetwork",
"pk": 2,
"fields": {
"created_at": "2020-10-26T09:50:08.083Z",
"updated_at": "2021-10-14T09:04:11.263Z",
"cloud_id": "afd0a5a6-a4ce-415a-9a28-9325a6857dfd",
"name": "Public network (vlan16)",
"provider": "openstack",
"is_available": true,
"network_type": "PUBLIC"
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:47:03.785Z",
"updated_at": "2020-10-26T09:53:18.052Z",
"cloud_id": null,
"name": "1GB Standaard",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 2,
"fields": {
"created_at": "2020-03-31T08:47:28.233Z",
"updated_at": "2020-10-26T09:53:10.606Z",
"cloud_id": null,
"name": "1GB NVMe",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 3,
"fields": {
"created_at": "2020-03-31T08:47:44.444Z",
"updated_at": "2020-10-26T09:53:04.073Z",
"cloud_id": null,
"name": "1GB SSD",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 4,
"fields": {
"created_at": "2021-10-14T09:06:28.827Z",
"updated_at": "2021-10-14T09:06:28.827Z",
"cloud_id": null,
"name": "1GB NVMe",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 5,
"fields": {
"created_at": "2021-10-14T09:06:39.683Z",
"updated_at": "2021-10-14T09:06:39.683Z",
"cloud_id": null,
"name": "1GB SSD",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinestorage",
"pk": 6,
"fields": {
"created_at": "2021-10-14T09:06:49.722Z",
"updated_at": "2021-10-14T09:06:49.723Z",
"cloud_id": null,
"name": "1GB Standaard",
"provider": "openstack",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachinegpu",
"pk": 1,
"fields": {
"created_at": "2020-03-31T08:44:23.755Z",
"updated_at": "2020-03-31T08:44:23.755Z",
"cloud_id": null,
"name": "NVIDIA GTX1080",
"provider": "vrw",
"is_available": true
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 1,
"fields": {
"created_at": "2020-03-31T09:17:11.143Z",
"updated_at": "2021-10-14T09:07:15.290Z",
"cloud_id": null,
"provider": "vrw",
"is_available": true,
"name": "Basic",
"os": 2,
"memory_type": 1,
"memory_amount": 4,
"storage_type": 1,
"storage_amount": 20,
"gpu_type": null,
"gpu_amount": 0,
"networks": []
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 2,
"fields": {
"created_at": "2020-03-31T09:17:35.444Z",
"updated_at": "2021-10-14T09:07:27.470Z",
"cloud_id": null,
"provider": "vrw",
"is_available": true,
"name": "Premium",
"os": 2,
"memory_type": 2,
"memory_amount": 12,
"storage_type": 2,
"storage_amount": 100,
"gpu_type": 1,
"gpu_amount": 1,
"networks": []
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 3,
"fields": {
"created_at": "2021-10-14T09:09:00.550Z",
"updated_at": "2021-10-14T09:09:00.550Z",
"cloud_id": null,
"provider": "openstack",
"is_available": true,
"name": "Basic",
"os": 1,
"memory_type": 4,
"memory_amount": 4,
"storage_type": 6,
"storage_amount": 40,
"gpu_type": null,
"gpu_amount": 0,
"networks": [
1,
2
]
}
},
{
"model": "virtual_machine.virtualmachineprofile",
"pk": 4,
"fields": {
"created_at": "2021-10-14T09:09:26.033Z",
"updated_at": "2021-10-14T09:09:26.033Z",
"cloud_id": null,
"provider": "openstack",
"is_available": true,
"name": "Premium",
"os": 1,
"memory_type": 3,
"memory_amount": 12,
"storage_type": 4,
"storage_amount": 40,
"gpu_type": null,
"gpu_amount": 0,
"networks": [
1,
2
]
}
}
]

147
VRE/apps/virtual_machine/migrations/0002_auto_20211014_0853.py

@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
# Generated by Django 3.2.8 on 2021-10-14 08:53
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('virtual_machine', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='virtualmachine',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinegpu',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachinegpu',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinememory',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachinememory',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinenetwork',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachinenetwork',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachineoperatingsystem',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachineoperatingsystem',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachineprofile',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachineprofile',
name='os',
field=models.ForeignKey(default=1, help_text='Operating system', limit_choices_to={'is_available': True}, on_delete=django.db.models.deletion.CASCADE, to='virtual_machine.virtualmachineoperatingsystem'),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachineprofile',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinestorage',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AddField(
model_name='virtualmachinestorage',
name='provider',
field=models.CharField(choices=[('vrw', 'VRW RUG'), ('openstack', 'OpenStack RUG HPC')], default='vrw', help_text='Cloud provider type.', max_length=50),
preserve_default=False,
),
migrations.AlterField(
model_name='virtualmachine',
name='additional_gpu_amount',
field=models.PositiveSmallIntegerField(blank=True, default=0, help_text='Amount of GPUs. Default is 0'),
),
migrations.AlterField(
model_name='virtualmachine',
name='additional_memory_amount',
field=models.PositiveSmallIntegerField(blank=True, default=0, help_text='Amount of memory. Default is 0'),
),
migrations.AlterField(
model_name='virtualmachine',
name='additional_storage_amount',
field=models.PositiveSmallIntegerField(blank=True, default=0, help_text='Amount of storage. Default is 0'),
),
migrations.AlterField(
model_name='virtualmachine',
name='networks',
field=models.ManyToManyField(blank=True, help_text='Networks connected to this virtual machine.', to='virtual_machine.VirtualMachineNetwork'),
),
migrations.AlterField(
model_name='virtualmachinegpu',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
migrations.AlterField(
model_name='virtualmachinememory',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
migrations.AlterField(
model_name='virtualmachinenetwork',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
migrations.AlterField(
model_name='virtualmachineoperatingsystem',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
migrations.AlterField(
model_name='virtualmachineprofile',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
migrations.AlterField(
model_name='virtualmachineprofile',
name='networks',
field=models.ManyToManyField(blank=True, help_text='Select the networks that should be connected.', to='virtual_machine.VirtualMachineNetwork'),
),
migrations.AlterField(
model_name='virtualmachinestorage',
name='is_available',
field=models.BooleanField(default=True, help_text='Only selected virtual machine parts can be chosen from.'),
),
]

43
VRE/apps/virtual_machine/models.py

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -10,6 +11,8 @@ from apps.study.models import Study @@ -10,6 +11,8 @@ from apps.study.models import Study
from math import pow
from lib.models.cloud import CloudBasicDataModel
# Create your models here.
@ -21,7 +24,7 @@ class VirtualNetworkType(models.TextChoices): @@ -21,7 +24,7 @@ class VirtualNetworkType(models.TextChoices):
PUBLIC = ('PUBLIC', _('Public'))
class VirtualMachinePart(models.Model):
class VirtualMachinePart(CloudBasicDataModel, models.Model):
"""
This is a base abstract class for multiple virtual machine models. This model provides default fields
@ -36,13 +39,24 @@ class VirtualMachinePart(models.Model): @@ -36,13 +39,24 @@ class VirtualMachinePart(models.Model):
"""
name = models.CharField(max_length=50, help_text=_('Easy to remember name for this virtual machine part.'))
is_available = models.BooleanField(help_text=_('Only selected virtual machine parts can be chosen from.'))
provider = models.CharField(max_length=50, choices=tuple((provider_id, provider_data['name']) for provider_id, provider_data in settings.CLOUD_PROVIDERS.items()), help_text=_('Cloud provider type.'))
is_available = models.BooleanField(help_text=_('Only selected virtual machine parts can be chosen from.'), default=True)
class Meta:
abstract = True
@property
def cloud_providers(self):
return settings.CLOUD_PROVIDERS
def __str__(self):
return self.name
provider = _('Unknown')
try:
provider = self.cloud_providers.get(self.provider).get('name')
except:
pass
return f'{self.name} ({provider})'
class VirtualMachineOperatingSystem(MetaDataModel, VirtualMachinePart):
@ -171,7 +185,9 @@ class VirtualMachineProfile(MetaDataModel, VirtualMachinePart): @@ -171,7 +185,9 @@ class VirtualMachineProfile(MetaDataModel, VirtualMachinePart):
name = models.CharField(max_length=50, help_text=_('Easy to remember name for this virtual machine profile.'))
networks = models.ManyToManyField(VirtualMachineNetwork, help_text=_('Select the networks that should be connected.'))
os = models.ForeignKey(VirtualMachineOperatingSystem, on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('Operating system'))
networks = models.ManyToManyField(VirtualMachineNetwork, blank=True, help_text=_('Select the networks that should be connected.'))
memory_type = models.ForeignKey(VirtualMachineMemory, on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('Basic memory'))
memory_amount = models.PositiveSmallIntegerField(default=1, help_text=_('Amount of memory. Default is 1'))
@ -266,9 +282,11 @@ class VirtualMachine(MetaDataModel): @@ -266,9 +282,11 @@ class VirtualMachine(MetaDataModel):
study = models.ForeignKey(Study, on_delete=models.CASCADE, help_text=_('The study for which this virtual machine is used.'))
provider = models.CharField(max_length=50, choices=tuple((provider_id, provider_data['name']) for provider_id, provider_data in settings.CLOUD_PROVIDERS.items()), help_text=_('Cloud provider type.'))
profile = models.ForeignKey(VirtualMachineProfile, on_delete=models.CASCADE, help_text=_('The virtual machine selected profile.'))
networks = models.ManyToManyField(VirtualMachineNetwork, help_text=_('Networks connected to this virtual machine.'))
networks = models.ManyToManyField(VirtualMachineNetwork, blank=True, help_text=_('Networks connected to this virtual machine.'))
operating_system = models.ForeignKey(VirtualMachineOperatingSystem, on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('The operating system for this virtual machine.'))
@ -279,20 +297,20 @@ class VirtualMachine(MetaDataModel): @@ -279,20 +297,20 @@ class VirtualMachine(MetaDataModel):
base_storage_amount = models.PositiveSmallIntegerField(default=1, help_text=_('Amount of disk storage. Default is 1'))
additional_gpu_type = models.ForeignKey(VirtualMachineGPU, blank=True, null=True, on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('Additional GPU'))
additional_gpu_amount = models.PositiveSmallIntegerField(default=0, help_text=_('Amount of GPUs. Default is 0'))
additional_gpu_amount = models.PositiveSmallIntegerField(default=0, blank=True, help_text=_('Amount of GPUs. Default is 0'))
additional_memory_type = models.ForeignKey(VirtualMachineMemory, blank=True, null=True, related_name='additional_memory', on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('Additional memory'))
additional_memory_amount = models.PositiveSmallIntegerField(default=0, help_text=_('Amount of memory. Default is 0'))
additional_memory_amount = models.PositiveSmallIntegerField(default=0, blank=True, help_text=_('Amount of memory. Default is 0'))
additional_storage_type = models.ForeignKey(VirtualMachineStorage, blank=True, null=True, related_name='additional_storage', on_delete=models.CASCADE, limit_choices_to={'is_available': True}, help_text=_('Additional storage'))
additional_storage_amount = models.PositiveSmallIntegerField(default=0, help_text=_('Amount of storage. Default is 0'))
additional_storage_amount = models.PositiveSmallIntegerField(default=0, blank=True, help_text=_('Amount of storage. Default is 0'))
def __str__(self):
return self.name
@property
def has_workspace(self):
"""This function will look through all the attribute looking for an atribute starting with `workspace_` that has a value. This will mean that there is a virtual workspace created for this machine.
"""This function will look through all the attribute looking for an attribute starting with `workspace_` that has a value. This will mean that there is a virtual workspace created for this machine.
Returns:
bool: Return True when this Virtual Machine has a workspace configured. If not it return False
@ -359,7 +377,12 @@ class VirtualMachineAccess(MetaDataModel): @@ -359,7 +377,12 @@ class VirtualMachineAccess(MetaDataModel):
virtual_machine_ip = models.CharField(_('Login IP'), max_length=46, help_text=_('The IP address to login to the virtual machine.'))
@property
def user(self):
def username(self):
"""The username for the login
Returns:
string: the username to login with
"""
return self.researcher.user.username
def __str__(self):

1
VRE/apps/virtual_machine/providers/openstack/__init__.py

@ -0,0 +1 @@ @@ -0,0 +1 @@
default_app_config = 'apps.virtual_machine.providers.openstack.apps.OpenstackConfig'

12
VRE/apps/virtual_machine/providers/openstack/admin.py

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
from django.contrib import admin
from .models import Workspace
# Register your models here.
@admin.register(Workspace)
class OpenstackWorkspaceAdmin(admin.ModelAdmin):
list_display = ('virtual_machine', 'created_at')
ordering = ('-created_at',)
readonly_fields = ('created_at', 'updated_at')

32
VRE/apps/virtual_machine/providers/openstack/apps.py

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
from django.apps import AppConfig
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from datetime import timedelta
class OpenstackConfig(AppConfig):
"""This is the default configuration for the OpenStack application.
The settings can be configured in the main settings.py file of the project.
Args:
settings.OPENSTACK_MACHINE_ACTIVE_DURATION (timedelta): This is the duration of which a :term:`VPS` is available. Default 1 year.
"""
name = 'apps.virtual_machine.providers.openstack'
label = 'openstack'
verbose_name = _('OpenStack RUG HPC')
verbose_name_plural = _('OpenStack RUG HPC')
settings.CLOUD_PROVIDERS[label] = {'name': verbose_name, 'app': name}
if not hasattr(settings, 'OPENSTACK_MACHINE_ACTIVE_DURATION'):
# We only load this setting, if it is not available in the overall settings.py file
settings.OPENSTACK_MACHINE_ACTIVE_DURATION = timedelta(days=365)
def ready(self):
"""
Load custom :attr:`~apps.openstack.signals` for creating :term:`VPS` models
"""
import apps.virtual_machine.providers.openstack.signals

48
VRE/apps/virtual_machine/providers/openstack/migrations/0001_initial.py

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
# Generated by Django 3.2 on 2021-05-25 08:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('virtual_machine', '0001_initial'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.CreateModel(
name='Workspace',
fields=[
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this model has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this model has been updated', verbose_name='Date updated')),
('cloud_id', models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=50, null=True)),
('virtual_machine', models.OneToOneField(help_text='The virtual machine configuration.', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='workspace_openstack', serialize=False, to='virtual_machine.virtualmachine')),
('starting_at', models.DateTimeField(help_text='Enter the start date when this workspace should be created', verbose_name='Starting at')),
('ending_at', models.DateTimeField(help_text='Enter the end date when this workspace should be terminated', verbose_name='Ending at')),
('status', models.CharField(choices=[('NEW', 'New'), ('UPDATING', 'Updating'), ('DONE', 'Done'), ('ERROR', 'Error'), ('OFFLINE', 'Offline'), ('DELETE', 'Delete'), ('TERMINATED', 'Terminated')], default='NEW', help_text='The status of the workspace.', max_length=10, verbose_name='Status')),
],
options={
'verbose_name': 'virtual workspace',
'verbose_name_plural': 'virtual workspaces',
},
),
migrations.CreateModel(
name='WorkspacePart',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this model has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this model has been updated', verbose_name='Date updated')),
('cloud_id', models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=50, null=True)),
('name', models.CharField(help_text='Human readable name for this workspace part.', max_length=100, verbose_name='Name')),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(help_text='Select the Virtual Machine part that correspondents with this workspace part.', limit_choices_to={'app_label': 'virtual_machine'}, on_delete=django.db.models.deletion.CASCADE, related_name='openstack_ct', to='contenttypes.contenttype')),
],
options={
'abstract': False,
},
),
]

23
VRE/apps/virtual_machine/providers/openstack/migrations/0002_auto_20210625_0900.py

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-06-25 09:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('openstack', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='workspace',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
migrations.AlterField(
model_name='workspacepart',
name='cloud_id',
field=models.CharField(blank=True, help_text='The ID on the cloud platform', max_length=255, null=True),
),
]

16
VRE/apps/virtual_machine/providers/openstack/migrations/0003_delete_workspacepart.py

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
# Generated by Django 3.2.8 on 2021-10-14 08:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('openstack', '0002_auto_20210625_0900'),
]
operations = [
migrations.DeleteModel(
name='WorkspacePart',
),
]

0
VRE/apps/vrw/migrations/__init__.py → VRE/apps/virtual_machine/providers/openstack/migrations/__init__.py

82
VRE/apps/virtual_machine/providers/openstack/models.py

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from lib.models.base import MetaDataModel
from lib.models.cloud import CloudBasicDataModel
from apps.virtual_machine.models import VirtualMachine
# Create your models here.
class WorkspaceStatus(models.TextChoices):
"""
A class for defining :term:`VPS` status as choices. Currently the following statusses are supported:
.. data:: NEW
This is the state where every :term:`VPS` starts with. This means that the :term:`VPS` config is created, but it has not been created yet by the cloud provider
.. data:: UPDATING
The :term:`VPS` is being created on the cloud provider infrastructure.
.. data:: DONE
The :term:`VPS` is created by the cloud provider and can be used by the customer
.. data:: ERROR
The :term:`VPS` could not be created by the cloud provider. There was an (unknown) error.
.. data:: OFFLINE
The :term:`VPS` is offline and not reachable. Reasons are not known. But this can be used for trigger an investigation
.. data:: DELETE
The :term:`VPS` is marked for deleting from the cloud platform
.. data:: TERMINATED
The :term:`VPS` is closed/deleted by the cloud provider without reason.
"""
NEW = ('NEW', _('New'))
UPDATING = ('UPDATING', _('Updating'))
DONE = ('DONE', _('Done'))
ERROR = ('ERROR', _('Error'))
OFFLINE = ('OFFLINE', _('Offline'))
DELETE = ('DELETE', _('Delete'))
TERMINATED = ('TERMINATED', _('Terminated'))
class Workspace(MetaDataModel, CloudBasicDataModel):
"""
A class for creating :term:`VPS` from Virtual Machines. When creating a new Workspace with the status :attr:`~WorkspaceStatus.NEW`, it should be picked up by the Workspace system in order to create the new :term:`VPS`.
By default the :term:`VPS` is created by a signal process where it uses the variable ':attr:`~settings.OPENSTACK_MACHINE_ACTIVE_DURATION`' to create :term:`VPS` for 1 year.
It will inherit the attributes :attr:`~lib.models.base.MetaDataModel.created_at` and :attr:`~lib.models.base.MetaDataModel.updated_at` from the Abstract model :class:`~lib.models.base.MetaDataModel`
It will inherit the attributes :attr:`~lib.models.cloud.CloudBasicDataModel.cloud_id` from the Abstract model :class:`~lib.models.cloud.CloudBasicDataModel`
Attributes
----------
virtual_machine : :class:`~apps.virtual_machine.models.VirtualMachine`
The Virtual Machine that needs to be created as a :term:`VPS`
starting_at : Datetime
The date and timestamp when this :term:`VPS` should be created.
ending_at : int
The date and timestamp when this :term:`VPS` should be removed.
status : string
The status of the :term:`VPS`. When created. Default is :attr:`~WorkspaceStatus.NEW`
"""
class Meta:
verbose_name = _('virtual workspace')
verbose_name_plural = _('virtual workspaces')
virtual_machine = models.OneToOneField(VirtualMachine, on_delete=models.CASCADE, primary_key=True, help_text=_('The virtual machine configuration.'), related_name='workspace_openstack')
starting_at = models.DateTimeField(_('Starting at'), help_text=_('Enter the start date when this workspace should be created'))
ending_at = models.DateTimeField(_('Ending at'), help_text=_('Enter the end date when this workspace should be terminated'))
status = models.CharField(_('Status'), default=WorkspaceStatus.NEW, max_length=10, choices=WorkspaceStatus.choices, help_text=_('The status of the workspace.'))

51
VRE/apps/virtual_machine/providers/openstack/signals.py

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.utils import timezone
from apps.virtual_machine.models import VirtualMachine
from .models import Workspace as OpenStackWorkspace
from .tasks import create_virtual_machine_task, delete_virtual_machine_task
@receiver(post_save, sender=VirtualMachine)
def create_virtual_machine_openstack(sender, instance, created, **kwargs):
"""When a new virtual machine is created, this signal will be fired in order to check if a :term:`VPS` needs te be created.
When a VirtualMachine is created for the first time, and does not have a OpenStack Workspace attached, the software will check on the operating system what to do.
At this point the check if a :term:`VPS` needs te be created is done on the **operating system**. If the selected operating system has a :term:`VPS` Part configured, it is assumed that we need to create the :term:`VPS`.
Args:
sender (class): The modelclass VirtualMachine
instance (VirtualMachine): The Virtual machine that is either created or updated.
created (bool): Is the Virtual Machine created. If false, it is an update.
"""
# When a new virtual machine is created and does not have a OpenStack Workspace attached to it, it is a candidate for the OpenStack
if created and 'openstack' == instance.provider:
new_workspace = OpenStackWorkspace(
virtual_machine=instance,
starting_at=timezone.now(),
ending_at=timezone.now() + settings.OPENSTACK_MACHINE_ACTIVE_DURATION
)
new_workspace.save()
# We need a delay to make sure the database has stored the data....
create_virtual_machine_task.schedule((new_workspace.pk,), delay=3)
@receiver(pre_delete, sender=VirtualMachine)
def terminate_virtual_machine(sender, instance, **kwargs):
if 'openstack' == instance.provider:
try:
workspace = OpenStackWorkspace.objects.get(pk=instance.workspace_openstack.pk)
# When the workspace is not a OpenStackWorkspace, we get a Does Not Exist exception.
if workspace.cloud_id:
delete_virtual_machine_task(workspace.cloud_id)
except OpenStackWorkspace.DoesNotExist:
# This workspace is not an Openstack Workspace
pass

169
VRE/apps/virtual_machine/providers/openstack/tasks.py

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
from django.contrib.contenttypes.models import ContentType
from huey.contrib.djhuey import task
from lib.cloud.openstack_client import VRE_OpenStackClient
from Crypto.PublicKey import RSA
import crypt
import string
import random
from apps.storage.models import StorageLocation, StorageEngine
from apps.virtual_machine.models import VirtualNetworkType, VirtualMachineAccess
from .models import Workspace as OpenStackWorkspace
from datetime import datetime
# Create the virtual machine in the background with a 30 sec delay, so that the model is fully saved (M2M)
@task()
def create_virtual_machine_task(workspace_id):
"""
This task will create a new :term:`VPS` with a 30 seconds delay. It will use the requested resources to create a new :term:`VPS`.
The actions that are done here are:
- Generate a new password for authentication on the :term:`VPS`
- Generate a public and private SSH key (RSA 4096 bits) for authentication on the :term:`VPS`
- Create a mount point for the research data
- Add auto mounting of the research data
- Setting up the right network connections
- Store the IPv4 of the new :term:`VPS` for later use.
Args:
workspace_id (int): The ID of the newly created workspace
"""
print(f'[{datetime.now()}] New workspace ID: {workspace_id} is being created')
workspace = OpenStackWorkspace.objects.get(pk=int(workspace_id))
virtual_machine = workspace.virtual_machine
# Create a SHA512 password for the password file.
ssh_password = "".join(random.sample(string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation, 16))
hashed_password = crypt.crypt(ssh_password, crypt.mksalt(crypt.METHOD_SHA512))
key = RSA.generate(4096)
private_key = key.export_key().decode('utf8')
public_key = key.publickey().export_key(format='OpenSSH').decode('utf8')
mount_share = ''
try:
mount_share = f' - echo "{virtual_machine.study.storagelocation.storageengine.location}{virtual_machine.study.storagelocation.path} {virtual_machine.study.storagelocation.storageengine.username} {virtual_machine.study.storagelocation.storageengine.password}" >> /etc/davfs2/secrets\n'
mount_share += f' - echo "{virtual_machine.study.storagelocation.storageengine.location}{virtual_machine.study.storagelocation.path} /opt/research_data davfs user,rw,auto,uid={virtual_machine.researcher.user.username},gid={virtual_machine.researcher.user.username} 0 0" >> /etc/fstab\n'
mount_share += ' - [mount, /opt/research_data]\n'
except StorageLocation.DoesNotExist:
# No storagelocation connected to the study, therefore no 'mounted' drives
pass
except StorageEngine.DoesNotExist:
# No storageengine connected to the study, therefore no 'mounted' drives
pass
user_data = f'''#cloud-config
users:
- default
- name: {virtual_machine.researcher.user.username}
gecos: {virtual_machine.researcher.user.get_full_name()}
sudo: ALL=(ALL) ALL
groups: davfs2
shell: /bin/bash
lock_passwd: false
passwd: {hashed_password}
ssh_authorized_keys:
- {public_key}
ssh_pwauth: True
apt:
preserve_sources_list: true
sources:
x2go.ppa:
source: "ppa:x2go/stable"
package_update: true
# package_upgrade: true
packages:
- davfs2
- ubuntu-mate-core
- x2goserver
- x2goserver-xsession
- x2gomatebindings
groups:
- davfs2: [root,ubuntu]
# We add 2FA to the SSH by using a private key AND password
runcmd:
- echo 'AuthenticationMethods publickey,password' >> /etc/ssh/sshd_config
- service ssh restart
- [mkdir, /opt/research_data]
{mount_share}
'''
# Openstack values:
name = f'VRE_{virtual_machine.study.name}_({virtual_machine.researcher.user.get_full_name()})'
# flavour = OpenStackWorkspacePart.objects.filter(content_type=ContentType.objects.get_for_model(virtual_machine.profile),
# object_id=virtual_machine.profile.pk).get()
flavour = virtual_machine.profile.cloud_id
# image = OpenStackWorkspacePart.objects.filter(content_type=ContentType.objects.get_for_model(virtual_machine.operating_system),
# object_id=virtual_machine.operating_system.pk).get()
# image = image.cloud_id
image = virtual_machine.operating_system.cloud_id
networks = {
'private': [],
'float': []
}
# Only add private networks now, the public network should be added as a floating IP
for network in virtual_machine.networks.all():
# openstack_network = OpenStackWorkspacePart.objects.filter(content_type=ContentType.objects.get_for_model(network),
# object_id=network.pk).get()
if network.network_type == VirtualNetworkType.PRIVATE:
networks['private'].append(network.cloud_id)
elif network.network_type == VirtualNetworkType.PUBLIC:
networks['float'].append(network.cloud_id)
opts = {
'memory': virtual_machine.total_memory,
'storage': virtual_machine.total_storage,
'user_data': user_data
}
client = VRE_OpenStackClient('hpc')
result = client.create_vps(name, image, flavour, networks, opts)
workspace.cloud_id = result['id']
workspace.save()
# # TODO: figure this out.... why update_or_create does not work... For now, we work around it.
access, created = VirtualMachineAccess.objects.get_or_create(
researcher=virtual_machine.researcher,
virtual_machine=virtual_machine
)
access.virtual_machine_ip = result['ipv4']
access.login_key = private_key
access.password = ssh_password
access.save()
@task()
def delete_virtual_machine_task(cloud_id):
"""
This task will delete a :term:`VPS` with a 30 seconds delay.
Args:
cloud_id (int): The remote ID of the :term:`VPS` that needs to be deleted.