1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/tests/test_views.py
catborise 301b73155d feat(instances): a VM's console settings in two cards, with a password action
Settings > Console was one horizontal form, and on a running VM its
Update button was disabled. The owner chose this layout from three
rendered options: the console type on top, then a "VNC connection" card
(listen address, keymap) and a "VNC password" card.

- The password is changed by an action: keep it, generate a new one,
  set a new one or remove it. This replaces the Generate and Clear
  boxes and their precedence, and an emptied field no longer removes
  the password silently. The pane says whether a password is set; the
  form no longer carries the password itself.
- A VNC password takes at most 8 bytes: libvirt refuses a longer one
  ("VNC password is 9 characters long, only 8 permitted"). Generate and
  a new VM's random password now make 8 characters; they made 12, which
  libvirt 11 refuses. A set password keeps its spaces.
- Every check is in the form, so a refused save changes nothing; before,
  a refused password removal still saved the other settings.
- The settings can be saved while the VM runs: they are in the
  persistent definition. A line says the running console keeps its
  settings until the VM is shut down and started.
- The console type shown and compared is the persistent one; it was the
  running VM's, so a pending switch to Serial showed VNC and a later
  save could switch again.
- While the saved type is Serial, the VNC settings are not shown or
  taken; choosing Serial ignores the posted VNC settings.
2026-10-30 14:55:57 +03:00

1157 lines
40 KiB
Python

import os
import re
import unittest
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 RequestFactory, TestCase
from vrtManager.create import wvmCreate
from vrtManager.util import randomUUID
from operations.models import Operation
from . import livetest
from .test_create_instance import run
from instances.models import Flavor, Instance
from instances.utils import refr
TEST_COMPUTE_HOST = os.environ.get("TEST_LIBVIRT_HOST", "localhost")
TEST_COMPUTE_LOGIN = os.environ.get("TEST_LIBVIRT_LOGIN", "")
TEST_COMPUTE_PASSWORD = os.environ.get("TEST_LIBVIRT_PASSWORD", "")
TEST_COMPUTE_TYPE = int(os.environ.get("TEST_LIBVIRT_TYPE", 4))
class InstancesTestCase(TestCase):
@classmethod
def setUpClass(cls):
if not livetest.enabled():
raise unittest.SkipTest("Set TEST_LIBVIRT_HOST to run the live libvirt tests")
super().setUpClass()
# Add users for testing purposes
User = get_user_model()
cls.admin_user = User.objects.filter(is_superuser=True).first() or User.objects.get(username="admin")
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 compute
cls.compute = Compute.objects.create(
name="instances-test-compute",
hostname=TEST_COMPUTE_HOST,
login=TEST_COMPUTE_LOGIN,
password=TEST_COMPUTE_PASSWORD,
details="test",
type=TEST_COMPUTE_TYPE,
)
try:
cls.connection = wvmCreate(
cls.compute.hostname,
cls.compute.login,
cls.compute.password,
cls.compute.type,
)
# Remove leftovers of an interrupted run, then record what
# else is on the host so tearDownClass can prove it is untouched.
livetest.cleanup(cls.connection.wvm)
livetest.ensure_pool(cls.connection.wvm)
cls.inventory = livetest.inventory(cls.connection.wvm)
# Add disks for testing
cls.connection.create_volume(
livetest.POOL,
"wvc-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)
# The compute may hold other VMs; never pick one of them.
cls.instance: Instance = Instance.objects.get(compute=cls.compute, name="wvc-test-vm")
except Exception as e:
cls.instance = None
if getattr(cls, "connection", None):
try:
livetest.remove_pool(cls.connection.wvm)
except Exception:
pass
if getattr(cls, "compute", None):
try:
cls.compute.delete()
except Exception:
pass
try:
super().tearDownClass()
except Exception:
pass
raise unittest.SkipTest(f"Live libvirt host not available for instances tests: {e}")
@classmethod
def tearDownClass(cls):
conn = cls.connection.wvm
try:
livetest.remove_pool(conn)
finally:
cls.compute.delete()
super().tearDownClass()
domains, volumes = livetest.inventory(conn)
if (domains, volumes) != cls.inventory:
raise AssertionError(
"Live tests changed objects outside the test namespace: "
f"domains {cls.inventory[0] ^ domains}, volumes {cls.inventory[1] ^ volumes}"
)
def setUp(self):
self.client.login(request=RequestFactory().get("/"), 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"))
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=[self.compute.id])
)
self.assertEqual(response.status_code, 200)
def test_instance_page(self):
response = self.client.get(
reverse("instances:instance", args=[self.instance.id])
)
self.assertEqual(response.status_code, 200)
self.client.force_login(self.test_user)
response = self.client.get(
reverse("instances:instance", args=[self.instance.id])
)
self.assertRaises(Http404)
# 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=[self.compute.id, "x86_64", "q35"]
),
{
"name": "wvc-test-new",
"firmware": "BIOS",
"vcpu": 1,
"vcpu_mode": "host-model",
"memory": 128,
"device0": "disk",
"bus0": "virtio",
"images": "wvc-test-volume.qcow2",
"storage-control": livetest.POOL,
"image-control": "wvc-test.qcow2",
"networks": "default",
"network-control": "default",
"cache_mode": "directsync",
"nwfilter": "",
"graphics": "vnc",
"video": "vga",
"listener_addr": "0.0.0.0",
"qemu_ga": False,
"virtio": True,
"add_cdrom": "None",
"add_input": "None",
"create": True,
"submission_key": "key-create-destroy",
},
)
self.assertEqual(response.status_code, 302)
run(Operation.objects.get(kind="instance.create"))
instance_qs: Instance = Instance.objects.filter(name="wvc-test-new")
self.assertEqual(len(instance_qs), 1)
instance = instance_qs[0]
# Destroy
response = self.client.get(reverse("instances:destroy", args=[instance.id]))
self.assertEqual(response.status_code, 200)
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"))
def test_create_from_xml(self):
uuid = randomUUID()
xml = self.xml.replace("wvc-test-vm", "wvc-test-vm-xml")
xml = re.sub(r"\s?<uuid>.*?</uuid>", f"<uuid>{uuid}</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)
xml_instance_qs: Instance = Instance.objects.filter(name="wvc-test-vm-xml")
self.assertEqual(len(xml_instance_qs), 1)
xml_instance = xml_instance_qs[0]
# destroy started instance to maximize coverage
xml_instance.proxy.start()
response = self.client.post(
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": livetest.POOL,
"name": "wvc-test-volume-2",
"size": 1,
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.disks
self.assertEqual(len(self.instance.disks), 2)
response = self.client.post(
reverse("instances:delete_vol", args=[self.instance.id]),
{
"storage": livetest.POOL,
"dev": "vdb",
"name": "wvc-test-volume-2.qcow2",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
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=[self.instance.id]),
{
"dev": "vda",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.disks
self.assertEqual(len(self.instance.disks), 0)
# reattach volume
response = self.client.post(
reverse("instances:add_existing_vol", args=[self.instance.id]),
{
"selected_storage": livetest.POOL,
"vols": "wvc-test-volume.qcow2",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.disks
self.assertEqual(len(self.instance.disks), 1)
def test_edit_volume(self):
response = self.client.post(
reverse("instances:edit_volume", args=[self.instance.id]),
{
"vol_path": f"{livetest.POOL_PATH}/wvc-test-volume.qcow2",
# 'vol_shareable': False,
# 'vol_readonly': False,
"vol_bus": "virtio",
"vol_bus_old": "virtio",
"vol_format": "qcow2",
"dev": "vda",
"edit_volume": True,
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
def test_attach_detach_cdrom(self):
self.assertEqual(len(self.instance.media), 1)
response = self.client.post(
reverse("instances:add_cdrom", args=[self.instance.id]),
{
"bus": "sata",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.media
self.assertEqual(len(self.instance.media), 2)
# create dummy iso
# with tempfile.NamedTemporaryFile() as f:
# f.write(b'\x00' * 1024**2)
# response = self.client.post(
# reverse('storage', args=[instance.compute.id, 'default']),
# {
# 'iso_upload': True,
# 'file': f
# },
# )
# detach CD-ROM drive
response = self.client.post(
reverse("instances:detach_cdrom", args=[self.instance.id, "sda"]),
{},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.media
self.assertEqual(len(self.instance.media), 1)
def test_snapshots(self):
self.assertEqual(len(self.instance.snapshots), 0)
self.instance.proxy.create_snapshot("test-snapshot")
for cached in ("snapshots", "internal_snapshots", "external_snapshots"):
self.instance.__dict__.pop(cached, None)
self.assertEqual(len(self.instance.snapshots), 1)
response = self.client.post(
reverse("instances:delete_snapshot", args=[self.instance.id]),
{
"name": "test-snapshot",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
for cached in ("snapshots", "internal_snapshots", "external_snapshots"):
self.instance.__dict__.pop(cached, None)
self.assertEqual(len(self.instance.snapshots), 0)
def test_autostart(self):
self.assertEqual(self.instance.autostart, 0)
response = self.client.post(
reverse("instances:set_autostart", args=[self.instance.id]),
{},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.autostart
self.assertEqual(self.instance.autostart, 1)
response = self.client.post(
reverse("instances:unset_autostart", args=[self.instance.id]),
{},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.autostart
self.assertEqual(self.instance.autostart, 0)
def test_bootmenu(self):
self.assertEqual(self.instance.bootmenu, True)
response = self.client.post(
reverse("instances:unset_bootmenu", args=[self.instance.id]),
{},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.bootmenu
self.assertEqual(self.instance.bootmenu, False)
response = self.client.post(
reverse("instances:set_bootmenu", args=[self.instance.id]),
{},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.bootmenu
self.assertEqual(self.instance.bootmenu, True)
def test_guest_agent(self):
self.assertEqual(self.instance.guest_agent, False)
response = self.client.post(
reverse("instances:set_guest_agent", args=[self.instance.id]),
{"guest_agent": True},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.guest_agent
self.assertEqual(self.instance.guest_agent, True)
response = self.client.post(
reverse("instances:set_guest_agent", args=[self.instance.id]),
{"guest_agent": False},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.guest_agent
self.assertEqual(self.instance.guest_agent, False)
def test_video_model(self):
self.assertEqual(self.instance.video_model, "vga")
response = self.client.post(
reverse("instances:set_video_model", args=[self.instance.id]),
{"video_model": "virtio"},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.video_model
self.assertEqual(self.instance.video_model, "virtio")
def test_owner(self):
self.assertEqual(UserInstance.objects.count(), 0)
response = self.client.post(
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": "wvc-test-vm-clone",
"clone-net-mac-0": "de:ad:be:ef:de:ad",
"disk-vda": "wvc-test-clone.img",
"clone-title": "",
"clone-description": "",
"clone": "",
"submission_key": "key-clone",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
run(Operation.objects.get(kind="instance.clone"))
self.assertEqual(Instance.objects.count(), instance_count + 1)
clone_qs = Instance.objects.filter(name="wvc-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")
clone.proxy.create_snapshot("test")
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()
# Cloning needs change permission on the source VM
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:clone", args=[self.instance.id]),
{
"name": "wvc-test-vm-clone",
"clone-net-mac-0": "de:ad:be:ef:de:ad",
"disk-vda": "wvc-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": "wvc-test-vm",
"clone-net-mac-0": "de:ad:be:ef:de:ad",
"disk-vda": "wvc-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": "wvc-test-vm-clone",
"clone-net-mac-0": "gh:ad:be:ef:de:ad",
"disk-vda": "wvc-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]),
{"listen_on": "0.0.0.0", "password_action": "keep", "keymap": "auto"},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
def test_status(self):
response = self.client.get(reverse("instances:status", args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
def test_stats(self):
response = self.client.get(reverse("instances:stats", args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
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)
def test_random_mac_address(self):
response = self.client.get(reverse("instances:random_mac_address"))
self.assertEqual(response.status_code, 200)
def test_guess_clone_name(self):
response = self.client.get(reverse("instances:guess_clone_name"))
self.assertEqual(response.status_code, 200)
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)
response = self.client.get(
reverse("instances:sshkeys", args=[self.instance.id]) + "?plain=true"
)
self.assertEqual(response.status_code, 200)
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.post(
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.post(
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.post(
reverse("instances:suspend", args=[self.instance.id]),
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.status
self.assertEqual(self.instance.status, 3)
# resume
response = self.client.post(
reverse("instances:resume", args=[self.instance.id]),
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.status
self.assertEqual(self.instance.status, 1)
# poweroff
response = self.client.post(
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 self.instance.status
self.assertEqual(self.instance.status, 1)
# powercycle
response = self.client.post(
reverse("instances:powercycle", args=[self.instance.id]),
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.status
self.assertEqual(self.instance.status, 1)
# force_off
response = self.client.post(
reverse("instances:force_off", args=[self.instance.id]),
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.status
self.assertEqual(self.instance.status, 5)
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-old-mac-0": "52:54:00:a2:3c:e7",
"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(len(self.instance.networks), 1)
self.assertEqual(self.instance.networks[0]["mac"], "52:54:00:a2:3c:e8")
# The second NIC's form changes only the second NIC
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"},
HTTP_REFERER=reverse("index"),
)
self.client.post(
reverse("instances:change_network", args=[self.instance.id]),
{
"net-old-mac-1": "52:54:00:a2:3c:e9",
"net-mac-1": "52:54:00:a2:3c:ea",
"net-source-1": "net:default",
"net-nwfilter-1": "",
"net-model-1": "e1000",
},
HTTP_REFERER=reverse("index"),
)
del self.instance.networks
self.assertEqual([n["mac"] for n in self.instance.networks], ["52:54:00:a2:3c:e8", "52:54:00:a2:3c:ea"])
self.assertEqual(self.instance.networks[1]["model"], "e1000")
# Restore the shared test VM
self.client.post(
reverse("instances:delete_network", args=[self.instance.id]),
{"delete_network": "52:54:00:a2:3c:ea"},
HTTP_REFERER=reverse("index"),
)
self.client.post(
reverse("instances:change_network", args=[self.instance.id]),
{
"net-old-mac-0": "52:54:00:a2:3c:e8",
"net-mac-0": "52:54:00:a2:3c:e7",
"net-source-0": "net:default",
"net-model-0": "virtio",
},
HTTP_REFERER=reverse("index"),
)
del self.instance.networks
self.assertEqual([n["mac"] for n in self.instance.networks], ["52:54:00:a2:3c:e7"])
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:delete_network", args=[self.instance.id]),
{
"delete_network": "52:54:00:a2:3c:e9",
},
HTTP_REFERER=reverse("index"),
)
self.assertEqual(response.status_code, 302)
del self.instance.networks
self.assertEqual(len(self.instance.networks), 1)
self.assertEqual(self.instance.networks[0]["mac"], net_mac)
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.assertFalse(self.instance.config_networks[0]["inbound"])
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.config_networks
self.assertEqual(self.instance.config_networks[0]["inbound"]["average"], "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.config_networks
self.assertFalse(self.instance.config_networks[0]["inbound"])
# test on running instance
# self.instance.proxy.start()
# 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)
# 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]