Commit ee5bcf00 by Belákovics Ádám

Merge branch 'instance_model' into 'DEV'

Instance model

See merge request !8
parents 9de39a2f 0870644f
Pipeline #779 failed with stages
in 3 minutes 22 seconds
.vscode/ .vscode/
recircle/db.sqlite3 .idea/
interface-openstack/ environment.sh
recircle/clouds.yaml
recircle/implementation/ # Byte-compiled / optimized / DLL files
recircle/interface/ __pycache__/
recircle/interface_openstack/ *.py[cod]
environment.sh *$py.class
\ No newline at end of file
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
example.py
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don’t work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Cloud configure
clouds.yaml
# pyc files
*.pyc
...@@ -7,6 +7,7 @@ verify_ssl = true ...@@ -7,6 +7,7 @@ verify_ssl = true
httpie = "*" httpie = "*"
flake8 = "*" flake8 = "*"
django-rest-swagger = "*" django-rest-swagger = "*"
coverage = "*"
[packages] [packages]
django = "*" django = "*"
......
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "b00ba9882fe44a9e7e69f6b395f0790ba52f85f1e8ee2b116c599b4d6ce88401" "sha256": "2adbcb2f6437576c7ac5794c996bf94a73714555a6ccc0e393fcb24bf184c1da"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
...@@ -460,6 +460,43 @@ ...@@ -460,6 +460,43 @@
], ],
"version": "==0.0.4" "version": "==0.0.4"
}, },
"coverage": {
"hashes": [
"sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9",
"sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74",
"sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390",
"sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8",
"sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe",
"sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf",
"sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e",
"sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741",
"sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09",
"sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd",
"sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034",
"sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420",
"sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c",
"sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab",
"sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba",
"sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e",
"sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609",
"sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2",
"sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49",
"sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b",
"sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d",
"sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce",
"sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9",
"sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4",
"sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773",
"sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723",
"sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c",
"sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f",
"sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1",
"sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260",
"sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a"
],
"index": "pypi",
"version": "==4.5.3"
},
"django-rest-swagger": { "django-rest-swagger": {
"hashes": [ "hashes": [
"sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b", "sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b",
......
...@@ -3,7 +3,6 @@ from image.serializers import DiskSerializer ...@@ -3,7 +3,6 @@ from image.serializers import DiskSerializer
# from django.shortcuts import render # from django.shortcuts import render
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from interface_openstack.implementation.image.openstack_image_manager import ( from interface_openstack.implementation.image.openstack_image_manager import (
OpenstackImageManager, OpenstackImageManager,
) )
......
from django.contrib import admin from django.contrib import admin
from instance.models import Instance from instance.models import Instance, Flavor, Lease
admin.site.register(Instance) admin.site.register(Instance)
admin.site.register(Flavor)
admin.site.register(Lease)
# 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 12:56
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0006_auto_20190704_1154'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='owner',
field=models.ForeignKey(null=True, on_delete='CASCADE', to=settings.AUTH_USER_MODEL),
),
]
# 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'),
),
]
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from django.utils import timezone
from datetime import timedelta
from image.models import Disk
from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager
)
ACCESS_METHODS = tuple( ACCESS_METHODS = tuple(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()] [(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
) )
# Later stored in database
LEASE_TYPES = tuple( class Lease(models.Model):
[(val["name"], val["verbose_name"]) for val in settings.LEASE_TYPES] """ Users can use the virtual machine until the lease dates.
) After suspend interval the vm suspends and after delete it will be
destroyed
"""
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)
class Flavor(models.Model):
""" Flavors are reasource packages, contains all information about
resources accociated with the virtual machine
"""
name = models.CharField(blank=True, max_length=100)
description = models.CharField(blank=True, max_length=200)
remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend"
)
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)
@classmethod
def create(cls, name, description, ram=0, vcpu=0,
initial_disk=0, priority=0):
interface = OSVirtualMachineManager(settings.CONNECTION)
remote_flavor = interface.create_flavor(name, ram, vcpu, initial_disk)
flavor = cls(name=name, description=description,
remote_id=remote_flavor.id, ram=ram, vcpu=vcpu,
initial_disk=initial_disk, priority=priority)
flavor.save()
return flavor
class Instance(models.Model): class Instance(models.Model):
name = models.CharField(max_length=100, help_text="Human readable name of instance") """Virtual machine instance.
"""
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"
) )
...@@ -21,25 +66,79 @@ class Instance(models.Model): ...@@ -21,25 +66,79 @@ class Instance(models.Model):
blank=True, help_text="The description of the instance" blank=True, help_text="The description of the instance"
) )
access_method = models.CharField( access_method = models.CharField(
max_length=10, choices=ACCESS_METHODS, help_text="Primary remote access method" max_length=10, choices=ACCESS_METHODS,
help_text="Primary remote access method"
) )
system = models.CharField(max_length=50, help_text="Operating system type") 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"
) )
lease = models.CharField(
max_length=50, choices=LEASE_TYPES, help_text="Expiration method"
)
time_of_suspend = models.DateTimeField( time_of_suspend = models.DateTimeField(
blank=True,
help_text="After this point in time, the instance will be suspended" help_text="After this point in time, the instance will be suspended"
) )
time_of_delete = models.DateTimeField( time_of_delete = models.DateTimeField(
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!"
) )
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,
) )
# template
# disks # image = models.ForeignKey(TemplateImage, related_name="vm", null=True,
# owner # help_text="The base image of the vm")
#
# snapshot = models.ForeignKey(Snapshot, related_name="vm", null=True,
# help_text="The base snapshot of the vm")
disks = models.ManyToManyField(Disk, related_name="instance",
help_text="Disks attached to instance",
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
def create(cls, lease, owner, flavor, remote_id, params):
inst = cls(lease=lease, flavor=flavor, owner=owner,
remote_id=remote_id, **params)
inst.full_clean()
inst.save()
return inst
@classmethod
def create_instance_from_template(cls, params, template, owner, lease,
disks, networks, flavor):
# TODO: attach disks when the remote instance created
interface = OSVirtualMachineManager(settings.CONNECTION)
remote_inst = interface.create_vm_from_template(params["name"],
template.remote_ID,
flavor.remote_id,
networks,
)
remote_id = remote_inst.id
new_inst = cls.create(lease, owner, flavor, remote_id, params)
return new_inst
def clean(self, *args, **kwargs):
self.time_of_suspend, self.time_of_delete = self.get_renew_times()
super(Instance, self).clean(*args, **kwargs)
def get_renew_times(self, lease=None):
"""Returns new suspend and delete times if renew would be called.
"""
if lease is None:
lease = self.lease
return (
timezone.now() + timedelta(
seconds=lease.suspend_interval_in_sec),
timezone.now() + timedelta(
seconds=lease.delete_interval_in_sec)
)
from rest_framework import serializers from rest_framework import serializers
from .models import Flavor, Instance
from .models import Instance
class InstanceSerializer(serializers.ModelSerializer):
class InstanceSerializer(serializers.ModelSerializer): lease = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta: flavor = serializers.PrimaryKeyRelatedField(read_only=True)
model = Instance
fields = "__all__" class Meta:
model = Instance
fields = ("name", "description", "system", "lease", "flavor")
class FlavorSerializer(serializers.ModelSerializer):
class Meta:
model = Flavor
fields = "__all__"
from django.dispatch import receiver
from django.db.models.signals import pre_delete
from django.conf import settings
from .models import Instance
from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager
)
@receiver(pre_delete, sender=Instance)
def delete_remote_instance(sender, instance, **kwargs):
interface = OSVirtualMachineManager(settings.CONNECTION)
interface.destroy_vm(instance.remote_id)
...@@ -6,6 +6,7 @@ urlpatterns = [ ...@@ -6,6 +6,7 @@ urlpatterns = [
path("instances/", views.InstanceList.as_view()), path("instances/", views.InstanceList.as_view()),
path("instances/<int:pk>/", views.InstanceDetail.as_view()), path("instances/<int:pk>/", views.InstanceDetail.as_view()),
# path('instances/<int:pk>/action/', views.InstanceAction.as_view()) # path('instances/<int:pk>/action/', views.InstanceAction.as_view())
path("flavors/", views.FlavorListView.as_view()),
] ]
urlpatterns = format_suffix_patterns(urlpatterns) urlpatterns = format_suffix_patterns(urlpatterns)
from instance.models import Instance from instance.serializers import InstanceSerializer, FlavorSerializer
from instance.serializers import InstanceSerializer
from django.http import Http404 from django.http import Http404
from django.conf import settings
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
import datetime from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager
from interface_openstack.implementation.vm.instance import OSVirtualMachineManager )
from django.conf import settings from template.models import InstanceTemplate
from instance.models import Instance, Flavor, Lease
class InstanceList(APIView): class InstanceList(APIView):
...@@ -17,26 +18,23 @@ class InstanceList(APIView): ...@@ -17,26 +18,23 @@ class InstanceList(APIView):
def post(self, request, format=None): def post(self, request, format=None):
data = request.data data = request.data
interface = OSVirtualMachineManager(settings.CONNECTION) template = InstanceTemplate.objects.get(pk=data["template"])
imageId = "da51253f-867c-472d-8ce0-81e7b7126d60" flavor = Flavor.objects.get(pk=data["flavor"])
flavorId = "1" lease = Lease.objects.get(pk=data["lease"])
networks = [{"uuid": "c03d0d4b-413e-4cc6-9ebe-c0b5ca0dac3a"}]
newBackendInstance = interface.create_vm_from_template( newInstance = Instance.create_instance_from_template(
"Integration test vm 2", imageId, flavorId, networks params={"name": data["name"],
) "description": data["description"],
newInstance = Instance( "access_method": data["access"],
name=data["name"], "system": data["system"],
remote_id=newBackendInstance.id, },
description=data["description"], lease=lease,
access_method="ssh", networks=template.networks,
system=data["system"], template=template,
password="12345678", flavor=flavor,
lease=data["lease"], owner=request.user,
time_of_suspend=datetime.datetime.now(), disks=template.disks
time_of_delete=datetime.datetime.now(),
deleted=False,
) )
newInstance.save()
return Response(newInstance.pk) return Response(newInstance.pk)
...@@ -75,3 +73,24 @@ class InstanceDetail(APIView): ...@@ -75,3 +73,24 @@ class InstanceDetail(APIView):
instance = self.get_object(pk) instance = self.get_object(pk)
instance.delete() instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class FlavorListView(APIView):
"""
Create, update or delete a flavor.
"""
def get(self, request, format=None):
flavors = Flavor.objects.all()
return Response(FlavorSerializer(flavors, many=True).data)
def post(self, request, format=None):
data = request.data
new_flavor = Flavor.create(name=data["name"],
description=data["description"],
ram=data["ram"],
vcpu=data["vcpu"],
initial_disk=data["initial_disk"],
priority=data["priority"])
return Response(new_flavor.pk)
Subproject commit e01d873c78ac17fed0438936f979de3cbaca6a5e Subproject commit 7b2647531e54da62fcf1db4c61a5e0530e977da4
...@@ -42,6 +42,7 @@ INSTALLED_APPS = [ ...@@ -42,6 +42,7 @@ INSTALLED_APPS = [
"djoser", "djoser",
"rest_framework_swagger", "rest_framework_swagger",
"corsheaders", "corsheaders",
"template"
] ]
MIDDLEWARE = [ MIDDLEWARE = [
......
# Generated by Django 2.2.1 on 2019-07-04 12:12
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='InstanceTemplate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Human readable name of template.', max_length=100, verbose_name='name')),
('description', models.TextField(blank=True, help_text='Description of the template.', verbose_name='description')),
('remote_ID', models.CharField(help_text='ID, which helps access the template.', max_length=40, unique=True, verbose_name='remote_ID')),
],
),
]
from django.db import models from django.db import models
class InstanceTemplate: class InstanceTemplate(models.Model):
"""Virtual machine template. """Virtual machine template.
""" """
......
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