Commit eb627644 by Chif Gergő

Merge branch 'instance-fixes' into 'DEV'

Instance fixes

See merge request !22
parents 70bd9c63 720ab1e4
Pipeline #1095 passed with stage
in 1 minute 31 seconds
...@@ -9,6 +9,7 @@ flake8 = "*" ...@@ -9,6 +9,7 @@ flake8 = "*"
django-rest-swagger = "*" django-rest-swagger = "*"
coverage = "*" coverage = "*"
django-nose = "*" django-nose = "*"
v = {editable = true,version = "*"}
[packages] [packages]
django = "*" django = "*"
......
# Generated by Django 2.2.1 on 2019-05-13 12:02 # Generated by Django 3.0.4 on 2020-04-06 12:09
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
...@@ -8,6 +10,7 @@ class Migration(migrations.Migration): ...@@ -8,6 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
...@@ -16,7 +19,19 @@ class Migration(migrations.Migration): ...@@ -16,7 +19,19 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, help_text='Name of the disk', max_length=100, verbose_name='name')), ('name', models.CharField(blank=True, help_text='Name of the disk', max_length=100, verbose_name='name')),
('remote_ID', models.CharField(help_text='ID, which helps access the disk', max_length=40, unique=True, verbose_name='remote_ID')), ('remote_id', models.CharField(help_text='ID, which helps access the disk', max_length=40, unique=True, verbose_name='remote_ID')),
],
),
migrations.CreateModel(
name='Image',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Human readable name of image.', max_length=100, verbose_name='name')),
('description', models.TextField(blank=True, help_text='Description of the image.', verbose_name='description')),
('remote_id', models.CharField(help_text='ID, which helps access the image.', max_length=40, unique=True, verbose_name='remote_ID')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Date, when the image created.')),
('uploaded_by_user', models.BooleanField(default=True, editable=False, help_text='The field is false if the image created from instance')),
('created_by', models.ForeignKey(help_text='The user, who create the image', on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_images', to=settings.AUTH_USER_MODEL)),
], ],
), ),
] ]
# Generated by Django 2.2.3 on 2019-07-15 13:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('image', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Image',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Human readable name of image.', max_length=100, verbose_name='name')),
('description', models.TextField(blank=True, help_text='Description of the image.', verbose_name='description')),
('remote_ID', models.CharField(help_text='ID, which helps access the image.', max_length=40, unique=True, verbose_name='remote_ID')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Date, when the image created.')),
],
),
]
# Generated by Django 2.2.3 on 2019-07-16 11:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('image', '0002_image'),
]
operations = [
migrations.RenameField(
model_name='disk',
old_name='remote_ID',
new_name='remote_id',
),
migrations.RenameField(
model_name='image',
old_name='remote_ID',
new_name='remote_id',
),
migrations.AddField(
model_name='image',
name='created_by',
field=models.ForeignKey(default=0, help_text='The user, who create the image', on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_images', to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.AddField(
model_name='image',
name='uploaded_by_user',
field=models.BooleanField(default=True, editable=False, help_text='The field is false if the image created from instance'),
),
]
from django.test import TestCase from django.test import TestCase
"""
from django.contrib.auth.models import User from django.contrib.auth.models import User
from unittest.mock import Mock from unittest.mock import Mock
from unittest.mock import patch from unittest.mock import patch
...@@ -11,10 +12,16 @@ from instance.models import Lease ...@@ -11,10 +12,16 @@ from instance.models import Lease
from interface_openstack.interface.image.image import Image as RemoteImage from interface_openstack.interface.image.image import Image as RemoteImage
from interface_openstack.interface.vm.resources import Flavor as RemoteFlavor from interface_openstack.interface.vm.resources import Flavor as RemoteFlavor
from template.models import ImageTemplate from template.models import ImageTemplate
"""
class ImageModelsTest(TestCase): class ImageModelsTest(TestCase):
@staticmethod # Need review and rewrite because multiple changes in other modules
# (Btw unit tests should not depend on other modules)
pass
""" @staticmethod
def create_user(name="test_user", email="test_email", password="test_password"): def create_user(name="test_user", email="test_email", password="test_password"):
return User.objects.create_user(username=name, email=email, password=password) return User.objects.create_user(username=name, email=email, password=password)
...@@ -29,11 +36,11 @@ class ImageModelsTest(TestCase): ...@@ -29,11 +36,11 @@ class ImageModelsTest(TestCase):
def create_instance(self): def create_instance(self):
params = {"name": "test_instance", params = {"name": "test_instance",
"description": "test instance description", "description": "test instance description",
"access_method": "ssh", "access_protocol": "SSH",
"system": "test_system", "system": "LINUX",
"distro": "test_distro"
"time_of_suspend": "2019-05-28 23:59", "time_of_suspend": "2019-05-28 23:59",
"time_of_delete": "2019-05-28 23:59", "time_of_delete": "2019-05-28 23:59",
} }
flavor = self.create_flavor() flavor = self.create_flavor()
lease = Lease.objects.create(name="test_lease", description="test lease description", lease = Lease.objects.create(name="test_lease", description="test lease description",
...@@ -91,3 +98,4 @@ class ImageModelsTest(TestCase): ...@@ -91,3 +98,4 @@ class ImageModelsTest(TestCase):
self.assertEqual(image.created_by_id, user.id) self.assertEqual(image.created_by_id, user.id)
self.assertEqual(image.uploaded_by_user, True) self.assertEqual(image.uploaded_by_user, True)
self.assertEqual(image.description, description) self.assertEqual(image.description, description)
"""
# Generated by Django 2.2 on 2019-04-17 14:01 # Generated by Django 3.0.4 on 2020-04-06 12:09
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
...@@ -8,15 +10,67 @@ class Migration(migrations.Migration): ...@@ -8,15 +10,67 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('image', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Instance', name='Flavor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=200)),
('remote_id', models.CharField(help_text='ID of the instance on the backend', max_length=100)),
('ram', models.PositiveIntegerField(default=0)),
('vcpu', models.PositiveIntegerField(default=0)),
('initial_disk', models.PositiveIntegerField(default=0)),
('priority', models.PositiveIntegerField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='Lease',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=100)),
('suspend_interval_in_sec', models.IntegerField(default=3600)),
('delete_interval_in_sec', models.IntegerField(default=7200)),
],
),
migrations.CreateModel(
name='BaseMachineDescriptor',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, help_text='Human readable name of instance.', max_length=100, verbose_name='name')), ('name', models.CharField(help_text='Human readable name of template.', max_length=100, verbose_name='name')),
('description', models.TextField(blank=True, help_text='The description of the instance.', verbose_name='description')), ('description', models.TextField(blank=True, help_text='Description of the template.', verbose_name='description')),
('system_type', models.CharField(choices=[('LINUX', 'The system based on Linux'), ('WINDOWS', 'Windows based system')], help_text='The base name of the operating system', max_length=50, verbose_name='operating_system')),
('distro', models.CharField(blank=True, help_text='The specific name and version of the installed OSe. g. Win 7, Ubuntu 18.04', max_length=100, verbose_name='system distribution')),
('access_protocol', models.CharField(choices=[('rdp', 'Remote Desktop Protocol'), ('ssh', 'Secure Shell')], help_text='The protocol which used to connect to the machinethat created from this template', max_length=50, verbose_name='remote_access_protocol')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Date, when the template created.')),
('network_id', models.CharField(blank=True, help_text='The new instance will be in this network.', max_length=100, null=True, verbose_name='network_id')),
('created_by', models.ForeignKey(help_text='The user, who create the template', on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_templates', to=settings.AUTH_USER_MODEL)),
('flavor', models.ForeignKey(help_text='Resources given to the vm', on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='instance.Flavor', verbose_name='flavor')),
('lease', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='instance.Lease')),
],
),
migrations.CreateModel(
name='Instance',
fields=[
('basemachinedescriptor_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='instance.BaseMachineDescriptor')),
('remote_id', models.CharField(help_text='ID of the instance on the backend', max_length=100)),
('password', models.CharField(help_text='Original password of the instance', max_length=50)),
('time_of_suspend', models.DateTimeField(blank=True, help_text='After this point in time, the instance will be suspended')),
('time_of_delete', models.DateTimeField(blank=True, help_text='After this point in time, the instance will be deleted!')),
('status', models.CharField(default='NO_STATE', max_length=50, verbose_name='instance_status')),
('deleted', models.BooleanField(default=False, help_text='Indicates if the instance is ready for garbage collection')),
('disks', models.ManyToManyField(help_text='Disks attached to instance', related_name='instance', to='image.Disk', verbose_name='disks')),
('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
options={
'permissions': (('create_instance', 'Can create a new VM.'), ('create_template_from_instance', 'Can create template from instance.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.')),
'default_permissions': (),
},
bases=('instance.basemachinedescriptor',),
), ),
] ]
# Generated by Django 2.2 on 2019-05-02 13:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='instance',
name='access_method',
field=models.CharField(choices=[('rdp', 'Remote Desktop Protocol'), ('ssh', 'Secure Shell')], default='rdp', help_text='Primary remote access method.', max_length=10),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='lease',
field=models.CharField(choices=[('project', 'Project'), ('server', 'Server'), ('infinite', 'Infinite lease')], default='project', help_text='Expiration method', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='password',
field=models.CharField(default=12345678, help_text='Original password of the instance.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='system',
field=models.CharField(default='windows', help_text='Operating system type', max_length=50),
preserve_default=False,
),
migrations.AlterField(
model_name='instance',
name='description',
field=models.TextField(blank=True, help_text='The description of the instance'),
),
migrations.AlterField(
model_name='instance',
name='name',
field=models.CharField(help_text='Human readable name of instance', max_length=100),
),
]
# Generated by Django 2.2.4 on 2019-08-08 10:41 # Generated by Django 3.0.4 on 2020-04-06 12:09
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
('template', '0007_basetemplate_network_id'), ('template', '0001_initial'),
('instance', '0009_auto_20190715_1354'), ('instance', '0001_initial'),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='instance', model_name='instance',
name='template', name='template',
field=models.ForeignKey(help_text='The base image of the vm', null=True, on_delete='DO_NOTHING', related_name='vm', to='template.ImageTemplate'), field=models.ForeignKey(help_text='The base image of the vm', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='vm', to='template.ImageTemplate'),
), ),
] ]
# Generated by Django 2.2.1 on 2019-05-10 13:07
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('instance', '0002_auto_20190502_1341'),
]
operations = [
migrations.AddField(
model_name='instance',
name='deleted',
field=models.BooleanField(default=False, help_text='Indicates if the instance is ready for garbage collection'),
),
migrations.AddField(
model_name='instance',
name='time_of_delete',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='After this point in time, the instance will be deleted!'),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='time_of_suspend',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='After this point in time, the instance will be suspended'),
preserve_default=False,
),
migrations.AlterField(
model_name='instance',
name='access_method',
field=models.CharField(choices=[('rdp', 'Remote Desktop Protocol'), ('ssh', 'Secure Shell')], help_text='Primary remote access method', max_length=10),
),
migrations.AlterField(
model_name='instance',
name='lease',
field=models.CharField(choices=[('shortlab', 'Short laboratory'), ('lab', 'Laboratory'), ('project', 'Project')], help_text='Expiration method', max_length=50),
),
migrations.AlterField(
model_name='instance',
name='password',
field=models.CharField(help_text='Original password of the instance', max_length=50),
),
]
# Generated by Django 2.2.1 on 2019-07-04 12:56 # Generated by Django 3.0.4 on 2020-04-08 13:03
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('instance', '0006_auto_20190704_1154'), ('image', '0001_initial'),
('instance', '0002_instance_template'),
] ]
operations = [ operations = [
migrations.AlterField( migrations.RemoveField(
model_name='instance', model_name='instance',
name='owner', name='owner',
field=models.ForeignKey(null=True, on_delete='CASCADE', to=settings.AUTH_USER_MODEL), ),
migrations.AlterField(
model_name='instance',
name='disks',
field=models.ManyToManyField(blank=True, help_text='Disks attached to instance', related_name='instance', to='image.Disk', verbose_name='disks'),
), ),
] ]
# Generated by Django 2.2.1 on 2019-05-13 12:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0003_auto_20190510_1307'),
]
operations = [
migrations.AddField(
model_name='instance',
name='remoteId',
field=models.CharField(default=0, help_text='ID of the instance on the backend', max_length=100),
preserve_default=False,
),
]
# Generated by Django 2.2.1 on 2019-05-13 12:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0004_instance_remoteid'),
]
operations = [
migrations.RenameField(
model_name='instance',
old_name='remoteId',
new_name='remote_id',
),
]
# Generated by Django 2.2.1 on 2019-07-04 11:54
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('image', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instance', '0005_auto_20190513_1203'),
]
operations = [
migrations.CreateModel(
name='Flavor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=100)),
('description', models.CharField(blank=True, max_length=200)),
('remote_id', models.CharField(help_text='ID of the instance on the backend', max_length=100)),
('ram', models.IntegerField(blank=True, null=True)),
('vcpu', models.IntegerField(blank=True, null=True)),
('initial_disk', models.IntegerField(blank=True, null=True)),
('priority', models.IntegerField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='Lease',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=100)),
('description', models.CharField(blank=True, max_length=100)),
('suspend_interval_in_sec', models.IntegerField(blank=True, null=True)),
('delete_interval_in_sec', models.IntegerField(blank=True, null=True)),
],
),
migrations.AddField(
model_name='instance',
name='disks',
field=models.ManyToManyField(help_text='Disks attached to instance', related_name='instance', to='image.Disk', verbose_name='disks'),
),
migrations.AddField(
model_name='instance',
name='owner',
field=models.ForeignKey(default=None, on_delete='CASCADE', to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.AlterField(
model_name='instance',
name='lease',
field=models.ForeignKey(on_delete='CASCADE', to='instance.Lease'),
),
migrations.AddField(
model_name='instance',
name='flavor',
field=models.ForeignKey(default=None, help_text='Reasources given to the vm', on_delete='CASCADE', to='instance.Flavor', verbose_name='flavor'),
preserve_default=False,
),
]
# Generated by Django 2.2.1 on 2019-07-04 13:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0007_auto_20190704_1256'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='time_of_delete',
field=models.DateTimeField(blank=True, help_text='After this point in time, the instance will be deleted!'),
),
migrations.AlterField(
model_name='instance',
name='time_of_suspend',
field=models.DateTimeField(blank=True, help_text='After this point in time, the instance will be suspended'),
),
]
# Generated by Django 2.2.3 on 2019-07-15 13:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0008_auto_20190704_1310'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='flavor',
field=models.ForeignKey(help_text='Reasources given to the vm', on_delete='CASCADE', related_name='instances', to='instance.Flavor', verbose_name='flavor'),
),
migrations.AlterField(
model_name='instance',
name='lease',
field=models.ForeignKey(on_delete='CASCADE', related_name='instances', to='instance.Lease'),
),
]
# Generated by Django 2.2.4 on 2019-08-08 11:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0010_instance_template'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'permissions': (('create_instance', 'Can create a new VM.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
# Generated by Django 2.2.4 on 2019-08-29 07:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0011_auto_20190808_1137'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'default_permissions': (), 'permissions': (('create_instance', 'Can create a new VM.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
# Generated by Django 2.2.4 on 2019-08-30 12:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0012_auto_20190829_0754'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'default_permissions': (), 'permissions': (('create_instance', 'Can create a new VM.'), ('create_template_from_instance', 'Can create template from instance.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
...@@ -11,10 +11,6 @@ import logging ...@@ -11,10 +11,6 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ACCESS_METHODS = tuple(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
)
interface = OSVirtualMachineManager(settings.CONNECTION) interface = OSVirtualMachineManager(settings.CONNECTION)
ACTIONS = { ACTIONS = {
...@@ -32,25 +28,25 @@ class Lease(models.Model): ...@@ -32,25 +28,25 @@ class Lease(models.Model):
After suspend interval the vm suspends and after delete it will be After suspend interval the vm suspends and after delete it will be
destroyed destroyed
""" """
name = models.CharField(blank=True, max_length=100) name = models.CharField(max_length=100)
description = models.CharField(blank=True, max_length=100) description = models.CharField(blank=True, max_length=100)
suspend_interval_in_sec = models.IntegerField(blank=True, null=True) suspend_interval_in_sec = models.IntegerField(default=3600)
delete_interval_in_sec = models.IntegerField(blank=True, null=True) delete_interval_in_sec = models.IntegerField(default=7200)
class Flavor(models.Model): class Flavor(models.Model):
""" Flavors are reasource packages, contains all information about """ Flavors are reasource packages, contains all information about
resources accociated with the virtual machine resources accociated with the virtual machine
""" """
name = models.CharField(blank=True, max_length=100) name = models.CharField(blank=False, max_length=100)
description = models.CharField(blank=True, max_length=200) description = models.CharField(blank=True, max_length=200)
remote_id = models.CharField( remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend" max_length=100, help_text="ID of the instance on the backend"
) )
ram = models.IntegerField(blank=True, null=True) ram = models.PositiveIntegerField(default=0)
vcpu = models.IntegerField(blank=True, null=True) vcpu = models.PositiveIntegerField(default=0)
initial_disk = models.IntegerField(blank=True, null=True) initial_disk = models.PositiveIntegerField(default=0)
priority = models.IntegerField(blank=True, null=True) priority = models.PositiveIntegerField(blank=True, null=True)
@classmethod @classmethod
def create(cls, name, description, ram=0, vcpu=0, def create(cls, name, description, ram=0, vcpu=0,
...@@ -68,7 +64,6 @@ class Flavor(models.Model): ...@@ -68,7 +64,6 @@ class Flavor(models.Model):
raise ValueError("Can't create Flavor in remote cloud.") raise ValueError("Can't create Flavor in remote cloud.")
def delete(self): def delete(self):
interface = OSVirtualMachineManager(settings.CONNECTION)
try: try:
interface.delete_flavor(self.remote_id) interface.delete_flavor(self.remote_id)
super(Flavor, self).delete() super(Flavor, self).delete()
...@@ -77,7 +72,63 @@ class Flavor(models.Model): ...@@ -77,7 +72,63 @@ class Flavor(models.Model):
logger.error("Can not delete the flavor in remote cloud") logger.error("Can not delete the flavor in remote cloud")
class Instance(models.Model): class BaseMachineDescriptor(models.Model):
"""Virtual machine template.
"""
OSTypes = (
('LINUX', 'The system based on Linux'),
('WINDOWS', 'Windows based system'),
)
CONN_PROTOCOL = tuple(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
)
name = models.CharField(max_length=100,
verbose_name="name",
help_text="Human readable name of template.")
description = models.TextField(verbose_name="description",
blank=True,
help_text="Description of the template.")
system_type = models.CharField(max_length=50,
choices=OSTypes,
verbose_name="operating_system",
help_text="The base name of the operating system"
)
distro = models.CharField(max_length=100,
blank=True,
verbose_name='system distribution',
help_text="The specific name and version of the installed OS"
"e. g. Win 7, Ubuntu 18.04"
)
access_protocol = models.CharField(max_length=50,
choices=CONN_PROTOCOL,
verbose_name="remote_access_protocol",
help_text="The protocol which used to connect to the machine"
"that created from this template"
)
created_at = models.DateTimeField(auto_now_add=True,
editable=False,
help_text="Date, when the template created.")
created_by = models.ForeignKey(User,
on_delete=models.DO_NOTHING,
related_name="created_templates",
help_text="The user, who create the template")
flavor = models.ForeignKey(Flavor,
help_text="Resources given to the vm",
verbose_name="flavor",
on_delete=models.CASCADE,
related_name='templates')
lease = models.ForeignKey(Lease,
on_delete=models.CASCADE,
related_name='templates')
network_id = models.CharField(max_length=100,
verbose_name="network_id",
help_text="The new instance will be in this network.",
null=True,
blank=True)
class Instance(BaseMachineDescriptor):
"""Virtual machine instance. """Virtual machine instance.
""" """
from template.models import ImageTemplate from template.models import ImageTemplate
...@@ -95,21 +146,9 @@ class Instance(models.Model): ...@@ -95,21 +146,9 @@ class Instance(models.Model):
('manage_access', 'Can manage access rights for the VM.'), ('manage_access', 'Can manage access rights for the VM.'),
('config_ports', 'Can configure port forwards.'), ('config_ports', 'Can configure port forwards.'),
) )
name = models.CharField(max_length=100,
help_text="Human readable name of instance")
remote_id = models.CharField( remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend" max_length=100, help_text="ID of the instance on the backend"
) )
description = models.TextField(
blank=True, help_text="The description of the instance"
)
access_method = models.CharField(
max_length=10, choices=ACCESS_METHODS,
help_text="Primary remote access method"
)
system = models.CharField(max_length=50, help_text="Operating system type")
password = models.CharField( password = models.CharField(
max_length=50, help_text="Original password of the instance" max_length=50, help_text="Original password of the instance"
) )
...@@ -121,6 +160,7 @@ class Instance(models.Model): ...@@ -121,6 +160,7 @@ class Instance(models.Model):
blank=True, blank=True,
help_text="After this point in time, the instance will be deleted!" help_text="After this point in time, the instance will be deleted!"
) )
status = models.CharField(max_length=50, verbose_name="instance_status", default="NO_STATE")
deleted = models.BooleanField( deleted = models.BooleanField(
help_text="Indicates if the instance is ready for garbage collection", help_text="Indicates if the instance is ready for garbage collection",
default=False, default=False,
...@@ -128,24 +168,20 @@ class Instance(models.Model): ...@@ -128,24 +168,20 @@ class Instance(models.Model):
template = models.ForeignKey(ImageTemplate, related_name="vm", null=True, template = models.ForeignKey(ImageTemplate, related_name="vm", null=True,
help_text="The base image of the vm", help_text="The base image of the vm",
on_delete="DO_NOTHING") on_delete=models.DO_NOTHING)
disks = models.ManyToManyField(Disk, related_name="instance", disks = models.ManyToManyField(Disk, related_name="instance",
blank=True,
help_text="Disks attached to instance", help_text="Disks attached to instance",
verbose_name="disks") verbose_name="disks")
owner = models.ForeignKey(User, on_delete="CASCADE", null=True)
flavor = models.ForeignKey(Flavor, help_text="Reasources given to the vm",
verbose_name="flavor", on_delete="CASCADE",
related_name='instances')
lease = models.ForeignKey(Lease, on_delete="CASCADE",
related_name='instances')
@classmethod @classmethod
def create(cls, lease, owner, flavor, template, remote_id, params): def create(cls, lease, owner, flavor, template, remote_id, params):
params["password"] = cls.generate_password() params["password"] = cls.generate_password()
inst = cls(lease=lease, flavor=flavor, owner=owner, inst = cls(lease=lease, flavor=flavor, created_by=owner, system_type=template.system_type,
remote_id=remote_id, template=template, **params) distro=template.distro, access_protocol=template.access_protocol,
remote_id=remote_id, template=template, status="CREATING",
**params)
inst.full_clean() inst.full_clean()
inst.save() inst.save()
...@@ -155,8 +191,7 @@ class Instance(models.Model): ...@@ -155,8 +191,7 @@ class Instance(models.Model):
@classmethod @classmethod
def create_instance_from_template(cls, params, template, owner, lease, def create_instance_from_template(cls, params, template, owner, lease,
disks, networks, flavor): networks, flavor):
# TODO: attach disks when the remote instance created
try: try:
remote_id = interface.create_vm_from_template(params["name"], remote_id = interface.create_vm_from_template(params["name"],
template.image.remote_id, template.image.remote_id,
...@@ -214,12 +249,21 @@ class Instance(models.Model): ...@@ -214,12 +249,21 @@ class Instance(models.Model):
def get_remote_instance(self): def get_remote_instance(self):
return interface.get_vm(self.remote_id) return interface.get_vm(self.remote_id)
def update_status(self):
remote = self.get_remote_instance()
self.status = remote.status
self.save()
@classmethod @classmethod
def generate_password(self): def generate_password(self):
return User.objects.make_random_password( return User.objects.make_random_password(
allowed_chars='abcdefghijklmnopqrstuvwx' allowed_chars='abcdefghijklmnopqrstuvwx'
'ABCDEFGHIJKLMNOPQRSTUVWX123456789') 'ABCDEFGHIJKLMNOPQRSTUVWX123456789')
def reset_password(self):
self.password = self.generate_password()
self.save()
def change_name(self, new_name): def change_name(self, new_name):
self.name = new_name self.name = new_name
self.save() self.save()
...@@ -227,3 +271,7 @@ class Instance(models.Model): ...@@ -227,3 +271,7 @@ class Instance(models.Model):
def change_description(self, new_description): def change_description(self, new_description):
self.description = new_description self.description = new_description
self.save() self.save()
def destroy(self):
self.deleted = True
self.save()
...@@ -2,26 +2,6 @@ from rest_framework import serializers ...@@ -2,26 +2,6 @@ from rest_framework import serializers
from .models import Flavor, Instance, Lease from .models import Flavor, Instance, Lease
class InstanceSerializer(serializers.ModelSerializer):
lease = serializers.PrimaryKeyRelatedField(read_only=True)
flavor = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Instance
fields = (
"id",
"name",
"description",
"system",
"lease",
"flavor",
"password",
"template",
"time_of_suspend",
"time_of_delete")
read_only_fields = ("id", "password", "template", "time_of_suspend", "time_of_delete")
class FlavorSerializer(serializers.ModelSerializer): class FlavorSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Flavor model = Flavor
...@@ -31,4 +11,23 @@ class FlavorSerializer(serializers.ModelSerializer): ...@@ -31,4 +11,23 @@ class FlavorSerializer(serializers.ModelSerializer):
class LeaseSerializer(serializers.ModelSerializer): class LeaseSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Lease model = Lease
fields = '__all__' fields = "__all__"
class InstanceListItemSerializer(serializers.ModelSerializer):
lease = LeaseSerializer(read_only=True)
class Meta:
model = Instance
fields = ["id", "name", "status", "system_type", "distro", "status", "lease"]
class InstanceSerializer(serializers.ModelSerializer):
lease = serializers.PrimaryKeyRelatedField(read_only=True)
flavor = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Instance
fields = "__all__"
read_only_fields = ("id", "status" "password", "template",
"time_of_suspend", "time_of_delete")
...@@ -143,8 +143,8 @@ STATIC_URL = "/static/" ...@@ -143,8 +143,8 @@ STATIC_URL = "/static/"
# VM ACCESS PROTOCOLS # VM ACCESS PROTOCOLS
VM_ACCESS_PROTOCOLS = { VM_ACCESS_PROTOCOLS = {
"rdp": ["Remote Desktop Protocol", 3389, "tcp"], "RDP": ["Remote Desktop Protocol", 3389, "tcp"],
"ssh": ["Secure Shell", 22, "tcp"], "SSH": ["Secure Shell", 22, "tcp"],
} }
# LEASE TYPES # LEASE TYPES
......
# Generated by Django 2.2.3 on 2019-07-03 12:09 # Generated by Django 3.0.4 on 2020-04-06 12:09
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
...@@ -9,26 +9,18 @@ class Migration(migrations.Migration): ...@@ -9,26 +9,18 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('instance', '0001_initial'),
('image', '0001_initial'), ('image', '0001_initial'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='BaseTemplate', name='ImageTemplate',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('basemachinedescriptor_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='instance.BaseMachineDescriptor')),
('name', models.CharField(help_text='Human readable name of template.', max_length=100, verbose_name='name')), ('type', models.CharField(choices=[('SYSTEM', 'Common templates provided by the system.'), ('IMAGE', 'Template created from image by a user'), ('INSTANCE', 'Template created from instance by a user')], default='SYSTEM', max_length=10)),
('description', models.TextField(blank=True, help_text='Description of the template.', verbose_name='description')), ('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='image.Image')),
('remote_ID', models.CharField(help_text='ID, which helps access the template.', max_length=40, unique=True, verbose_name='remote_ID')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Date, when the template created.')),
], ],
), bases=('instance.basemachinedescriptor',),
migrations.CreateModel(
name='Template',
fields=[
('basetemplate_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='template.BaseTemplate')),
('disk', models.ForeignKey(help_text='The disk where the template is located.', on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='image.Disk')),
],
bases=('template.basetemplate',),
), ),
] ]
# Generated by Django 2.2.3 on 2019-07-15 13:54
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('image', '0002_image'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('template', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='basetemplate',
name='created_by',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_templates', to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.CreateModel(
name='PureTemplate',
fields=[
('basetemplate_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='template.BaseTemplate')),
('images', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='image.Image')),
],
bases=('template.basetemplate',),
),
]
# Generated by Django 2.2.3 on 2019-07-16 11:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('image', '0003_auto_20190716_1151'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('template', '0002_auto_20190715_1354'),
]
operations = [
migrations.RenameModel(
old_name='Template',
new_name='DiskTemplate',
),
migrations.RenameModel(
old_name='PureTemplate',
new_name='ImageTemplate',
),
migrations.RemoveField(
model_name='basetemplate',
name='remote_ID',
),
migrations.AlterField(
model_name='basetemplate',
name='created_by',
field=models.ForeignKey(help_text='The user, who create the template', on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_templates', to=settings.AUTH_USER_MODEL),
),
]
# Generated by Django 2.2.3 on 2019-07-16 12:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('template', '0003_auto_20190716_1151'),
]
operations = [
migrations.RenameField(
model_name='imagetemplate',
old_name='images',
new_name='image',
),
]
# Generated by Django 2.2.3 on 2019-07-17 09:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0009_auto_20190715_1354'),
('template', '0004_auto_20190716_1227'),
]
operations = [
migrations.AddField(
model_name='basetemplate',
name='flavor',
field=models.ForeignKey(default=1, help_text='Reasources given to the vm', on_delete='CASCADE', related_name='templates', to='instance.Flavor', verbose_name='flavor'),
preserve_default=False,
),
migrations.AddField(
model_name='basetemplate',
name='lease',
field=models.ForeignKey(default=1, on_delete='CASCADE', related_name='templates', to='instance.Lease'),
preserve_default=False,
),
migrations.AddField(
model_name='imagetemplate',
name='type',
field=models.CharField(choices=[('U', 'User create the template from image'), ('I', 'Template created from instance'), ('D', 'Default "Pure" template')], default='I', max_length=10),
preserve_default=False,
),
]
# Generated by Django 2.2.3 on 2019-07-19 14:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('template', '0005_auto_20190717_0948'),
]
operations = [
migrations.AlterField(
model_name='imagetemplate',
name='type',
field=models.CharField(choices=[('U', 'User create the template from image'), ('I', 'Template created from instance'), ('P', '"Pure" template')], default='U', max_length=10),
),
]
# Generated by Django 2.2.3 on 2019-08-07 12:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('template', '0006_auto_20190719_1416'),
]
operations = [
migrations.AddField(
model_name='basetemplate',
name='network_id',
field=models.CharField(blank=True, help_text='The new instance will be in this network.', max_length=100, null=True, verbose_name='network_id'),
),
]
from django.db import models from django.db import models
from django.contrib.auth.models import User from image.models import Image
from django.conf import settings from instance.models import BaseMachineDescriptor
from image.models import Disk, Image
from instance.models import Lease, Flavor
from interface_openstack.implementation.storage.openstack_snapshot_manager import SnapshotManager
class ImageTemplate(BaseMachineDescriptor):
class BaseTemplate(models.Model):
"""Virtual machine template.
"""
name = models.CharField(max_length=100,
verbose_name="name",
help_text="Human readable name of template.")
description = models.TextField(verbose_name="description",
blank=True,
help_text="Description of the template.")
created_at = models.DateTimeField(auto_now_add=True,
editable=False,
help_text="Date, when the template created.")
created_by = models.ForeignKey(User,
on_delete=models.DO_NOTHING,
related_name="created_templates",
help_text="The user, who create the template")
flavor = models.ForeignKey(Flavor,
help_text="Resources given to the vm",
verbose_name="flavor",
on_delete="CASCADE",
related_name='templates')
lease = models.ForeignKey(Lease,
on_delete="CASCADE",
related_name='templates')
network_id = models.CharField(max_length=100,
verbose_name="network_id",
help_text="The new instance will be in this network.",
null=True,
blank=True)
class DiskTemplate(BaseTemplate):
disk = models.ForeignKey(Disk,
related_name="templates",
on_delete=models.CASCADE,
help_text="The disk where the template is located.")
@classmethod
def create_from_volume(cls, name, description, disk, user):
interface = SnapshotManager(settings.CONNECTION)
remote_template = interface.create_from_volume(disk.remote_id)
remote_id = remote_template.id
new_template = DiskTemplate.objects.create(name=name,
description=description,
disk=disk,
remote_id=remote_id,
created_by=user)
return new_template
class ImageTemplate(BaseTemplate):
TYPES = ( TYPES = (
('U', 'User create the template from image'), ('SYSTEM', 'Common templates provided by the system.'),
('I', 'Template created from instance'), ('IMAGE', 'Template created from image by a user'),
('P', '"Pure" template'), ('INSTANCE', 'Template created from instance by a user'),
) )
image = models.ForeignKey(Image, image = models.ForeignKey(Image,
related_name="templates", related_name="templates",
...@@ -70,7 +15,7 @@ class ImageTemplate(BaseTemplate): ...@@ -70,7 +15,7 @@ class ImageTemplate(BaseTemplate):
help_text="") help_text="")
type = models.CharField(max_length=10, type = models.CharField(max_length=10,
choices=TYPES, choices=TYPES,
default="U") default="SYSTEM")
@classmethod @classmethod
def create_from_instance(cls, name, description, instance, user): def create_from_instance(cls, name, description, instance, user):
...@@ -79,7 +24,10 @@ class ImageTemplate(BaseTemplate): ...@@ -79,7 +24,10 @@ class ImageTemplate(BaseTemplate):
description=description, description=description,
created_by=user, created_by=user,
image=image, image=image,
system_type=instance.system_type,
distro=instance.distro,
access_protocol=instance.access_protocol,
lease=instance.lease, lease=instance.lease,
flavor=instance.flavor, flavor=instance.flavor,
type="I") type="INSTANCE")
return new_template return new_template
from rest_framework import serializers from rest_framework import serializers
from template.models import ImageTemplate from template.models import ImageTemplate
from instance.serializers import FlavorSerializer, LeaseSerializer
class ImageTemplateListItemSerializer(serializers.ModelSerializer):
flavor = FlavorSerializer(read_only=True)
lease = LeaseSerializer(read_only=True)
class Meta:
model = ImageTemplate
fields = ["id", "name", "distro", "flavor", "lease", "system_type"]
read_only_fields = ("id", "name", "distro", "flavor", "lease", "system_type")
class InstanceFromTemplateSerializer(serializers.Serializer): class InstanceFromTemplateSerializer(serializers.Serializer):
...@@ -11,17 +22,7 @@ class InstanceFromTemplateSerializer(serializers.Serializer): ...@@ -11,17 +22,7 @@ class InstanceFromTemplateSerializer(serializers.Serializer):
class ImageTemplateModelSerializer(serializers.ModelSerializer): class ImageTemplateModelSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ImageTemplate model = ImageTemplate
fields = ( fields = "__all__"
"id",
"name",
"description",
"created_at",
"created_by",
"image",
"flavor",
"lease",
"type",
)
read_only_fields = ( read_only_fields = (
"created_at", "created_at",
"created_by", "created_by",
......
...@@ -3,6 +3,6 @@ from rest_framework import routers ...@@ -3,6 +3,6 @@ from rest_framework import routers
from template import views from template import views
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r"templates/image-templates", views.ImageTemplateViewSet, basename="image-template") router.register(r"templates", views.ImageTemplateViewSet, basename="image-template")
urlpatterns = router.urls urlpatterns = router.urls
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from template.serializers import ImageTemplateModelSerializer from template.serializers import ImageTemplateModelSerializer, ImageTemplateListItemSerializer
from template.models import ImageTemplate from template.models import ImageTemplate
from django.db.models import Q
from rest_framework.permissions import IsAuthenticated
class ImageTemplateViewSet(ModelViewSet): class ImageTemplateViewSet(ModelViewSet):
serializer_class = ImageTemplateModelSerializer serializer_class = ImageTemplateModelSerializer
queryset = ImageTemplate.objects.all() queryset = ImageTemplate.objects.all()
permission_classes = [IsAuthenticated]
def create(self, request): def create(self, request):
# TODO: Check the permissions to create template and using the flavors and leases
serializer = ImageTemplateModelSerializer(data=request.data) serializer = ImageTemplateModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
data = serializer.validated_data data = serializer.validated_data
new_template = ImageTemplate.create( new_template = ImageTemplate(
name=data["name"], name=data["name"],
description=data["description"], description=data["description"],
access_protocol=data["access_protocol"],
system_type=data["system_type"],
distro=data["distro"],
created_by=request.user, created_by=request.user,
image=data["image"], image=data["image"],
lease=data["lease"], lease=data["lease"],
flavor=data["flavor"], flavor=data["flavor"],
type="U" type="IMAGE"
) )
new_template.save()
serializer = ImageTemplateModelSerializer(instance=new_template) serializer = ImageTemplateModelSerializer(instance=new_template)
return Response(serializer.data) return Response(serializer.data)
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
# only the name, description, lease, flavor can be updated # only the name, description, lease, flavor can be updated
allowed_keys = ["name", "description", "lease", "flavor"] allowed_keys = ["name", "description", "access_prtotocol", "distro", "lease", "flavor"]
# delete not allowed key # delete not allowed key
for key in request.data.keys(): for key in request.data.keys():
...@@ -35,3 +43,9 @@ class ImageTemplateViewSet(ModelViewSet): ...@@ -35,3 +43,9 @@ class ImageTemplateViewSet(ModelViewSet):
request.data.pop(key, None) request.data.pop(key, None)
return super(ImageTemplateViewSet, self).update(request, partial=True) return super(ImageTemplateViewSet, self).update(request, partial=True)
def list(self, request, *args, **kwargs):
# TODO: Filter for shared tempaltes
query = ImageTemplate.objects.filter(Q(created_by=request.user.id) | Q(type="SYSTEM"))
serializer = ImageTemplateListItemSerializer(query, many=True)
return Response(serializer.data)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment