mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
feat(instances): VM clone runs as a job
This commit is contained in:
parent
49232c6e1d
commit
fbf82da5a9
12 changed files with 346 additions and 270 deletions
|
|
@ -1923,7 +1923,7 @@ class wvmInstance(wvmConnect):
|
|||
mac_tuples = [mac[i : i + n] for i in range(0, len(mac), n)]
|
||||
return ":".join(mac_tuples)
|
||||
|
||||
def clone_instance(self, clone_data):
|
||||
def clone_instance(self, clone_data, uuid):
|
||||
"""Copy a shut-off VM, with its disks and UEFI variables (NVRAM).
|
||||
Inputs are checked before anything is allocated, and on failure only
|
||||
the volumes this call created are removed. Returns the clone's UUID."""
|
||||
|
|
@ -1935,10 +1935,10 @@ class wvmInstance(wvmConnect):
|
|||
|
||||
tree = etree.fromstring(self._XMLDesc(PERSISTENT_XML))
|
||||
tree.find("name").text = clone_data["name"]
|
||||
uuid = tree.find("uuid")
|
||||
if uuid is None:
|
||||
uuid = etree.SubElement(tree, "uuid")
|
||||
uuid.text = util.randomUUID()
|
||||
uuid_el = tree.find("uuid")
|
||||
if uuid_el is None:
|
||||
uuid_el = etree.SubElement(tree, "uuid")
|
||||
uuid_el.text = uuid
|
||||
self._set_options(
|
||||
tree,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def proxy(nvram):
|
|||
class CloneNvramTestCase(unittest.TestCase):
|
||||
def test_the_copy_keeps_the_format_owner_and_mode(self):
|
||||
p = proxy("<nvram format='qcow2'>/var/lib/libvirt/qemu/nvram/vm_VARS.qcow2</nvram>")
|
||||
p.clone_instance({"name": "copy"})
|
||||
p.clone_instance({"name": "copy"}, "abababab-0000-1111-2222-333333333333")
|
||||
xml, original, _ = p.pool.createXMLFrom.call_args.args
|
||||
volume = etree.fromstring(xml)
|
||||
self.assertEqual(volume.findtext("name"), "copy_VARS.qcow2")
|
||||
|
|
@ -54,7 +54,7 @@ class CloneNvramTestCase(unittest.TestCase):
|
|||
|
||||
def test_an_nvram_source_element_points_at_the_copy(self):
|
||||
p = proxy("<nvram type='file'>\n <source file='/var/lib/libvirt/qemu/nvram/vm_VARS.qcow2'/>\n</nvram>")
|
||||
p.clone_instance({"name": "copy"})
|
||||
p.clone_instance({"name": "copy"}, "abababab-0000-1111-2222-333333333333")
|
||||
p.pool.storageVolLookupByName.assert_called_once_with("vm_VARS.qcow2")
|
||||
(defined,), _ = p._defineXML.call_args
|
||||
self.assertEqual(
|
||||
|
|
@ -64,5 +64,5 @@ class CloneNvramTestCase(unittest.TestCase):
|
|||
def test_an_nvram_that_is_not_a_file_is_refused_before_any_copy(self):
|
||||
p = proxy("<nvram type='network'><source protocol='iscsi' name='x'/></nvram>")
|
||||
with self.assertRaises(util.OperationError):
|
||||
p.clone_instance({"name": "copy"})
|
||||
p.clone_instance({"name": "copy"}, "abababab-0000-1111-2222-333333333333")
|
||||
p.pool.createXMLFrom.assert_not_called()
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ class TestCloneRollback(unittest.TestCase):
|
|||
inst._defineXML = MagicMock(side_effect=libvirt.libvirtError("bad definition"))
|
||||
|
||||
with self.assertRaises(libvirt.libvirtError):
|
||||
inst.clone_instance({"name": "copy", "disk-vda": "copy.qcow2", "disk_owner_uid": 0, "disk_owner_gid": 0})
|
||||
inst.clone_instance({"name": "copy", "disk-vda": "copy.qcow2", "disk_owner_uid": 0, "disk_owner_gid": 0}, "abababab-0000-1111-2222-333333333333")
|
||||
copy.delete.assert_called_once_with(0)
|
||||
source_vol.delete.assert_not_called()
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ class TestCloneRollback(unittest.TestCase):
|
|||
inst.get_status = MagicMock(return_value=1)
|
||||
inst._XMLDesc = MagicMock()
|
||||
with self.assertRaisesRegex(libvirt.libvirtError, "Shut the VM down"):
|
||||
inst.clone_instance({"name": "copy"})
|
||||
inst.clone_instance({"name": "copy"}, "abababab-0000-1111-2222-333333333333")
|
||||
inst._XMLDesc.assert_not_called()
|
||||
|
||||
|
||||
|
|
@ -352,7 +352,7 @@ class TestEmptyDiskClone(unittest.TestCase):
|
|||
# clone is still defined and looked up through the real test driver.
|
||||
inst._XMLDesc = MagicMock(return_value=etree.tostring(config).decode())
|
||||
inst.get_status = MagicMock(return_value=5) # only shut-off VMs are cloned
|
||||
clone_uuid = inst.clone_instance({"name": "empty-disk-clone"})
|
||||
clone_uuid = inst.clone_instance({"name": "empty-disk-clone"}, "abababab-0000-1111-2222-333333333333")
|
||||
clone = conn.lookupByUUIDString(clone_uuid)
|
||||
self.addCleanup(clone.undefine)
|
||||
clone_config = etree.fromstring(clone.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
|
||||
|
|
@ -371,5 +371,5 @@ class TestEmptyDiskClone(unittest.TestCase):
|
|||
inst._XMLDesc = MagicMock()
|
||||
inst.get_status = MagicMock(return_value=5)
|
||||
with self.assertRaisesRegex(ValueError, "already exists"):
|
||||
inst.clone_instance({"name": "test"})
|
||||
inst.clone_instance({"name": "test"}, "abababab-0000-1111-2222-333333333333")
|
||||
inst._XMLDesc.assert_not_called()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue