mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
fix(instances): a custom VM gets the MAC addresses its create form shows
The custom create form shows a MAC per network (random, or typed in), but had no mac field: the script wrote the joined list into every name="mac" input of the page, the other six-plus forms' own MAC fields, so a flavor form then sent one MAC per custom network and was refused, and the custom VM got other MACs from libvirt. The custom form gets its own hidden mac field and the script writes only there. The field took 17 characters, one MAC, so the API also refused a VM with two NICs and their MACs; it takes 255. The serializer's test sent no submission_key, so its check for more MACs than networks never ran; it does now, next to the case of two networks with two MACs.
This commit is contained in:
parent
d14a311862
commit
35468bc0fd
4 changed files with 40 additions and 11 deletions
|
|
@ -372,7 +372,7 @@ class CreateInstance(models.Model):
|
|||
meta_prealloc = models.BooleanField(default=False, blank=True)
|
||||
virtio = models.BooleanField(default=True)
|
||||
qemu_ga = models.BooleanField(default=False)
|
||||
mac = models.CharField(max_length=17, blank=True)
|
||||
mac = models.CharField(max_length=255, blank=True) # one per NIC, comma separated
|
||||
add_cdrom = models.CharField(max_length=16)
|
||||
add_input = models.CharField(max_length=16)
|
||||
video = models.CharField(
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@
|
|||
<!-- populated from javascript -->
|
||||
</ul>
|
||||
<input id="networks" type="hidden" name="networks" value=""/>
|
||||
<input id="custom_mac" type="hidden" name="mac" value=""/>
|
||||
<select class="form-select" id="network-control" name="network-control" multiple="multiple">
|
||||
{% for network in networks %}
|
||||
<option value="{{ network }}">{{ network }}</option>
|
||||
|
|
@ -864,6 +865,7 @@
|
|||
$('#networks').val(input_value);
|
||||
let selected_list_html = '';
|
||||
mac_array = [];
|
||||
$('#custom_mac').val('');
|
||||
net_counter = 0;
|
||||
if (input_value != '') {
|
||||
$.each(input_value.split(','), function (index, value) {
|
||||
|
|
@ -894,7 +896,8 @@
|
|||
|
||||
function populate_mac(c){
|
||||
mac_array[c] = $('input[name="mac' + c + '"').val();
|
||||
$('input[name="mac"').val(mac_array.join(","));
|
||||
// the custom form's MACs only: the other create forms have their own field
|
||||
$('#custom_mac').val(mac_array.join(","));
|
||||
}
|
||||
|
||||
function get_cust_vols(compute_id, pool) {
|
||||
|
|
|
|||
|
|
@ -157,12 +157,17 @@ class NicMacsTestCase(TestCase):
|
|||
with self.subTest(mac=mac), self.assertRaises(ValueError):
|
||||
nic_macs(mac, networks)
|
||||
|
||||
def test_api_serializer_refuses_extra_macs(self):
|
||||
serializer = CreateInstanceSerializer(data={
|
||||
"name": "api-vm", "vcpu": 1, "vcpu_mode": "host-model", "memory": 512,
|
||||
"networks": "default", "mac": "52:54:00:00:00:01,52:54:00:00:00:02", "nwfilter": "",
|
||||
"storage": "default", "hdd_size": 1, "cache_mode": "none", "meta_prealloc": False,
|
||||
"virtio": True, "qemu_ga": True, "video": "vga", "listener_addr": "0.0.0.0",
|
||||
})
|
||||
self.assertFalse(serializer.is_valid())
|
||||
self.assertIn("mac", serializer.errors)
|
||||
def test_api_serializer_takes_a_mac_per_network(self):
|
||||
def serializer(networks):
|
||||
return CreateInstanceSerializer(data={
|
||||
"name": "api-vm", "vcpu": 1, "vcpu_mode": "host-model", "memory": 512,
|
||||
"networks": networks, "mac": "52:54:00:00:00:01,52:54:00:00:00:02", "nwfilter": "",
|
||||
"storage": "default", "hdd_size": 1, "cache_mode": "none", "meta_prealloc": False,
|
||||
"virtio": True, "qemu_ga": True, "video": "vga", "listener_addr": "0.0.0.0",
|
||||
"submission_key": "test-key",
|
||||
})
|
||||
two_nics = serializer("default,second")
|
||||
self.assertTrue(two_nics.is_valid(), two_nics.errors)
|
||||
extra = serializer("default")
|
||||
self.assertFalse(extra.is_valid())
|
||||
self.assertEqual(extra.errors["mac"], ["More MAC addresses than networks"])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Scripts of the VM create page, rendered on libvirt's test driver and run
|
||||
in Node against stand-ins."""
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
|
@ -12,6 +13,8 @@ from computes.models import Compute
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from instances.forms import NewVMForm
|
||||
from lxml import html
|
||||
|
||||
|
||||
class CreatePageScriptTests(TestCase):
|
||||
|
|
@ -37,3 +40,21 @@ class CreatePageScriptTests(TestCase):
|
|||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True, timeout=30)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout.strip(), reverse("buses", args=[compute.id, "i686", "pc", "cdrom"]))
|
||||
|
||||
@unittest.skipUnless(shutil.which("node"), "node is not installed")
|
||||
def test_the_custom_form_sends_its_own_macs_and_leaves_the_others_alone(self):
|
||||
compute, page = self.page()
|
||||
custom = html.fromstring(page).xpath("//form[@aria-label='Create custom instance form']")[0]
|
||||
self.assertEqual(custom.xpath(".//input[@name='mac']/@id"), ["custom_mac"])
|
||||
function = re.search(r"function populate_mac\(.*?\n }\n", page, re.S).group(0)
|
||||
# the stand-in $ records each write; a MAC field holds what its selector names
|
||||
script = "const writes = []; let mac_array = [];\n" \
|
||||
"const $ = sel => ({val: v => v === undefined ? sel.replace(/\\D/g, '') : writes.push([sel, v])});\n" + \
|
||||
function + "populate_mac(0); populate_mac(1); console.log(JSON.stringify(writes));"
|
||||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True, timeout=30)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(json.loads(result.stdout), [["#custom_mac", "0"], ["#custom_mac", "0,1"]])
|
||||
|
||||
def test_a_mac_per_nic_fits_the_form(self):
|
||||
macs = ",".join(f"52:54:00:00:00:{n:02x}" for n in range(8))
|
||||
self.assertEqual(NewVMForm.base_fields["mac"].clean(macs), macs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue