diff --git a/instances/templates/instances/destroy_instance_form.html b/instances/templates/instances/destroy_instance_form.html
index 94265ec..9ae46f2 100644
--- a/instances/templates/instances/destroy_instance_form.html
+++ b/instances/templates/instances/destroy_instance_form.html
@@ -4,9 +4,9 @@
{% trans "Confirm Destroy" %}
{% endblock title %}
-{% block page_header %}
+{% block page_heading %}
{% trans "Destroy instance" %} {{ instance }}
-{% endblock page_header %}
+{% endblock page_heading %}
{% block content %}
{% if request.user.is_superuser or userinstance.is_delete %}
diff --git a/instances/tests.py b/instances/tests.py
index 3dac112..5c70322 100644
--- a/instances/tests.py
+++ b/instances/tests.py
@@ -1,65 +1,138 @@
-import tempfile
+import re
+from accounts.models import UserAttributes, UserInstance, UserSSHKey
+from appsettings.models import AppSettings
+from computes.models import Compute
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Permission
+from django.http.response import Http404
from django.shortcuts import reverse
from django.test import TestCase
+from libvirt import VIR_DOMAIN_UNDEFINE_NVRAM
+from vrtManager.create import wvmCreate
+from vrtManager.util import randomUUID
-from computes.models import Compute
+from instances.views import instance
-from .models import Instance
+from .models import Flavor, Instance
+from .utils import refr
class InstancesTestCase(TestCase):
- def setUp(self):
- self.client.login(username='admin', password='admin')
- Compute(
- name='local',
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ # Add users for testing purposes
+ User = get_user_model()
+ cls.admin_user = User.objects.get(pk=1)
+ cls.test_user = User.objects.create(username='test-user')
+ UserAttributes.objects.create(
+ user=cls.test_user,
+ max_instances=1,
+ max_cpus=1,
+ max_memory=128,
+ max_disk_size=1,
+ )
+ permission = Permission.objects.get(codename='clone_instances')
+ cls.test_user.user_permissions.add(permission)
+
+ # Add localhost compute
+ cls.compute = Compute(
+ name='test-compute',
hostname='localhost',
login='',
password='',
details='local',
type=4,
- ).save()
+ )
+ cls.compute.save()
+
+ cls.connection = wvmCreate(
+ cls.compute.hostname,
+ cls.compute.login,
+ cls.compute.password,
+ cls.compute.type,
+ )
+
+ # Add disks for testing
+ cls.connection.create_volume(
+ 'default',
+ 'test-volume',
+ 1,
+ 'qcow2',
+ False,
+ 0,
+ 0,
+ )
+ # XML for testing vm
+ with open('conf/test-vm.xml', 'r') as f:
+ cls.xml = f.read()
+
+ # Create testing vm from XML
+ cls.connection._defineXML(cls.xml)
+ refr(cls.compute)
+ cls.instance: Instance = Instance.objects.get(pk=1)
+
+ @classmethod
+ def tearDownClass(cls):
+ # Destroy testing vm
+ cls.instance.proxy.delete_all_disks()
+ cls.instance.proxy.delete(VIR_DOMAIN_UNDEFINE_NVRAM)
+ super().tearDownClass()
+
+ def setUp(self):
+ self.client.login(username='admin', password='admin')
+ self.rsa_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6OOdbfv27QVnSC6sKxGaHb6YFc+3gxCkyVR3cTSXE/n5BEGf8aOgBpepULWa1RZfxYHY14PlKULDygdXSdrrR2kNSwoKz/Oo4d+3EE92L7ocl1+djZbptzgWgtw1OseLwbFik+iKlIdqPsH+IUQvX7yV545ZQtAP8Qj1R+uCqkw== test@test'
def test_index(self):
response = self.client.get(reverse('instances:index'))
- # with open('index.html', 'wb') as f:
- # f.write(response.content)
+ self.assertEqual(response.status_code, 200)
+
+ self.client.force_login(self.test_user)
+ response = self.client.get(reverse('instances:index'))
self.assertEqual(response.status_code, 200)
def test_create_select_type(self):
response = self.client.get(reverse('instances:create_instance_select_type', args=[1]))
self.assertEqual(response.status_code, 200)
- def test_instance(self):
- compute = Compute.objects.get(pk=1)
+ def test_instance_page(self):
+ response = self.client.get(reverse('instances:instance', args=[self.instance.id]))
+ self.assertEqual(response.status_code, 200)
- # create volume
- response = self.client.post(
- reverse('create_volume', args=[compute.id, 'default']),
- {
- 'name': 'test',
- 'format': 'qcow2',
- 'size': '1',
- 'meta_prealloc': False,
- },
- )
- self.assertRedirects(response, reverse('storage', args=[compute.id, 'default']))
+ self.client.force_login(self.test_user)
+ response = self.client.get(reverse('instances:instance', args=[self.instance.id]))
+ self.assertRaises(Http404)
- # create instance
- response = self.client.get(reverse('instances:create_instance', args=[compute.id, 'x86_64', 'q35']))
+ # def test_create_volume(self):
+ # response = self.client.post(
+ # reverse('create_volume', args=[self.compute.id, 'default']),
+ # {
+ # 'name': 'test',
+ # 'format': 'qcow2',
+ # 'size': '1',
+ # 'meta_prealloc': False,
+ # },
+ # )
+ # self.assertRedirects(response, reverse('storage', args=[self.compute.id, 'default']))
+
+ def test_create_destroy_instance(self):
+ # Create
+ response = self.client.get(reverse('instances:create_instance', args=[self.compute.id, 'x86_64', 'q35']))
self.assertEqual(response.status_code, 200)
response = self.client.post(
- reverse('instances:create_instance', args=[compute.id, 'x86_64', 'q35']),
+ reverse('instances:create_instance', args=[self.compute.id, 'x86_64', 'q35']),
{
'name': 'test',
'firmware': 'BIOS',
'vcpu': 1,
'vcpu_mode': 'host-model',
- 'memory': 512,
+ 'memory': 128,
'device0': 'disk',
'bus0': 'virtio',
- 'images': 'test.qcow2',
+ 'images': 'test-volume.qcow2',
'storage-control': 'default',
'image-control': 'test.qcow2',
'networks': 'default',
@@ -77,87 +150,200 @@ class InstancesTestCase(TestCase):
)
self.assertEqual(response.status_code, 302)
- instance: Instance = Instance.objects.get(pk=1)
- self.assertEqual(instance.name, 'test')
+ instance_qs: Instance = Instance.objects.filter(name='test')
+ self.assertEqual(len(instance_qs), 1)
- # get instance page
- response = self.client.get(reverse('instances:instance', args=[instance.id]))
+ instance = instance_qs[0]
+
+ # Destroy
+ response = self.client.get(reverse('instances:destroy', args=[instance.id]))
self.assertEqual(response.status_code, 200)
- # resize cpu
- self.assertEqual(instance.vcpu, 1)
- self.assertEqual(instance.cur_vcpu, 1)
+ response = self.client.post(
+ reverse('instances:destroy', args=[instance.id]),
+ {}, # do not destroy disk image
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertRedirects(response, reverse('instances:index'))
- response = self.client.post(reverse('instances:resizevm_cpu', args=[instance.id]), {'vcpu': 4, 'cur_vcpu': 2})
- self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
+ def test_create_from_xml(self):
+ uuid = randomUUID()
+ xml = self.xml.replace('test-vm', 'test-vm-xml')
+ xml = re.sub('\s?
.*? ', f'
{uuid} ', xml)
+ response = self.client.post(
+ reverse('instances:create_instance_select_type', args=[self.compute.id]),
+ {
+ 'create_xml': True,
+ 'dom_xml': xml,
+ },
+ )
+ self.assertEqual(response.status_code, 302)
- # reset cached properties
- del instance.vcpu
- del instance.cur_vcpu
- self.assertEqual(instance.vcpu, 4)
- self.assertEqual(instance.cur_vcpu, 2)
+ xml_instance_qs: Instance = Instance.objects.filter(name='test-vm-xml')
+ self.assertEqual(len(xml_instance_qs), 1)
- # resize memory
- self.assertEqual(instance.memory, 512)
- self.assertEqual(instance.cur_memory, 512)
+ xml_instance = xml_instance_qs[0]
- response = self.client.post(reverse('instances:resize_memory', args=[instance.id]), {
- 'memory': 2048,
- 'cur_memory': 1024
- })
- self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
-
- del instance.memory
- del instance.cur_memory
- self.assertEqual(instance.memory, 2048)
- self.assertEqual(instance.cur_memory, 1024)
-
- # resize disk
- self.assertEqual(instance.disks[0]['size'], 1024**3)
-
- response = self.client.post(reverse('instances:resize_disk', args=[instance.id]), {
- 'disk_size_vda': '2',
- })
- self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
-
- del instance.disks
- self.assertEqual(instance.disks[0]['size'], 2 * 1024**3)
-
- # add new volume
- self.assertEqual(len(instance.disks), 1)
+ # destroy started instance to maximize coverage
+ xml_instance.proxy.start()
response = self.client.post(
- reverse('instances:add_new_vol', args=[instance.id]),
+ reverse('instances:destroy', args=[xml_instance.id]),
+ {}, # do not delete disk image
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertRedirects(response, reverse('instances:index'))
+
+ def test_resize_cpu(self):
+ self.assertEqual(self.instance.vcpu, 1)
+ self.assertEqual(self.instance.cur_vcpu, 1)
+
+ response = self.client.post(reverse('instances:resizevm_cpu', args=[self.instance.id]), {
+ 'vcpu': 4,
+ 'cur_vcpu': 2,
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ # reset cached properties
+ del self.instance.vcpu
+ del self.instance.cur_vcpu
+
+ self.assertEqual(self.instance.vcpu, 4)
+ self.assertEqual(self.instance.cur_vcpu, 2)
+
+ def test_resize_cpu_with_quota(self):
+ # test for non admin user with quotas
+ vcpu = self.instance.vcpu
+ cur_vcpu = self.instance.cur_vcpu
+
+ UserInstance.objects.create(user=self.test_user, instance=self.instance, is_change=True)
+
+ self.client.force_login(self.test_user)
+
+ response = self.client.post(reverse('instances:resizevm_cpu', args=[self.instance.id]), {
+ 'vcpu': 4,
+ 'cur_vcpu': 2,
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ del self.instance.vcpu
+ del self.instance.cur_vcpu
+
+ # no changes as user reached quota
+ self.assertEqual(self.instance.vcpu, vcpu)
+ self.assertEqual(self.instance.cur_vcpu, cur_vcpu)
+
+ def test_resize_memory(self):
+ self.assertEqual(self.instance.memory, 128)
+ self.assertEqual(self.instance.cur_memory, 128)
+
+ response = self.client.post(reverse('instances:resize_memory', args=[self.instance.id]), {
+ 'memory': 512,
+ 'cur_memory': 256
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ del self.instance.memory
+ del self.instance.cur_memory
+ self.assertEqual(self.instance.memory, 512)
+ self.assertEqual(self.instance.cur_memory, 256)
+
+ response = self.client.post(reverse('instances:resize_memory', args=[self.instance.id]), {
+ 'memory_custom': 500,
+ 'cur_memory_custom': 200
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ del self.instance.memory
+ del self.instance.cur_memory
+
+ self.assertEqual(self.instance.memory, 500)
+ self.assertEqual(self.instance.cur_memory, 200)
+
+ def test_resize_memory_with_quota(self):
+ # test for non admin user with quotas
+ memory = self.instance.memory
+ cur_memory = self.instance.cur_memory
+
+ UserInstance.objects.create(user=self.test_user, instance=self.instance, is_change=True)
+
+ self.client.force_login(self.test_user)
+
+ response = self.client.post(reverse('instances:resize_memory', args=[self.instance.id]), {
+ 'memory': 512,
+ 'cur_memory': 256
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ del self.instance.memory
+ del self.instance.cur_memory
+
+ # no changes as user reached quota
+ self.assertEqual(self.instance.memory, memory)
+ self.assertEqual(self.instance.cur_memory, cur_memory)
+
+ def test_resize_disk(self):
+ self.assertEqual(self.instance.disks[0]['size'], 1024**3)
+
+ response = self.client.post(reverse('instances:resize_disk', args=[self.instance.id]), {
+ 'disk_size_vda': 2,
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ del self.instance.disks
+ self.assertEqual(self.instance.disks[0]['size'], 2 * 1024**3)
+
+ def test_resize_disk_with_quota(self):
+ # test for non admin user with quotas
+ disk_size = self.instance.disks[0]['size']
+ UserInstance.objects.create(user=self.test_user, instance=self.instance, is_change=True)
+
+ self.client.force_login(self.test_user)
+
+ response = self.client.post(reverse('instances:resize_disk', args=[self.instance.id]), {
+ 'disk_size_vda': 3,
+ })
+ self.assertRedirects(response, reverse('instances:instance', args=[self.instance.id]) + '#resize')
+
+ # no changes as user reached quota
+ del self.instance.disks
+ self.assertEqual(self.instance.disks[0]['size'], disk_size)
+
+ def test_add_delete_new_volume(self):
+ self.assertEqual(len(self.instance.disks), 1)
+
+ response = self.client.post(
+ reverse('instances:add_new_vol', args=[self.instance.id]),
{
'storage': 'default',
- 'name': 'test2',
+ 'name': 'test-volume-2',
'size': 1,
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.disks
- self.assertEqual(len(instance.disks), 2)
+ del self.instance.disks
+ self.assertEqual(len(self.instance.disks), 2)
- # delete volume from instance
response = self.client.post(
- reverse('instances:delete_vol', args=[instance.id]),
+ reverse('instances:delete_vol', args=[self.instance.id]),
{
'storage': 'default',
'dev': 'vdb',
- 'name': 'test2.qcow2',
+ 'name': 'test-volume-2.qcow2',
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.disks
- self.assertEqual(len(instance.disks), 1)
+ del self.instance.disks
+ self.assertEqual(len(self.instance.disks), 1)
+ def test_detach_attach_volume(self):
# detach volume
response = self.client.post(
- reverse('instances:detach_vol', args=[instance.id]),
+ reverse('instances:detach_vol', args=[self.instance.id]),
{
'dev': 'vda',
},
@@ -165,28 +351,28 @@ class InstancesTestCase(TestCase):
)
self.assertEqual(response.status_code, 302)
- del instance.disks
- self.assertEqual(len(instance.disks), 0)
+ del self.instance.disks
+ self.assertEqual(len(self.instance.disks), 0)
- # add existing volume
+ # reattach volume
response = self.client.post(
- reverse('instances:add_existing_vol', args=[instance.id]),
+ reverse('instances:add_existing_vol', args=[self.instance.id]),
{
'selected_storage': 'default',
- 'vols': 'test.qcow2',
+ 'vols': 'test-volume.qcow2',
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.disks
- self.assertEqual(len(instance.disks), 1)
+ del self.instance.disks
+ self.assertEqual(len(self.instance.disks), 1)
- # edit volume
+ def test_edit_volume(self):
response = self.client.post(
- reverse('instances:edit_volume', args=[instance.id]),
+ reverse('instances:edit_volume', args=[self.instance.id]),
{
- 'vol_path': '/var/lib/libvirt/images/test.qcow2',
+ 'vol_path': '/var/lib/libvirt/images/test-volume.qcow2',
# 'vol_shareable': False,
# 'vol_readonly': False,
'vol_bus': 'virtio',
@@ -199,11 +385,11 @@ class InstancesTestCase(TestCase):
)
self.assertEqual(response.status_code, 302)
- # add media device
- self.assertEqual(len(instance.media), 1)
+ def test_attach_detach_cdrom(self):
+ self.assertEqual(len(self.instance.media), 1)
response = self.client.post(
- reverse('instances:add_cdrom', args=[instance.id]),
+ reverse('instances:add_cdrom', args=[self.instance.id]),
{
'bus': 'sata',
},
@@ -211,8 +397,8 @@ class InstancesTestCase(TestCase):
)
self.assertEqual(response.status_code, 302)
- del instance.media
- self.assertEqual(len(instance.media), 2)
+ del self.instance.media
+ self.assertEqual(len(self.instance.media), 2)
# create dummy iso
# with tempfile.NamedTemporaryFile() as f:
@@ -226,139 +412,291 @@ class InstancesTestCase(TestCase):
# },
# )
- # remove media device
+ # detach CD-ROM drive
response = self.client.post(
- reverse('instances:detach_cdrom', args=[instance.id, 'sda']),
+ reverse('instances:detach_cdrom', args=[self.instance.id, 'sda']),
{},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.media
- self.assertEqual(len(instance.media), 1)
+ del self.instance.media
+ self.assertEqual(len(self.instance.media), 1)
- # snapshots
- self.assertEqual(len(instance.snapshots), 0)
+ def test_snapshots(self):
+ self.assertEqual(len(self.instance.snapshots), 0)
response = self.client.post(
- reverse('instances:snapshot', args=[instance.id]),
+ reverse('instances:snapshot', args=[self.instance.id]),
{
- 'name': 'test',
+ 'name': 'test-snapshot',
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.snapshots
- self.assertEqual(len(instance.snapshots), 1)
+ del self.instance.snapshots
+ self.assertEqual(len(self.instance.snapshots), 1)
response = self.client.post(
- reverse('instances:revert_snapshot', args=[instance.id]),
+ reverse('instances:revert_snapshot', args=[self.instance.id]),
{
- 'name': 'test',
+ 'name': 'test-snapshot',
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
response = self.client.post(
- reverse('instances:delete_snapshot', args=[instance.id]),
+ reverse('instances:delete_snapshot', args=[self.instance.id]),
{
- 'name': 'test',
+ 'name': 'test-snapshot',
},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.snapshots
- self.assertEqual(len(instance.snapshots), 0)
+ del self.instance.snapshots
+ self.assertEqual(len(self.instance.snapshots), 0)
- # autostart
- self.assertEqual(instance.autostart, 0)
+ def test_autostart(self):
+ self.assertEqual(self.instance.autostart, 0)
response = self.client.post(
- reverse('instances:set_autostart', args=[instance.id]),
+ reverse('instances:set_autostart', args=[self.instance.id]),
{},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.autostart
- self.assertEqual(instance.autostart, 1)
+ del self.instance.autostart
+ self.assertEqual(self.instance.autostart, 1)
response = self.client.post(
- reverse('instances:unset_autostart', args=[instance.id]),
+ reverse('instances:unset_autostart', args=[self.instance.id]),
{},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.autostart
- self.assertEqual(instance.autostart, 0)
+ del self.instance.autostart
+ self.assertEqual(self.instance.autostart, 0)
- # bootmenu
- self.assertEqual(instance.bootmenu, True)
+ def test_bootmenu(self):
+ self.assertEqual(self.instance.bootmenu, True)
response = self.client.post(
- reverse('instances:unset_bootmenu', args=[instance.id]),
+ reverse('instances:unset_bootmenu', args=[self.instance.id]),
{},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.bootmenu
- self.assertEqual(instance.bootmenu, False)
+ del self.instance.bootmenu
+ self.assertEqual(self.instance.bootmenu, False)
response = self.client.post(
- reverse('instances:set_bootmenu', args=[instance.id]),
+ reverse('instances:set_bootmenu', args=[self.instance.id]),
{},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.bootmenu
- self.assertEqual(instance.bootmenu, True)
+ del self.instance.bootmenu
+ self.assertEqual(self.instance.bootmenu, True)
- # guest agent
-
- self.assertEqual(instance.guest_agent, False)
+ def test_guest_agent(self):
+ self.assertEqual(self.instance.guest_agent, False)
response = self.client.post(
- reverse('instances:set_guest_agent', args=[instance.id]),
+ reverse('instances:set_guest_agent', args=[self.instance.id]),
{'guest_agent': True},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.guest_agent
- self.assertEqual(instance.guest_agent, True)
+ del self.instance.guest_agent
+ self.assertEqual(self.instance.guest_agent, True)
response = self.client.post(
- reverse('instances:set_guest_agent', args=[instance.id]),
+ reverse('instances:set_guest_agent', args=[self.instance.id]),
{'guest_agent': False},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.guest_agent
- self.assertEqual(instance.guest_agent, False)
+ del self.instance.guest_agent
+ self.assertEqual(self.instance.guest_agent, False)
- # video model
- self.assertEqual(instance.video_model, 'vga')
+ def test_video_model(self):
+ self.assertEqual(self.instance.video_model, 'vga')
response = self.client.post(
- reverse('instances:set_video_model', args=[instance.id]),
+ reverse('instances:set_video_model', args=[self.instance.id]),
{'video_model': 'virtio'},
HTTP_REFERER=reverse('index'),
)
self.assertEqual(response.status_code, 302)
- del instance.video_model
- self.assertEqual(instance.video_model, 'virtio')
+ del self.instance.video_model
+ self.assertEqual(self.instance.video_model, 'virtio')
- # console
+ def test_owner(self):
+ self.assertEqual(UserInstance.objects.count(), 0)
response = self.client.post(
- reverse('instances:update_console', args=[instance.id]),
+ reverse('instances:add_owner', args=[self.instance.id]),
+ {'user_id': self.admin_user.id},
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(UserInstance.objects.count(), 1)
+
+ user_instance: UserInstance = UserInstance.objects.get(id=1)
+ self.assertEqual(user_instance.instance_id, self.instance.id)
+ self.assertEqual(user_instance.user_id, self.admin_user.id)
+
+ # test when no multiple owners allowed
+ setting = AppSettings.objects.get(key='ALLOW_INSTANCE_MULTIPLE_OWNER')
+ setting.value = 'False'
+ setting.save()
+
+ response = self.client.post(
+ reverse('instances:add_owner', args=[self.instance.id]),
+ {'user_id': self.test_user.id},
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(UserInstance.objects.count(), 1)
+
+ response = self.client.post(
+ reverse('instances:del_owner', args=[self.instance.id]),
+ {'userinstance': user_instance.id},
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(UserInstance.objects.count(), 0)
+
+ def test_clone(self):
+ instance_count = Instance.objects.count()
+ response = self.client.post(
+ reverse('instances:clone', args=[self.instance.id]),
+ {
+ 'name': 'test-vm-clone',
+ 'clone-net-mac-0': 'de:ad:be:ef:de:ad',
+ 'disk-vda': 'test-clone.img',
+ 'clone-title': '',
+ 'clone-description': '',
+ 'clone': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Instance.objects.count(), instance_count + 1)
+
+ clone_qs = Instance.objects.filter(name='test-vm-clone')
+ self.assertEqual(len(clone_qs), 1)
+ clone = clone_qs[0]
+
+ self.assertEqual(clone.proxy.get_net_devices()[0]['mac'], 'de:ad:be:ef:de:ad')
+
+ response = self.client.post(
+ reverse('instances:snapshot', args=[clone.id]),
+ {
+ 'name': 'test',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ response = self.client.post(
+ reverse('instances:destroy', args=[clone.id]),
+ {
+ 'delete_disk': True,
+ 'delete_nvram': True
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertRedirects(response, reverse('instances:index'))
+ self.assertEqual(Instance.objects.count(), instance_count)
+
+ def test_clone_with_quota(self):
+ # test for non admin user with quotas
+ instance_count = Instance.objects.count()
+
+ UserInstance.objects.create(user=self.test_user, instance=self.instance)
+
+ self.client.force_login(self.test_user)
+
+ response = self.client.post(
+ reverse('instances:clone', args=[self.instance.id]),
+ {
+ 'name': 'test-vm-clone',
+ 'clone-net-mac-0': 'de:ad:be:ef:de:ad',
+ 'disk-vda': 'test-clone.img',
+ 'clone-title': '',
+ 'clone-description': '',
+ 'clone': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ # no new instances created as user reached quota
+ self.assertEqual(Instance.objects.count(), instance_count)
+
+ def test_clone_errors(self):
+ instance_count = Instance.objects.count()
+
+ # duplicate name
+ response = self.client.post(
+ reverse('instances:clone', args=[self.instance.id]),
+ {
+ 'name': 'test-vm',
+ 'clone-net-mac-0': 'de:ad:be:ef:de:ad',
+ 'disk-vda': 'test.img',
+ 'clone-title': '',
+ 'clone-description': '',
+ 'clone': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Instance.objects.count(), instance_count)
+
+ # wrong name
+ response = self.client.post(
+ reverse('instances:clone', args=[self.instance.id]),
+ {
+ 'name': '!@#$',
+ 'clone-net-mac-0': 'de:ad:be:ef:de:ad',
+ 'disk-vda': '!@#$.img',
+ 'clone-title': '',
+ 'clone-description': '',
+ 'clone': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Instance.objects.count(), instance_count)
+
+ # wrong mac
+ response = self.client.post(
+ reverse('instances:clone', args=[self.instance.id]),
+ {
+ 'name': 'test-vm-clone',
+ 'clone-net-mac-0': 'gh:ad:be:ef:de:ad',
+ 'disk-vda': 'test-clone.img',
+ 'clone-title': '',
+ 'clone-description': '',
+ 'clone': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Instance.objects.count(), instance_count)
+
+ def test_console(self):
+ response = self.client.post(
+ reverse('instances:update_console', args=[self.instance.id]),
{
'type': 'spice',
'listen_on': '0.0.0.0',
@@ -369,109 +707,320 @@ class InstancesTestCase(TestCase):
)
self.assertEqual(response.status_code, 302)
- # poweron
- self.assertEqual(instance.status, 5)
-
- response = self.client.get(reverse('instances:poweron', args=[instance.id]), HTTP_REFERER=reverse('index'))
- self.assertEqual(response.status_code, 302)
-
- del instance.status
-
- self.assertEqual(instance.status, 1)
-
- # status
- response = self.client.get(reverse('instances:status', args=[instance.id]))
+ def test_status(self):
+ response = self.client.get(reverse('instances:status', args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
- # stats
- response = self.client.get(reverse('instances:stats', args=[instance.id]))
+ def test_stats(self):
+ response = self.client.get(reverse('instances:stats', args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
- # guess_mac_address
- response = self.client.get(reverse('instances:guess_mac_address', args=[instance.name]))
+ def test_guess_mac_address(self):
+ response = self.client.get(reverse('instances:guess_mac_address', args=[self.instance.name]))
self.assertEqual(response.status_code, 200)
- # random_mac_address
+ def test_random_mac_address(self):
response = self.client.get(reverse('instances:random_mac_address'))
self.assertEqual(response.status_code, 200)
- # random_mac_address
+ def test_guess_clone_name(self):
response = self.client.get(reverse('instances:guess_clone_name'))
self.assertEqual(response.status_code, 200)
- # guess_mac_address
- response = self.client.get(reverse('instances:check_instance', args=[instance.name]))
+ def test_sshkeys(self):
+ UserSSHKey.objects.create(keyname='keyname', keypublic=self.rsa_key, user=self.test_user)
+ UserInstance.objects.create(user=self.test_user, instance=self.instance)
+
+ response = self.client.get(reverse('instances:sshkeys', args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
- # sshkeys
- response = self.client.get(reverse('instances:sshkeys', args=[instance.name]))
+ response = self.client.get(reverse('instances:sshkeys', args=[self.instance.id]) + '?plain=true')
self.assertEqual(response.status_code, 200)
+ def test_check_instance(self):
+ response = self.client.get(reverse('instances:check_instance', args=['test-vm']))
+ self.assertEqual(response.status_code, 200)
+ self.assertJSONEqual(response.content, {'vname': 'test-vm', 'exists': True})
+
+ def test_start_template(self):
+ # starting templates must fail
+ self.assertEqual(self.instance.status, 5)
+
+ self.instance.is_template = True
+ self.instance.save()
+
+ response = self.client.get(reverse('instances:poweron', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.status
+ self.assertEqual(self.instance.status, 5)
+
+ self.instance.is_template = False
+ self.instance.save()
+
+ def test_power(self):
+ # poweron
+ self.assertEqual(self.instance.status, 5)
+
+ response = self.client.get(reverse('instances:poweron', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.status
+
+ self.assertEqual(self.instance.status, 1)
+
# suspend
- response = self.client.get(reverse('instances:suspend', args=[instance.id]), HTTP_REFERER=reverse('index'))
+ response = self.client.get(reverse('instances:suspend', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
self.assertEqual(response.status_code, 302)
- del instance.status
- self.assertEqual(instance.status, 3)
+ del self.instance.status
+ self.assertEqual(self.instance.status, 3)
# resume
- response = self.client.get(reverse('instances:resume', args=[instance.id]), HTTP_REFERER=reverse('index'))
+ response = self.client.get(reverse('instances:resume', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
self.assertEqual(response.status_code, 302)
- del instance.status
- self.assertEqual(instance.status, 1)
+ del self.instance.status
+ self.assertEqual(self.instance.status, 1)
# poweroff
- response = self.client.get(reverse('instances:poweroff', args=[instance.id]), HTTP_REFERER=reverse('index'))
+ response = self.client.get(reverse('instances:poweroff', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
self.assertEqual(response.status_code, 302)
# as no OS is installed ACPI won't work
- del instance.status
- self.assertEqual(instance.status, 1)
+ del self.instance.status
+ self.assertEqual(self.instance.status, 1)
# powercycle
- response = self.client.get(reverse('instances:powercycle', args=[instance.id]), HTTP_REFERER=reverse('index'))
+ response = self.client.get(reverse('instances:powercycle', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
self.assertEqual(response.status_code, 302)
- self.assertEqual(instance.status, 1)
+
+ del self.instance.status
+ self.assertEqual(self.instance.status, 1)
# force_off
- response = self.client.get(reverse('instances:force_off', args=[instance.id]), HTTP_REFERER=reverse('index'))
+ response = self.client.get(reverse('instances:force_off', args=[self.instance.id]), HTTP_REFERER=reverse('index'))
self.assertEqual(response.status_code, 302)
- del instance.status
- self.assertEqual(instance.status, 5)
+ del self.instance.status
+ self.assertEqual(self.instance.status, 5)
- # delete started instance with disks
- instance.proxy.start()
- del instance.status
- self.assertEqual(instance.status, 1)
+ def test_vv_file(self):
+ response = self.client.get(reverse('instances:getvvfile', args=[self.instance.id]))
+ self.assertEqual(response.status_code, 200)
+
+ def test_vcpu_hotplug(self):
+ response = self.client.post(
+ reverse('instances:set_vcpu_hotplug', args=[self.instance.id]),
+ {'vcpu_hotplug': 'True'},
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ def test_change_network(self):
+ self.assertEqual(self.instance.networks[0]['mac'], '52:54:00:a2:3c:e7')
+ response = self.client.post(
+ reverse('instances:change_network', args=[self.instance.id]),
+ {
+ 'net-mac-0': '52:54:00:a2:3c:e8',
+ 'net-source-0': 'net:default',
+ 'net-nwfilter-0': '',
+ 'net-model-0': 'virtio',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.networks
+ self.assertEqual(self.instance.networks[0]['mac'], '52:54:00:a2:3c:e8')
+
+ def test_add_delete_network(self):
+ self.assertEqual(len(self.instance.networks), 1)
+ net_mac = self.instance.networks[0]['mac']
+ response = self.client.post(
+ reverse('instances:add_network', args=[self.instance.id]),
+ {
+ 'add-net-mac': '52:54:00:a2:3c:e9',
+ 'add-net-network': 'net:default',
+ 'add_net-nwfilter': '',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.networks
+ self.assertEqual(len(self.instance.networks), 2)
+ self.assertEqual(self.instance.networks[1]['mac'], '52:54:00:a2:3c:e9')
response = self.client.post(
- reverse('instances:destroy', args=[instance.id]),
- {'delete_disk': True},
+ reverse('instances:delete_network', args=[self.instance.id]),
+ {
+ 'delete_network': '52:54:00:a2:3c:e9',
+ },
+ HTTP_REFERER=reverse('index'),
)
- self.assertRedirects(response, reverse('instances:index'))
+ self.assertEqual(response.status_code, 302)
- # # create volume
- # response = self.client.post(
- # reverse('create_volume', args=[compute.id, 'default']),
- # {
- # 'name': 'test3',
- # 'format': 'qcow2',
- # 'size': '1',
- # 'meta_prealloc': False,
- # },
- # )
- # self.assertRedirects(response, reverse('storage', args=[compute.id, 'default']))
+ del self.instance.networks
+ self.assertEqual(len(self.instance.networks), 1)
+ self.assertEqual(self.instance.networks[0]['mac'], net_mac)
- # # delete volume
+ def test_set_link_state(self):
+ self.assertEqual(self.instance.networks[0]['state'], 'up')
+ response = self.client.post(
+ reverse('instances:set_link_state', args=[self.instance.id]),
+ {
+ 'mac': self.instance.networks[0]['mac'],
+ 'set_link_state': 'up',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.networks
+ self.assertEqual(self.instance.networks[0]['state'], 'down')
+
+ def test_set_unset_qos(self):
+ self.assertEqual(len(self.instance.qos.keys()), 0)
+ net_mac = self.instance.networks[0]['mac']
+ response = self.client.post(
+ reverse('instances:set_qos', args=[self.instance.id]),
+ {
+ 'net-mac-0': net_mac,
+ 'qos_direction': 'inbound',
+ 'qos_average': 1,
+ 'qos_peak': 1,
+ 'qos_burst': 1,
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.qos
+ self.assertEqual(len(self.instance.qos.keys()), 1)
+
+ response = self.client.post(
+ reverse('instances:unset_qos', args=[self.instance.id]),
+ {
+ 'net-mac': net_mac,
+ 'qos_direction': 'inbound',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+ del self.instance.qos
+ self.assertEqual(len(self.instance.qos.keys()), 0)
+
+ # test on running instance
+ # self.instance.proxy.start()
# response = self.client.post(
- # reverse('instances:delete_vol', args=[instance.id]),
+ # reverse('instances:set_qos', args=[self.instance.id]),
# {
- # 'storage': 'default',
- # 'dev': 'vdb',
- # 'name': 'test3.qcow2',
+ # 'net-mac-0': net_mac,
+ # 'qos_direction': 'inbound',
+ # 'qos_average': 1,
+ # 'qos_peak': 1,
+ # 'qos_burst': 1,
# },
# HTTP_REFERER=reverse('index'),
# )
# self.assertEqual(response.status_code, 302)
+ # self.instance.proxy.force_shutdown()
+
+ def test_change_options(self):
+ self.assertEqual(self.instance.title, '')
+ self.assertEqual(self.instance.description, '')
+
+ response = self.client.post(
+ reverse('instances:change_options', args=[self.instance.id]),
+ {
+ 'title': 'test-vm-title',
+ 'description': 'test-vm description',
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ del self.instance.title
+ del self.instance.description
+
+ self.assertEqual(self.instance.title, 'test-vm-title')
+ self.assertEqual(self.instance.description, 'test-vm description')
+
+ def test_flavors(self):
+ response = self.client.get(reverse('instances:flavor_create'))
+ self.assertEqual(response.status_code, 200)
+
+ response = self.client.post(
+ reverse('instances:flavor_create'),
+ {
+ 'label': 'test_flavor',
+ 'memory': 256,
+ 'vcpu': 1,
+ 'disk': 10,
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ id = Flavor.objects.last().id
+
+ response = self.client.get(reverse('instances:flavor_update', args=[id]))
+ self.assertEqual(response.status_code, 200)
+
+ response = self.client.post(
+ reverse('instances:flavor_update', args=[id]),
+ {
+ 'label': 'test_flavor_',
+ 'memory': 256,
+ 'vcpu': 1,
+ 'disk': 10,
+ },
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ response = self.client.get(reverse('instances:flavor_delete', args=[id]))
+ self.assertEqual(response.status_code, 200)
+
+ response = self.client.post(
+ reverse('instances:flavor_delete', args=[id]),
+ {},
+ HTTP_REFERER=reverse('index'),
+ )
+ self.assertEqual(response.status_code, 302)
+
+ # def donot_test_instance(self):
+ # compute: Compute = Compute.objects.get(pk=1)
+ # user: User = User.objects.get(pk=1)
+
+ # # delete started instance with disks
+ # self.instance.proxy.start()
+ # del self.instance.status
+ # self.assertEqual(self.instance.status, 1)
+
+ # # create volume
+ # response = self.client.post(
+ # reverse('create_volume', args=[compute.id, 'default']),
+ # {
+ # 'name': 'test3',
+ # 'format': 'qcow2',
+ # 'size': '1',
+ # 'meta_prealloc': False,
+ # },
+ # )
+ # self.assertRedirects(response, reverse('storage', args=[compute.id, 'default']))
+
+ # # delete volume
+ # response = self.client.post(
+ # reverse('instances:delete_vol', args=[instance.id]),
+ # {
+ # 'storage': 'default',
+ # 'dev': 'vdb',
+ # 'name': 'test3.qcow2',
+ # },
+ # HTTP_REFERER=reverse('index'),
+ # )
+ # self.assertEqual(response.status_code, 302)
+
+ # , list(response.context['messages'])[0]
diff --git a/instances/urls.py b/instances/urls.py
index cd1cd2c..7c7f5a0 100644
--- a/instances/urls.py
+++ b/instances/urls.py
@@ -65,5 +65,5 @@ urlpatterns = [
path('guess_clone_name/', views.guess_clone_name, name='guess_clone_name'),
path('random_mac_address/', views.random_mac_address, name='random_mac_address'),
path('check_instance/
/', views.check_instance, name='check_instance'),
- path('sshkeys//', views.sshkeys, name='sshkeys'),
+ path('/sshkeys/', views.sshkeys, name='sshkeys'),
]
diff --git a/instances/utils.py b/instances/utils.py
index c1497a8..c4f20b8 100644
--- a/instances/utils.py
+++ b/instances/utils.py
@@ -1,40 +1,17 @@
import os
import random
import string
-from collections import OrderedDict
-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.utils.translation import ugettext_lazy as _
from accounts.models import UserInstance
from appsettings.settings import app_settings
-from logs.views import addlogmsg
+from django.conf import settings
+from django.utils.translation import gettext_lazy as _
from vrtManager.connection import connection_manager
-from vrtManager.hostdetails import wvmHostDetails
from vrtManager.instance import wvmInstance, wvmInstances
from .models import Instance
-def filesizefstr(size_str):
- if size_str == '':
- return 0
- size_str = size_str.upper().replace("B", "")
- if size_str[-1] == 'K':
- return int(float(size_str[:-1])) << 10
- elif size_str[-1] == 'M':
- return int(float(size_str[:-1])) << 20
- elif size_str[-1] == 'G':
- return int(float(size_str[:-1])) << 30
- elif size_str[-1] == 'T':
- return int(float(size_str[:-1])) << 40
- elif size_str[-1] == 'P':
- return int(float(size_str[:-1])) << 50
- else:
- return int(float(size_str))
-
-
def get_clone_free_names(size=10):
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
free_names = []
@@ -186,36 +163,6 @@ def migrate_instance(
instance.save()
-def get_hosts_status(computes):
- """
- Function return all hosts all vds on host
- """
- compute_data = []
- for compute in computes:
- compute_data.append({
- 'id': compute.id,
- 'name': compute.name,
- 'hostname': compute.hostname,
- 'status': connection_manager.host_is_up(compute.type, compute.hostname),
- 'type': compute.type,
- 'login': compute.login,
- 'password': compute.password,
- 'details': compute.details,
- })
- return compute_data
-
-
-def get_userinstances_info(instance):
- info = {}
- uis = UserInstance.objects.filter(instance=instance)
- info['count'] = uis.count()
- if info['count'] > 0:
- info['first_user'] = uis[0]
- else:
- info['first_user'] = None
- return info
-
-
def refr(compute):
if compute.status is True:
domains = compute.proxy.wvm.listAllDomains()
@@ -229,83 +176,6 @@ def refr(compute):
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
-def refresh_instance_database(comp, inst_name, info, all_host_vms, user):
- # Multiple Instance Name Check
- instances = Instance.objects.filter(name=inst_name)
- if instances.count() > 1:
- for i in instances:
- user_instances_count = UserInstance.objects.filter(instance=i).count()
- if user_instances_count == 0:
- addlogmsg(user.username, i.name, _("Deleting due to multiple(Instance Name) records."))
- i.delete()
-
- # Multiple UUID Check
- instances = Instance.objects.filter(uuid=info['uuid'])
- if instances.count() > 1:
- for i in instances:
- if i.name != inst_name:
- addlogmsg(user.username, i.name, _("Deleting due to multiple(UUID) records."))
- i.delete()
-
- try:
- inst_on_db = Instance.objects.get(compute_id=comp["id"], name=inst_name)
- if inst_on_db.uuid != info['uuid']:
- inst_on_db.save()
-
- all_host_vms[comp["id"], comp["name"], comp["status"], comp["cpu"], comp["mem_size"],
- comp["mem_perc"]][inst_name]['is_template'] = inst_on_db.is_template
- all_host_vms[comp["id"], comp["name"], comp["status"], comp["cpu"], comp["mem_size"],
- comp["mem_perc"]][inst_name]['userinstances'] = get_userinstances_info(inst_on_db)
- except Instance.DoesNotExist:
- inst_on_db = Instance(compute_id=comp["id"], name=inst_name, uuid=info['uuid'])
- inst_on_db.save()
-
-
-def get_user_instances(user):
- all_user_vms = {}
- user_instances = UserInstance.objects.filter(user=user)
- for usr_inst in user_instances:
- if connection_manager.host_is_up(usr_inst.instance.compute.type, usr_inst.instance.compute.hostname):
- conn = wvmHostDetails(
- usr_inst.instance.compute.hostname,
- usr_inst.instance.compute.login,
- usr_inst.instance.compute.password,
- usr_inst.instance.compute.type,
- )
- all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name)
- all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
- return all_user_vms
-
-
-def get_host_instances(compute):
- all_host_vms = OrderedDict()
-
- # if compute.status:
- comp_node_info = compute.proxy.get_node_info()
- comp_mem = compute.proxy.get_memory_usage()
- comp_instances = compute.proxy.get_host_instances(True)
-
- # if comp_instances:
- comp_info = {
- "id": compute.id,
- "name": compute.name,
- "status": compute.status,
- "cpu": comp_node_info[3],
- "mem_size": comp_node_info[2],
- "mem_perc": comp_mem['percent'],
- }
- # refr(compute)
- all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"], comp_info["mem_size"],
- comp_info["mem_perc"]] = comp_instances
- for vm, info in comp_instances.items():
- # TODO: Delete this function completely
- refresh_instance_database(comp_info, vm, info, all_host_vms, User.objects.get(pk=1))
-
- # else:
- # raise libvirtError(_(f"Problem occurred with host: {compute.name} - {status}"))
- return all_host_vms
-
-
def get_dhcp_mac_address(vname):
dhcp_file = settings.BASE_DIR + '/dhcpd.conf'
mac = ''
@@ -342,20 +212,3 @@ def get_clone_disk_name(disk, prefix, clone_name=''):
else:
image = f"{disk['image']}-clone"
return image
-
-
-# TODO: this function is not used
-def _get_clone_disks(disks, vname=''):
- clone_disks = []
- for disk in disks:
- new_image = get_clone_disk_name(disk, vname)
- if not new_image:
- continue
- new_disk = {
- 'dev': disk['dev'],
- 'storage': disk['storage'],
- 'image': new_image,
- 'format': disk['format'],
- }
- clone_disks.append(new_disk)
- return clone_disks
diff --git a/instances/views.py b/instances/views.py
index 6f6c4d7..66a0e1b 100644
--- a/instances/views.py
+++ b/instances/views.py
@@ -18,7 +18,7 @@ from django.contrib.auth.models import User
from django.http import Http404, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from libvirt import (VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, VIR_DOMAIN_UNDEFINE_NVRAM, libvirtError)
from logs.views import addlogmsg
from vrtManager import util
@@ -51,7 +51,7 @@ def index(request):
def instance(request, pk):
- instance: Instance = get_object_or_404(Instance, pk=pk)
+ instance: Instance = get_instance(request.user, pk)
compute: Compute = instance.compute
computes = Compute.objects.all().order_by('name')
computes_count = computes.count()
@@ -59,7 +59,6 @@ def instance(request, pk):
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
keymaps = settings.QEMU_KEYMAPS
console_types = AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list()
- #
console_form = ConsoleForm(
initial={
'type': instance.console_type,
@@ -67,7 +66,6 @@ def instance(request, pk):
'password': instance.console_passwd,
'keymap': instance.console_keymap,
})
- #
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_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
@@ -78,15 +76,6 @@ def instance(request, pk):
except UserInstance.DoesNotExist:
userinstance = None
- if not request.user.is_superuser:
- if not userinstance:
- return redirect(reverse('index'))
-
- if len(instance.media) != 0:
- media_iso = sorted(instance.proxy.get_iso_media())
- else:
- media_iso = []
-
memory_range = [256, 512, 768, 1024, 2048, 3072, 4096, 6144, 8192, 16384]
if instance.memory not in memory_range:
insort(memory_range, instance.memory)
@@ -127,18 +116,11 @@ def instance(request, pk):
vcpu_host = len(instance.vcpu_range)
memory_host = instance.proxy.get_max_memory()
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
- # videos_host = instance.proxy.get_video_models(instance.arch, instance.machine)
networks_host = sorted(instance.proxy.get_networks())
nwfilters_host = instance.proxy.get_nwfilters()
storages_host = sorted(instance.proxy.get_storages(True))
net_models_host = instance.proxy.get_network_models()
- try:
- interfaces_host = sorted(instance.proxy.get_ifaces())
- except Exception as e:
- addlogmsg(request.user.username, instance.name, e)
- messages.error(request, e)
-
return render(request, 'instance.html', locals())
@@ -210,18 +192,18 @@ def check_instance(request, vname):
data = {'vname': vname, 'exists': False}
if instance:
data['exists'] = True
- return HttpResponse(json.dumps(data))
+ return JsonResponse(data)
-def sshkeys(request, vname):
+def sshkeys(request, pk):
"""
:param request:
:param vname:
:return:
"""
-
+ instance = get_instance(request.user, pk)
instance_keys = []
- userinstances = UserInstance.objects.filter(instance__name=vname)
+ userinstances = UserInstance.objects.filter(instance=instance)
for ui in userinstances:
keys = UserSSHKey.objects.filter(user=ui.user)
@@ -239,7 +221,7 @@ def get_instance(user, pk):
'''
Check that instance is available for user, if not raise 404
'''
- instance = Instance.objects.get(pk=pk)
+ instance = get_object_or_404(Instance, pk=pk)
user_instances = user.userinstance_set.all().values_list('instance', flat=True)
if user.is_superuser or instance.id in user_instances:
@@ -498,7 +480,6 @@ def resize_disk(request, pk):
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
disks_new = list()
for disk in disks:
- # input_disk_size = utils.filesizefstr(request.POST.get('disk_size_' + disk['dev'], ''))
input_disk_size = int(request.POST.get('disk_size_' + disk['dev'], '0')) * 1073741824
if input_disk_size > disk['size'] + (64 << 20):
disk['size_new'] = input_disk_size
@@ -1005,7 +986,7 @@ def add_owner(request, pk):
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == 'False':
check_inst = UserInstance.objects.filter(instance=instance).count()
- if check_inst > 1:
+ if check_inst > 0:
messages.error(request, _("Only one owner is allowed and the one already added"))
else:
add_user_inst = UserInstance(instance=instance, user_id=user_id)
@@ -1027,7 +1008,7 @@ def del_owner(request, pk):
return redirect(request.META.get('HTTP_REFERER') + '#users')
-@permission_required('instances.clone_instances')
+@permission_required('instances.clone_instances', raise_exception=True)
def clone(request, pk):
instance = get_instance(request.user, pk)
diff --git a/interfaces/forms.py b/interfaces/forms.py
index 8767f5c..4490d0b 100644
--- a/interfaces/forms.py
+++ b/interfaces/forms.py
@@ -1,6 +1,6 @@
import re
from django import forms
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class AddInterface(forms.Form):
diff --git a/interfaces/templates/interface.html b/interfaces/templates/interface.html
index 62436f8..a06a950 100644
--- a/interfaces/templates/interface.html
+++ b/interfaces/templates/interface.html
@@ -1,11 +1,13 @@
{% extends "base.html" %}
{% load i18n %}
+
{% block title %}{% trans "Interface" %} - {{ iface }}{% endblock %}
+
+{% block page_heading %}{% trans "Interface" %}: {{ iface }}{% endblock page_heading %}
+
{% block content %}
-
-
-
- {% include 'errors_block.html' %}
diff --git a/interfaces/templates/interfaces.html b/interfaces/templates/interfaces.html
index b6221cc..c103de6 100644
--- a/interfaces/templates/interfaces.html
+++ b/interfaces/templates/interfaces.html
@@ -1,13 +1,16 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
+
{% block title %}{% trans "Interfaces" %} - {{ compute.name }}{% endblock %}
+
+{% block page_heading%}{% trans "Interfaces" %} - {{ compute.name }}{% endblock page_heading %}
+
+{% block page_heading_extra %}{% include 'create_iface_block.html' %}{% endblock page_heading_extra %}
+
{% block content %}
-
- {% include 'create_iface_block.html' %}
-
@@ -35,9 +38,6 @@
-
-
- {% include 'errors_block.html' %}
{% if not ifaces_all %}
diff --git a/interfaces/views.py b/interfaces/views.py
index b7fdcc5..7a12a38 100644
--- a/interfaces/views.py
+++ b/interfaces/views.py
@@ -1,3 +1,4 @@
+from django.contrib import messages
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
@@ -17,7 +18,6 @@ def interfaces(request, compute_id):
"""
ifaces_all = []
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
@@ -42,10 +42,10 @@ def interfaces(request, compute_id):
return HttpResponseRedirect(request.get_full_path())
else:
for msg_err in form.errors.values():
- error_messages.append(msg_err.as_text())
+ messages.error(request, msg_err.as_text())
conn.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
return render(request, 'interfaces.html', locals())
@@ -60,7 +60,6 @@ def interface(request, compute_id, iface):
"""
ifaces_all = []
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
@@ -88,6 +87,6 @@ def interface(request, compute_id, iface):
return HttpResponseRedirect(reverse('interfaces', args=[compute_id]))
conn.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
return render(request, 'interface.html', locals())
diff --git a/logs/models.py b/logs/models.py
index 8302030..d45c056 100644
--- a/logs/models.py
+++ b/logs/models.py
@@ -1,5 +1,5 @@
from django.db.models import Model, CharField, DateTimeField
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class Logs(Model):
diff --git a/logs/templates/paging.html b/logs/templates/paging.html
deleted file mode 100644
index 9901dff..0000000
--- a/logs/templates/paging.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
- {% if page > 1 %}
-
←
- {% else %}
-
- {% endif %}
- {% if has_next_page %}
-
→
- {% else %}
-
- {% endif %}
-
diff --git a/networks/forms.py b/networks/forms.py
index 82d71a0..6b9189f 100644
--- a/networks/forms.py
+++ b/networks/forms.py
@@ -1,6 +1,6 @@
import re
from django import forms
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class AddNetPool(forms.Form):
diff --git a/networks/templates/network.html b/networks/templates/network.html
index 5deea60..a096eca 100644
--- a/networks/templates/network.html
+++ b/networks/templates/network.html
@@ -1,13 +1,14 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
+
{% block title %}{% trans "Network" %} - {{ pool }}{% endblock %}
+
+{% block page_heading %}{% trans "Network" %}: {{ pool }}{% endblock page_heading %}
+
{% block content %}
-
-
-
- {% include 'errors_block.html' %}
diff --git a/networks/templates/networks.html b/networks/templates/networks.html
index 30a05ac..5415acd 100644
--- a/networks/templates/networks.html
+++ b/networks/templates/networks.html
@@ -1,14 +1,13 @@
{% extends "base.html" %}
{% load i18n %}
-{% block title %}{% trans "Networks" %} - {{ compute.name }}{% endblock %}
+
+{% block title %}{{ compute.name }} - {% trans "Networks" %}{% endblock %}
+
+{% block page_heading %}{{ compute.name }} - {% trans "Networks" %}{% endblock page_heading %}
+
+{% block page_heading_extra %}{% include 'create_net_block.html' %}{% endblock page_heading_extra %}
+
{% block content %}
-
-
-
- {% include 'create_net_block.html' %}
-
-
-
-
-
- {% include 'errors_block.html' %}
{% if not networks %}
diff --git a/networks/views.py b/networks/views.py
index 9072636..aade157 100644
--- a/networks/views.py
+++ b/networks/views.py
@@ -2,7 +2,7 @@ from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from libvirt import libvirtError
from admin.decorators import superuser_only
@@ -19,8 +19,8 @@ def networks(request, compute_id):
:return:
"""
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
+ errors = False
try:
conn = wvmNetworks(
@@ -41,9 +41,11 @@ def networks(request, compute_id):
data = form.cleaned_data
if data['name'] in networks:
msg = _("Network pool name already in use")
- error_messages.append(msg)
+ messages.error(request, msg)
+ errors = True
if data['forward'] in ['bridge', 'macvtap'] and data['bridge_name'] == '':
- error_messages.append(_('Please enter bridge/dev name'))
+ messages.error(request, _('Please enter bridge/dev name'))
+ errors = True
if data['subnet']:
ipv4 = True
gateway4, netmask4, dhcp4 = network_size(data['subnet'], data['dhcp4'])
@@ -51,8 +53,9 @@ def networks(request, compute_id):
ipv6 = True
gateway6, prefix6, dhcp6 = network_size(data['subnet6'], data['dhcp6'])
if prefix6 != '64':
- error_messages.append(_('For libvirt, the IPv6 network prefix must be /64'))
- if not error_messages:
+ messages.error(request, _('For libvirt, the IPv6 network prefix must be /64'))
+ errors = True
+ if not errors:
conn.create_network(
data['name'],
data['forward'],
@@ -71,10 +74,10 @@ def networks(request, compute_id):
return HttpResponseRedirect(reverse('network', args=[compute_id, data['name']]))
else:
for msg_err in form.errors.values():
- error_messages.append(msg_err.as_text())
+ messages.error(request, msg_err.as_text())
conn.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
return render(request, 'networks.html', locals())
@@ -88,7 +91,6 @@ def network(request, compute_id, pool):
:return:
"""
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
@@ -125,7 +127,7 @@ def network(request, compute_id, pool):
xml = conn._XMLDesc(0)
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
return HttpResponseRedirect(reverse('networks', args=compute_id))
if request.method == 'POST':
@@ -134,31 +136,31 @@ def network(request, compute_id, pool):
conn.start()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'stop' in request.POST:
try:
conn.stop()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'delete' in request.POST:
try:
conn.delete()
return HttpResponseRedirect(reverse('networks', args=[compute_id]))
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'set_autostart' in request.POST:
try:
conn.set_autostart(1)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'unset_autostart' in request.POST:
try:
conn.set_autostart(0)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'modify_fixed_address' in request.POST:
name = request.POST.get('name', '')
address = request.POST.get('address', '')
@@ -174,9 +176,9 @@ def network(request, compute_id, pool):
messages.success(request, _(f"{family.upper()} Fixed Address Operation Completed."))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
except ValueError as val_err:
- error_messages.append(val_err)
+ messages.error(request, val_err)
if 'delete_fixed_address' in request.POST:
ip = request.POST.get('address', '')
family = request.POST.get('family', 'ipv4')
@@ -192,7 +194,7 @@ def network(request, compute_id, pool):
messages.success(request, _(f"{family.upper()} DHCP Range is Changed."))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
if 'edit_network' in request.POST:
edit_xml = request.POST.get('edit_xml', '')
if edit_xml:
@@ -212,7 +214,7 @@ def network(request, compute_id, pool):
conn.set_qos(qos_dir, average, peak, burst)
if conn.is_active():
messages.success(
- request,
+ request,
_(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") +
_("Stop and start network to activate new config"))
else:
@@ -226,7 +228,7 @@ def network(request, compute_id, pool):
if conn.is_active():
messages.success(
- request,
+ request,
_(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") +
_("Stop and start network to activate new config."))
else:
diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html
index 4a41abf..c14282f 100644
--- a/nwfilters/templates/nwfilter.html
+++ b/nwfilters/templates/nwfilter.html
@@ -1,12 +1,14 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
-{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
+
+{% block title %}{% trans "NWFilter" %}: {{ name }}{% endblock %}
+
+{% block page_heading %}{% trans "NWFilter" %}: {{ name }}{% endblock page_heading %}
+
{% block content %}
-
-
- {% include 'errors_block.html' %}
- {% include 'messages_block.html' %}
-
-
{% trans "UUID" %}:
{{ uuid }}
diff --git a/nwfilters/templates/nwfilters.html b/nwfilters/templates/nwfilters.html
index b1fb566..d4110af 100644
--- a/nwfilters/templates/nwfilters.html
+++ b/nwfilters/templates/nwfilters.html
@@ -1,18 +1,19 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
-{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
+
+{% block title %}{{ compute.name }} - {% trans "NWFilters" %}{% endblock %}
+
+{% block page_heading %}{{ compute.name }} - {% trans "NWFilters" %}{% endblock page_heading %}
+
+{% block page_heading_extra %}
+{% include 'create_nwfilter_block.html' %}
+
+
+
+{% endblock page_heading_extra %}
+
{% block content %}
-
-
-
- {% include 'create_nwfilter_block.html' %}
-
-
-
-
-
-
-
-
- {% include 'errors_block.html' %}
- {% include 'messages_block.html' %}
diff --git a/nwfilters/views.py b/nwfilters/views.py
index 982a9b5..3ac9407 100644
--- a/nwfilters/views.py
+++ b/nwfilters/views.py
@@ -1,11 +1,10 @@
-from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404, render
-from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
-from libvirt import libvirtError
-
from admin.decorators import superuser_only
from computes.models import Compute
+from django.contrib import messages
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+from django.utils.translation import gettext_lazy as _
+from libvirt import libvirtError
from logs.views import addlogmsg
from vrtManager import util
from vrtManager.instance import wvmInstance, wvmInstances
@@ -20,7 +19,6 @@ def nwfilters(request, compute_id):
:return:
"""
- error_messages = []
nwfilters_all = []
compute = get_object_or_404(Compute, pk=compute_id)
@@ -51,7 +49,7 @@ def nwfilters(request, compute_id):
conn.create_nwfilter(xml)
addlogmsg(request.user.username, compute.hostname, msg)
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
addlogmsg(request.user.username, compute.hostname, lib_err)
if 'del_nwfilter' in request.POST:
@@ -69,7 +67,7 @@ def nwfilters(request, compute_id):
if name in dom_filterrefs:
in_use = True
msg = _(f"NWFilter is in use by {inst}. Cannot be deleted.")
- error_messages.append(msg)
+ messages.error(request, msg)
addlogmsg(request.user.username, compute.hostname, msg)
i_conn.close()
break
@@ -93,16 +91,15 @@ def nwfilters(request, compute_id):
conn.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
addlogmsg(request.user.username, compute.hostname, lib_err)
except Exception as err:
- error_messages.append(err)
+ messages.error(request, err)
addlogmsg(request.user.username, compute.hostname, err)
return render(request, 'nwfilters.html', {
- 'error_messages': error_messages,
'nwfilters': nwfilters_all,
- 'compute': compute
+ 'compute': compute,
})
@@ -113,7 +110,6 @@ def nwfilter(request, compute_id, nwfltr):
:param nwfltr:
:return:
"""
- error_messages = []
nwfilters_all = []
compute = get_object_or_404(Compute, pk=compute_id)
@@ -194,8 +190,8 @@ def nwfilter(request, compute_id, nwfltr):
conn.close()
nwfilter.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
except Exception as error_msg:
- error_messages.append(error_msg)
+ messages.error(request, error_msg)
return render(request, 'nwfilter.html', locals())
diff --git a/secrets/templates/secrets.html b/secrets/templates/secrets.html
index a3274a0..b5e21ba 100644
--- a/secrets/templates/secrets.html
+++ b/secrets/templates/secrets.html
@@ -2,15 +2,19 @@
{% load i18n %}
{% load staticfiles %}
{% block title %}{% trans "Secrets" %} - {{ compute.name }}{% endblock %}
+
{% block style %}
{% endblock %}
+
+{% block page_heading %}{{ compute }} - {% trans "Secrets" %}{% endblock page_heading %}
+
+{% block page_heading_extra %}{% include 'create_secret_block.html' %}{% endblock page_heading_extra %}
+
{% block content %}
-
- {% include 'create_secret_block.html' %}
-
+
@@ -38,9 +42,6 @@
-
-
- {% include 'errors_block.html' %}
{% if not secrets_all %}
diff --git a/secrets/views.py b/secrets/views.py
index 9b47f18..3788d87 100644
--- a/secrets/views.py
+++ b/secrets/views.py
@@ -1,17 +1,12 @@
-
-from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404, render
-from django.urls import reverse
-from libvirt import libvirtError
-from libvirt import (VIR_SECRET_USAGE_TYPE_NONE,
- VIR_SECRET_USAGE_TYPE_CEPH,
- VIR_SECRET_USAGE_TYPE_TLS,
- VIR_SECRET_USAGE_TYPE_VOLUME,
- VIR_SECRET_USAGE_TYPE_ISCSI)
+from secrets.forms import AddSecret
from admin.decorators import superuser_only
from computes.models import Compute
-from secrets.forms import AddSecret
+from django.contrib import messages
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+from libvirt import (VIR_SECRET_USAGE_TYPE_CEPH, VIR_SECRET_USAGE_TYPE_ISCSI, VIR_SECRET_USAGE_TYPE_NONE,
+ VIR_SECRET_USAGE_TYPE_TLS, VIR_SECRET_USAGE_TYPE_VOLUME, libvirtError)
from vrtManager.secrets import wvmSecrets
@@ -24,7 +19,6 @@ def secrets(request, compute_id):
"""
secrets_all = []
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
secret_usage_types = {
VIR_SECRET_USAGE_TYPE_NONE: "none",
@@ -59,11 +53,12 @@ def secrets(request, compute_id):
data['ephemeral'],
data['private'],
data['usage_type'],
- data['data'])
+ data['data'],
+ )
return HttpResponseRedirect(request.get_full_path())
else:
for msg_err in form.errors.values():
- error_messages.append(msg_err.as_text())
+ messages.error(request, msg_err.as_text())
if 'delete' in request.POST:
uuid = request.POST.get('uuid', '')
conn.delete_secret(uuid)
@@ -74,9 +69,9 @@ def secrets(request, compute_id):
try:
conn.set_secret_value(uuid, value)
except Exception as err:
- error_messages.append(err)
+ messages.error(request, err)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as err:
- error_messages.append(err)
+ messages.error(request, err)
return render(request, 'secrets.html', locals())
diff --git a/storages/forms.py b/storages/forms.py
index eea9bb4..f2fd89b 100644
--- a/storages/forms.py
+++ b/storages/forms.py
@@ -1,6 +1,6 @@
import re
from django import forms
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class AddStgPool(forms.Form):
diff --git a/storages/templates/storage.html b/storages/templates/storage.html
index b23aa04..cec96b5 100644
--- a/storages/templates/storage.html
+++ b/storages/templates/storage.html
@@ -1,17 +1,19 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
-{% block title %}{% trans "Storage" %} - {{ pool }}{% endblock %}
+{% block title %}{{ compute }} - {% trans "Storage" %}: {{ pool }}{% endblock %}
{% block style %}
{% endblock %}
-{% block content %}
-
+{% block page_heading %}{{ compute }} - {% trans "Storage" %}: {{ pool }}{% endblock page_heading %}
+
+{% block page_heading_extra %}{% include 'create_stg_vol_block.html' %}{% endblock page_heading_extra %}
+
+{% block content %}
-
-
- {% include 'errors_block.html' %}
{% trans "Pool name" %}
diff --git a/storages/templates/storages.html b/storages/templates/storages.html
index dba6770..fddcc58 100644
--- a/storages/templates/storages.html
+++ b/storages/templates/storages.html
@@ -1,14 +1,13 @@
{% extends "base.html" %}
{% load i18n %}
-{% block title %}{% trans "Storages" %} - {{ compute.name }}{% endblock %}
+
+{% block title %}{{ compute.name }} - {% trans "Storages" %}{% endblock %}
+
+{% block page_heading %}{{ compute.name }} - {% trans "Storages" %}{% endblock page_heading %}
+
+{% block page_heading_extra %}{% include 'create_stg_block.html' %}{% endblock page_heading_extra %}
+
{% block content %}
-
-
-
- {% include 'create_stg_block.html' %}
-
-
-
-
-
- {% include 'errors_block.html' %}
-
{% if not storages %}
diff --git a/storages/views.py b/storages/views.py
index 9f3f0fe..b3ee0b1 100644
--- a/storages/views.py
+++ b/storages/views.py
@@ -4,7 +4,7 @@ from django.contrib import messages
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from libvirt import libvirtError
from admin.decorators import superuser_only
@@ -23,8 +23,8 @@ def storages(request, compute_id):
:return:
"""
- error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
+ errors = False
try:
conn = wvmStorages(compute.hostname, compute.login, compute.password, compute.type)
@@ -38,15 +38,18 @@ def storages(request, compute_id):
data = form.cleaned_data
if data['name'] in storages:
msg = _("Pool name already use")
- error_messages.append(msg)
+ messages.error(request, msg)
+ errors = True
if data['stg_type'] == 'rbd':
if not data['secret']:
msg = _("You need create secret for pool")
- error_messages.append(msg)
+ messages.error(request, msg)
+ errors = True
if not data['ceph_pool'] and not data['ceph_host'] and not data['ceph_user']:
msg = _("You need input all fields for creating ceph pool")
- error_messages.append(msg)
- if not error_messages:
+ messages.error(request, msg)
+ errors = True
+ if not errors:
if data['stg_type'] == 'rbd':
conn.create_storage_ceph(data['stg_type'], data['name'], data['ceph_pool'], data['ceph_host'],
data['ceph_user'], data['secret'])
@@ -58,10 +61,10 @@ def storages(request, compute_id):
return HttpResponseRedirect(reverse('storage', args=[compute_id, data['name']]))
else:
for msg_err in form.errors.values():
- error_messages.append(msg_err.as_text())
+ messages.error(request, msg_err.as_text())
conn.close()
except libvirtError as lib_err:
- error_messages.append(lib_err)
+ messages.error(request, lib_err)
return render(request, 'storages.html', locals())
diff --git a/templates/base.html b/templates/base.html
index 02a17e1..0d71065 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -40,15 +40,16 @@
- {% block page_header_extra %}
- {% endblock page_header_extra %}
+ {% block page_heading_extra %}
+ {% endblock page_heading_extra %}
+
{% bootstrap_messages %}
{% block content %}{% endblock %}
diff --git a/templates/common/confirm_delete.html b/templates/common/confirm_delete.html
index 6f4a42c..fc22c52 100644
--- a/templates/common/confirm_delete.html
+++ b/templates/common/confirm_delete.html
@@ -5,7 +5,7 @@
{% block title %}{{ title }}{% endblock %}
-{% block page_header %}{{ title }}{% endblock page_header %}
+{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block content %}
diff --git a/templates/common/form.html b/templates/common/form.html
index 0e5a000..6c58af0 100644
--- a/templates/common/form.html
+++ b/templates/common/form.html
@@ -5,7 +5,7 @@
{% block title %}{{ title }}{% endblock %}
-{% block page_header %}{{ title }}{% endblock page_header %}
+{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block content %}
diff --git a/templates/errors_block.html b/templates/errors_block.html
deleted file mode 100644
index 2fb6c61..0000000
--- a/templates/errors_block.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-{% if error_messages %}
- {% for error in error_messages %}
-
-
-
-
- {% trans 'Error' %}: {{ error }}
-
- ×
-
-
-
-
-
- {% endfor %}
-{% endif %}
\ No newline at end of file
diff --git a/templates/messages_block.html b/templates/messages_block.html
deleted file mode 100644
index 95773cf..0000000
--- a/templates/messages_block.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-{% if messages %}
- {% for message in messages %}
-
-
-
-
- {% trans 'Success' %}: {{ message }}
-
- ×
-
-
-
-
-
- {% endfor %}
-{% endif %}
\ No newline at end of file
diff --git a/webvirtcloud/middleware.py b/webvirtcloud/middleware.py
index 5ca2940..6759ff1 100644
--- a/webvirtcloud/middleware.py
+++ b/webvirtcloud/middleware.py
@@ -1,6 +1,6 @@
from django.contrib import messages
from django.shortcuts import render
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from libvirt import libvirtError