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/
recircle/db.sqlite3
interface-openstack/
recircle/clouds.yaml
recircle/implementation/
recircle/interface/
recircle/interface_openstack/
environment.sh
\ No newline at end of file
.idea/
environment.sh
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# 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
httpie = "*"
flake8 = "*"
django-rest-swagger = "*"
coverage = "*"
[packages]
django = "*"
......
{
"_meta": {
"hash": {
"sha256": "b00ba9882fe44a9e7e69f6b395f0790ba52f85f1e8ee2b116c599b4d6ce88401"
"sha256": "2adbcb2f6437576c7ac5794c996bf94a73714555a6ccc0e393fcb24bf184c1da"
},
"pipfile-spec": 6,
"requires": {
......@@ -460,6 +460,43 @@
],
"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": {
"hashes": [
"sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b",
......
......@@ -3,7 +3,6 @@ from image.serializers import DiskSerializer
# from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from interface_openstack.implementation.image.openstack_image_manager import (
OpenstackImageManager,
)
......
from django.contrib import admin
from instance.models import Instance
from instance.models import Instance, Flavor, Lease
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.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(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
)
# Later stored in database
LEASE_TYPES = tuple(
[(val["name"], val["verbose_name"]) for val in settings.LEASE_TYPES]
)
class Lease(models.Model):
""" 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):
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(
max_length=100, help_text="ID of the instance on the backend"
)
......@@ -21,25 +66,79 @@ class Instance(models.Model):
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"
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(
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(
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!"
)
deleted = models.BooleanField(
help_text="Indicates if the instance is ready for garbage collection",
default=False,
)
# template
# disks
# owner
# image = models.ForeignKey(TemplateImage, related_name="vm", null=True,
# 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 .models import Instance
class InstanceSerializer(serializers.ModelSerializer):
class Meta:
model = Instance
fields = "__all__"
from rest_framework import serializers
from .models import Flavor, Instance
class InstanceSerializer(serializers.ModelSerializer):
lease = serializers.PrimaryKeyRelatedField(read_only=True)
flavor = serializers.PrimaryKeyRelatedField(read_only=True)
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 = [
path("instances/", views.InstanceList.as_view()),
path("instances/<int:pk>/", views.InstanceDetail.as_view()),
# path('instances/<int:pk>/action/', views.InstanceAction.as_view())
path("flavors/", views.FlavorListView.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
from instance.models import Instance
from instance.serializers import InstanceSerializer
from instance.serializers import InstanceSerializer, FlavorSerializer
from django.http import Http404
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
import datetime
from interface_openstack.implementation.vm.instance import OSVirtualMachineManager
from django.conf import settings
from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager
)
from template.models import InstanceTemplate
from instance.models import Instance, Flavor, Lease
class InstanceList(APIView):
......@@ -17,26 +18,23 @@ class InstanceList(APIView):
def post(self, request, format=None):
data = request.data
interface = OSVirtualMachineManager(settings.CONNECTION)
imageId = "da51253f-867c-472d-8ce0-81e7b7126d60"
flavorId = "1"
networks = [{"uuid": "c03d0d4b-413e-4cc6-9ebe-c0b5ca0dac3a"}]
newBackendInstance = interface.create_vm_from_template(
"Integration test vm 2", imageId, flavorId, networks
)
newInstance = Instance(
name=data["name"],
remote_id=newBackendInstance.id,
description=data["description"],
access_method="ssh",
system=data["system"],
password="12345678",
lease=data["lease"],
time_of_suspend=datetime.datetime.now(),
time_of_delete=datetime.datetime.now(),
deleted=False,
template = InstanceTemplate.objects.get(pk=data["template"])
flavor = Flavor.objects.get(pk=data["flavor"])
lease = Lease.objects.get(pk=data["lease"])
newInstance = Instance.create_instance_from_template(
params={"name": data["name"],
"description": data["description"],
"access_method": data["access"],
"system": data["system"],
},
lease=lease,
networks=template.networks,
template=template,
flavor=flavor,
owner=request.user,
disks=template.disks
)
newInstance.save()
return Response(newInstance.pk)
......@@ -75,3 +73,24 @@ class InstanceDetail(APIView):
instance = self.get_object(pk)
instance.delete()
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 = [
"djoser",
"rest_framework_swagger",
"corsheaders",
"template"
]
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
class InstanceTemplate:
class InstanceTemplate(models.Model):
"""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