Commit 736b1c95 by Bodor Máté

Merge DEV to template

parents 183bc1db ee5bcf00
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
recircle/db.sqlite3 recircle/db.sqlite3
recircle/clouds.yaml recircle/clouds.yaml
environment.sh environment.sh
.idea/
\ No newline at end of file
...@@ -4,19 +4,18 @@ stages: ...@@ -4,19 +4,18 @@ stages:
- lint - lint
- test - test
before_script:
- pip install pipenv
- pipenv install -d
- git submodule sync --recursive
- git submodule update --init --recursive
flake8: flake8:
before_script:
- pip install flake8
stage: lint stage: lint
script: script:
- flake8 --max-line-length=125 --exclude=migrations - pipenv run flake8
test: test:
before_script:
- pip install pipenv
- pipenv install -d
- git submodule sync --recursive
- git submodule update --init --recursive
script: script:
- cd recircle - cd recircle
- pipenv run python manage.py test - pipenv run python manage.py test
...@@ -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 = "*"
......
...@@ -458,6 +458,43 @@ ...@@ -458,6 +458,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",
......
# 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.')),
],
),
]
...@@ -6,7 +6,7 @@ from django.conf import settings ...@@ -6,7 +6,7 @@ from django.conf import settings
from image.serializers import DiskSerializer from image.serializers import DiskSerializer
from image.models import Disk from image.models import Disk
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'),
),
]
# 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'),
),
]
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 PureTemplate
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
# 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',),
),
]
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from template import views
urlpatterns = [
path("template/", views.TemplateList.as_view()),
# path("template/<int:pk>/", views.TemplateDetail.as_view()),
# path('instances/<int:pk>/action/', views.InstanceAction.as_view())
]
urlpatterns = format_suffix_patterns(urlpatterns)
\ No newline at end of file
[flake8]
exclude =
# No need to traverse our git directory
.git,
# No need to flake8 the other repo
recircle/interface_openstack
# No need to check migrations
migrations
max-line-length = 100
\ No newline at end of file
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