mirror of
https://github.com/retspen/webvirtcloud
synced 2025-07-31 12:41:08 +00:00
Rest framework (#24)
* Add rest framework for API: First Commit * modify some shell scripts to make variable references safer; modify some python scripts to reduce the code complexity and cyclomatic complexity of functions. * Add REST API for some webvirtcloud functions. Instance list/delete/create, compute list/delete/create, storages-network list/retrieve. Add swagger and redoc for API interface * update requirements Co-authored-by: herengui <herengui@uniontech.com>
This commit is contained in:
parent
92254401dc
commit
cfce71ec2b
42 changed files with 1170 additions and 348 deletions
100
instances/api/serializers.py
Normal file
100
instances/api/serializers.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from instances.models import Flavor, Instance, MigrateInstance, CreateInstance
|
||||
|
||||
|
||||
class InstanceSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Instance
|
||||
fields = ['id', 'compute', 'name', 'uuid', 'is_template', 'created', 'drbd']
|
||||
|
||||
|
||||
class InstanceDetailsSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Instance
|
||||
fields = [
|
||||
'id',
|
||||
'compute',
|
||||
'status',
|
||||
'uuid',
|
||||
'name',
|
||||
'title',
|
||||
'description',
|
||||
'is_template',
|
||||
'created',
|
||||
'drbd',
|
||||
'arch',
|
||||
'machine',
|
||||
'vcpu',
|
||||
'memory',
|
||||
'firmware',
|
||||
'nvram',
|
||||
'bootmenu',
|
||||
'boot_order',
|
||||
'disks',
|
||||
'media',
|
||||
'media_iso',
|
||||
'snapshots',
|
||||
'networks',
|
||||
'console_type',
|
||||
'console_port',
|
||||
'console_keymap',
|
||||
'console_listener_address',
|
||||
'video_model',
|
||||
'guest_agent_ready',
|
||||
'autostart']
|
||||
|
||||
|
||||
class FlavorSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Flavor
|
||||
fields = ['label', 'memory', 'vcpu', 'disk']
|
||||
|
||||
|
||||
class CreateInstanceSerializer(serializers.ModelSerializer):
|
||||
firmware_choices = (
|
||||
('', 'BIOS'),
|
||||
#('UEFI', 'UEFI'),
|
||||
)
|
||||
firmware = serializers.ChoiceField(choices = firmware_choices)
|
||||
graphics = serializers.CharField(initial='vnc')
|
||||
video = serializers.CharField(initial='vga')
|
||||
storage = serializers.CharField(initial='default')
|
||||
cache_mode = serializers.CharField(initial='none')
|
||||
virtio = serializers.BooleanField(initial=True)
|
||||
qemu_ga = serializers.BooleanField(initial=True)
|
||||
|
||||
class Meta:
|
||||
model = CreateInstance
|
||||
fields = [
|
||||
'name',
|
||||
'firmware',
|
||||
'vcpu',
|
||||
'vcpu_mode',
|
||||
'memory',
|
||||
'networks',
|
||||
'mac',
|
||||
'nwfilter',
|
||||
'storage',
|
||||
'hdd_size',
|
||||
'cache_mode',
|
||||
'meta_prealloc',
|
||||
'virtio',
|
||||
'qemu_ga',
|
||||
'console_pass',
|
||||
'graphics',
|
||||
'video',
|
||||
'listener_addr'
|
||||
]
|
||||
|
||||
|
||||
class MigrateSerializer(serializers.ModelSerializer):
|
||||
instance = Instance.objects.all().prefetch_related("userinstance_set")
|
||||
live = serializers.BooleanField(initial=True)
|
||||
xml_del = serializers.BooleanField(initial=True)
|
||||
class Meta:
|
||||
model = MigrateInstance
|
||||
fields = ['instance', 'target_compute', 'live', 'xml_del', 'offline', 'autoconverge', 'compress', 'postcopy', 'unsafe']
|
||||
224
instances/api/viewsets.py
Normal file
224
instances/api/viewsets.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from appsettings.settings import app_settings
|
||||
from computes.models import Compute
|
||||
from computes import utils
|
||||
from instances.models import Flavor, Instance
|
||||
from instances.views import get_instance
|
||||
from instances.utils import migrate_instance
|
||||
from instances.views import poweron, powercycle, poweroff, force_off, suspend, resume, destroy as instance_destroy
|
||||
|
||||
from rest_framework import status, viewsets, permissions
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from vrtManager import util
|
||||
|
||||
from vrtManager.create import wvmCreate
|
||||
from .serializers import FlavorSerializer, InstanceSerializer, InstanceDetailsSerializer, MigrateSerializer, CreateInstanceSerializer
|
||||
|
||||
class InstancesViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A simple ViewSet for listing or retrieving ALL/Compute Instances.
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
def list(self, request):
|
||||
|
||||
if request.user.is_superuser or request.user.has_perm("instances.view_instances"):
|
||||
queryset = Instance.objects.all().prefetch_related("userinstance_set")
|
||||
else:
|
||||
queryset = Instance.objects.filter(userinstance__user=request.user).prefetch_related("userinstance_set")
|
||||
serializer = InstanceSerializer(queryset, many=True, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None, compute_pk=None):
|
||||
queryset = get_instance(request.user, pk)
|
||||
serializer = InstanceSerializer(queryset, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class InstanceViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A simple ViewSet for listing or retrieving Compute Instances.
|
||||
"""
|
||||
#serializer_class = CreateInstanceSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
def list(self, request, compute_pk=None):
|
||||
compute = get_object_or_404(Compute, pk=compute_pk)
|
||||
|
||||
utils.refresh_instance_database(compute)
|
||||
|
||||
queryset = Instance.objects.filter(compute=compute).prefetch_related("userinstance_set")
|
||||
serializer = InstanceSerializer(queryset, many=True, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def retrieve(self, request, pk=None, compute_pk=None):
|
||||
queryset = get_instance(request.user, pk)
|
||||
serializer = InstanceDetailsSerializer(queryset, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def destroy(self, request, pk=None, compute_pk=None):
|
||||
instance_destroy(request, pk)
|
||||
return Response({'status': 'Instance is destroyed'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def poweron(self, request, pk=None):
|
||||
poweron(request, pk)
|
||||
return Response({'status': 'poweron command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def poweroff(self, request, pk=None):
|
||||
poweroff(request, pk)
|
||||
return Response({'status': 'poweroff command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def powercycle(self, request, pk=None):
|
||||
powercycle(request, pk)
|
||||
return Response({'status': 'powercycle command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def forceoff(self, request, pk=None):
|
||||
force_off(request, pk)
|
||||
return Response({'status': 'force off command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def suspend(self, request, pk=None):
|
||||
suspend(request, pk)
|
||||
return Response({'status': 'suspend command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def resume(self, request, pk=None):
|
||||
resume(request, pk)
|
||||
return Response({'status': 'resume command send'})
|
||||
|
||||
|
||||
class MigrateViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A viewset for migrating instances.
|
||||
"""
|
||||
serializer_class = MigrateSerializer
|
||||
queryset = ""
|
||||
|
||||
def create(self, request):
|
||||
serializer = MigrateSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
instance = serializer.validated_data['instance']
|
||||
target_host = serializer.validated_data['target_compute']
|
||||
live = serializer.validated_data['live']
|
||||
unsafe = serializer.validated_data['unsafe']
|
||||
xml_del = serializer.validated_data['xml_del']
|
||||
offline = serializer.validated_data['offline']
|
||||
autoconverge = serializer.validated_data['autoconverge']
|
||||
postcopy = serializer.validated_data['postcopy']
|
||||
compress = serializer.validated_data['compress']
|
||||
|
||||
migrate_instance(target_host, instance, request.user, live, unsafe, xml_del, offline, autoconverge, compress, postcopy)
|
||||
|
||||
return Response({'status': 'instance migrate is started'})
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
|
||||
class FlavorViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint that allows flavor to be viewed.
|
||||
"""
|
||||
queryset = Flavor.objects.all().order_by('id')
|
||||
serializer_class = FlavorSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
class CreateInstanceViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A viewset for creating instances.
|
||||
"""
|
||||
serializer_class = CreateInstanceSerializer
|
||||
queryset = ""
|
||||
|
||||
def create(self, request, compute_pk=None, arch=None, machine=None):
|
||||
serializer = CreateInstanceSerializer(data=request.data,
|
||||
context = {'compute_pk': compute_pk,
|
||||
'arch': arch,
|
||||
'machine': machine
|
||||
})
|
||||
if serializer.is_valid():
|
||||
volume_list = []
|
||||
default_bus = app_settings.INSTANCE_VOLUME_DEFAULT_BUS
|
||||
default_io = app_settings.INSTANCE_VOLUME_DEFAULT_IO
|
||||
default_discard = app_settings.INSTANCE_VOLUME_DEFAULT_DISCARD
|
||||
default_zeroes = app_settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
|
||||
default_scsi_disk_model = app_settings.INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
|
||||
default_disk_format = app_settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
||||
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
||||
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
||||
compute = Compute.objects.get(pk=compute_pk)
|
||||
conn = wvmCreate(
|
||||
compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type,
|
||||
)
|
||||
|
||||
path = conn.create_volume(
|
||||
serializer.validated_data['storage'],
|
||||
serializer.validated_data['name'],
|
||||
serializer.validated_data['hdd_size'],
|
||||
default_disk_format,
|
||||
serializer.validated_data['meta_prealloc'],
|
||||
default_disk_owner_uid,
|
||||
default_disk_owner_gid,
|
||||
)
|
||||
volume = {}
|
||||
firmware = {}
|
||||
volume["device"] = "disk"
|
||||
volume["path"] = path
|
||||
volume["type"] = conn.get_volume_format_type(path)
|
||||
volume["cache_mode"] = serializer.validated_data["cache_mode"]
|
||||
volume["bus"] = default_bus
|
||||
if volume["bus"] == "scsi":
|
||||
volume["scsi_model"] = default_scsi_disk_model
|
||||
volume["discard_mode"] = default_discard
|
||||
volume["detect_zeroes_mode"] = default_zeroes
|
||||
volume["io_mode"] = default_io
|
||||
|
||||
volume_list.append(volume)
|
||||
|
||||
if "UEFI" in serializer.validated_data['firmware']:
|
||||
firmware["loader"] = serializer.validated_data['firmware'].split(":")[1].strip()
|
||||
firmware["secure"] = "no"
|
||||
firmware["readonly"] = "yes"
|
||||
firmware["type"] = "pflash"
|
||||
if "secboot" in firmware["loader"] and machine != "q35":
|
||||
machine = "q35"
|
||||
firmware["secure"] = "yes"
|
||||
|
||||
ret = conn.create_instance(
|
||||
serializer.validated_data['name'],
|
||||
serializer.validated_data['memory'],
|
||||
serializer.validated_data['vcpu'],
|
||||
serializer.validated_data['vcpu_mode'],
|
||||
util.randomUUID(),
|
||||
arch,
|
||||
machine,
|
||||
firmware,
|
||||
volume_list,
|
||||
serializer.validated_data['networks'],
|
||||
serializer.validated_data['nwfilter'],
|
||||
serializer.validated_data['graphics'],
|
||||
serializer.validated_data['virtio'],
|
||||
serializer.validated_data['listener_addr'],
|
||||
serializer.validated_data['video'],
|
||||
serializer.validated_data['console_pass'],
|
||||
serializer.validated_data['mac'],
|
||||
serializer.validated_data['qemu_ga'],
|
||||
)
|
||||
msg = f"Instance {serializer.validated_data['name']} is created"
|
||||
return Response({'status': msg })
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
|
@ -4,9 +4,9 @@ from django import forms
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from appsettings.models import AppSettings
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES, QEMU_KEYMAPS
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTENER_ADDRESSES, QEMU_KEYMAPS
|
||||
|
||||
from .models import Flavor
|
||||
from .models import CreateInstance, Flavor
|
||||
|
||||
|
||||
class FlavorForm(forms.ModelForm):
|
||||
|
|
@ -29,32 +29,36 @@ class ConsoleForm(forms.Form):
|
|||
type_choices = ((c, c) for c in AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list())
|
||||
keymap_choices = [('auto', 'Auto')] + list((c, c) for c in QEMU_KEYMAPS)
|
||||
self.fields['type'] = forms.ChoiceField(choices=type_choices)
|
||||
self.fields['listen_on'] = forms.ChoiceField(choices=QEMU_CONSOLE_LISTEN_ADDRESSES)
|
||||
self.fields['listen_on'] = forms.ChoiceField(choices=QEMU_CONSOLE_LISTENER_ADDRESSES)
|
||||
self.fields['keymap'] = forms.ChoiceField(choices=keymap_choices)
|
||||
|
||||
|
||||
class NewVMForm(forms.Form):
|
||||
name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')}, max_length=64)
|
||||
firmware = forms.CharField(max_length=50, required=False)
|
||||
vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')})
|
||||
vcpu_mode = forms.CharField(max_length=20, required=False)
|
||||
disk = forms.IntegerField(required=False)
|
||||
memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
||||
networks = forms.CharField(error_messages={'required': _('No Network pool has been choosen')})
|
||||
nwfilter = forms.CharField(required=False)
|
||||
storage = forms.CharField(max_length=20, required=False)
|
||||
template = forms.CharField(required=False)
|
||||
images = forms.CharField(required=False)
|
||||
cache_mode = forms.CharField(error_messages={'required': _('Please select HDD cache mode')})
|
||||
hdd_size = forms.IntegerField(required=False)
|
||||
meta_prealloc = forms.BooleanField(required=False)
|
||||
virtio = forms.BooleanField(required=False)
|
||||
qemu_ga = forms.BooleanField(required=False)
|
||||
mac = forms.CharField(required=False)
|
||||
console_pass = forms.CharField(required=False, empty_value="", widget=forms.PasswordInput())
|
||||
graphics = forms.CharField(error_messages={'required': _('Please select a graphics type')})
|
||||
video = forms.CharField(error_messages={'required': _('Please select a video driver')})
|
||||
listener_addr = forms.ChoiceField(required=True, widget=forms.RadioSelect, choices=QEMU_CONSOLE_LISTEN_ADDRESSES)
|
||||
class NewVMForm(forms.ModelForm):
|
||||
# name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')}, max_length=64)
|
||||
# firmware = forms.CharField(max_length=50, required=False)
|
||||
# vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')})
|
||||
# vcpu_mode = forms.CharField(max_length=20, required=False)
|
||||
# disk = forms.IntegerField(required=False)
|
||||
# memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
||||
# networks = forms.CharField(error_messages={'required': _('No Network pool has been choosen')})
|
||||
# nwfilter = forms.CharField(required=False)
|
||||
# storage = forms.CharField(max_length=20, required=False)
|
||||
# template = forms.CharField(required=False)
|
||||
# images = forms.CharField(required=False)
|
||||
# cache_mode = forms.CharField(error_messages={'required': _('Please select HDD cache mode')})
|
||||
# hdd_size = forms.IntegerField(required=False)
|
||||
# meta_prealloc = forms.BooleanField(required=False)
|
||||
# virtio = forms.BooleanField(required=False)
|
||||
# qemu_ga = forms.BooleanField(required=False)
|
||||
# mac = forms.CharField(required=False)
|
||||
# console_pass = forms.CharField(required=False, empty_value="", widget=forms.PasswordInput())
|
||||
# graphics = forms.CharField(error_messages={'required': _('Please select a graphics type')})
|
||||
# video = forms.CharField(error_messages={'required': _('Please select a video driver')})
|
||||
# listener_addr = forms.ChoiceField(required=True, widget=forms.RadioSelect, choices=QEMU_CONSOLE_LISTENER_ADDRESSES)
|
||||
class Meta:
|
||||
model = CreateInstance
|
||||
fields = '__all__'
|
||||
exclude = ['compute']
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
|
|
|
|||
22
instances/migrations/0010_auto_20220722_0812.py
Normal file
22
instances/migrations/0010_auto_20220722_0812.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 3.2.14 on 2022-07-22 08:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('instances', '0009_auto_20200717_0524'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='permissionset',
|
||||
options={'default_permissions': (), 'managed': False, 'permissions': [('clone_instances', 'Can clone instances'), ('passwordless_console', 'Can access console without password'), ('view_instances', 'Can view instances'), ('snapshot_instances', 'Can snapshot instances')]},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='instance',
|
||||
name='drbd',
|
||||
field=models.CharField(default='None', max_length=24, verbose_name='drbd'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from libvirt import VIR_DOMAIN_XML_SECURE
|
||||
from vrtManager.create import wvmCreate
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTENER_ADDRESSES
|
||||
|
||||
from computes.models import Compute
|
||||
from vrtManager.instance import wvmInstance
|
||||
|
|
@ -150,8 +153,8 @@ class Instance(models.Model):
|
|||
return self.proxy.get_console_keymap()
|
||||
|
||||
@cached_property
|
||||
def console_listen_address(self):
|
||||
return self.proxy.get_console_listen_addr()
|
||||
def console_listener_address(self):
|
||||
return self.proxy.get_console_listener_addr()
|
||||
|
||||
@cached_property
|
||||
def guest_agent(self):
|
||||
|
|
@ -206,6 +209,50 @@ class Instance(models.Model):
|
|||
return self.proxy.get_image_formats()
|
||||
|
||||
|
||||
class MigrateInstance(models.Model):
|
||||
instance = models.ForeignKey(Instance, on_delete=models.DO_NOTHING)
|
||||
target_compute = models.ForeignKey(Compute, related_name='target', on_delete=models.DO_NOTHING)
|
||||
|
||||
live = models.BooleanField(_('Live'), blank=False)
|
||||
xml_del = models.BooleanField(_('Undefine XML'), blank=False, default=True)
|
||||
offline = models.BooleanField(_('Offline'), blank=False)
|
||||
autoconverge = models.BooleanField(_('Auto Converge'), blank=False, default=True)
|
||||
compress = models.BooleanField(_('Compress'), blank=False, default=False)
|
||||
postcopy = models.BooleanField(_('Post Copy'), blank=False, default=False)
|
||||
unsafe = models.BooleanField(_('Unsafe'), blank=False, default=False)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
|
||||
|
||||
class CreateInstance(models.Model):
|
||||
compute = models.ForeignKey(Compute, related_name='host', on_delete=models.DO_NOTHING)
|
||||
name = models.CharField(max_length=64, error_messages={'required': _('No Virtual Machine name has been entered')})
|
||||
firmware = models.CharField(max_length=50)
|
||||
vcpu = models.IntegerField(error_messages={'required': _('No VCPU has been entered')})
|
||||
vcpu_mode = models.CharField(max_length=20, blank=True)
|
||||
disk = models.IntegerField(blank=True)
|
||||
memory = models.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
||||
networks = models.CharField(max_length=256, error_messages={'required': _('No Network pool has been choosen')})
|
||||
nwfilter = models.CharField(max_length=256, blank=True)
|
||||
storage = models.CharField(max_length=256, blank=True)
|
||||
template = models.CharField(max_length=256, blank=True)
|
||||
images = models.CharField(max_length=256, blank=True)
|
||||
cache_mode = models.CharField(max_length=12, error_messages={'required': _('Please select HDD cache mode')})
|
||||
hdd_size = models.IntegerField(blank=True)
|
||||
meta_prealloc = models.BooleanField(default=False, blank=True)
|
||||
virtio = models.BooleanField(default=True)
|
||||
qemu_ga = models.BooleanField(default=False)
|
||||
mac = models.CharField(max_length=17, blank=True)
|
||||
console_pass = models.CharField(max_length=64, blank=True)
|
||||
graphics = models.CharField(max_length=12, error_messages={'required': _('Please select a graphics type')})
|
||||
video = models.CharField(max_length=12, error_messages={'required': _('Please select a video driver')})
|
||||
listener_addr = models.CharField(max_length=20, choices=QEMU_CONSOLE_LISTENER_ADDRESSES)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
|
||||
|
||||
class PermissionSet(models.Model):
|
||||
"""
|
||||
Dummy model for holding set of permissions we need to be automatically added by Django
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
<td>
|
||||
<span class="text-success">{% trans "Connected" %}</span>
|
||||
</td>
|
||||
{% if app_settings.VM_DRBD_STATUS == 'True' %}
|
||||
<td class="d-none d-sm-table-cell"></td>
|
||||
{% endif %}
|
||||
{% if app_settings.VM_DRBD_STATUS == 'True' %}
|
||||
<td class="d-none d-sm-table-cell"></td>
|
||||
{% endif %}
|
||||
<td class="d-none d-sm-table-cell text-center">{{ compute.cpu_count }}</td>
|
||||
<td class="d-none d-sm-table-cell text-right">{{ compute.ram_size|filesizeformat }}</td>
|
||||
<td>
|
||||
|
|
@ -68,11 +68,11 @@
|
|||
<span class="text-warning">{% trans "Suspended" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if app_settings.VM_DRBD_STATUS == 'True' %}
|
||||
<td>
|
||||
{% if instance.drbd == "Primary/OK" or instance.drbd == "Secondary/OK" %}<span class="text-success">{% else %}<span class="text-danger">{% endif %}{{ instance.drbd }}</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if app_settings.VM_DRBD_STATUS == 'True' %}
|
||||
<td>
|
||||
{% if instance.drbd == "Primary/OK" or instance.drbd == "Secondary/OK" %}<span class="text-success">{% else %}<span class="text-danger">{% endif %}{{ instance.drbd }}</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{{ instance.proxy.instance.info.3 }}</td>
|
||||
<td>{{ instance.cur_memory }} MB</td>
|
||||
<td class="text-nowrap">
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
<strong>{{ field.label }}:</strong> <span>{{ error|escape }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
<strong>{{ field.label }}:</strong> <span>{{ error|escape }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -269,9 +269,9 @@
|
|||
});
|
||||
$(document).ready(function () {
|
||||
// set current console listen address or fall back to default
|
||||
var console_listen_address = "{{ console_listen_address }}";
|
||||
if (console_listen_address != '') {
|
||||
$("#console_select_listen_address option[value='" + console_listen_address + "']").prop('selected', true);
|
||||
var console_listener_address = "{{ console_listener_address }}";
|
||||
if (console_listener_address != '') {
|
||||
$("#console_select_listener_address option[value='" + console_listener_address + "']").prop('selected', true);
|
||||
}
|
||||
});
|
||||
$(document).ready(function () {
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ def instance(request, pk):
|
|||
console_form = ConsoleForm(
|
||||
initial={
|
||||
"type": instance.console_type,
|
||||
"listen_on": instance.console_listen_address,
|
||||
"listen_on": instance.console_listener_address,
|
||||
"password": instance.console_passwd,
|
||||
"keymap": instance.console_keymap,
|
||||
}
|
||||
)
|
||||
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
console_listener_addresses = settings.QEMU_CONSOLE_LISTENER_ADDRESSES
|
||||
bottom_bar = app_settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR
|
||||
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
||||
try:
|
||||
|
|
@ -344,7 +344,7 @@ def destroy(request, pk):
|
|||
except Exception:
|
||||
userinstance = UserInstance(is_delete=request.user.is_superuser)
|
||||
|
||||
if request.method == "POST" and userinstance.is_delete:
|
||||
if request.method in ["POST", "DELETE"] and userinstance.is_delete:
|
||||
if instance.proxy.get_status() == 1:
|
||||
instance.proxy.force_shutdown()
|
||||
|
||||
|
|
@ -390,7 +390,7 @@ def migrate(request, pk):
|
|||
target_host = Compute.objects.get(id=compute_id)
|
||||
|
||||
try:
|
||||
utils.migrate_instance(target_host, instance, request.user, live, unsafe, xml_del, offline)
|
||||
utils.migrate_instance(target_host, instance, request.user, live, unsafe, xml_del, offline, autoconverge, compress, postcopy)
|
||||
except libvirtError as err:
|
||||
messages.error(request, err)
|
||||
|
||||
|
|
@ -1239,7 +1239,7 @@ def update_console(request, pk):
|
|||
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
||||
|
||||
if "listen_on" in form.changed_data:
|
||||
instance.proxy.set_console_listen_addr(form.cleaned_data["listen_on"])
|
||||
instance.proxy.set_console_listener_addr(form.cleaned_data["listen_on"])
|
||||
msg = _("Set VNC listen address")
|
||||
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
||||
|
||||
|
|
@ -1385,7 +1385,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
||||
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
||||
default_scsi_disk_model = app_settings.INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
|
||||
listener_addr = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
listener_addr = settings.QEMU_CONSOLE_LISTENER_ADDRESSES
|
||||
mac_auto = util.randomMAC()
|
||||
disk_devices = conn.get_disk_device_types(arch, machine)
|
||||
disk_buses = conn.get_disk_bus_types(arch, machine)
|
||||
|
|
@ -1545,7 +1545,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
volumes=volume_list,
|
||||
networks=data["networks"],
|
||||
virtio=data["virtio"],
|
||||
listen_addr=data["listener_addr"],
|
||||
listener_addr=data["listener_addr"],
|
||||
nwfilter=data["nwfilter"],
|
||||
graphics=data["graphics"],
|
||||
video=data["video"],
|
||||
|
|
@ -1568,6 +1568,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
messages.error(request, lib_err)
|
||||
|
||||
return render(request, "create_instance_w2.html", locals())
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue