Commit d4935afb by Chif Gergő

Resolve conflicts

# Conflicts:
#   recircle/image/models.py
parents 3421dbd8 799c0577
Pipeline #902 passed with stage
in 1 minute 22 seconds
...@@ -17,6 +17,7 @@ django-cors-headers = "*" ...@@ -17,6 +17,7 @@ django-cors-headers = "*"
openstacksdk = "*" openstacksdk = "*"
python-novaclient = "*" python-novaclient = "*"
keystoneauth1 = "*" keystoneauth1 = "*"
django-guardian = "*"
djoser = "*" djoser = "*"
[requires] [requires]
......
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "8cb2efb1cfa79de96297c90953faf757011ca1f1ff4784ac05226a249001e1f5" "sha256": "dd7bfbb33d07cbcc96d1f3b1f838538dba41f3eb0bf5279381714b4b9abf90d7"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
...@@ -135,6 +135,14 @@ ...@@ -135,6 +135,14 @@
"index": "pypi", "index": "pypi",
"version": "==3.0.2" "version": "==3.0.2"
}, },
"django-guardian": {
"hashes": [
"sha256:965d3a1e20fb3639e0ab16b0e768611f694c02d762916f80d9f3f7520c16aa7b",
"sha256:e8c4556c4e145028a5dcfd7b2611d52e1ac104af562017ce17c3f67e47a62693"
],
"index": "pypi",
"version": "==2.0.0"
},
"django-templated-mail": { "django-templated-mail": {
"hashes": [ "hashes": [
"sha256:8db807effebb42a532622e2d142dfd453dafcd0d7794c4c3332acb90656315f9", "sha256:8db807effebb42a532622e2d142dfd453dafcd0d7794c4c3332acb90656315f9",
......
from django.contrib.auth.models import Permission
from django.contrib import admin
admin.site.register(Permission)
from django.apps import AppConfig
class AuthorizationConfig(AppConfig):
name = 'authorization'
from guardian.shortcuts import get_objects_for_user
import logging
logger = logging.getLogger(__name__)
class AuthorizationMixin():
authorization = {}
def get_objects_with_perms(self, user, method, instance):
auth_params = self.authorization[method]
if auth_params:
return get_objects_for_user(user, auth_params["filter"], instance)
else:
logger.error(f"Invalid method for authorization: {method}")
return False
def has_perms_for_object(self, user, method, instance):
auth_params = self.authorization[method]
if auth_params:
for perm in auth_params["object"]:
if not user.has_perm(perm, instance):
return False
return True
else:
logger.error(f"Invalid method for authorization: {method}")
return False
def has_perms_for_model(self, user, method):
auth_params = self.authorization[method]
if auth_params:
for perm in auth_params["model"]:
if not user.has_perm(perm):
return False
return True
else:
logger.error(f"Invalid method for authorization: {method}")
return False
from django.contrib import admin from django.contrib import admin
from image.models import Image from image.models import Image, Disk
from image.models import Disk
admin.site.register(Image) admin.site.register(Image)
admin.site.register(Disk) admin.site.register(Disk)
...@@ -7,7 +7,6 @@ from interface_openstack.implementation.image.openstack_image_manager import Ope ...@@ -7,7 +7,6 @@ from interface_openstack.implementation.image.openstack_image_manager import Ope
class Disk(models.Model): class Disk(models.Model):
"""A virtual disk. """A virtual disk.
""" """
...@@ -26,7 +25,6 @@ class Disk(models.Model): ...@@ -26,7 +25,6 @@ class Disk(models.Model):
class Image(models.Model): class Image(models.Model):
"""A virtual image. """A virtual image.
""" """
...@@ -79,16 +77,12 @@ class Image(models.Model): ...@@ -79,16 +77,12 @@ class Image(models.Model):
remote_image = interface.create_image(instance.remote_id) remote_image = interface.create_image(instance.remote_id)
remote_id = remote_image.id remote_id = remote_image.id
name = remote_image.name name = remote_image.name
new_image = cls.create( new_image = Image.objects.create(name=name,
name=name, remote_id=remote_id,
remote_id=remote_id, created_by=user,
created_by=user, uploaded_by_user=False,
uploaded_by_user=False, description=description)
description=description return new_image
)
for attr, value in new_image.__dict__.items():
setattr(remote_image, attr, value)
return remote_image
@classmethod @classmethod
def create_from_user(cls, description, def create_from_user(cls, description,
...@@ -97,18 +91,16 @@ class Image(models.Model): ...@@ -97,18 +91,16 @@ class Image(models.Model):
remote_image = interface.upload_file(name=name, remote_image = interface.upload_file(name=name,
path=image_file.temporary_file_path(), path=image_file.temporary_file_path(),
format=file_format) format=file_format)
new_image = cls.create( new_image = Image.objects.create(name=name,
name=name, remote_id=remote_image.id,
remote_id=remote_image.id, created_by=user,
created_by=user, uploaded_by_user=True,
uploaded_by_user=True, description=description)
description=description
)
for attr, value in new_image.__dict__.items(): for attr, value in new_image.__dict__.items():
setattr(remote_image, attr, value) setattr(remote_image, attr, value)
return remote_image return remote_image
def delete(self): def delete(self, **kwargs):
interface = OpenstackImageManager(settings.CONNECTION) interface = OpenstackImageManager(settings.CONNECTION)
if interface.delete(self.remote_id): if interface.delete(self.remote_id):
super().delete() super().delete()
......
...@@ -11,7 +11,7 @@ class ImageUpdateSerializer(serializers.Serializer): ...@@ -11,7 +11,7 @@ class ImageUpdateSerializer(serializers.Serializer):
class ImageSerializer(serializers.ModelSerializer): class ImageSerializer(serializers.ModelSerializer):
image_file = serializers.FileField(write_only=True) image_file = serializers.FileField(write_only=True)
file_format = serializers.CharField(max_length=10) file_format = serializers.CharField(max_length=10)
size = serializers.IntegerField() size = serializers.IntegerField(read_only=True)
class Meta: class Meta:
model = Image model = Image
......
from rest_framework import routers from rest_framework import routers
from image import views from image import views
router = routers.DefaultRouter() router = routers.DefaultRouter()
......
# from django.shortcuts import render
from rest_framework.viewsets import ViewSet from rest_framework.viewsets import ViewSet
from rest_framework.response import Response from rest_framework.response import Response
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
...@@ -6,8 +5,7 @@ from rest_framework import status ...@@ -6,8 +5,7 @@ from rest_framework import status
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from image.models import Image from image.models import Image
from image.serializers import ImageSerializer from image.serializers import ImageSerializer, ImageUpdateSerializer
from image.serializers import ImageUpdateSerializer
class ImageViewSet(ViewSet): class ImageViewSet(ViewSet):
...@@ -37,7 +35,6 @@ class ImageViewSet(ViewSet): ...@@ -37,7 +35,6 @@ class ImageViewSet(ViewSet):
def retrieve(self, request, pk=None): def retrieve(self, request, pk=None):
queryset = Image.objects.all() queryset = Image.objects.all()
image = get_object_or_404(queryset, pk=pk) image = get_object_or_404(queryset, pk=pk)
serializer = ImageSerializer(instance=image)
image = image.get() image = image.get()
serializer = ImageSerializer(instance=image) serializer = ImageSerializer(instance=image)
return Response(serializer.data) return Response(serializer.data)
......
...@@ -2,6 +2,13 @@ from django.contrib import admin ...@@ -2,6 +2,13 @@ from django.contrib import admin
from instance.models import Instance, Flavor, Lease from instance.models import Instance, Flavor, Lease
admin.site.register(Instance) from guardian.admin import GuardedModelAdmin
class InstanceAdmin(GuardedModelAdmin):
pass
admin.site.register(Instance, InstanceAdmin)
admin.site.register(Flavor) admin.site.register(Flavor)
admin.site.register(Lease) admin.site.register(Lease)
# Generated by Django 2.2.4 on 2019-08-08 11:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0010_instance_template'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'permissions': (('create_instance', 'Can create a new VM.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
# Generated by Django 2.2.4 on 2019-08-29 07:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0011_auto_20190808_1137'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'default_permissions': (), 'permissions': (('create_instance', 'Can create a new VM.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
# Generated by Django 2.2.4 on 2019-08-30 12:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0012_auto_20190829_0754'),
]
operations = [
migrations.AlterModelOptions(
name='instance',
options={'default_permissions': (), 'permissions': (('create_instance', 'Can create a new VM.'), ('create_template_from_instance', 'Can create template from instance.'), ('use_instance', 'Can access the VM connection info.'), ('operate_instance', 'Can use basic lifecycle methods of the VM.'), ('administer_instance', 'Can delete VM.'), ('access_console', 'Can access the graphical console of a VM.'), ('change_resources', 'Can change resources of a VM.'), ('manage_access', 'Can manage access rights for the VM.'), ('config_ports', 'Can configure port forwards.'))},
),
]
...@@ -5,8 +5,8 @@ from django.utils import timezone ...@@ -5,8 +5,8 @@ from django.utils import timezone
from datetime import timedelta from datetime import timedelta
from image.models import Disk from image.models import Disk
from interface_openstack.implementation.vm.instance import ( from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager OSVirtualMachineManager
) )
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -45,8 +45,8 @@ class Flavor(models.Model): ...@@ -45,8 +45,8 @@ class Flavor(models.Model):
name = models.CharField(blank=True, max_length=100) name = models.CharField(blank=True, max_length=100)
description = models.CharField(blank=True, max_length=200) description = models.CharField(blank=True, max_length=200)
remote_id = models.CharField( remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend" max_length=100, help_text="ID of the instance on the backend"
) )
ram = models.IntegerField(blank=True, null=True) ram = models.IntegerField(blank=True, null=True)
vcpu = models.IntegerField(blank=True, null=True) vcpu = models.IntegerField(blank=True, null=True)
initial_disk = models.IntegerField(blank=True, null=True) initial_disk = models.IntegerField(blank=True, null=True)
...@@ -82,6 +82,20 @@ class Instance(models.Model): ...@@ -82,6 +82,20 @@ class Instance(models.Model):
""" """
from template.models import ImageTemplate from template.models import ImageTemplate
class Meta:
default_permissions = ()
permissions = (
('create_instance', 'Can create a new VM.'),
('create_template_from_instance', 'Can create template from instance.'),
('use_instance', 'Can access the VM connection info.'),
('operate_instance', 'Can use basic lifecycle methods of the VM.'),
('administer_instance', 'Can delete VM.'),
('access_console', 'Can access the graphical console of a VM.'),
('change_resources', 'Can change resources of a VM.'),
('manage_access', 'Can manage access rights for the VM.'),
('config_ports', 'Can configure port forwards.'),
)
name = models.CharField(max_length=100, name = models.CharField(max_length=100,
help_text="Human readable name of instance") help_text="Human readable name of instance")
...@@ -168,10 +182,20 @@ class Instance(models.Model): ...@@ -168,10 +182,20 @@ class Instance(models.Model):
lease = self.lease lease = self.lease
return ( return (
timezone.now() + timedelta( timezone.now() + timedelta(
seconds=lease.suspend_interval_in_sec), seconds=lease.suspend_interval_in_sec),
timezone.now() + timedelta( timezone.now() + timedelta(
seconds=lease.delete_interval_in_sec) seconds=lease.delete_interval_in_sec)
) )
def renew(self, lease=None):
"""Renew virtual machine, if a new lease is provided it changes it as well.
"""
if lease is None:
lease = self.lease
else:
self.lease = lease
self.time_of_suspend, self.time_of_delete = self.get_renew_times(lease)
self.save()
def delete(self): def delete(self):
try: try:
...@@ -193,5 +217,13 @@ class Instance(models.Model): ...@@ -193,5 +217,13 @@ class Instance(models.Model):
@classmethod @classmethod
def generate_password(self): def generate_password(self):
return User.objects.make_random_password( return User.objects.make_random_password(
allowed_chars='abcdefghijklmnopqrstuvwx' allowed_chars='abcdefghijklmnopqrstuvwx'
'ABCDEFGHIJKLMNOPQRSTUVWX123456789') 'ABCDEFGHIJKLMNOPQRSTUVWX123456789')
def change_name(self, new_name):
self.name = new_name
self.save()
def change_description(self, new_description):
self.description = new_description
self.save()
...@@ -9,6 +9,7 @@ class InstanceSerializer(serializers.ModelSerializer): ...@@ -9,6 +9,7 @@ class InstanceSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Instance model = Instance
fields = ( fields = (
"id",
"name", "name",
"description", "description",
"system", "system",
...@@ -18,7 +19,7 @@ class InstanceSerializer(serializers.ModelSerializer): ...@@ -18,7 +19,7 @@ class InstanceSerializer(serializers.ModelSerializer):
"template", "template",
"time_of_suspend", "time_of_suspend",
"time_of_delete") "time_of_delete")
read_only_fields = ("password", "template", "time_of_suspend", "time_of_delete") read_only_fields = ("id", "password", "template", "time_of_suspend", "time_of_delete")
class FlavorSerializer(serializers.ModelSerializer): class FlavorSerializer(serializers.ModelSerializer):
......
...@@ -9,9 +9,46 @@ from template.serializers import InstanceFromTemplateSerializer ...@@ -9,9 +9,46 @@ from template.serializers import InstanceFromTemplateSerializer
from instance.models import Instance, Flavor, Lease from instance.models import Instance, Flavor, Lease
from template.models import ImageTemplate from template.models import ImageTemplate
from template.serializers import ImageTemplateModelSerializer from template.serializers import ImageTemplateModelSerializer
from authorization.mixins import AuthorizationMixin
class InstanceViewSet(ViewSet):
authorization = {
"list": {"filter": ["use_instance"]},
"create": {"model": ["instance.create_instance"]},
"retrieve": {"object": ["use_instance"]},
"update": {"object": ["use_instance"]},
"destroy": {"object": ["administer_instance"]},
"template": {"model": ["create_template_from_instance"],
"object": ["use_instance"]},
"start": {"object": ["operate_instance"]},
"stop": {"object": ["operate_instance"]},
"suspend": {"object": ["operate_instance"]},
"wake_up": {"object": ["operate_instance"]},
"reset": {"object": ["operate_instance"]},
"reboot": {"object": ["operate_instance"]},
}
update_actions = [
"change_name",
"change_description",
"renew",
"change_lease",
"change_flavor",
"attach_disk",
"resize_disk",
"add_permission",
"remove_permission",
"open_port",
"close_port",
"add_network",
"remove_network",
"new_password",
]
class InstanceViewSet(AuthorizationMixin, ViewSet):
authorization = authorization
def get_object(self, pk): def get_object(self, pk):
try: try:
...@@ -20,10 +57,14 @@ class InstanceViewSet(ViewSet): ...@@ -20,10 +57,14 @@ class InstanceViewSet(ViewSet):
raise Http404 raise Http404
def list(self, request): def list(self, request):
instances = Instance.objects.all() instances = self.get_objects_with_perms(request.user, "list", Instance)
return Response(InstanceSerializer(instances, many=True).data) return Response(InstanceSerializer(instances, many=True).data)
def create(self, request): def create(self, request):
if not self.has_perms_for_model(request.user, 'create'):
return Response({"error": "No permission to create Virtual Machine."},
status=status.HTTP_401_UNAUTHORIZED)
data = request.data data = request.data
template = ImageTemplate.objects.get(pk=data["template"]) template = ImageTemplate.objects.get(pk=data["template"])
...@@ -50,6 +91,10 @@ class InstanceViewSet(ViewSet): ...@@ -50,6 +91,10 @@ class InstanceViewSet(ViewSet):
def retrieve(self, request, pk): def retrieve(self, request, pk):
instance = self.get_object(pk) instance = self.get_object(pk)
if not self.has_perms_for_object(request.user, 'retrieve', instance):
return Response({"error": "No permission to access the Virtual Machine."},
status=status.HTTP_401_UNAUTHORIZED)
instanceDict = InstanceSerializer(instance).data instanceDict = InstanceSerializer(instance).data
remoteInstance = instance.get_remote_instance() remoteInstance = instance.get_remote_instance()
remoteInstanceDict = remoteInstance.__dict__ remoteInstanceDict = remoteInstance.__dict__
...@@ -59,21 +104,67 @@ class InstanceViewSet(ViewSet): ...@@ -59,21 +104,67 @@ class InstanceViewSet(ViewSet):
return Response(merged_dict) return Response(merged_dict)
def update(self, request, pk, format=None): def update(self, request, pk, format=None):
instance = self.get_object(pk) if request.data["action"] in update_actions:
serializer = InstanceSerializer(instance, data=request.data) instance = self.get_object(pk)
if serializer.is_valid(): if not self.has_perms_for_object(request.user, 'update', instance):
serializer.save() return Response({"error": "No permission to access the Virtual Machine."},
return Response(serializer.data) status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) action = request.data["action"]
if action == "change_name":
instance.change_name(request.data["name"])
elif action == "change_description":
instance.change_description(request.data["description"])
elif action == "renew":
instance.renew()
elif action == "change_lease":
lease = Lease.objects.get(pk=request.data["lease"])
instance.renew(lease)
elif action == "change_flavor":
pass
elif action == "attach_disk":
pass
elif action == "resize_disk":
pass
elif action == "add_permission":
pass
elif action == "remove_permission":
pass
elif action == "open_port":
pass
elif action == "close_port":
pass
elif action == "add_network":
pass
elif action == "remove_network":
pass
elif action == "new_password":
pass
instanceDict = InstanceSerializer(instance).data
remoteInstance = instance.get_remote_instance()
remoteInstanceDict = remoteInstance.__dict__
merged_dict = {"db": instanceDict, "openstack": remoteInstanceDict}
return Response(merged_dict)
else:
return Response({"error": "Unknown update action."}, status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, pk, format=None): def destroy(self, request, pk, format=None):
instance = self.get_object(pk) instance = self.get_object(pk)
if not self.has_perms_for_object(request.user, 'destroy', instance):
return Response({"error": "No permission to destroy the Virtual Machine."},
status=status.HTTP_401_UNAUTHORIZED)
instance.delete() instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
@action(detail=True, methods=["post"]) @action(detail=True, methods=["post"])
def template(self, request, pk): def template(self, request, pk):
instance = self.get_object(pk) instance = self.get_object(pk)
if not self.has_perms_for_model(request.user, 'template'):
return Response({"error": "No permission to create template from instance."},
status=status.HTTP_401_UNAUTHORIZED)
if not self.has_perms_for_object(request.user, 'template', instance):
return Response({"error": "No permission to access the Virtual Machine."},
status=status.HTTP_401_UNAUTHORIZED)
serializer = InstanceFromTemplateSerializer(data=request.data) serializer = InstanceFromTemplateSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
data = serializer.validated_data data = serializer.validated_data
...@@ -87,8 +178,10 @@ class InstanceViewSet(ViewSet): ...@@ -87,8 +178,10 @@ class InstanceViewSet(ViewSet):
@action(detail=True, methods=["POST"]) @action(detail=True, methods=["POST"])
def actions(self, request, pk): def actions(self, request, pk):
instance = self.get_object(pk) instance = self.get_object(pk)
if not self.has_perms_for_object(request.user, request.data["action"], instance):
return Response({"error": "No permission to use this action on the VM."},
status=status.HTTP_401_UNAUTHORIZED)
success = instance.execute_common_action(action=request.data["action"]) success = instance.execute_common_action(action=request.data["action"])
return Response(success) return Response(success)
......
...@@ -41,6 +41,7 @@ INSTALLED_APPS = [ ...@@ -41,6 +41,7 @@ INSTALLED_APPS = [
"djoser", "djoser",
"rest_framework_swagger", "rest_framework_swagger",
"corsheaders", "corsheaders",
"guardian",
"django_nose", "django_nose",
] ]
...@@ -49,6 +50,7 @@ LOCAL_APPS = [ ...@@ -49,6 +50,7 @@ LOCAL_APPS = [
"instance", "instance",
"storage", "storage",
"template", "template",
"authorization",
] ]
INSTALLED_APPS += LOCAL_APPS INSTALLED_APPS += LOCAL_APPS
...@@ -112,6 +114,7 @@ REST_FRAMEWORK = { ...@@ -112,6 +114,7 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
), ),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' # needed for swagger
} }
# Internationalization # Internationalization
...@@ -119,7 +122,7 @@ REST_FRAMEWORK = { ...@@ -119,7 +122,7 @@ REST_FRAMEWORK = {
LANGUAGE_CODE = "en-us" LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC" TIME_ZONE = "Europe/Budapest"
USE_I18N = True USE_I18N = True
...@@ -230,3 +233,10 @@ LOGGING = { ...@@ -230,3 +233,10 @@ LOGGING = {
for i in LOCAL_APPS: for i in LOCAL_APPS:
LOGGING['loggers'][i] = {'handlers': ['console'], 'level': 'DEBUG'} LOGGING['loggers'][i] = {'handlers': ['console'], 'level': 'DEBUG'}
# Configure django-guardian for the authorization
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # this is default
'guardian.backends.ObjectPermissionBackend',
)
...@@ -12,5 +12,8 @@ ADMIN_ENABLED = True ...@@ -12,5 +12,8 @@ ADMIN_ENABLED = True
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
AUTH_PASSWORD_VALIDATORS = []
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = LOCAL_APPS
\ No newline at end of file NOSE_ARGS = LOCAL_APPS
...@@ -8,71 +8,53 @@ from interface_openstack.implementation.storage.openstack_snapshot_manager impor ...@@ -8,71 +8,53 @@ from interface_openstack.implementation.storage.openstack_snapshot_manager impor
class BaseTemplate(models.Model): class BaseTemplate(models.Model):
"""Virtual machine template. """Virtual machine template.
""" """
name = models.CharField( name = models.CharField(max_length=100,
max_length=100, verbose_name="name",
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,
description = models.TextField( help_text="Description of the template.")
verbose_name="description", created_at = models.DateTimeField(auto_now_add=True,
blank=True, editable=False,
help_text="Description of the template." help_text="Date, when the template created.")
) created_by = models.ForeignKey(User,
# remote_id = models.CharField( on_delete=models.DO_NOTHING,
# max_length=40, related_name="created_templates",
# unique=True, help_text="The user, who create the template")
# verbose_name="remote_ID", flavor = models.ForeignKey(Flavor,
# help_text="ID, which helps access the template." help_text="Resources given to the vm",
# ) verbose_name="flavor",
created_at = models.DateTimeField( on_delete="CASCADE",
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') related_name='templates')
lease = models.ForeignKey(Lease, on_delete="CASCADE", lease = models.ForeignKey(Lease,
on_delete="CASCADE",
related_name='templates') related_name='templates')
network_id = models.CharField( network_id = models.CharField(max_length=100,
max_length=100, verbose_name="network_id",
verbose_name="network_id", help_text="The new instance will be in this network.",
help_text="The new instance will be in this network.", null=True,
null=True, blank=True)
blank=True
)
class DiskTemplate(BaseTemplate): class DiskTemplate(BaseTemplate):
disk = models.ForeignKey( disk = models.ForeignKey(Disk,
Disk, related_name="templates",
related_name="templates", on_delete=models.CASCADE,
on_delete=models.CASCADE, help_text="The disk where the template is located.")
help_text="The disk where the template is located."
)
@classmethod @classmethod
def create_from_volume(cls, name, description, disk, user): def create_from_volume(cls, name, description, disk, user):
interface = SnapshotManager(settings.CONNECTION) interface = SnapshotManager(settings.CONNECTION)
remote_template = interface.create_from_volume(disk.remote_id) remote_template = interface.create_from_volume(disk.remote_id)
remote_id = remote_template.id remote_id = remote_template.id
new_template = cls.create( new_template = DiskTemplate.objects.create(name=name,
name=name, description=description,
description=description, disk=disk,
disk=disk, remote_id=remote_id,
remote_id=remote_id, created_by=user)
created_by=user
)
return new_template return new_template
...@@ -82,33 +64,22 @@ class ImageTemplate(BaseTemplate): ...@@ -82,33 +64,22 @@ class ImageTemplate(BaseTemplate):
('I', 'Template created from instance'), ('I', 'Template created from instance'),
('P', '"Pure" template'), ('P', '"Pure" template'),
) )
image = models.ForeignKey(Image,
image = models.ForeignKey( related_name="templates",
Image, on_delete=models.CASCADE,
related_name="templates", help_text="")
on_delete=models.CASCADE, type = models.CharField(max_length=10,
help_text="" choices=TYPES,
) default="U")
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 @classmethod
def create_from_instance(cls, name, description, instance, user): def create_from_instance(cls, name, description, instance, user):
image = Image.create_from_instance(user, instance, description) image = Image.create_from_instance(user, instance, description)
new_template = cls.create( new_template = ImageTemplate.objects.create(name=name,
name=name, description=description,
description=description, created_by=user,
created_by=user, image=image,
image=image, lease=instance.lease,
lease=instance.lease, flavor=instance.flavor,
flavor=instance.flavor, type="I")
type="I"
)
return new_template return new_template
from rest_framework import routers from rest_framework import routers
from template import views from template import views
router = routers.DefaultRouter() router = routers.DefaultRouter()
......
# from django.shortcuts import render
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from template.serializers import ImageTemplateModelSerializer from template.serializers import ImageTemplateModelSerializer
from template.models import ImageTemplate from template.models import ImageTemplate
......
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