Commit 31723e86 by Chif Gergő

Merge branch 'instance_views' into DEV

parents 4f79bde7 ac9a0cb8
# IDEs
.vscode/
.idea/
environment.sh
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
......@@ -64,25 +62,6 @@ coverage.xml
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
......@@ -93,12 +72,6 @@ ipython_config.py
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
......@@ -107,27 +80,10 @@ 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/
environment.sh
# Cloud configure
clouds.yaml
# pyc files
*.pyc
# from django.contrib import admin
from django.contrib import admin
# Register your models here.
from image.models import Image
from image.models import Disk
admin.site.register(Image)
admin.site.register(Disk)
# 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.db import models
from django.contrib.auth.models import User
from django.conf import settings
from interface_openstack.implementation.vm.instance import OSVirtualMachineManager
from interface_openstack.implementation.image.openstack_image_manager import OpenstackImageManager
class Disk(models.Model):
......@@ -9,9 +14,86 @@ class Disk(models.Model):
name = models.CharField(
blank=True, max_length=100, verbose_name="name", help_text="Name of the disk"
)
remote_ID = models.CharField(
remote_id = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the disk",
)
class Image(models.Model):
"""A virtual image.
"""
name = models.CharField(
max_length=100,
verbose_name="name",
help_text="Human readable name of image."
)
description = models.TextField(
verbose_name="description",
blank=True,
help_text="Description of the image."
)
remote_id = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the image."
)
created_at = models.DateTimeField(
auto_now_add=True,
editable=False,
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(
User,
on_delete=models.DO_NOTHING,
related_name="created_images",
help_text="The user, who create the image"
)
@classmethod
def create(cls, name, description, remote_id, uploaded_by_user, created_by):
inst = cls(name=name, description=description, uploaded_by_user=uploaded_by_user,
remote_id=remote_id, created_by=created_by)
inst.full_clean()
inst.save()
return inst
@classmethod
def create_from_instance(cls, user, instance, description):
interface = OSVirtualMachineManager(settings.CONNECTION)
remote_image = interface.create_image(instance.remote_id)
remote_id = remote_image.id
name = remote_image.name
new_image = cls.create(
name=name,
remote_id=remote_id,
created_by=user,
uploaded_by_user=False,
description=description
)
return new_image
@classmethod
def create_from_user(cls, description, file_format, image_file, name, user):
interface = OpenstackImageManager(settings.CONNECTION)
remote_image = interface.upload_file(name=name, path=image_file.temporary_file_path(),
format=file_format)
new_image = cls.create(
name=name,
remote_id=remote_image.id,
created_by=user,
uploaded_by_user=True,
description=description
)
return new_image
from rest_framework import serializers
from .models import Disk
from .models import Image
class DiskSerializer(serializers.ModelSerializer):
class ImageSerializer(serializers.ModelSerializer):
image_file = serializers.FileField(write_only=True)
file_format = serializers.CharField(max_length=10, write_only=True)
class Meta:
model = Disk
fields = ("name", "remote_ID")
model = Image
fields = (
"name",
"remote_id",
"description",
"image_file",
"file_format",
"created_at",
"uploaded_by_user",
"created_by",
)
read_only_fields = ("created_at", "uploaded_by_user", "created_by", "remote_id", )
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from rest_framework import routers
from image import views
urlpatterns = [path("", views.DiskList.as_view())]
router = routers.DefaultRouter()
router.register(r"images", views.ImageViewSet, basename="image")
urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns = router.urls
from image.models import Disk
from image.serializers import DiskSerializer
# from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from interface_openstack.implementation.image.openstack_image_manager import (
OpenstackImageManager,
)
from django.conf import settings
from image.models import Image
from image.serializers import ImageSerializer
class DiskList(APIView):
def get(self, request, format=None):
# OpenStack
interface = OpenstackImageManager(settings.CONNECTION)
return Response([disk.__dict__ for disk in interface.list()])
# Create response
disks = Disk.object.all()
serializer = DiskSerializer(disks, many=True)
class ImageViewSet(ModelViewSet):
serializer_class = ImageSerializer
queryset = Image.objects.all()
# def list(self, request):
# return HttpResponse("list")
def create(self, request):
serializer = ImageSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
new_image = Image.create_from_user(
description=data['description'],
file_format=data['file_format'],
image_file=data['image_file'],
name=data['name'],
user=request.user
)
serializer = ImageSerializer(instance=new_image)
return Response(serializer.data)
# def retrieve(self, request, pk=None):
# return HttpResponse("retrive")
# def update(self, request, pk=None):
# return HttpResponse("update")
# def partial_update(self, request, pk=None):
# return HttpResponse("patch")
# def destroy(self, request, pk=None):
# return HttpResponse("delete")
# 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.3 on 2019-07-22 12:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0009_auto_20190715_1354'),
('instance', '0009_auto_20190715_0929'),
]
operations = [
]
# Generated by Django 2.2.3 on 2019-07-22 12:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('template', '0006_auto_20190719_1416'),
('instance', '0010_merge_20190722_1216'),
]
operations = [
migrations.AddField(
model_name='instance',
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'),
),
]
......@@ -15,6 +15,17 @@ ACCESS_METHODS = tuple(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
)
interface = OSVirtualMachineManager(settings.CONNECTION)
ACTIONS = {
"start": interface.start_vm,
"stop": interface.stop_vm,
"suspend": interface.suspend_vm,
"wake_up": interface.wake_up_vm,
"reset": interface.reset_vm,
"reboot": interface.reboot_vm,
}
class Lease(models.Model):
""" Users can use the virtual machine until the lease dates.
......@@ -44,7 +55,6 @@ class Flavor(models.Model):
@classmethod
def create(cls, name, description, ram=0, vcpu=0,
initial_disk=0, priority=0):
interface = OSVirtualMachineManager(settings.CONNECTION)
try:
remote_flavor = interface.create_flavor(name, ram, vcpu, initial_disk)
......@@ -55,12 +65,22 @@ class Flavor(models.Model):
return flavor
except Exception as e:
logger.error(str(e))
raise ValueError("Couldn't create Flavor in remote cloud.")
raise ValueError("Can't create Flavor in remote cloud.")
def delete(self):
interface = OSVirtualMachineManager(settings.CONNECTION)
try:
interface.delete_flavor(self.remote_id)
super(Flavor, self).delete()
except Exception as e:
if e.OpenStackError:
logger.error("Can not delete the flavor in remote cloud")
class Instance(models.Model):
"""Virtual machine instance.
"""
from template.models import ImageTemplate
name = models.CharField(max_length=100,
help_text="Human readable name of instance")
......@@ -92,11 +112,9 @@ class Instance(models.Model):
default=False,
)
# 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")
template = models.ForeignKey(ImageTemplate, related_name="vm", null=True,
help_text="The base image of the vm",
on_delete="DO_NOTHING")
disks = models.ManyToManyField(Disk, related_name="instance",
help_text="Disks attached to instance",
......@@ -110,9 +128,10 @@ class Instance(models.Model):
related_name='instances')
@classmethod
def create(cls, lease, owner, flavor, remote_id, params):
def create(cls, lease, owner, flavor, template, remote_id, params):
params["password"] = cls.generate_password()
inst = cls(lease=lease, flavor=flavor, owner=owner,
remote_id=remote_id, **params)
remote_id=remote_id, template=template, **params)
inst.full_clean()
inst.save()
......@@ -123,19 +142,18 @@ class Instance(models.Model):
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)
try:
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)
remote_id = interface.create_vm_from_template(params["name"],
template.image.remote_id,
flavor.remote_id,
networks,
)
new_inst = cls.create(lease, owner, flavor, template,
remote_id, params)
return new_inst
except Exception as e:
logger.error(str(e))
raise ValueError("Couldn't create Flavor in remote cloud."
raise ValueError("Can't create Instance in remote cloud."
"Search the logs for more detail.")
def clean(self, *args, **kwargs):
......@@ -153,3 +171,26 @@ class Instance(models.Model):
timezone.now() + timedelta(
seconds=lease.delete_interval_in_sec)
)
def delete(self):
try:
interface.destroy_vm(self.remote_id)
super(Instance, self).delete()
except Exception as e:
if e.OpenStackError:
logger.error("Can not delete the instance in remote cloud")
def execute_common_action(self, action):
if ACTIONS[action]:
return ACTIONS[action](self.remote_id)
else:
raise ValueError("This action is not supported!")
def get_remote_instance(self):
return interface.get_vm(self.remote_id)
@classmethod
def generate_password(self):
return User.objects.make_random_password(
allowed_chars='abcdefghijklmnopqrstuvwx'
'ABCDEFGHIJKLMNOPQRSTUVWX123456789')
from rest_framework import serializers
from .models import Flavor, Instance
from .models import Flavor, Instance, Lease
class InstanceSerializer(serializers.ModelSerializer):
......@@ -8,10 +8,26 @@ class InstanceSerializer(serializers.ModelSerializer):
class Meta:
model = Instance
fields = ("name", "description", "system", "lease", "flavor")
fields = (
"name",
"description",
"system",
"lease",
"flavor",
"password",
"template",
"time_of_suspend",
"time_of_delete")
read_only_fields = ("password", "template", "time_of_suspend", "time_of_delete")
class FlavorSerializer(serializers.ModelSerializer):
class Meta:
model = Flavor
fields = "__all__"
class LeaseSerializer(serializers.ModelSerializer):
class Meta:
model = Lease
fields = '__all__'
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from instance import views
from rest_framework import routers
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()),
]
router = routers.SimpleRouter()
router.register(r'instances', views.InstanceViewSet, basename='instance')
router.register(r'flavors', views.FlavorViewSet, basename='flavor')
router.register(r'leases', views.LeaseViewSet, basename='lease')
urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns = router.urls
from instance.serializers import InstanceSerializer, FlavorSerializer
from instance.serializers import InstanceSerializer, FlavorSerializer, LeaseSerializer
from django.http import Http404
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet, ModelViewSet
from rest_framework.response import Response
from rest_framework import status
from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager
)
from template.models import InstanceTemplate
from rest_framework.decorators import action
from template.serializers import InstanceFromTemplateSerializer
from instance.models import Instance, Flavor, Lease
from template.models import ImageTemplate
from template.serializers import ImageTemplateModelSerializer
class InstanceViewSet(ViewSet):
class InstanceList(APIView):
def get(self, request, format=None):
def get_object(self, pk):
try:
return Instance.objects.get(pk=pk)
except Instance.DoesNotExist:
raise Http404
def list(self, request):
instances = Instance.objects.all()
return Response(InstanceSerializer(instances, many=True).data)
def post(self, request, format=None):
def create(self, request):
data = request.data
template = InstanceTemplate.objects.get(pk=data["template"])
flavor = Flavor.objects.get(pk=data["flavor"])
lease = Lease.objects.get(pk=data["lease"])
template = ImageTemplate.objects.get(pk=data["template"])
# TODO: if the user can select anouther lease and flavor
# That will be applied, not from the 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"],
......@@ -28,40 +39,26 @@ class InstanceList(APIView):
"access_method": data["access"],
"system": data["system"],
},
lease=lease,
networks=template.networks,
lease=template.lease,
networks=[{"uuid": template.network_id}],
template=template,
flavor=flavor,
flavor=template.flavor,
owner=request.user,
disks=template.disks
disks=None
)
return Response(newInstance.pk)
class InstanceDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Instance.objects.get(pk=pk)
except Instance.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
def retrieve(self, request, pk):
instance = self.get_object(pk)
instanceDict = InstanceSerializer(instance).data
interface = OSVirtualMachineManager(settings.CONNECTION)
remoteInstance = interface.get_vm(instance.remote_id)
remoteInstance = instance.get_remote_instance()
remoteInstanceDict = remoteInstance.__dict__
merged_dict = {**instanceDict, **remoteInstanceDict}
return Response(merged_dict)
def put(self, request, pk, format=None):
def update(self, request, pk, format=None):
instance = self.get_object(pk)
serializer = InstanceSerializer(instance, data=request.data)
if serializer.is_valid():
......@@ -69,22 +66,48 @@ class InstanceDetail(APIView):
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
def destroy(self, request, pk, format=None):
instance = self.get_object(pk)
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@action(detail=True, methods=["post"])
def template(self, request, pk):
instance = self.get_object(pk)
serializer = InstanceFromTemplateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
new_template = ImageTemplate.create_from_instance(data["name"],
data["description"],
instance,
request.user)
serializer = ImageTemplateModelSerializer(instance=new_template)
return Response(serializer.data)
@action(detail=True, methods=["POST"])
def actions(self, request, pk):
instance = self.get_object(pk)
success = instance.execute_common_action(action=request.data["action"])
class FlavorListView(APIView):
return Response(success)
class FlavorViewSet(ViewSet):
"""
Create, update or delete a flavor.
"""
def get(self, request, format=None):
def get_object(self, pk):
try:
return Flavor.objects.get(pk=pk)
except Flavor.DoesNotExist:
raise Http404
def list(self, request, format=None):
flavors = Flavor.objects.all()
return Response(FlavorSerializer(flavors, many=True).data)
def post(self, request, format=None):
def create(self, request, format=None):
data = request.data
new_flavor = Flavor.create(name=data["name"],
description=data["description"],
......@@ -94,3 +117,19 @@ class FlavorListView(APIView):
priority=data["priority"])
return Response(new_flavor.pk)
def update(self, request, pk):
return Response(status=status.HTTP_400_BAD_REQUEST)
def partial_update(self, request, pk):
return Response(status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, pk):
flavor = self.get_object(pk)
flavor.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class LeaseViewSet(ModelViewSet):
queryset = Lease.objects.all()
serializer_class = LeaseSerializer
Subproject commit 7b2647531e54da62fcf1db4c61a5e0530e977da4
Subproject commit 1a19e4355f4af1abb49a3f6e07dc3a6c3f8bdf47
......@@ -21,8 +21,9 @@ from rest_framework_swagger.views import get_swagger_view
schema_view = get_swagger_view(title="RECIRCLE API")
urlpatterns = [
path("images/", include("image.urls")),
path("api/v1/", include("image.urls")),
path("api/v1/", include("instance.urls")),
path("api/v1/", include("template.urls")),
path("admin/", admin.site.urls),
re_path(r"^auth/", include("djoser.urls")),
re_path(r"^auth/", include("djoser.urls.authtoken")),
......
# from django.contrib import admin
from django.contrib import admin
# Register your models here.
from template.models import ImageTemplate
admin.site.register(ImageTemplate)
# Generated by Django 2.2.1 on 2019-07-04 12:12
# Generated by Django 2.2.3 on 2019-07-03 12:09
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
......@@ -8,16 +9,26 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('image', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='InstanceTemplate',
name='BaseTemplate',
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')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Date, when the template created.')),
],
),
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.contrib.auth.models import User
from django.conf import settings
from image.models import Disk, Image
from instance.models import Lease, Flavor
from interface_openstack.implementation.storage.openstack_snapshot_manager import SnapshotManager
class InstanceTemplate(models.Model):
class BaseTemplate(models.Model):
"""Virtual machine template.
"""
......@@ -9,15 +15,100 @@ class InstanceTemplate(models.Model):
name = models.CharField(
max_length=100,
verbose_name="name",
help_text="Human readable name of template.",
help_text="Human readable name of template."
)
description = models.TextField(
verbose_name="description", blank=True, help_text="Description of the template."
verbose_name="description",
blank=True,
help_text="Description of the template."
)
# remote_id = models.CharField(
# max_length=40,
# unique=True,
# verbose_name="remote_ID",
# help_text="ID, which helps access 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="Reasources 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
)
# owner = models.ForeignKey(User)
remote_ID = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the template.",
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 = cls.create(
name=name,
description=description,
disk=disk,
remote_id=remote_id,
created_by=user
)
return new_template
class ImageTemplate(BaseTemplate):
TYPES = (
('U', 'User create the template from image'),
('I', 'Template created from instance'),
('P', '"Pure" template'),
)
image = models.ForeignKey(
Image,
related_name="templates",
on_delete=models.CASCADE,
help_text=""
)
type = models.CharField(max_length=10, choices=TYPES, default="U")
@classmethod
def create(cls, name, description, image, lease, flavor, created_by, type='U'):
inst = cls(name=name, description=description, image=image, lease=lease,
flavor=flavor, created_by=created_by, type=type)
inst.full_clean()
inst.save()
return inst
@classmethod
def create_from_instance(cls, name, description, instance, user):
image = Image.create_from_instance(user, instance, description)
new_template = cls.create(
name=name,
description=description,
created_by=user,
image=image,
lease=instance.lease,
flavor=instance.flavor,
type="I"
)
return new_template
from rest_framework import serializers
from .models import InstanceTemplate
from template.models import ImageTemplate
class InstanceTemplateSerializer(serializers.ModelSerializer):
class InstanceFromTemplateSerializer(serializers.Serializer):
name = serializers.CharField()
description = serializers.CharField()
class ImageTemplateModelSerializer(serializers.ModelSerializer):
class Meta:
model = InstanceTemplate
fields = ("name", "description", "owner", "remote_ID")
model = ImageTemplate
fields = (
"name",
"description",
"created_at",
"created_by",
"image",
"flavor",
"lease",
"type",
)
read_only_fields = (
"created_at",
"created_by",
"type",
)
from rest_framework import routers
from template import views
router = routers.DefaultRouter()
router.register(r"templates/image-templates", views.ImageTemplateViewSet, basename="image-template")
urlpatterns = router.urls
# from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from template.serializers import ImageTemplateModelSerializer
from template.models import ImageTemplate
# Create your views here.
class ImageTemplateViewSet(ModelViewSet):
serializer_class = ImageTemplateModelSerializer
queryset = ImageTemplate.objects.all()
def create(self, request):
serializer = ImageTemplateModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
new_template = ImageTemplate.create(
name=data["name"],
description=data["description"],
created_by=request.user,
image=data["image"],
lease=data["lease"],
flavor=data["flavor"],
type="U"
)
serializer = ImageTemplateModelSerializer(instance=new_template)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
# only the name, description, lease, flavor can be updated
allowed_keys = ["name", "description", "lease", "flavor"]
# delete not allowed key
for key in request.data.keys():
if key not in allowed_keys:
request.data.pop(key, None)
return super(ImageTemplateViewSet, self).update(request, partial=True)
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