Commit 140a1932 by Karsa Zoltán István

add rest endpoints to node, vlan, deploy, shutdown, destroy

parent 60007ab6
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework import serializers from rest_framework import serializers
from vm.models import Instance, InstanceTemplate, Lease, Interface from vm.models import Instance, InstanceTemplate, Lease, Interface, Node
from firewall.models import Vlan from firewall.models import Vlan
from storage.models import Disk from storage.models import Disk
class NodeSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = [ 'id', 'name', 'priority', 'host', 'enabled', 'schedule_enabled',
'traits', 'overcommit', 'ram_weight', 'cpu_weight', 'time_stamp' ]
class InstanceTemplateSerializer(serializers.ModelSerializer): class InstanceTemplateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = InstanceTemplate model = InstanceTemplate
...@@ -42,7 +49,10 @@ class InstanceSerializer(serializers.ModelSerializer): ...@@ -42,7 +49,10 @@ class InstanceSerializer(serializers.ModelSerializer):
fields = ['id', 'name', 'description', 'status', 'owner', 'access_method', 'boot_menu', 'pw', 'is_base', fields = ['id', 'name', 'description', 'status', 'owner', 'access_method', 'boot_menu', 'pw', 'is_base',
'lease', 'raw_data', 'cloud_init', 'ci_meta_data', 'ci_user_data', 'system', 'req_traits', 'lease', 'raw_data', 'cloud_init', 'ci_meta_data', 'ci_user_data', 'system', 'req_traits',
'has_agent', 'num_cores', 'ram_size', 'max_ram_size', 'arch', 'priority', 'disks', 'node', 'ipv4addr', 'ipv6addr'] 'has_agent', 'num_cores', 'ram_size', 'max_ram_size', 'arch', 'priority', 'disks', 'node', 'ipv4addr', 'ipv6addr']
extra_kwargs = {'disks': {'required': False}} extra_kwargs = {
'disks': {'required': False, 'allow_empty': True,},
'req_traits': {'required': False, 'allow_empty': True,}
}
class InterfaceSerializer(serializers.ModelSerializer): class InterfaceSerializer(serializers.ModelSerializer):
...@@ -58,10 +68,21 @@ class VlanSerializer(serializers.ModelSerializer): ...@@ -58,10 +68,21 @@ class VlanSerializer(serializers.ModelSerializer):
class CreateDiskSerializer(serializers.Serializer): class CreateDiskSerializer(serializers.Serializer):
size = serializers.IntegerField() size = serializers.CharField(max_length=50)
name = serializers.CharField(max_length=100) name = serializers.CharField(max_length=100)
class ResizeDiskSerializer(serializers.Serializer):
size = serializers.CharField(max_length=50)
disk = serializers.IntegerField()
class DownloadDiskSerializer(serializers.Serializer): class DownloadDiskSerializer(serializers.Serializer):
url = serializers.CharField(max_length=500) url = serializers.CharField(max_length=500)
name = serializers.CharField(max_length=100) name = serializers.CharField(max_length=100)
class VMDeploySerializer(serializers.Serializer):
node = serializers.IntegerField(required=False)
class Meta:
extra_kwargs = {'node': {'required': False, 'allow_null': True}}
\ No newline at end of file
...@@ -56,19 +56,22 @@ from .views import ( ...@@ -56,19 +56,22 @@ from .views import (
NodeActivityView, NodeActivityView,
UserList, TemplateREST, LeaseREST, DiskRest, InstanceREST, UserList, TemplateREST, LeaseREST, DiskRest, InstanceREST,
InterfaceREST, InstanceFromTemplateREST, InstanceFTforUsersREST, InterfaceREST, InstanceFromTemplateREST, InstanceFTforUsersREST,
DownloadDiskREST, GetInstanceREST, GetInterfaceREST, DownloadDiskREST, GetInstanceREST, GetInterfaceREST, ShutdownInstanceREST,
GetLeaseREST, GetDiskRest, GetLeaseREST, GetDiskRest, DeployInstanceREST, CreateDiskREST,
VlanREST, ResizeDiskREST, GetVlanREST,
StorageDetail, DiskDetail, StorageDetail, DiskDetail,
MessageList, MessageDetail, MessageCreate, MessageDelete, MessageList, MessageDetail, MessageCreate, MessageDelete,
EnableTwoFactorView, DisableTwoFactorView, EnableTwoFactorView, DisableTwoFactorView,
AclUserGroupAutocomplete, AclUserAutocomplete, AclUserGroupAutocomplete, AclUserAutocomplete,
RescheduleView, GroupImportView, GroupExportView, RescheduleView, GroupImportView, GroupExportView,
) )
from .views.node import node_ops from .views.node import node_ops, NodeREST, GetNodeREST
from .views.vm import vm_ops, vm_mass_ops from .views.vm import vm_ops, vm_mass_ops
urlpatterns = [ urlpatterns = [
path('acpi/vm/', InstanceREST.as_view()), path('acpi/vm/', InstanceREST.as_view()),
path('acpi/node/', NodeREST.as_view()),
path('acpi/node/<int:pk>/', GetNodeREST.as_view()),
path('acpi/vm/<int:pk>/', GetInstanceREST.as_view()), path('acpi/vm/<int:pk>/', GetInstanceREST.as_view()),
path('acpi/template/', TemplateREST.as_view()), path('acpi/template/', TemplateREST.as_view()),
path('acpi/ft/', InstanceFromTemplateREST.as_view()), path('acpi/ft/', InstanceFromTemplateREST.as_view()),
...@@ -77,9 +80,15 @@ urlpatterns = [ ...@@ -77,9 +80,15 @@ urlpatterns = [
path('acpi/disk/', DiskRest.as_view()), path('acpi/disk/', DiskRest.as_view()),
path('acpi/disk/<int:pk>/', GetDiskRest.as_view()), path('acpi/disk/<int:pk>/', GetDiskRest.as_view()),
path('acpi/interface/', InterfaceREST.as_view()), path('acpi/interface/', InterfaceREST.as_view()),
path('acpi/vlan/', VlanREST.as_view()),
path('acpi/vlan/<int:pk>/', GetVlanREST.as_view()),
path('acpi/interface/<int:pk>/', GetInterfaceREST.as_view()), path('acpi/interface/<int:pk>/', GetInterfaceREST.as_view()),
path('acpi/ftusers/', InstanceFTforUsersREST.as_view()), path('acpi/ftusers/', InstanceFTforUsersREST.as_view()),
path('acpi/vm/<int:pk>/downloaddisk/', DownloadDiskREST.as_view()), path('acpi/vm/<int:pk>/downloaddisk/', DownloadDiskREST.as_view()),
path('acpi/vm/<int:pk>/createdisk/', CreateDiskREST.as_view()),
path('acpi/vm/<int:pk>/deploy/', DeployInstanceREST.as_view()),
path('acpi/vm/<int:pk>/shutdown/', ShutdownInstanceREST.as_view()),
path('acpi/vm/<int:pk>/resizedisk/', ResizeDiskREST.as_view()),
url(r'^$', IndexView.as_view(), name="dashboard.index"), url(r'^$', IndexView.as_view(), name="dashboard.index"),
url(r"^profile/list/$", UserList.as_view(), url(r"^profile/list/$", UserList.as_view(),
name="dashboard.views.user-list"), name="dashboard.views.user-list"),
......
...@@ -31,6 +31,12 @@ from django.template.loader import render_to_string ...@@ -31,6 +31,12 @@ from django.template.loader import render_to_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import DetailView, TemplateView, View from django.views.generic import DetailView, TemplateView, View
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser
from rest_framework.authentication import TokenAuthentication, BasicAuthentication
from rest_framework.permissions import IsAdminUser
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
...@@ -45,6 +51,35 @@ from .util import AjaxOperationMixin, OperationView, GraphMixin, DeleteViewBase ...@@ -45,6 +51,35 @@ from .util import AjaxOperationMixin, OperationView, GraphMixin, DeleteViewBase
from manager.mancelery import crontab_parser from manager.mancelery import crontab_parser
from dashboard.serializers import NodeSerializer
class NodeREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def get(self, request, format=None):
if request.query_params.get('name'):
try:
template = Node.objects.filter(name__istartswith=request.query_params.get('name')).get()
serializer = NodeSerializer(template, many=False)
return JsonResponse(serializer.data, safe=False)
except:
return JsonResponse({}, status=404)
templates = Node.objects.all()
serializer = NodeSerializer(templates, many=True)
return JsonResponse(serializer.data, safe=False)
class GetNodeREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def get(self, request, pk, format=None):
interface = Node.objects.get(pk=pk)
serializer = NodeSerializer(interface, many=False)
return JsonResponse(serializer.data, safe=False)
def get_operations(instance, user): def get_operations(instance, user):
ops = [] ops = []
......
...@@ -20,6 +20,7 @@ from datetime import timedelta ...@@ -20,6 +20,7 @@ from datetime import timedelta
import json import json
import logging import logging
from string import Template from string import Template
from xml.dom import NotFoundErr
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -283,6 +284,13 @@ class LeaseREST(APIView): ...@@ -283,6 +284,13 @@ class LeaseREST(APIView):
permission_classes = [IsAdminUser] permission_classes = [IsAdminUser]
def get(self, request, format=None): def get(self, request, format=None):
if request.query_params.get('name'):
try:
template = Lease.objects.filter(name__istartswith=request.query_params.get('name')).get()
serializer = LeaseSerializer(template, many=False)
return JsonResponse(serializer.data, safe=False)
except:
return JsonResponse({}, status=404)
templates = Lease.objects.all() templates = Lease.objects.all()
serializer = LeaseSerializer(templates, many=True) serializer = LeaseSerializer(templates, many=True)
return JsonResponse(serializer.data, safe=False) return JsonResponse(serializer.data, safe=False)
......
...@@ -21,6 +21,7 @@ import logging ...@@ -21,6 +21,7 @@ import logging
from collections import OrderedDict from collections import OrderedDict
from os import getenv from os import getenv
from urllib import response from urllib import response
from xml.dom import ValidationErr
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
...@@ -43,7 +44,7 @@ from django.views.generic import ( ...@@ -43,7 +44,7 @@ from django.views.generic import (
) )
from braces.views import SuperuserRequiredMixin, LoginRequiredMixin from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
from vm.operations import DownloadDiskOperation from vm.operations import DeployOperation, DestroyOperation, DownloadDiskOperation, ShutdownOperation, RenewOperation, ResizeDiskOperation
from common.models import ( from common.models import (
create_readable, HumanReadableException, fetch_human_exception, create_readable, HumanReadableException, fetch_human_exception,
...@@ -78,6 +79,7 @@ from request.forms import LeaseRequestForm, TemplateRequestForm ...@@ -78,6 +79,7 @@ from request.forms import LeaseRequestForm, TemplateRequestForm
from ..models import Favourite, pwgen from ..models import Favourite, pwgen
from manager.scheduler import has_traits from manager.scheduler import has_traits
import re
try: try:
# Python 2: "unicode" is built-in # Python 2: "unicode" is built-in
...@@ -95,7 +97,36 @@ from rest_framework.parsers import JSONParser ...@@ -95,7 +97,36 @@ from rest_framework.parsers import JSONParser
from rest_framework.authentication import TokenAuthentication, BasicAuthentication from rest_framework.authentication import TokenAuthentication, BasicAuthentication
from rest_framework.permissions import IsAdminUser from rest_framework.permissions import IsAdminUser
from dashboard.serializers import InstanceSerializer, InterfaceSerializer, CreateDiskSerializer, DownloadDiskSerializer from dashboard.serializers import (
DiskSerializer, InstanceSerializer, InterfaceSerializer, CreateDiskSerializer, DownloadDiskSerializer,
VMDeploySerializer, VlanSerializer, ResizeDiskSerializer
)
class VlanREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def get(self, request, format=None):
if request.query_params.get('name'):
try:
template = Vlan.objects.filter(name__istartswith=request.query_params.get('name')).get()
serializer = VlanSerializer(template, many=False)
return JsonResponse(serializer.data, safe=False)
except:
return JsonResponse({}, status=404)
templates = Vlan.objects.all()
serializer = VlanSerializer(templates, many=True)
return JsonResponse(serializer.data, safe=False)
class GetVlanREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def get(self, request, pk, format=None):
interface = Vlan.objects.get(pk=pk)
serializer = VlanSerializer(interface, many=False)
return JsonResponse(serializer.data, safe=False)
class InterfaceREST(APIView): class InterfaceREST(APIView):
...@@ -150,6 +181,8 @@ class InstanceREST(APIView): ...@@ -150,6 +181,8 @@ class InstanceREST(APIView):
with inst.activity(code_suffix='create', with inst.activity(code_suffix='create',
readable_name=ugettext_noop("create instance (REST)"), readable_name=ugettext_noop("create instance (REST)"),
on_commit=__on_commit, user=inst.owner) as act: on_commit=__on_commit, user=inst.owner) as act:
lease = Lease.objects.get(pk=str(data['lease']))
RenewOperation(inst).call(user=request.user, lease=lease, force=True, save=True)
for net in networks: for net in networks:
Interface.create(instance=inst, vlan=net.vlan, Interface.create(instance=inst, vlan=net.vlan,
owner=inst.owner, managed=net.managed, owner=inst.owner, managed=net.managed,
...@@ -173,6 +206,56 @@ class GetInstanceREST(APIView): ...@@ -173,6 +206,56 @@ class GetInstanceREST(APIView):
serializer = InstanceSerializer(instance, many=False) serializer = InstanceSerializer(instance, many=False)
return JsonResponse(serializer.data, safe=False) return JsonResponse(serializer.data, safe=False)
def post(self, request, pk, format=None):
instance = Instance.objects.get(pk=pk)
data = JSONParser().parse(request)
deploy = VMDeploySerializer(data=data)
if deploy.is_valid():
if 'node' in data:
selected = Node.objects.get(pk=int(data['node']))
DeployOperation(instance).call(node=selected, user=instance.owner)
else:
DeployOperation(instance).call(node=None, user=instance.owner)
serializer = InstanceSerializer(instance, many=False)
return JsonResponse(serializer.data, safe=False)
return JsonResponse(deploy.errors, status=400)
def delete(self, request, pk, format=None):
instance = Instance.objects.get(pk=pk)
DestroyOperation(instance).call(user=instance.owner)
serializer = InstanceSerializer(instance, many=False)
return JsonResponse(serializer.data, status=401)
class DeployInstanceREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def post(self, request, pk, format=None):
instance = Instance.objects.get(pk=pk)
data = JSONParser().parse(request)
deploy = VMDeploySerializer(data=data)
if deploy.is_valid():
if 'node' in data:
selected = Node.objects.get(pk=int(data['node']))
DeployOperation(instance).call(node=selected, user=instance.owner)
else:
DeployOperation(instance).call(node=None, user=instance.owner)
serializer = InstanceSerializer(instance, many=False)
return JsonResponse(serializer.data, safe=False)
return JsonResponse(deploy.errors, status=400)
class ShutdownInstanceREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def post(self, request, pk, format=None):
instance = Instance.objects.get(pk=pk)
ShutdownOperation(instance).call(user=instance.owner)
serializer = InstanceSerializer(instance, many=False)
return JsonResponse(serializer.data, status=400)
class DownloadDiskREST(APIView): class DownloadDiskREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication] authentication_classes = [TokenAuthentication,BasicAuthentication]
...@@ -186,11 +269,28 @@ class DownloadDiskREST(APIView): ...@@ -186,11 +269,28 @@ class DownloadDiskREST(APIView):
disk_url = str(data['url']) disk_url = str(data['url'])
disk_name = str(data['name']) disk_name = str(data['name'])
instance = Instance.objects.get(pk=vm_id) instance = Instance.objects.get(pk=vm_id)
old_disks = list(instance.disks.all())
DownloadDiskOperation(instance)._async(name=disk_name, url=disk_url, user=instance.owner) DownloadDiskOperation(instance)._async(name=disk_name, url=disk_url, user=instance.owner)
ret = InstanceSerializer(instance, many=False) new_disks = instance.disks.all()
for d in new_disks:
if d not in old_disks:
ret = DiskSerializer(d, many=False)
return JsonResponse(ret.data, status=201) return JsonResponse(ret.data, status=201)
return JsonResponse(serializer.errors, status=400) return JsonResponse(serializer.errors, status=400)
def size_util(size: str):
size_dict = {
"GB": 1000000000,
"Gi": 1073741824,
"MB": 1000000,
"Mi": 1048576,
"KB": 1000,
"Ki": 1024,
}
res = re.search(r"(\d*)\s*(\w*)", size)
if res and res.group(1) and res.group(2):
return int(res.group(1)) * size_dict[str(res.group(2))]
raise ValidationErr()
class CreateDiskREST(APIView): class CreateDiskREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication] authentication_classes = [TokenAuthentication,BasicAuthentication]
...@@ -201,12 +301,35 @@ class CreateDiskREST(APIView): ...@@ -201,12 +301,35 @@ class CreateDiskREST(APIView):
serializer = CreateDiskSerializer(data=data) serializer = CreateDiskSerializer(data=data)
if serializer.is_valid(): if serializer.is_valid():
vm_id = pk vm_id = pk
disk_size = int(data['size']) disk_size = str(size_util(str(data['size'])))
disk_name = str(data['name']) disk_name = str(data['name'])
instance = Instance.objects.get(pk=vm_id) instance = Instance.objects.get(pk=vm_id)
old_disks = list(instance.disks.all())
instance.create_disk(size=disk_size, user=instance.owner, instance.create_disk(size=disk_size, user=instance.owner,
name=disk_name, activity=None) name=disk_name, activity=None)
ret = InstanceSerializer(instance, many=False) new_disks = instance.disks.all()
for d in new_disks:
if d not in old_disks:
ret = DiskSerializer(d, many=False)
return JsonResponse(ret.data, status=201)
return JsonResponse(serializer.errors, status=400)
class ResizeDiskREST(APIView):
authentication_classes = [TokenAuthentication,BasicAuthentication]
permission_classes = [IsAdminUser]
def post(self, request, pk, format=None):
data = JSONParser().parse(request)
serializer = ResizeDiskSerializer(data=data)
if serializer.is_valid():
vm_id = pk
disk_size = str(size_util(str(data['size'])))
disk_id = str(data['disk'])
instance = Instance.objects.get(pk=vm_id)
disk = Disk.objects.get(pk=disk_id)
ResizeDiskOperation(instance).call(disk=disk, size=disk_size, user=instance.owner)
ret = DiskSerializer(disk, many=False)
return JsonResponse(ret.data, status=201) return JsonResponse(ret.data, status=201)
return JsonResponse(serializer.errors, status=400) return JsonResponse(serializer.errors, status=400)
......
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