"Fossies" - the Fresh Open Source Software Archive

Member "salt-3002.2/tests/unit/modules/test_virt.py" (18 Nov 2020, 220199 Bytes) of package /linux/misc/salt-3002.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_virt.py": 3002.1_vs_3002.2.

    1 """
    2 virt execution module unit tests
    3 """
    4 
    5 # pylint: disable=3rd-party-module-not-gated
    6 
    7 
    8 import datetime
    9 import os
   10 import shutil
   11 import tempfile
   12 
   13 import salt.config
   14 import salt.modules.config as config
   15 import salt.modules.virt as virt
   16 import salt.syspaths
   17 import salt.utils.yaml
   18 from salt._compat import ElementTree as ET
   19 from salt.exceptions import CommandExecutionError, SaltInvocationError
   20 
   21 # pylint: disable=import-error
   22 from salt.ext.six.moves import range  # pylint: disable=redefined-builtin
   23 from tests.support.helpers import dedent
   24 from tests.support.mixins import LoaderModuleMockMixin
   25 from tests.support.mock import MagicMock, patch
   26 from tests.support.unit import TestCase
   27 
   28 # pylint: disable=invalid-name,protected-access,attribute-defined-outside-init,too-many-public-methods,unused-argument
   29 
   30 
   31 class LibvirtMock(MagicMock):  # pylint: disable=too-many-ancestors
   32     """
   33     Libvirt library mock
   34     """
   35 
   36     class virDomain(MagicMock):
   37         """
   38         virDomain mock
   39         """
   40 
   41     class libvirtError(Exception):
   42         """
   43         libvirtError mock
   44         """
   45 
   46         def __init__(self, msg):
   47             super().__init__(msg)
   48             self.msg = msg
   49 
   50         def get_error_message(self):
   51             return self.msg
   52 
   53 
   54 class VirtTestCase(TestCase, LoaderModuleMockMixin):
   55     """
   56     Test cases for salt.module.virt
   57     """
   58 
   59     def setup_loader_modules(self):
   60         self.mock_libvirt = LibvirtMock()
   61         self.mock_conn = MagicMock()
   62         self.mock_conn.getStoragePoolCapabilities.return_value = (
   63             "<storagepoolCapabilities/>"
   64         )
   65         self.mock_libvirt.openAuth.return_value = self.mock_conn
   66         self.mock_popen = MagicMock()
   67         self.addCleanup(delattr, self, "mock_libvirt")
   68         self.addCleanup(delattr, self, "mock_conn")
   69         self.addCleanup(delattr, self, "mock_popen")
   70         self.mock_subprocess = MagicMock()
   71         self.mock_subprocess.return_value = (
   72             self.mock_subprocess
   73         )  # pylint: disable=no-member
   74         self.mock_subprocess.Popen.return_value = (
   75             self.mock_popen
   76         )  # pylint: disable=no-member
   77         loader_globals = {
   78             "__salt__": {"config.get": config.get, "config.option": config.option},
   79             "libvirt": self.mock_libvirt,
   80             "subprocess": self.mock_subprocess,
   81         }
   82         return {virt: loader_globals, config: loader_globals}
   83 
   84     def set_mock_vm(self, name, xml):
   85         """
   86         Define VM to use in tests
   87         """
   88         self.mock_conn.listDefinedDomains.return_value = [
   89             name
   90         ]  # pylint: disable=no-member
   91         mock_domain = self.mock_libvirt.virDomain()
   92         self.mock_conn.lookupByName.return_value = (
   93             mock_domain  # pylint: disable=no-member
   94         )
   95         mock_domain.XMLDesc.return_value = xml  # pylint: disable=no-member
   96 
   97         # Return state as shutdown
   98         mock_domain.info.return_value = [
   99             4,
  100             2048 * 1024,
  101             1024 * 1024,
  102             2,
  103             1234,
  104         ]  # pylint: disable=no-member
  105         mock_domain.ID.return_value = 1
  106         mock_domain.name.return_value = name
  107         return mock_domain
  108 
  109     def test_disk_profile_merge(self):
  110         """
  111         Test virt._disk_profile() when merging with user-defined disks
  112         """
  113         root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
  114         userdisks = [
  115             {"name": "system", "image": "/path/to/image"},
  116             {"name": "data", "size": 16384, "format": "raw"},
  117         ]
  118 
  119         disks = virt._disk_profile(self.mock_conn, "default", "kvm", userdisks, "myvm")
  120         self.assertEqual(
  121             [
  122                 {
  123                     "name": "system",
  124                     "device": "disk",
  125                     "size": 8192,
  126                     "format": "qcow2",
  127                     "model": "virtio",
  128                     "filename": "myvm_system.qcow2",
  129                     "image": "/path/to/image",
  130                     "source_file": "{}{}myvm_system.qcow2".format(root_dir, os.sep),
  131                 },
  132                 {
  133                     "name": "data",
  134                     "device": "disk",
  135                     "size": 16384,
  136                     "format": "raw",
  137                     "model": "virtio",
  138                     "filename": "myvm_data.raw",
  139                     "source_file": "{}{}myvm_data.raw".format(root_dir, os.sep),
  140                 },
  141             ],
  142             disks,
  143         )
  144 
  145     def test_boot_default_dev(self):
  146         """
  147         Test virt._gen_xml() default boot device
  148         """
  149         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  150         nicp = virt._nic_profile("default", "kvm")
  151         xml_data = virt._gen_xml(
  152             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64"
  153         )
  154         root = ET.fromstring(xml_data)
  155         self.assertEqual(root.find("os/boot").attrib["dev"], "hd")
  156         self.assertEqual(root.find("os/type").attrib["arch"], "x86_64")
  157         self.assertEqual(root.find("os/type").text, "hvm")
  158 
  159     def test_boot_custom_dev(self):
  160         """
  161         Test virt._gen_xml() custom boot device
  162         """
  163         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  164         nicp = virt._nic_profile("default", "kvm")
  165         xml_data = virt._gen_xml(
  166             self.mock_conn,
  167             "hello",
  168             1,
  169             512,
  170             diskp,
  171             nicp,
  172             "kvm",
  173             "hvm",
  174             "x86_64",
  175             boot_dev="cdrom",
  176         )
  177         root = ET.fromstring(xml_data)
  178         self.assertEqual(root.find("os/boot").attrib["dev"], "cdrom")
  179 
  180     def test_boot_multiple_devs(self):
  181         """
  182         Test virt._gen_xml() multiple boot devices
  183         """
  184         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  185         nicp = virt._nic_profile("default", "kvm")
  186         xml_data = virt._gen_xml(
  187             self.mock_conn,
  188             "hello",
  189             1,
  190             512,
  191             diskp,
  192             nicp,
  193             "kvm",
  194             "hvm",
  195             "x86_64",
  196             boot_dev="cdrom network",
  197         )
  198         root = ET.fromstring(xml_data)
  199         devs = root.findall(".//boot")
  200         self.assertTrue(len(devs) == 2)
  201 
  202     def test_gen_xml_no_nic(self):
  203         """
  204         Test virt._gen_xml() serial console
  205         """
  206         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  207         nicp = virt._nic_profile("default", "kvm")
  208         xml_data = virt._gen_xml(
  209             self.mock_conn,
  210             "hello",
  211             1,
  212             512,
  213             diskp,
  214             nicp,
  215             "kvm",
  216             "hvm",
  217             "x86_64",
  218             serial_type="pty",
  219             console=True,
  220         )
  221         root = ET.fromstring(xml_data)
  222         self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
  223         self.assertEqual(root.find("devices/console").attrib["type"], "pty")
  224 
  225     def test_gen_xml_for_serial_console(self):
  226         """
  227         Test virt._gen_xml() serial console
  228         """
  229         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  230         nicp = virt._nic_profile("default", "kvm")
  231         xml_data = virt._gen_xml(
  232             self.mock_conn,
  233             "hello",
  234             1,
  235             512,
  236             diskp,
  237             nicp,
  238             "kvm",
  239             "hvm",
  240             "x86_64",
  241             serial_type="pty",
  242             console=True,
  243         )
  244         root = ET.fromstring(xml_data)
  245         self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
  246         self.assertEqual(root.find("devices/console").attrib["type"], "pty")
  247 
  248     def test_gen_xml_for_telnet_console(self):
  249         """
  250         Test virt._gen_xml() telnet console
  251         """
  252         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  253         nicp = virt._nic_profile("default", "kvm")
  254         xml_data = virt._gen_xml(
  255             self.mock_conn,
  256             "hello",
  257             1,
  258             512,
  259             diskp,
  260             nicp,
  261             "kvm",
  262             "hvm",
  263             "x86_64",
  264             serial_type="tcp",
  265             console=True,
  266             telnet_port=22223,
  267         )
  268         root = ET.fromstring(xml_data)
  269         self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
  270         self.assertEqual(root.find("devices/console").attrib["type"], "tcp")
  271         self.assertEqual(root.find("devices/console/source").attrib["service"], "22223")
  272 
  273     def test_gen_xml_for_telnet_console_unspecified_port(self):
  274         """
  275         Test virt._gen_xml() telnet console without any specified port
  276         """
  277         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  278         nicp = virt._nic_profile("default", "kvm")
  279         xml_data = virt._gen_xml(
  280             self.mock_conn,
  281             "hello",
  282             1,
  283             512,
  284             diskp,
  285             nicp,
  286             "kvm",
  287             "hvm",
  288             "x86_64",
  289             serial_type="tcp",
  290             console=True,
  291         )
  292         root = ET.fromstring(xml_data)
  293         self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
  294         self.assertEqual(root.find("devices/console").attrib["type"], "tcp")
  295         self.assertIsInstance(
  296             int(root.find("devices/console/source").attrib["service"]), int
  297         )
  298 
  299     def test_gen_xml_for_serial_no_console(self):
  300         """
  301         Test virt._gen_xml() with no serial console
  302         """
  303         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  304         nicp = virt._nic_profile("default", "kvm")
  305         xml_data = virt._gen_xml(
  306             self.mock_conn,
  307             "hello",
  308             1,
  309             512,
  310             diskp,
  311             nicp,
  312             "kvm",
  313             "hvm",
  314             "x86_64",
  315             serial_type="pty",
  316             console=False,
  317         )
  318         root = ET.fromstring(xml_data)
  319         self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
  320         self.assertEqual(root.find("devices/console"), None)
  321 
  322     def test_gen_xml_for_telnet_no_console(self):
  323         """
  324         Test virt._gen_xml() with no telnet console
  325         """
  326         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  327         nicp = virt._nic_profile("default", "kvm")
  328         xml_data = virt._gen_xml(
  329             self.mock_conn,
  330             "hello",
  331             1,
  332             512,
  333             diskp,
  334             nicp,
  335             "kvm",
  336             "hvm",
  337             "x86_64",
  338             serial_type="tcp",
  339             console=False,
  340         )
  341         root = ET.fromstring(xml_data)
  342         self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
  343         self.assertEqual(root.find("devices/console"), None)
  344 
  345     def test_gen_xml_nographics_default(self):
  346         """
  347         Test virt._gen_xml() with default no graphics device
  348         """
  349         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  350         nicp = virt._nic_profile("default", "kvm")
  351         xml_data = virt._gen_xml(
  352             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64"
  353         )
  354         root = ET.fromstring(xml_data)
  355         self.assertIsNone(root.find("devices/graphics"))
  356 
  357     def test_gen_xml_noloader_default(self):
  358         """
  359         Test virt._gen_xml() with default no loader
  360         """
  361         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  362         nicp = virt._nic_profile("default", "kvm")
  363         xml_data = virt._gen_xml(
  364             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64"
  365         )
  366         root = ET.fromstring(xml_data)
  367         self.assertIsNone(root.find("os/loader"))
  368 
  369     def test_gen_xml_vnc_default(self):
  370         """
  371         Test virt._gen_xml() with default vnc graphics device
  372         """
  373         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  374         nicp = virt._nic_profile("default", "kvm")
  375         xml_data = virt._gen_xml(
  376             self.mock_conn,
  377             "hello",
  378             1,
  379             512,
  380             diskp,
  381             nicp,
  382             "kvm",
  383             "hvm",
  384             "x86_64",
  385             graphics={
  386                 "type": "vnc",
  387                 "port": 1234,
  388                 "tlsPort": 5678,
  389                 "listen": {"type": "address", "address": "myhost"},
  390             },
  391         )
  392         root = ET.fromstring(xml_data)
  393         self.assertEqual(root.find("devices/graphics").attrib["type"], "vnc")
  394         self.assertEqual(root.find("devices/graphics").attrib["autoport"], "no")
  395         self.assertEqual(root.find("devices/graphics").attrib["port"], "1234")
  396         self.assertFalse("tlsPort" in root.find("devices/graphics").attrib)
  397         self.assertEqual(root.find("devices/graphics").attrib["listen"], "myhost")
  398         self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "address")
  399         self.assertEqual(
  400             root.find("devices/graphics/listen").attrib["address"], "myhost"
  401         )
  402 
  403     def test_gen_xml_spice_default(self):
  404         """
  405         Test virt._gen_xml() with default spice graphics device
  406         """
  407         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  408         nicp = virt._nic_profile("default", "kvm")
  409         xml_data = virt._gen_xml(
  410             self.mock_conn,
  411             "hello",
  412             1,
  413             512,
  414             diskp,
  415             nicp,
  416             "kvm",
  417             "hvm",
  418             "x86_64",
  419             graphics={"type": "spice"},
  420         )
  421         root = ET.fromstring(xml_data)
  422         self.assertEqual(root.find("devices/graphics").attrib["type"], "spice")
  423         self.assertEqual(root.find("devices/graphics").attrib["autoport"], "yes")
  424         self.assertEqual(root.find("devices/graphics").attrib["listen"], "0.0.0.0")
  425         self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "address")
  426         self.assertEqual(
  427             root.find("devices/graphics/listen").attrib["address"], "0.0.0.0"
  428         )
  429 
  430     def test_gen_xml_spice(self):
  431         """
  432         Test virt._gen_xml() with spice graphics device
  433         """
  434         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  435         nicp = virt._nic_profile("default", "kvm")
  436         xml_data = virt._gen_xml(
  437             self.mock_conn,
  438             "hello",
  439             1,
  440             512,
  441             diskp,
  442             nicp,
  443             "kvm",
  444             "hvm",
  445             "x86_64",
  446             graphics={
  447                 "type": "spice",
  448                 "port": 1234,
  449                 "tls_port": 5678,
  450                 "listen": {"type": "none"},
  451             },
  452         )
  453         root = ET.fromstring(xml_data)
  454         self.assertEqual(root.find("devices/graphics").attrib["type"], "spice")
  455         self.assertEqual(root.find("devices/graphics").attrib["autoport"], "no")
  456         self.assertEqual(root.find("devices/graphics").attrib["port"], "1234")
  457         self.assertEqual(root.find("devices/graphics").attrib["tlsPort"], "5678")
  458         self.assertFalse("listen" in root.find("devices/graphics").attrib)
  459         self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "none")
  460         self.assertFalse("address" in root.find("devices/graphics/listen").attrib)
  461 
  462     def test_default_disk_profile_hypervisor_esxi(self):
  463         """
  464         Test virt._disk_profile() default ESXi profile
  465         """
  466         mock = MagicMock(return_value={})
  467         with patch.dict(
  468             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  469         ):
  470             ret = virt._disk_profile(
  471                 self.mock_conn, "nonexistent", "vmware", None, "test-vm"
  472             )
  473             self.assertTrue(len(ret) == 1)
  474             found = [disk for disk in ret if disk["name"] == "system"]
  475             self.assertTrue(bool(found))
  476             system = found[0]
  477             self.assertEqual(system["format"], "vmdk")
  478             self.assertEqual(system["model"], "scsi")
  479             self.assertTrue(int(system["size"]) >= 1)
  480 
  481     def test_default_disk_profile_hypervisor_kvm(self):
  482         """
  483         Test virt._disk_profile() default KVM profile
  484         """
  485         mock = MagicMock(side_effect=[{}, "/images/dir"])
  486         with patch.dict(
  487             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  488         ):
  489             ret = virt._disk_profile(
  490                 self.mock_conn, "nonexistent", "kvm", None, "test-vm"
  491             )
  492             self.assertTrue(len(ret) == 1)
  493             found = [disk for disk in ret if disk["name"] == "system"]
  494             self.assertTrue(bool(found))
  495             system = found[0]
  496             self.assertEqual(system["format"], "qcow2")
  497             self.assertEqual(system["model"], "virtio")
  498             self.assertTrue(int(system["size"]) >= 1)
  499 
  500     def test_default_disk_profile_hypervisor_xen(self):
  501         """
  502         Test virt._disk_profile() default XEN profile
  503         """
  504         mock = MagicMock(side_effect=[{}, "/images/dir"])
  505         with patch.dict(
  506             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  507         ):
  508             ret = virt._disk_profile(
  509                 self.mock_conn, "nonexistent", "xen", None, "test-vm"
  510             )
  511             self.assertTrue(len(ret) == 1)
  512             found = [disk for disk in ret if disk["name"] == "system"]
  513             self.assertTrue(bool(found))
  514             system = found[0]
  515             self.assertEqual(system["format"], "qcow2")
  516             self.assertEqual(system["model"], "xen")
  517             self.assertTrue(int(system["size"]) >= 1)
  518 
  519     def test_default_nic_profile_hypervisor_esxi(self):
  520         """
  521         Test virt._nic_profile() default ESXi profile
  522         """
  523         mock = MagicMock(return_value={})
  524         with patch.dict(
  525             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  526         ):
  527             ret = virt._nic_profile("nonexistent", "vmware")
  528             self.assertTrue(len(ret) == 1)
  529             eth0 = ret[0]
  530             self.assertEqual(eth0["name"], "eth0")
  531             self.assertEqual(eth0["type"], "bridge")
  532             self.assertEqual(eth0["source"], "DEFAULT")
  533             self.assertEqual(eth0["model"], "e1000")
  534 
  535     def test_default_nic_profile_hypervisor_kvm(self):
  536         """
  537         Test virt._nic_profile() default KVM profile
  538         """
  539         mock = MagicMock(return_value={})
  540         with patch.dict(
  541             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  542         ):
  543             ret = virt._nic_profile("nonexistent", "kvm")
  544             self.assertTrue(len(ret) == 1)
  545             eth0 = ret[0]
  546             self.assertEqual(eth0["name"], "eth0")
  547             self.assertEqual(eth0["type"], "bridge")
  548             self.assertEqual(eth0["source"], "br0")
  549             self.assertEqual(eth0["model"], "virtio")
  550 
  551     def test_default_nic_profile_hypervisor_xen(self):
  552         """
  553         Test virt._nic_profile() default XEN profile
  554         """
  555         mock = MagicMock(return_value={})
  556         with patch.dict(
  557             virt.__salt__, {"config.get": mock}  # pylint: disable=no-member
  558         ):
  559             ret = virt._nic_profile("nonexistent", "xen")
  560             self.assertTrue(len(ret) == 1)
  561             eth0 = ret[0]
  562             self.assertEqual(eth0["name"], "eth0")
  563             self.assertEqual(eth0["type"], "bridge")
  564             self.assertEqual(eth0["source"], "br0")
  565             self.assertFalse(eth0["model"])
  566 
  567     def test_gen_vol_xml_esx(self):
  568         """
  569         Test virt._get_vol_xml() for the ESX case
  570         """
  571         xml_data = virt._gen_vol_xml("vmname/system.vmdk", 8192, format="vmdk")
  572         root = ET.fromstring(xml_data)
  573         self.assertIsNone(root.get("type"))
  574         self.assertEqual(root.find("name").text, "vmname/system.vmdk")
  575         self.assertEqual(root.find("capacity").attrib["unit"], "KiB")
  576         self.assertEqual(root.find("capacity").text, str(8192 * 1024))
  577         self.assertEqual(root.find("allocation").text, str(0))
  578         self.assertEqual(root.find("target/format").get("type"), "vmdk")
  579         self.assertIsNone(root.find("target/permissions"))
  580         self.assertIsNone(root.find("target/nocow"))
  581         self.assertIsNone(root.find("backingStore"))
  582 
  583     def test_gen_vol_xml_file(self):
  584         """
  585         Test virt._get_vol_xml() for a file volume
  586         """
  587         xml_data = virt._gen_vol_xml(
  588             "myvm_system.qcow2",
  589             8192,
  590             format="qcow2",
  591             allocation=4096,
  592             type="file",
  593             permissions={
  594                 "mode": "0775",
  595                 "owner": "123",
  596                 "group": "456",
  597                 "label": "sec_label",
  598             },
  599             backing_store={"path": "/backing/image", "format": "raw"},
  600             nocow=True,
  601         )
  602         root = ET.fromstring(xml_data)
  603         self.assertEqual(root.get("type"), "file")
  604         self.assertEqual(root.find("name").text, "myvm_system.qcow2")
  605         self.assertIsNone(root.find("key"))
  606         self.assertIsNone(root.find("target/path"))
  607         self.assertEqual(root.find("target/format").get("type"), "qcow2")
  608         self.assertEqual(root.find("capacity").attrib["unit"], "KiB")
  609         self.assertEqual(root.find("capacity").text, str(8192 * 1024))
  610         self.assertEqual(root.find("capacity").attrib["unit"], "KiB")
  611         self.assertEqual(root.find("allocation").text, str(4096 * 1024))
  612         self.assertEqual(root.find("target/permissions/mode").text, "0775")
  613         self.assertEqual(root.find("target/permissions/owner").text, "123")
  614         self.assertEqual(root.find("target/permissions/group").text, "456")
  615         self.assertEqual(root.find("target/permissions/label").text, "sec_label")
  616         self.assertIsNotNone(root.find("target/nocow"))
  617         self.assertEqual(root.find("backingStore/path").text, "/backing/image")
  618         self.assertEqual(root.find("backingStore/format").get("type"), "raw")
  619 
  620     def test_gen_xml_for_kvm_default_profile(self):
  621         """
  622         Test virt._gen_xml(), KVM default profile case
  623         """
  624         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
  625         nicp = virt._nic_profile("default", "kvm")
  626         xml_data = virt._gen_xml(
  627             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
  628         )
  629         root = ET.fromstring(xml_data)
  630         self.assertEqual(root.attrib["type"], "kvm")
  631         self.assertEqual(root.find("vcpu").text, "1")
  632         self.assertEqual(root.find("memory").text, str(512 * 1024))
  633         self.assertEqual(root.find("memory").attrib["unit"], "KiB")
  634 
  635         disks = root.findall(".//disk")
  636         self.assertEqual(len(disks), 1)
  637         disk = disks[0]
  638         root_dir = salt.config.DEFAULT_MINION_OPTS.get("root_dir")
  639         self.assertTrue(disk.find("source").attrib["file"].startswith(root_dir))
  640         self.assertTrue("hello_system" in disk.find("source").attrib["file"])
  641         self.assertEqual(disk.find("target").attrib["dev"], "vda")
  642         self.assertEqual(disk.find("target").attrib["bus"], "virtio")
  643         self.assertEqual(disk.find("driver").attrib["name"], "qemu")
  644         self.assertEqual(disk.find("driver").attrib["type"], "qcow2")
  645 
  646         interfaces = root.findall(".//interface")
  647         self.assertEqual(len(interfaces), 1)
  648         iface = interfaces[0]
  649         self.assertEqual(iface.attrib["type"], "bridge")
  650         self.assertEqual(iface.find("source").attrib["bridge"], "br0")
  651         self.assertEqual(iface.find("model").attrib["type"], "virtio")
  652 
  653     def test_gen_xml_for_esxi_default_profile(self):
  654         """
  655         Test virt._gen_xml(), ESXi/vmware default profile case
  656         """
  657         diskp = virt._disk_profile(self.mock_conn, "default", "vmware", [], "hello")
  658         nicp = virt._nic_profile("default", "vmware")
  659         xml_data = virt._gen_xml(
  660             self.mock_conn, "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64",
  661         )
  662         root = ET.fromstring(xml_data)
  663         self.assertEqual(root.attrib["type"], "vmware")
  664         self.assertEqual(root.find("vcpu").text, "1")
  665         self.assertEqual(root.find("memory").text, str(512 * 1024))
  666         self.assertEqual(root.find("memory").attrib["unit"], "KiB")
  667 
  668         disks = root.findall(".//disk")
  669         self.assertEqual(len(disks), 1)
  670         disk = disks[0]
  671         self.assertTrue("[0]" in disk.find("source").attrib["file"])
  672         self.assertTrue("hello_system" in disk.find("source").attrib["file"])
  673         self.assertEqual(disk.find("target").attrib["dev"], "sda")
  674         self.assertEqual(disk.find("target").attrib["bus"], "scsi")
  675         self.assertEqual(disk.find("address").attrib["unit"], "0")
  676 
  677         interfaces = root.findall(".//interface")
  678         self.assertEqual(len(interfaces), 1)
  679         iface = interfaces[0]
  680         self.assertEqual(iface.attrib["type"], "bridge")
  681         self.assertEqual(iface.find("source").attrib["bridge"], "DEFAULT")
  682         self.assertEqual(iface.find("model").attrib["type"], "e1000")
  683 
  684     def test_gen_xml_for_xen_default_profile(self):
  685         """
  686         Test virt._gen_xml(), XEN PV default profile case
  687         """
  688         diskp = virt._disk_profile(self.mock_conn, "default", "xen", [], "hello")
  689         nicp = virt._nic_profile("default", "xen")
  690         with patch.dict(
  691             virt.__grains__, {"os_family": "Suse"}  # pylint: disable=no-member
  692         ):
  693             xml_data = virt._gen_xml(
  694                 self.mock_conn,
  695                 "hello",
  696                 1,
  697                 512,
  698                 diskp,
  699                 nicp,
  700                 "xen",
  701                 "xen",
  702                 "x86_64",
  703                 boot=None,
  704             )
  705             root = ET.fromstring(xml_data)
  706             self.assertEqual(root.attrib["type"], "xen")
  707             self.assertEqual(root.find("vcpu").text, "1")
  708             self.assertEqual(root.find("memory").text, str(512 * 1024))
  709             self.assertEqual(root.find("memory").attrib["unit"], "KiB")
  710             self.assertEqual(
  711                 root.find(".//kernel").text, "/usr/lib/grub2/x86_64-xen/grub.xen"
  712             )
  713 
  714             disks = root.findall(".//disk")
  715             self.assertEqual(len(disks), 1)
  716             disk = disks[0]
  717             root_dir = salt.config.DEFAULT_MINION_OPTS.get("root_dir")
  718             self.assertTrue(disk.find("source").attrib["file"].startswith(root_dir))
  719             self.assertTrue("hello_system" in disk.find("source").attrib["file"])
  720             self.assertEqual(disk.find("target").attrib["dev"], "xvda")
  721             self.assertEqual(disk.find("target").attrib["bus"], "xen")
  722             self.assertEqual(disk.find("driver").attrib["name"], "qemu")
  723             self.assertEqual(disk.find("driver").attrib["type"], "qcow2")
  724 
  725             interfaces = root.findall(".//interface")
  726             self.assertEqual(len(interfaces), 1)
  727             iface = interfaces[0]
  728             self.assertEqual(iface.attrib["type"], "bridge")
  729             self.assertEqual(iface.find("source").attrib["bridge"], "br0")
  730             self.assertIsNone(iface.find("model"))
  731 
  732     def test_gen_xml_for_esxi_custom_profile(self):
  733         """
  734         Test virt._gen_xml(), ESXi/vmware custom profile case
  735         """
  736         disks = {
  737             "noeffect": [
  738                 {"first": {"size": 8192, "pool": "datastore1"}},
  739                 {"second": {"size": 4096, "pool": "datastore2"}},
  740             ]
  741         }
  742         nics = {
  743             "noeffect": [
  744                 {"name": "eth1", "source": "ONENET"},
  745                 {"name": "eth2", "source": "TWONET"},
  746             ]
  747         }
  748         with patch.dict(
  749             virt.__salt__,  # pylint: disable=no-member
  750             {"config.get": MagicMock(side_effect=[disks, nics])},
  751         ):
  752             diskp = virt._disk_profile(
  753                 self.mock_conn, "noeffect", "vmware", [], "hello"
  754             )
  755             nicp = virt._nic_profile("noeffect", "vmware")
  756             xml_data = virt._gen_xml(
  757                 self.mock_conn, "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64",
  758             )
  759             root = ET.fromstring(xml_data)
  760             self.assertEqual(root.attrib["type"], "vmware")
  761             self.assertEqual(root.find("vcpu").text, "1")
  762             self.assertEqual(root.find("memory").text, str(512 * 1024))
  763             self.assertEqual(root.find("memory").attrib["unit"], "KiB")
  764             self.assertTrue(len(root.findall(".//disk")) == 2)
  765             self.assertTrue(len(root.findall(".//interface")) == 2)
  766 
  767     def test_gen_xml_for_kvm_custom_profile(self):
  768         """
  769         Test virt._gen_xml(), KVM custom profile case
  770         """
  771         disks = {
  772             "noeffect": [
  773                 {"first": {"size": 8192, "pool": "/var/lib/images"}},
  774                 {"second": {"size": 4096, "pool": "/var/lib/images"}},
  775             ]
  776         }
  777         nics = {
  778             "noeffect": [
  779                 {"name": "eth1", "source": "b2"},
  780                 {"name": "eth2", "source": "b2"},
  781             ]
  782         }
  783         with patch.dict(
  784             virt.__salt__,  # pylint: disable=no-member
  785             {"config.get": MagicMock(side_effect=[disks, nics])},
  786         ):
  787             diskp = virt._disk_profile(self.mock_conn, "noeffect", "kvm", [], "hello")
  788             nicp = virt._nic_profile("noeffect", "kvm")
  789             xml_data = virt._gen_xml(
  790                 self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
  791             )
  792             root = ET.fromstring(xml_data)
  793             self.assertEqual(root.attrib["type"], "kvm")
  794             self.assertEqual(root.find("vcpu").text, "1")
  795             self.assertEqual(root.find("memory").text, str(512 * 1024))
  796             self.assertEqual(root.find("memory").attrib["unit"], "KiB")
  797             disks = root.findall(".//disk")
  798             self.assertTrue(len(disks) == 2)
  799             self.assertEqual(disks[0].find("target").get("dev"), "vda")
  800             self.assertEqual(disks[1].find("target").get("dev"), "vdb")
  801             self.assertTrue(len(root.findall(".//interface")) == 2)
  802 
  803     def test_disk_profile_kvm_disk_pool(self):
  804         """
  805         Test virt._disk_profile(), KVM case with pools defined.
  806         """
  807         disks = {
  808             "noeffect": [
  809                 {"first": {"size": 8192, "pool": "mypool"}},
  810                 {"second": {"size": 4096}},
  811             ]
  812         }
  813 
  814         # pylint: disable=no-member
  815         with patch.dict(
  816             virt.__salt__,
  817             {
  818                 "config.get": MagicMock(
  819                     side_effect=[
  820                         disks,
  821                         os.path.join(salt.syspaths.ROOT_DIR, "default", "path"),
  822                     ]
  823                 )
  824             },
  825         ):
  826 
  827             diskp = virt._disk_profile(self.mock_conn, "noeffect", "kvm", [], "hello")
  828 
  829             pools_path = (
  830                 os.path.join(salt.syspaths.ROOT_DIR, "pools", "mypool") + os.sep
  831             )
  832             default_path = (
  833                 os.path.join(salt.syspaths.ROOT_DIR, "default", "path") + os.sep
  834             )
  835 
  836             self.assertEqual(len(diskp), 2)
  837             self.assertTrue(diskp[1]["source_file"].startswith(default_path))
  838         # pylint: enable=no-member
  839 
  840     def test_disk_profile_kvm_disk_external_image(self):
  841         """
  842         Test virt._gen_xml(), KVM case with an external image.
  843         """
  844         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=True)}):
  845             diskp = virt._disk_profile(
  846                 self.mock_conn,
  847                 None,
  848                 "kvm",
  849                 [{"name": "mydisk", "source_file": "/path/to/my/image.qcow2"}],
  850                 "hello",
  851             )
  852 
  853             self.assertEqual(len(diskp), 1)
  854             self.assertEqual(diskp[0]["source_file"], ("/path/to/my/image.qcow2"))
  855 
  856     def test_disk_profile_cdrom_default(self):
  857         """
  858         Test virt._gen_xml(), KVM case with cdrom.
  859         """
  860         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=True)}):
  861             diskp = virt._disk_profile(
  862                 self.mock_conn,
  863                 None,
  864                 "kvm",
  865                 [
  866                     {
  867                         "name": "mydisk",
  868                         "device": "cdrom",
  869                         "source_file": "/path/to/my.iso",
  870                     }
  871                 ],
  872                 "hello",
  873             )
  874 
  875             self.assertEqual(len(diskp), 1)
  876             self.assertEqual(diskp[0]["model"], "ide")
  877             self.assertEqual(diskp[0]["format"], "raw")
  878 
  879     def test_disk_profile_pool_disk_type(self):
  880         """
  881         Test virt._disk_profile(), with a disk pool of disk type
  882         """
  883         self.mock_conn.listStoragePools.return_value = ["test-vdb"]
  884         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """
  885             <pool type="disk">
  886               <name>test-vdb</name>
  887               <source>
  888                 <device path='/dev/vdb'/>
  889               </source>
  890               <target>
  891                 <path>/dev</path>
  892               </target>
  893             </pool>
  894         """
  895 
  896         # No existing disk case
  897         self.mock_conn.storagePoolLookupByName.return_value.listVolumes.return_value = (
  898             []
  899         )
  900         diskp = virt._disk_profile(
  901             self.mock_conn,
  902             None,
  903             "kvm",
  904             [{"name": "mydisk", "pool": "test-vdb"}],
  905             "hello",
  906         )
  907         self.assertEqual(diskp[0]["filename"], ("vdb1"))
  908 
  909         # Append to the end case
  910         self.mock_conn.storagePoolLookupByName.return_value.listVolumes.return_value = [
  911             "vdb1",
  912             "vdb2",
  913         ]
  914         diskp = virt._disk_profile(
  915             self.mock_conn,
  916             None,
  917             "kvm",
  918             [{"name": "mydisk", "pool": "test-vdb"}],
  919             "hello",
  920         )
  921         self.assertEqual(diskp[0]["filename"], ("vdb3"))
  922 
  923         # Hole in the middle case
  924         self.mock_conn.storagePoolLookupByName.return_value.listVolumes.return_value = [
  925             "vdb1",
  926             "vdb3",
  927         ]
  928         diskp = virt._disk_profile(
  929             self.mock_conn,
  930             None,
  931             "kvm",
  932             [{"name": "mydisk", "pool": "test-vdb"}],
  933             "hello",
  934         )
  935         self.assertEqual(diskp[0]["filename"], ("vdb2"))
  936 
  937         # Reuse existing volume case
  938         diskp = virt._disk_profile(
  939             self.mock_conn,
  940             None,
  941             "kvm",
  942             [{"name": "mydisk", "pool": "test-vdb", "source_file": "vdb1"}],
  943             "hello",
  944         )
  945         self.assertEqual(diskp[0]["filename"], ("vdb1"))
  946 
  947     def test_gen_xml_volume(self):
  948         """
  949         Test virt._gen_xml(), generating a disk of volume type
  950         """
  951         self.mock_conn.listStoragePools.return_value = ["default"]
  952         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = (
  953             "<pool type='dir'/>"
  954         )
  955         self.mock_conn.storagePoolLookupByName.return_value.listVolumes.return_value = [
  956             "myvolume"
  957         ]
  958         diskp = virt._disk_profile(
  959             self.mock_conn,
  960             None,
  961             "kvm",
  962             [
  963                 {"name": "system", "pool": "default"},
  964                 {"name": "data", "pool": "default", "source_file": "myvolume"},
  965             ],
  966             "hello",
  967         )
  968         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = (
  969             "<pool type='dir'/>"
  970         )
  971         nicp = virt._nic_profile(None, "kvm")
  972         xml_data = virt._gen_xml(
  973             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
  974         )
  975         root = ET.fromstring(xml_data)
  976         disk = root.findall(".//disk")[0]
  977         self.assertEqual(disk.attrib["device"], "disk")
  978         self.assertEqual(disk.attrib["type"], "volume")
  979         source = disk.find("source")
  980         self.assertEqual("default", source.attrib["pool"])
  981         self.assertEqual("hello_system", source.attrib["volume"])
  982         self.assertEqual("myvolume", root.find(".//disk[2]/source").get("volume"))
  983 
  984         # RBD volume usage auth test case
  985         self.mock_conn.listStoragePools.return_value = ["test-rbd"]
  986         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """
  987             <pool type='rbd'>
  988               <name>test-rbd</name>
  989               <uuid>ede33e0a-9df0-479f-8afd-55085a01b244</uuid>
  990               <capacity unit='bytes'>526133493760</capacity>
  991               <allocation unit='bytes'>589928</allocation>
  992               <available unit='bytes'>515081306112</available>
  993               <source>
  994                 <host name='ses2.tf.local'/>
  995                 <host name='ses3.tf.local' port='1234'/>
  996                 <name>libvirt-pool</name>
  997                 <auth type='ceph' username='libvirt'>
  998                   <secret usage='pool_test-rbd'/>
  999                 </auth>
 1000               </source>
 1001             </pool>
 1002         """
 1003         self.mock_conn.getStoragePoolCapabilities.return_value = """
 1004             <storagepoolCapabilities>
 1005               <pool type='rbd' supported='yes'>
 1006                 <volOptions>
 1007                   <defaultFormat type='raw'/>
 1008                   <enum name='targetFormatType'>
 1009                   </enum>
 1010                 </volOptions>
 1011               </pool>
 1012             </storagepoolCapabilities>
 1013         """
 1014         diskp = virt._disk_profile(
 1015             self.mock_conn,
 1016             None,
 1017             "kvm",
 1018             [{"name": "system", "pool": "test-rbd"}],
 1019             "test-vm",
 1020         )
 1021         xml_data = virt._gen_xml(
 1022             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
 1023         )
 1024         root = ET.fromstring(xml_data)
 1025         disk = root.findall(".//disk")[0]
 1026         self.assertDictEqual(
 1027             {
 1028                 "type": "network",
 1029                 "device": "disk",
 1030                 "source": {
 1031                     "protocol": "rbd",
 1032                     "name": "libvirt-pool/test-vm_system",
 1033                     "host": [
 1034                         {"name": "ses2.tf.local"},
 1035                         {"name": "ses3.tf.local", "port": "1234"},
 1036                     ],
 1037                     "auth": {
 1038                         "username": "libvirt",
 1039                         "secret": {"type": "ceph", "usage": "pool_test-rbd"},
 1040                     },
 1041                 },
 1042                 "target": {"dev": "vda", "bus": "virtio"},
 1043                 "driver": {
 1044                     "name": "qemu",
 1045                     "type": "raw",
 1046                     "cache": "none",
 1047                     "io": "native",
 1048                 },
 1049             },
 1050             salt.utils.xmlutil.to_dict(disk, True),
 1051         )
 1052 
 1053         # RBD volume UUID auth test case
 1054         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """
 1055             <pool type='rbd'>
 1056               <name>test-rbd</name>
 1057               <uuid>ede33e0a-9df0-479f-8afd-55085a01b244</uuid>
 1058               <capacity unit='bytes'>526133493760</capacity>
 1059               <allocation unit='bytes'>589928</allocation>
 1060               <available unit='bytes'>515081306112</available>
 1061               <source>
 1062                 <host name='ses2.tf.local'/>
 1063                 <host name='ses3.tf.local' port='1234'/>
 1064                 <name>libvirt-pool</name>
 1065                 <auth type='ceph' username='libvirt'>
 1066                   <secret uuid='some-uuid'/>
 1067                 </auth>
 1068               </source>
 1069             </pool>
 1070         """
 1071         self.mock_conn.secretLookupByUUIDString.return_value.usageID.return_value = (
 1072             "pool_test-rbd"
 1073         )
 1074         diskp = virt._disk_profile(
 1075             self.mock_conn,
 1076             None,
 1077             "kvm",
 1078             [{"name": "system", "pool": "test-rbd"}],
 1079             "test-vm",
 1080         )
 1081         xml_data = virt._gen_xml(
 1082             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
 1083         )
 1084         root = ET.fromstring(xml_data)
 1085         self.assertDictEqual(
 1086             {
 1087                 "username": "libvirt",
 1088                 "secret": {"type": "ceph", "usage": "pool_test-rbd"},
 1089             },
 1090             salt.utils.xmlutil.to_dict(root.find(".//disk/source/auth"), True),
 1091         )
 1092         self.mock_conn.secretLookupByUUIDString.assert_called_once_with("some-uuid")
 1093 
 1094         # Disk volume test case
 1095         self.mock_conn.getStoragePoolCapabilities.return_value = """
 1096             <storagepoolCapabilities>
 1097               <pool type='disk' supported='yes'>
 1098                 <volOptions>
 1099                   <defaultFormat type='none'/>
 1100                   <enum name='targetFormatType'>
 1101                     <value>none</value>
 1102                     <value>linux</value>
 1103                     <value>fat16</value>
 1104                   </enum>
 1105                 </volOptions>
 1106               </pool>
 1107             </storagepoolCapabilities>
 1108         """
 1109         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """
 1110             <pool type='disk'>
 1111               <name>test-vdb</name>
 1112               <source>
 1113                 <device path='/dev/vdb'/>
 1114                 <format type='gpt'/>
 1115               </source>
 1116             </pool>
 1117         """
 1118         self.mock_conn.listStoragePools.return_value = ["test-vdb"]
 1119         self.mock_conn.storagePoolLookupByName.return_value.listVolumes.return_value = [
 1120             "vdb1",
 1121         ]
 1122         diskp = virt._disk_profile(
 1123             self.mock_conn,
 1124             None,
 1125             "kvm",
 1126             [{"name": "system", "pool": "test-vdb"}],
 1127             "test-vm",
 1128         )
 1129         xml_data = virt._gen_xml(
 1130             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
 1131         )
 1132         root = ET.fromstring(xml_data)
 1133         disk = root.findall(".//disk")[0]
 1134         self.assertEqual(disk.attrib["type"], "volume")
 1135         source = disk.find("source")
 1136         self.assertEqual("test-vdb", source.attrib["pool"])
 1137         self.assertEqual("vdb2", source.attrib["volume"])
 1138         self.assertEqual("raw", disk.find("driver").get("type"))
 1139 
 1140     def test_get_xml_volume_xen_dir(self):
 1141         """
 1142         Test virt._gen_xml generating disks for a Xen hypervisor
 1143         """
 1144         self.mock_conn.listStoragePools.return_value = ["default"]
 1145         pool_mock = MagicMock()
 1146         pool_mock.XMLDesc.return_value = (
 1147             "<pool type='dir'><target><path>/path/to/images</path></target></pool>"
 1148         )
 1149         volume_xml = "<volume><target><path>/path/to/images/hello_system</path></target></volume>"
 1150         pool_mock.storageVolLookupByName.return_value.XMLDesc.return_value = volume_xml
 1151         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 1152         diskp = virt._disk_profile(
 1153             self.mock_conn,
 1154             None,
 1155             "xen",
 1156             [{"name": "system", "pool": "default"}],
 1157             "hello",
 1158         )
 1159         xml_data = virt._gen_xml(
 1160             self.mock_conn, "hello", 1, 512, diskp, [], "xen", "hvm", "x86_64",
 1161         )
 1162         root = ET.fromstring(xml_data)
 1163         disk = root.findall(".//disk")[0]
 1164         self.assertEqual(disk.attrib["type"], "file")
 1165         self.assertEqual(
 1166             "/path/to/images/hello_system", disk.find("source").attrib["file"]
 1167         )
 1168 
 1169     def test_get_xml_volume_xen_block(self):
 1170         """
 1171         Test virt._gen_xml generating disks for a Xen hypervisor
 1172         """
 1173         self.mock_conn.listStoragePools.return_value = ["default"]
 1174         pool_mock = MagicMock()
 1175         pool_mock.listVolumes.return_value = ["vol01"]
 1176         volume_xml = "<volume><target><path>/dev/to/vol01</path></target></volume>"
 1177         pool_mock.storageVolLookupByName.return_value.XMLDesc.return_value = volume_xml
 1178         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 1179 
 1180         for pool_type in ["logical", "disk", "iscsi", "scsi"]:
 1181             pool_mock.XMLDesc.return_value = "<pool type='{}'><source><device path='/dev/sda'/></source></pool>".format(
 1182                 pool_type
 1183             )
 1184             diskp = virt._disk_profile(
 1185                 self.mock_conn,
 1186                 None,
 1187                 "xen",
 1188                 [{"name": "system", "pool": "default", "source_file": "vol01"}],
 1189                 "hello",
 1190             )
 1191             xml_data = virt._gen_xml(
 1192                 self.mock_conn, "hello", 1, 512, diskp, [], "xen", "hvm", "x86_64",
 1193             )
 1194             root = ET.fromstring(xml_data)
 1195             disk = root.findall(".//disk")[0]
 1196             self.assertEqual(disk.attrib["type"], "block")
 1197             self.assertEqual("/dev/to/vol01", disk.find("source").attrib["dev"])
 1198 
 1199     def test_gen_xml_cdrom(self):
 1200         """
 1201         Test virt._gen_xml(), generating a cdrom device (different disk type, no source)
 1202         """
 1203         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = (
 1204             "<pool type='dir'/>"
 1205         )
 1206         diskp = virt._disk_profile(
 1207             self.mock_conn,
 1208             None,
 1209             "kvm",
 1210             [
 1211                 {"name": "system", "pool": "default"},
 1212                 {
 1213                     "name": "tested",
 1214                     "device": "cdrom",
 1215                     "source_file": None,
 1216                     "model": "ide",
 1217                 },
 1218                 {
 1219                     "name": "remote",
 1220                     "device": "cdrom",
 1221                     "source_file": "http://myhost:8080/url/to/image?query=foo&filter=bar",
 1222                     "model": "ide",
 1223                 },
 1224             ],
 1225             "hello",
 1226         )
 1227         nicp = virt._nic_profile(None, "kvm")
 1228         xml_data = virt._gen_xml(
 1229             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
 1230         )
 1231         root = ET.fromstring(xml_data)
 1232         disk = root.findall(".//disk")[1]
 1233         self.assertEqual(disk.get("type"), "file")
 1234         self.assertEqual(disk.attrib["device"], "cdrom")
 1235         self.assertIsNone(disk.find("source"))
 1236         self.assertEqual(disk.find("target").get("dev"), "hda")
 1237 
 1238         disk = root.findall(".//disk")[2]
 1239         self.assertEqual(disk.get("type"), "network")
 1240         self.assertEqual(disk.attrib["device"], "cdrom")
 1241         self.assertEqual(
 1242             {
 1243                 "protocol": "http",
 1244                 "name": "/url/to/image",
 1245                 "query": "query=foo&filter=bar",
 1246                 "host": {"name": "myhost", "port": "8080"},
 1247             },
 1248             salt.utils.xmlutil.to_dict(disk.find("source"), True),
 1249         )
 1250 
 1251     def test_controller_for_esxi(self):
 1252         """
 1253         Test virt._gen_xml() generated device controller for ESXi/vmware
 1254         """
 1255         diskp = virt._disk_profile(self.mock_conn, "default", "vmware", [], "hello")
 1256         nicp = virt._nic_profile("default", "vmware")
 1257         xml_data = virt._gen_xml(
 1258             self.mock_conn, "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64",
 1259         )
 1260         root = ET.fromstring(xml_data)
 1261         controllers = root.findall(".//devices/controller")
 1262         self.assertTrue(len(controllers) == 1)
 1263         controller = controllers[0]
 1264         self.assertEqual(controller.attrib["model"], "lsilogic")
 1265 
 1266     def test_controller_for_kvm(self):
 1267         """
 1268         Test virt._gen_xml() generated device controller for KVM
 1269         """
 1270         diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
 1271         nicp = virt._nic_profile("default", "kvm")
 1272         xml_data = virt._gen_xml(
 1273             self.mock_conn, "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",
 1274         )
 1275         root = ET.fromstring(xml_data)
 1276         controllers = root.findall(".//devices/controller")
 1277         # There should be no controller
 1278         self.assertTrue(len(controllers) == 0)
 1279 
 1280     def test_diff_disks(self):
 1281         """
 1282         Test virt._diff_disks()
 1283         """
 1284         old_disks = ET.fromstring(
 1285             """
 1286             <devices>
 1287               <disk type='file' device='disk'>
 1288                 <source file='/path/to/img0.qcow2'/>
 1289                 <target dev='vda' bus='virtio'/>
 1290               </disk>
 1291               <disk type='file' device='disk'>
 1292                 <source file='/path/to/img1.qcow2'/>
 1293                 <target dev='vdb' bus='virtio'/>
 1294               </disk>
 1295               <disk type='file' device='disk'>
 1296                 <source file='/path/to/img2.qcow2'/>
 1297                 <target dev='hda' bus='ide'/>
 1298               </disk>
 1299               <disk type='file' device='disk'>
 1300                 <source file='/path/to/img4.qcow2'/>
 1301                 <target dev='hdb' bus='ide'/>
 1302               </disk>
 1303               <disk type='file' device='cdrom'>
 1304                 <target dev='hdc' bus='ide'/>
 1305               </disk>
 1306             </devices>
 1307         """
 1308         ).findall("disk")
 1309 
 1310         new_disks = ET.fromstring(
 1311             """
 1312             <devices>
 1313               <disk type='file' device='disk'>
 1314                 <source file='/path/to/img3.qcow2'/>
 1315                 <target dev='vda' bus='virtio'/>
 1316               </disk>
 1317               <disk type='file' device='disk' cache='default'>
 1318                 <source file='/path/to/img0.qcow2'/>
 1319                 <target dev='vda' bus='virtio'/>
 1320               </disk>
 1321               <disk type='file' device='disk'>
 1322                 <source file='/path/to/img4.qcow2'/>
 1323                 <target dev='vda' bus='virtio'/>
 1324               </disk>
 1325               <disk type='file' device='cdrom'>
 1326                 <target dev='hda' bus='ide'/>
 1327               </disk>
 1328             </devices>
 1329         """
 1330         ).findall("disk")
 1331         ret = virt._diff_disk_lists(old_disks, new_disks)
 1332         self.assertEqual(
 1333             [
 1334                 disk.find("source").get("file")
 1335                 if disk.find("source") is not None
 1336                 else None
 1337                 for disk in ret["unchanged"]
 1338             ],
 1339             [],
 1340         )
 1341         self.assertEqual(
 1342             [
 1343                 disk.find("source").get("file")
 1344                 if disk.find("source") is not None
 1345                 else None
 1346                 for disk in ret["new"]
 1347             ],
 1348             ["/path/to/img3.qcow2", "/path/to/img0.qcow2", "/path/to/img4.qcow2", None],
 1349         )
 1350         self.assertEqual(
 1351             [disk.find("target").get("dev") for disk in ret["sorted"]],
 1352             ["vda", "vdb", "vdc", "hda"],
 1353         )
 1354         self.assertEqual(
 1355             [
 1356                 disk.find("source").get("file")
 1357                 if disk.find("source") is not None
 1358                 else None
 1359                 for disk in ret["sorted"]
 1360             ],
 1361             ["/path/to/img3.qcow2", "/path/to/img0.qcow2", "/path/to/img4.qcow2", None],
 1362         )
 1363         self.assertEqual(ret["new"][1].find("target").get("bus"), "virtio")
 1364         self.assertEqual(
 1365             [
 1366                 disk.find("source").get("file")
 1367                 if disk.find("source") is not None
 1368                 else None
 1369                 for disk in ret["deleted"]
 1370             ],
 1371             [
 1372                 "/path/to/img0.qcow2",
 1373                 "/path/to/img1.qcow2",
 1374                 "/path/to/img2.qcow2",
 1375                 "/path/to/img4.qcow2",
 1376                 None,
 1377             ],
 1378         )
 1379 
 1380     def test_diff_nics(self):
 1381         """
 1382         Test virt._diff_nics()
 1383         """
 1384         old_nics = ET.fromstring(
 1385             """
 1386             <devices>
 1387                <interface type='network'>
 1388                  <mac address='52:54:00:39:02:b1'/>
 1389                  <source network='default'/>
 1390                  <model type='virtio'/>
 1391                  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 1392                </interface>
 1393                <interface type='network'>
 1394                  <mac address='52:54:00:39:02:b2'/>
 1395                  <source network='admin'/>
 1396                  <model type='virtio'/>
 1397                  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 1398                </interface>
 1399                <interface type='network'>
 1400                  <mac address='52:54:00:39:02:b3'/>
 1401                  <source network='admin'/>
 1402                  <model type='virtio'/>
 1403                  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 1404                </interface>
 1405             </devices>
 1406         """
 1407         ).findall("interface")
 1408 
 1409         new_nics = ET.fromstring(
 1410             """
 1411             <devices>
 1412                <interface type='network'>
 1413                  <mac address='52:54:00:39:02:b1'/>
 1414                  <source network='default'/>
 1415                  <model type='virtio'/>
 1416                </interface>
 1417                <interface type='network'>
 1418                  <mac address='52:54:00:39:02:b2'/>
 1419                  <source network='default'/>
 1420                  <model type='virtio'/>
 1421                </interface>
 1422                <interface type='network'>
 1423                  <mac address='52:54:00:39:02:b4'/>
 1424                  <source network='admin'/>
 1425                  <model type='virtio'/>
 1426                </interface>
 1427             </devices>
 1428         """
 1429         ).findall("interface")
 1430         ret = virt._diff_interface_lists(old_nics, new_nics)
 1431         self.assertEqual(
 1432             [nic.find("mac").get("address") for nic in ret["unchanged"]],
 1433             ["52:54:00:39:02:b1"],
 1434         )
 1435         self.assertEqual(
 1436             [nic.find("mac").get("address") for nic in ret["new"]],
 1437             ["52:54:00:39:02:b2", "52:54:00:39:02:b4"],
 1438         )
 1439         self.assertEqual(
 1440             [nic.find("mac").get("address") for nic in ret["deleted"]],
 1441             ["52:54:00:39:02:b2", "52:54:00:39:02:b3"],
 1442         )
 1443 
 1444     def test_init(self):
 1445         """
 1446         Test init() function
 1447         """
 1448         xml = """
 1449 <capabilities>
 1450   <host>
 1451     <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
 1452     <cpu>
 1453       <arch>x86_64</arch>
 1454       <model>Nehalem</model>
 1455       <vendor>Intel</vendor>
 1456       <microcode version='25'/>
 1457       <topology sockets='1' cores='4' threads='2'/>
 1458       <feature name='vme'/>
 1459       <feature name='ds'/>
 1460       <feature name='acpi'/>
 1461       <pages unit='KiB' size='4'/>
 1462       <pages unit='KiB' size='2048'/>
 1463     </cpu>
 1464     <power_management>
 1465       <suspend_mem/>
 1466       <suspend_disk/>
 1467       <suspend_hybrid/>
 1468     </power_management>
 1469     <migration_features>
 1470       <live/>
 1471       <uri_transports>
 1472         <uri_transport>tcp</uri_transport>
 1473         <uri_transport>rdma</uri_transport>
 1474       </uri_transports>
 1475     </migration_features>
 1476     <topology>
 1477       <cells num='1'>
 1478         <cell id='0'>
 1479           <memory unit='KiB'>12367120</memory>
 1480           <pages unit='KiB' size='4'>3091780</pages>
 1481           <pages unit='KiB' size='2048'>0</pages>
 1482           <distances>
 1483             <sibling id='0' value='10'/>
 1484           </distances>
 1485           <cpus num='8'>
 1486             <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
 1487             <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
 1488             <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
 1489             <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
 1490             <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
 1491             <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
 1492             <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
 1493             <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
 1494           </cpus>
 1495         </cell>
 1496       </cells>
 1497     </topology>
 1498     <cache>
 1499       <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
 1500     </cache>
 1501     <secmodel>
 1502       <model>apparmor</model>
 1503       <doi>0</doi>
 1504     </secmodel>
 1505     <secmodel>
 1506       <model>dac</model>
 1507       <doi>0</doi>
 1508       <baselabel type='kvm'>+487:+486</baselabel>
 1509       <baselabel type='qemu'>+487:+486</baselabel>
 1510     </secmodel>
 1511   </host>
 1512 
 1513   <guest>
 1514     <os_type>hvm</os_type>
 1515     <arch name='i686'>
 1516       <wordsize>32</wordsize>
 1517       <emulator>/usr/bin/qemu-system-i386</emulator>
 1518       <machine maxCpus='255'>pc-i440fx-2.6</machine>
 1519       <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 1520       <machine maxCpus='255'>pc-0.12</machine>
 1521       <domain type='qemu'/>
 1522       <domain type='kvm'>
 1523         <emulator>/usr/bin/qemu-kvm</emulator>
 1524         <machine maxCpus='255'>pc-i440fx-2.6</machine>
 1525         <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 1526         <machine maxCpus='255'>pc-0.12</machine>
 1527       </domain>
 1528     </arch>
 1529     <features>
 1530       <cpuselection/>
 1531       <deviceboot/>
 1532       <disksnapshot default='on' toggle='no'/>
 1533       <acpi default='on' toggle='yes'/>
 1534       <apic default='on' toggle='no'/>
 1535       <pae/>
 1536       <nonpae/>
 1537     </features>
 1538   </guest>
 1539 
 1540   <guest>
 1541     <os_type>hvm</os_type>
 1542     <arch name='x86_64'>
 1543       <wordsize>64</wordsize>
 1544       <emulator>/usr/bin/qemu-system-x86_64</emulator>
 1545       <machine maxCpus='255'>pc-i440fx-2.6</machine>
 1546       <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 1547       <machine maxCpus='255'>pc-0.12</machine>
 1548       <domain type='qemu'/>
 1549       <domain type='kvm'>
 1550         <emulator>/usr/bin/qemu-kvm</emulator>
 1551         <machine maxCpus='255'>pc-i440fx-2.6</machine>
 1552         <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 1553         <machine maxCpus='255'>pc-0.12</machine>
 1554       </domain>
 1555     </arch>
 1556     <features>
 1557       <cpuselection/>
 1558       <deviceboot/>
 1559       <disksnapshot default='on' toggle='no'/>
 1560       <acpi default='on' toggle='yes'/>
 1561       <apic default='on' toggle='no'/>
 1562     </features>
 1563   </guest>
 1564 
 1565 </capabilities>
 1566         """
 1567         self.mock_conn.getCapabilities.return_value = xml  # pylint: disable=no-member
 1568 
 1569         root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
 1570 
 1571         defineMock = MagicMock(return_value=1)
 1572         self.mock_conn.defineXML = defineMock
 1573         mock_chmod = MagicMock()
 1574         mock_run = MagicMock()
 1575         with patch.dict(
 1576             os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()}
 1577         ):  # pylint: disable=no-member
 1578             with patch.dict(
 1579                 virt.__salt__, {"cmd.run": mock_run}
 1580             ):  # pylint: disable=no-member
 1581 
 1582                 # Ensure the init() function allows creating VM without NIC and disk
 1583                 virt.init(
 1584                     "test vm", 2, 1234, nic=None, disk=None, seed=False, start=False
 1585                 )
 1586                 definition = defineMock.call_args_list[0][0][0]
 1587                 self.assertFalse("<interface" in definition)
 1588                 self.assertFalse("<disk" in definition)
 1589 
 1590                 # Ensure the init() function allows creating VM without NIC and
 1591                 # disk but with boot parameters.
 1592 
 1593                 defineMock.reset_mock()
 1594                 mock_run.reset_mock()
 1595                 boot = {
 1596                     "kernel": "/root/f8-i386-vmlinuz",
 1597                     "initrd": "/root/f8-i386-initrd",
 1598                     "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/",
 1599                 }
 1600                 retval = virt.init(
 1601                     "test vm boot params",
 1602                     2,
 1603                     1234,
 1604                     nic=None,
 1605                     disk=None,
 1606                     seed=False,
 1607                     start=False,
 1608                     boot=boot,
 1609                 )
 1610                 definition = defineMock.call_args_list[0][0][0]
 1611                 self.assertEqual("<kernel" in definition, True)
 1612                 self.assertEqual("<initrd" in definition, True)
 1613                 self.assertEqual("<cmdline" in definition, True)
 1614                 self.assertEqual(retval, True)
 1615 
 1616                 # Verify that remote paths are downloaded and the xml has been
 1617                 # modified
 1618                 mock_response = MagicMock()
 1619                 mock_response.read = MagicMock(return_value="filecontent")
 1620                 cache_dir = tempfile.mkdtemp()
 1621 
 1622                 with patch.dict(virt.__dict__, {"CACHE_DIR": cache_dir}):
 1623                     with patch(
 1624                         "salt.ext.six.moves.urllib.request.urlopen",
 1625                         MagicMock(return_value=mock_response),
 1626                     ):
 1627                         with patch(
 1628                             "salt.utils.files.fopen", return_value=mock_response
 1629                         ):
 1630 
 1631                             defineMock.reset_mock()
 1632                             mock_run.reset_mock()
 1633                             boot = {
 1634                                 "kernel": "https://www.example.com/download/vmlinuz",
 1635                                 "initrd": "",
 1636                                 "cmdline": "console=ttyS0 "
 1637                                 "ks=http://example.com/f8-i386/os/",
 1638                             }
 1639 
 1640                             retval = virt.init(
 1641                                 "test remote vm boot params",
 1642                                 2,
 1643                                 1234,
 1644                                 nic=None,
 1645                                 disk=None,
 1646                                 seed=False,
 1647                                 start=False,
 1648                                 boot=boot,
 1649                             )
 1650                             definition = defineMock.call_args_list[0][0][0]
 1651                             self.assertEqual(cache_dir in definition, True)
 1652 
 1653                     shutil.rmtree(cache_dir)
 1654 
 1655                 # Test case creating disks
 1656                 defineMock.reset_mock()
 1657                 mock_run.reset_mock()
 1658                 pool_mock = MagicMock()
 1659                 pool_mock.XMLDesc.return_value = '<pool type="dir"/>'
 1660                 self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 1661                 virt.init(
 1662                     "test vm",
 1663                     2,
 1664                     1234,
 1665                     nic=None,
 1666                     disk=None,
 1667                     disks=[
 1668                         {"name": "system", "size": 10240},
 1669                         {
 1670                             "name": "cddrive",
 1671                             "device": "cdrom",
 1672                             "source_file": None,
 1673                             "model": "ide",
 1674                         },
 1675                     ],
 1676                     seed=False,
 1677                     start=False,
 1678                 )
 1679                 definition = ET.fromstring(defineMock.call_args_list[0][0][0])
 1680                 expected_disk_path = os.path.join(root_dir, "test vm_system.qcow2")
 1681                 self.assertEqual(
 1682                     expected_disk_path,
 1683                     definition.find("./devices/disk[1]/source").get("file"),
 1684                 )
 1685                 self.assertIsNone(definition.find("./devices/disk[2]/source"))
 1686                 self.assertEqual(
 1687                     mock_run.call_args[0][0],
 1688                     'qemu-img create -f qcow2 "{}" 10240M'.format(expected_disk_path),
 1689                 )
 1690                 self.assertEqual(mock_chmod.call_args[0][0], expected_disk_path)
 1691 
 1692                 # Test case creating disks volumes
 1693                 defineMock.reset_mock()
 1694                 mock_run.reset_mock()
 1695                 vol_mock = MagicMock()
 1696                 pool_mock.storageVolLookupByName.return_value = vol_mock
 1697                 pool_mock.listVolumes.return_value = ["test vm_data"]
 1698                 stream_mock = MagicMock()
 1699                 self.mock_conn.newStream.return_value = stream_mock
 1700                 self.mock_conn.listStoragePools.return_value = ["default", "test"]
 1701                 with patch.dict(
 1702                     os.__dict__, {"open": MagicMock(), "close": MagicMock()}
 1703                 ):
 1704                     cache_mock = MagicMock()
 1705                     with patch.dict(virt.__salt__, {"cp.cache_file": cache_mock}):
 1706                         virt.init(
 1707                             "test vm",
 1708                             2,
 1709                             1234,
 1710                             nic=None,
 1711                             disk=None,
 1712                             disks=[
 1713                                 {
 1714                                     "name": "system",
 1715                                     "size": 10240,
 1716                                     "image": "/path/to/image",
 1717                                     "pool": "test",
 1718                                 },
 1719                                 {"name": "data", "size": 10240, "pool": "default"},
 1720                                 {
 1721                                     "name": "test",
 1722                                     "size": 1024,
 1723                                     "pool": "default",
 1724                                     "format": "qcow2",
 1725                                     "backing_store_path": "/backing/path",
 1726                                     "backing_store_format": "raw",
 1727                                 },
 1728                             ],
 1729                             seed=False,
 1730                             start=False,
 1731                         )
 1732                         definition = ET.fromstring(defineMock.call_args_list[0][0][0])
 1733                         self.assertTrue(
 1734                             all(
 1735                                 [
 1736                                     disk.get("type") == "volume"
 1737                                     for disk in definition.findall("./devices/disk")
 1738                                 ]
 1739                             )
 1740                         )
 1741                         self.assertEqual(
 1742                             ["test", "default", "default"],
 1743                             [
 1744                                 src.get("pool")
 1745                                 for src in definition.findall("./devices/disk/source")
 1746                             ],
 1747                         )
 1748                         self.assertEqual(
 1749                             ["test vm_system", "test vm_data", "test vm_test"],
 1750                             [
 1751                                 src.get("volume")
 1752                                 for src in definition.findall("./devices/disk/source")
 1753                             ],
 1754                         )
 1755 
 1756                         create_calls = pool_mock.createXML.call_args_list
 1757                         vol_names = [
 1758                             ET.fromstring(call[0][0]).find("name").text
 1759                             for call in create_calls
 1760                         ]
 1761                         self.assertEqual(
 1762                             ["test vm_system", "test vm_test"], vol_names,
 1763                         )
 1764 
 1765                         stream_mock.sendAll.assert_called_once()
 1766                         stream_mock.finish.assert_called_once()
 1767                         vol_mock.upload.assert_called_once_with(stream_mock, 0, 0, 0)
 1768 
 1769     def test_update(self):
 1770         """
 1771         Test virt.update()
 1772         """
 1773         root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
 1774         xml = """
 1775             <domain type='kvm' id='7'>
 1776               <name>my_vm</name>
 1777               <memory unit='KiB'>1048576</memory>
 1778               <currentMemory unit='KiB'>1048576</currentMemory>
 1779               <vcpu placement='auto'>1</vcpu>
 1780               <os>
 1781                 <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
 1782                 <boot dev="hd"/>
 1783               </os>
 1784               <devices>
 1785                 <disk type='file' device='disk'>
 1786                   <driver name='qemu' type='qcow2'/>
 1787                   <source file='{}{}my_vm_system.qcow2'/>
 1788                   <backingStore/>
 1789                   <target dev='vda' bus='virtio'/>
 1790                   <alias name='virtio-disk0'/>
 1791                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
 1792                 </disk>
 1793                 <disk type='volume' device='disk'>
 1794                   <driver name='qemu' type='qcow2'/>
 1795                   <source pool='default' volume='my_vm_data'/>
 1796                   <backingStore/>
 1797                   <target dev='vdb' bus='virtio'/>
 1798                   <alias name='virtio-disk1'/>
 1799                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/>
 1800                 </disk>
 1801                 <disk type="network" device="disk">
 1802                   <driver name='raw' type='qcow2'/>
 1803                   <source protocol='rbd' name='libvirt-pool/my_vm_data2'>
 1804                     <host name='ses2.tf.local'/>
 1805                     <host name='ses3.tf.local' port='1234'/>
 1806                     <auth username='libvirt'>
 1807                       <secret type='ceph' usage='pool_test-rbd'/>
 1808                     </auth>
 1809                   </source>
 1810                   <target dev='vdc' bus='virtio'/>
 1811                   <alias name='virtio-disk2'/>
 1812                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x2'/>
 1813                 </disk>
 1814                 <interface type='network'>
 1815                   <mac address='52:54:00:39:02:b1'/>
 1816                   <source network='default' bridge='virbr0'/>
 1817                   <target dev='vnet0'/>
 1818                   <model type='virtio'/>
 1819                   <alias name='net0'/>
 1820                   <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 1821                 </interface>
 1822                 <interface type='network'>
 1823                   <mac address='52:54:00:39:02:b2'/>
 1824                   <source network='oldnet' bridge='virbr1'/>
 1825                   <target dev='vnet1'/>
 1826                   <model type='virtio'/>
 1827                   <alias name='net1'/>
 1828                   <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
 1829                 </interface>
 1830                 <graphics type='spice' listen='127.0.0.1' autoport='yes'>
 1831                   <listen type='address' address='127.0.0.1'/>
 1832                 </graphics>
 1833                 <video>
 1834                   <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
 1835                   <alias name='video0'/>
 1836                   <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
 1837                 </video>
 1838               </devices>
 1839             </domain>
 1840         """.format(
 1841             root_dir, os.sep
 1842         )
 1843         domain_mock = self.set_mock_vm("my_vm", xml)
 1844         domain_mock.OSType = MagicMock(return_value="hvm")
 1845         define_mock = MagicMock(return_value=True)
 1846         self.mock_conn.defineXML = define_mock
 1847 
 1848         # No parameter passed case
 1849         self.assertEqual(
 1850             {
 1851                 "definition": False,
 1852                 "disk": {"attached": [], "detached": [], "updated": []},
 1853                 "interface": {"attached": [], "detached": []},
 1854             },
 1855             virt.update("my_vm"),
 1856         )
 1857 
 1858         # mem + cpu case
 1859         define_mock.reset_mock()
 1860         domain_mock.setMemoryFlags.return_value = 0
 1861         domain_mock.setVcpusFlags.return_value = 0
 1862         self.assertEqual(
 1863             {
 1864                 "definition": True,
 1865                 "disk": {"attached": [], "detached": [], "updated": []},
 1866                 "interface": {"attached": [], "detached": []},
 1867                 "mem": True,
 1868                 "cpu": True,
 1869             },
 1870             virt.update("my_vm", mem=2048, cpu=2),
 1871         )
 1872         setxml = ET.fromstring(define_mock.call_args[0][0])
 1873         self.assertEqual("2", setxml.find("vcpu").text)
 1874         self.assertEqual("2147483648", setxml.find("memory").text)
 1875         self.assertEqual(2048 * 1024, domain_mock.setMemoryFlags.call_args[0][0])
 1876 
 1877         # Same parameters passed than in default virt.defined state case
 1878         self.assertEqual(
 1879             {
 1880                 "definition": False,
 1881                 "disk": {"attached": [], "detached": [], "updated": []},
 1882                 "interface": {"attached": [], "detached": []},
 1883             },
 1884             virt.update(
 1885                 "my_vm",
 1886                 cpu=None,
 1887                 mem=None,
 1888                 disk_profile=None,
 1889                 disks=None,
 1890                 nic_profile=None,
 1891                 interfaces=None,
 1892                 graphics=None,
 1893                 live=True,
 1894                 connection=None,
 1895                 username=None,
 1896                 password=None,
 1897                 boot=None,
 1898             ),
 1899         )
 1900 
 1901         # Update vcpus case
 1902         setvcpus_mock = MagicMock(return_value=0)
 1903         domain_mock.setVcpusFlags = setvcpus_mock
 1904         self.assertEqual(
 1905             {
 1906                 "definition": True,
 1907                 "cpu": True,
 1908                 "disk": {"attached": [], "detached": [], "updated": []},
 1909                 "interface": {"attached": [], "detached": []},
 1910             },
 1911             virt.update("my_vm", cpu=2),
 1912         )
 1913         setxml = ET.fromstring(define_mock.call_args[0][0])
 1914         self.assertEqual(setxml.find("vcpu").text, "2")
 1915         self.assertEqual(setvcpus_mock.call_args[0][0], 2)
 1916 
 1917         boot = {
 1918             "kernel": "/root/f8-i386-vmlinuz",
 1919             "initrd": "/root/f8-i386-initrd",
 1920             "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/",
 1921         }
 1922 
 1923         # Update boot devices case
 1924         define_mock.reset_mock()
 1925         self.assertEqual(
 1926             {
 1927                 "definition": True,
 1928                 "disk": {"attached": [], "detached": [], "updated": []},
 1929                 "interface": {"attached": [], "detached": []},
 1930             },
 1931             virt.update("my_vm", boot_dev="cdrom network hd"),
 1932         )
 1933         setxml = ET.fromstring(define_mock.call_args[0][0])
 1934         self.assertEqual(
 1935             ["cdrom", "network", "hd"],
 1936             [node.get("dev") for node in setxml.findall("os/boot")],
 1937         )
 1938 
 1939         # Update unchanged boot devices case
 1940         define_mock.reset_mock()
 1941         self.assertEqual(
 1942             {
 1943                 "definition": False,
 1944                 "disk": {"attached": [], "detached": [], "updated": []},
 1945                 "interface": {"attached": [], "detached": []},
 1946             },
 1947             virt.update("my_vm", boot_dev="hd"),
 1948         )
 1949         define_mock.assert_not_called()
 1950 
 1951         # Update with boot parameter case
 1952         define_mock.reset_mock()
 1953         self.assertEqual(
 1954             {
 1955                 "definition": True,
 1956                 "disk": {"attached": [], "detached": [], "updated": []},
 1957                 "interface": {"attached": [], "detached": []},
 1958             },
 1959             virt.update("my_vm", boot=boot),
 1960         )
 1961         setxml = ET.fromstring(define_mock.call_args[0][0])
 1962         self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
 1963         self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
 1964         self.assertEqual(
 1965             setxml.find("os").find("cmdline").text,
 1966             "console=ttyS0 ks=http://example.com/f8-i386/os/",
 1967         )
 1968         setxml = ET.fromstring(define_mock.call_args[0][0])
 1969         self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
 1970         self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
 1971         self.assertEqual(
 1972             setxml.find("os").find("cmdline").text,
 1973             "console=ttyS0 ks=http://example.com/f8-i386/os/",
 1974         )
 1975 
 1976         boot_uefi = {
 1977             "loader": "/usr/share/OVMF/OVMF_CODE.fd",
 1978             "nvram": "/usr/share/OVMF/OVMF_VARS.ms.fd",
 1979         }
 1980 
 1981         self.assertEqual(
 1982             {
 1983                 "definition": True,
 1984                 "disk": {"attached": [], "detached": [], "updated": []},
 1985                 "interface": {"attached": [], "detached": []},
 1986             },
 1987             virt.update("my_vm", boot=boot_uefi),
 1988         )
 1989         setxml = ET.fromstring(define_mock.call_args[0][0])
 1990         self.assertEqual(
 1991             setxml.find("os").find("loader").text, "/usr/share/OVMF/OVMF_CODE.fd"
 1992         )
 1993         self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
 1994         self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
 1995         self.assertEqual(
 1996             setxml.find("os").find("nvram").attrib["template"],
 1997             "/usr/share/OVMF/OVMF_VARS.ms.fd",
 1998         )
 1999 
 2000         self.assertEqual(
 2001             {
 2002                 "definition": True,
 2003                 "disk": {"attached": [], "detached": [], "updated": []},
 2004                 "interface": {"attached": [], "detached": []},
 2005             },
 2006             virt.update("my_vm", boot={"efi": True}),
 2007         )
 2008         setxml = ET.fromstring(define_mock.call_args[0][0])
 2009         self.assertEqual(setxml.find("os").attrib.get("firmware"), "efi")
 2010 
 2011         invalid_boot = {
 2012             "loader": "/usr/share/OVMF/OVMF_CODE.fd",
 2013             "initrd": "/root/f8-i386-initrd",
 2014         }
 2015 
 2016         with self.assertRaises(SaltInvocationError):
 2017             virt.update("my_vm", boot=invalid_boot)
 2018 
 2019         with self.assertRaises(SaltInvocationError):
 2020             virt.update("my_vm", boot={"efi": "Not a boolean value"})
 2021 
 2022         # Update memtune parameter case
 2023         memtune = {
 2024             "soft_limit": "0.5g",
 2025             "hard_limit": "1024",
 2026             "swap_hard_limit": "2048m",
 2027             "min_guarantee": "1 g",
 2028         }
 2029 
 2030         self.assertEqual(
 2031             {
 2032                 "definition": True,
 2033                 "disk": {"attached": [], "detached": [], "updated": []},
 2034                 "interface": {"attached": [], "detached": []},
 2035             },
 2036             virt.update("my_vm", mem=memtune),
 2037         )
 2038 
 2039         setxml = ET.fromstring(define_mock.call_args[0][0])
 2040         self.assertEqual(
 2041             setxml.find("memtune").find("soft_limit").text, str(int(0.5 * 1024 ** 3))
 2042         )
 2043         self.assertEqual(setxml.find("memtune").find("soft_limit").get("unit"), "bytes")
 2044         self.assertEqual(
 2045             setxml.find("memtune").find("hard_limit").text, str(1024 * 1024 ** 2)
 2046         )
 2047         self.assertEqual(
 2048             setxml.find("memtune").find("swap_hard_limit").text, str(2048 * 1024 ** 2)
 2049         )
 2050         self.assertEqual(
 2051             setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
 2052         )
 2053 
 2054         invalid_unit = {"soft_limit": "2HB"}
 2055 
 2056         with self.assertRaises(SaltInvocationError):
 2057             virt.update("my_vm", mem=invalid_unit)
 2058 
 2059         invalid_number = {
 2060             "soft_limit": "3.4.MB",
 2061         }
 2062 
 2063         with self.assertRaises(SaltInvocationError):
 2064             virt.update("my_vm", mem=invalid_number)
 2065 
 2066         # Update memory case
 2067         setmem_mock = MagicMock(return_value=0)
 2068         domain_mock.setMemoryFlags = setmem_mock
 2069 
 2070         self.assertEqual(
 2071             {
 2072                 "definition": True,
 2073                 "mem": True,
 2074                 "disk": {"attached": [], "detached": [], "updated": []},
 2075                 "interface": {"attached": [], "detached": []},
 2076             },
 2077             virt.update("my_vm", mem=2048),
 2078         )
 2079         setxml = ET.fromstring(define_mock.call_args[0][0])
 2080         self.assertEqual(setxml.find("memory").text, str(2048 * 1024 ** 2))
 2081         self.assertEqual(setxml.find("memory").get("unit"), "bytes")
 2082         self.assertEqual(setmem_mock.call_args[0][0], 2048 * 1024)
 2083 
 2084         mem_dict = {"boot": "0.5g", "current": "2g", "max": "1g", "slots": 12}
 2085         self.assertEqual(
 2086             {
 2087                 "definition": True,
 2088                 "mem": True,
 2089                 "disk": {"attached": [], "detached": [], "updated": []},
 2090                 "interface": {"attached": [], "detached": []},
 2091             },
 2092             virt.update("my_vm", mem=mem_dict),
 2093         )
 2094 
 2095         setxml = ET.fromstring(define_mock.call_args[0][0])
 2096         self.assertEqual(setxml.find("memory").get("unit"), "bytes")
 2097         self.assertEqual(setxml.find("memory").text, str(int(0.5 * 1024 ** 3)))
 2098         self.assertEqual(setxml.find("maxMemory").text, str(1 * 1024 ** 3))
 2099         self.assertEqual(setxml.find("currentMemory").text, str(2 * 1024 ** 3))
 2100 
 2101         max_slot_reverse = {
 2102             "slots": "10",
 2103             "max": "3096m",
 2104         }
 2105         self.assertEqual(
 2106             {
 2107                 "definition": True,
 2108                 "disk": {"attached": [], "detached": [], "updated": []},
 2109                 "interface": {"attached": [], "detached": []},
 2110             },
 2111             virt.update("my_vm", mem=max_slot_reverse),
 2112         )
 2113         setxml = ET.fromstring(define_mock.call_args[0][0])
 2114         self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
 2115         self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
 2116 
 2117         # Update disks case
 2118         devattach_mock = MagicMock(return_value=0)
 2119         devdetach_mock = MagicMock(return_value=0)
 2120         domain_mock.attachDevice = devattach_mock
 2121         domain_mock.detachDevice = devdetach_mock
 2122         mock_chmod = MagicMock()
 2123         mock_run = MagicMock()
 2124         with patch.dict(
 2125             os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()}
 2126         ):  # pylint: disable=no-member
 2127             with patch.dict(
 2128                 virt.__salt__, {"cmd.run": mock_run}
 2129             ):  # pylint: disable=no-member
 2130                 ret = virt.update(
 2131                     "my_vm",
 2132                     disk_profile="default",
 2133                     disks=[
 2134                         {
 2135                             "name": "cddrive",
 2136                             "device": "cdrom",
 2137                             "source_file": None,
 2138                             "model": "ide",
 2139                         },
 2140                         {"name": "added", "size": 2048},
 2141                     ],
 2142                 )
 2143                 added_disk_path = os.path.join(
 2144                     virt.__salt__["config.get"]("virt:images"), "my_vm_added.qcow2"
 2145                 )  # pylint: disable=no-member
 2146                 self.assertEqual(
 2147                     mock_run.call_args[0][0],
 2148                     'qemu-img create -f qcow2 "{}" 2048M'.format(added_disk_path),
 2149                 )
 2150                 self.assertEqual(mock_chmod.call_args[0][0], added_disk_path)
 2151                 self.assertListEqual(
 2152                     [None, os.path.join(root_dir, "my_vm_added.qcow2")],
 2153                     [
 2154                         ET.fromstring(disk).find("source").get("file")
 2155                         if str(disk).find("<source") > -1
 2156                         else None
 2157                         for disk in ret["disk"]["attached"]
 2158                     ],
 2159                 )
 2160 
 2161                 self.assertListEqual(
 2162                     ["my_vm_data", "libvirt-pool/my_vm_data2"],
 2163                     [
 2164                         ET.fromstring(disk).find("source").get("volume")
 2165                         or ET.fromstring(disk).find("source").get("name")
 2166                         for disk in ret["disk"]["detached"]
 2167                     ],
 2168                 )
 2169                 self.assertEqual(devattach_mock.call_count, 2)
 2170                 self.assertEqual(devdetach_mock.call_count, 2)
 2171 
 2172         # Update nics case
 2173         yaml_config = """
 2174           virt:
 2175              nic:
 2176                 myprofile:
 2177                    - network: default
 2178                      name: eth0
 2179         """
 2180         mock_config = salt.utils.yaml.safe_load(yaml_config)
 2181         devattach_mock.reset_mock()
 2182         devdetach_mock.reset_mock()
 2183         with patch.dict(
 2184             salt.modules.config.__opts__, mock_config  # pylint: disable=no-member
 2185         ):
 2186             ret = virt.update(
 2187                 "my_vm",
 2188                 nic_profile="myprofile",
 2189                 interfaces=[
 2190                     {
 2191                         "name": "eth0",
 2192                         "type": "network",
 2193                         "source": "default",
 2194                         "mac": "52:54:00:39:02:b1",
 2195                     },
 2196                     {"name": "eth1", "type": "network", "source": "newnet"},
 2197                 ],
 2198             )
 2199             self.assertEqual(
 2200                 ["newnet"],
 2201                 [
 2202                     ET.fromstring(nic).find("source").get("network")
 2203                     for nic in ret["interface"]["attached"]
 2204                 ],
 2205             )
 2206             self.assertEqual(
 2207                 ["oldnet"],
 2208                 [
 2209                     ET.fromstring(nic).find("source").get("network")
 2210                     for nic in ret["interface"]["detached"]
 2211                 ],
 2212             )
 2213             devattach_mock.assert_called_once()
 2214             devdetach_mock.assert_called_once()
 2215 
 2216         # Remove nics case
 2217         devattach_mock.reset_mock()
 2218         devdetach_mock.reset_mock()
 2219         ret = virt.update("my_vm", nic_profile=None, interfaces=[])
 2220         self.assertEqual([], ret["interface"]["attached"])
 2221         self.assertEqual(2, len(ret["interface"]["detached"]))
 2222         devattach_mock.assert_not_called()
 2223         devdetach_mock.assert_called()
 2224 
 2225         # Remove disks case (yeah, it surely is silly)
 2226         devattach_mock.reset_mock()
 2227         devdetach_mock.reset_mock()
 2228         ret = virt.update("my_vm", disk_profile=None, disks=[])
 2229         self.assertEqual([], ret["disk"]["attached"])
 2230         self.assertEqual(3, len(ret["disk"]["detached"]))
 2231         devattach_mock.assert_not_called()
 2232         devdetach_mock.assert_called()
 2233 
 2234         # Graphics change test case
 2235         self.assertEqual(
 2236             {
 2237                 "definition": True,
 2238                 "disk": {"attached": [], "detached": [], "updated": []},
 2239                 "interface": {"attached": [], "detached": []},
 2240             },
 2241             virt.update("my_vm", graphics={"type": "vnc"}),
 2242         )
 2243         setxml = ET.fromstring(define_mock.call_args[0][0])
 2244         self.assertEqual("vnc", setxml.find("devices/graphics").get("type"))
 2245 
 2246         # Update with no diff case
 2247         pool_mock = MagicMock()
 2248         default_pool_desc = "<pool type='dir'></pool>"
 2249         rbd_pool_desc = """
 2250             <pool type='rbd'>
 2251               <name>test-rbd</name>
 2252               <source>
 2253                 <host name='ses2.tf.local'/>
 2254                 <host name='ses3.tf.local' port='1234'/>
 2255                 <name>libvirt-pool</name>
 2256                 <auth type='ceph' username='libvirt'>
 2257                   <secret usage='pool_test-rbd'/>
 2258                 </auth>
 2259               </source>
 2260             </pool>
 2261             """
 2262         pool_mock.XMLDesc.side_effect = [
 2263             default_pool_desc,
 2264             rbd_pool_desc,
 2265             default_pool_desc,
 2266             rbd_pool_desc,
 2267         ]
 2268         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 2269         self.mock_conn.listStoragePools.return_value = ["test-rbd", "default"]
 2270         self.assertEqual(
 2271             {
 2272                 "definition": False,
 2273                 "disk": {"attached": [], "detached": [], "updated": []},
 2274                 "interface": {"attached": [], "detached": []},
 2275             },
 2276             virt.update(
 2277                 "my_vm",
 2278                 cpu=1,
 2279                 mem=1024,
 2280                 disk_profile="default",
 2281                 disks=[
 2282                     {"name": "data", "size": 2048, "pool": "default"},
 2283                     {
 2284                         "name": "data2",
 2285                         "size": 4096,
 2286                         "pool": "test-rbd",
 2287                         "format": "raw",
 2288                     },
 2289                 ],
 2290                 nic_profile="myprofile",
 2291                 interfaces=[
 2292                     {
 2293                         "name": "eth0",
 2294                         "type": "network",
 2295                         "source": "default",
 2296                         "mac": "52:54:00:39:02:b1",
 2297                     },
 2298                     {"name": "eth1", "type": "network", "source": "oldnet"},
 2299                 ],
 2300                 graphics={
 2301                     "type": "spice",
 2302                     "listen": {"type": "address", "address": "127.0.0.1"},
 2303                 },
 2304             ),
 2305         )
 2306 
 2307         # Failed XML description update case
 2308         self.mock_conn.defineXML.side_effect = self.mock_libvirt.libvirtError(
 2309             "Test error"
 2310         )
 2311         setmem_mock.reset_mock()
 2312         with self.assertRaises(self.mock_libvirt.libvirtError):
 2313             virt.update("my_vm", mem=2048)
 2314 
 2315         # Failed single update failure case
 2316         self.mock_conn.defineXML = MagicMock(return_value=True)
 2317         setmem_mock.side_effect = self.mock_libvirt.libvirtError(
 2318             "Failed to live change memory"
 2319         )
 2320         self.assertEqual(
 2321             {
 2322                 "definition": True,
 2323                 "errors": ["Failed to live change memory"],
 2324                 "disk": {"attached": [], "detached": [], "updated": []},
 2325                 "interface": {"attached": [], "detached": []},
 2326             },
 2327             virt.update("my_vm", mem=2048),
 2328         )
 2329 
 2330         # Failed multiple updates failure case
 2331         self.assertEqual(
 2332             {
 2333                 "definition": True,
 2334                 "errors": ["Failed to live change memory"],
 2335                 "cpu": True,
 2336                 "disk": {"attached": [], "detached": [], "updated": []},
 2337                 "interface": {"attached": [], "detached": []},
 2338             },
 2339             virt.update("my_vm", cpu=4, mem=2048),
 2340         )
 2341 
 2342     def test_update_backing_store(self):
 2343         """
 2344         Test updating a disk with a backing store
 2345         """
 2346         xml = """
 2347             <domain type='kvm' id='7'>
 2348               <name>my_vm</name>
 2349               <memory unit='KiB'>1048576</memory>
 2350               <currentMemory unit='KiB'>1048576</currentMemory>
 2351               <vcpu placement='auto'>1</vcpu>
 2352               <os>
 2353                 <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
 2354               </os>
 2355               <devices>
 2356                 <disk type='volume' device='disk'>
 2357                   <driver name='qemu' type='qcow2' cache='none' io='native'/>
 2358                   <source pool='default' volume='my_vm_system' index='1'/>
 2359                   <backingStore type='file' index='2'>
 2360                     <format type='qcow2'/>
 2361                     <source file='/path/to/base.qcow2'/>
 2362                     <backingStore/>
 2363                   </backingStore>
 2364                   <target dev='vda' bus='virtio'/>
 2365                   <alias name='virtio-disk0'/>
 2366                   <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
 2367                 </disk>
 2368               </devices>
 2369             </domain>
 2370         """
 2371         domain_mock = self.set_mock_vm("my_vm", xml)
 2372         domain_mock.OSType.return_value = "hvm"
 2373         self.mock_conn.defineXML.return_value = True
 2374         updatedev_mock = MagicMock(return_value=0)
 2375         domain_mock.updateDeviceFlags = updatedev_mock
 2376         self.mock_conn.listStoragePools.return_value = ["default"]
 2377         self.mock_conn.storagePoolLookupByName.return_value.XMLDesc.return_value = (
 2378             "<pool type='dir'/>"
 2379         )
 2380 
 2381         ret = virt.update(
 2382             "my_vm",
 2383             disks=[
 2384                 {
 2385                     "name": "system",
 2386                     "pool": "default",
 2387                     "backing_store_path": "/path/to/base.qcow2",
 2388                     "backing_store_format": "qcow2",
 2389                 },
 2390             ],
 2391         )
 2392         self.assertFalse(ret["definition"])
 2393         self.assertFalse(ret["disk"]["attached"])
 2394         self.assertFalse(ret["disk"]["detached"])
 2395 
 2396     def test_update_removables(self):
 2397         """
 2398         Test attaching, detaching, changing removable devices
 2399         """
 2400         xml = """
 2401             <domain type='kvm' id='7'>
 2402               <name>my_vm</name>
 2403               <memory unit='KiB'>1048576</memory>
 2404               <currentMemory unit='KiB'>1048576</currentMemory>
 2405               <vcpu placement='auto'>1</vcpu>
 2406               <os>
 2407                 <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
 2408               </os>
 2409               <devices>
 2410                 <disk type='network' device='cdrom'>
 2411                   <driver name='qemu' type='raw' cache='none' io='native'/>
 2412                   <source protocol='https' name='/dvd-image-1.iso'>
 2413                     <host name='test-srv.local' port='80'/>
 2414                   </source>
 2415                   <backingStore/>
 2416                   <target dev='hda' bus='ide'/>
 2417                   <readonly/>
 2418                   <alias name='ide0-0-0'/>
 2419                   <address type='drive' controller='0' bus='0' target='0' unit='0'/>
 2420                 </disk>
 2421                 <disk type='file' device='cdrom'>
 2422                   <driver name='qemu' type='raw' cache='none' io='native'/>
 2423                   <target dev='hdb' bus='ide'/>
 2424                   <readonly/>
 2425                   <alias name='ide0-0-1'/>
 2426                   <address type='drive' controller='0' bus='0' target='0' unit='1'/>
 2427                 </disk>
 2428                 <disk type='file' device='cdrom'>
 2429                   <driver name='qemu' type='raw' cache='none' io='native'/>
 2430                   <source file='/srv/dvd-image-2.iso'/>
 2431                   <backingStore/>
 2432                   <target dev='hdc' bus='ide'/>
 2433                   <readonly/>
 2434                   <alias name='ide0-0-2'/>
 2435                   <address type='drive' controller='0' bus='0' target='0' unit='2'/>
 2436                 </disk>
 2437                 <disk type='file' device='cdrom'>
 2438                   <driver name='qemu' type='raw' cache='none' io='native'/>
 2439                   <source file='/srv/dvd-image-3.iso'/>
 2440                   <backingStore/>
 2441                   <target dev='hdd' bus='ide'/>
 2442                   <readonly/>
 2443                   <alias name='ide0-0-3'/>
 2444                   <address type='drive' controller='0' bus='0' target='0' unit='3'/>
 2445                 </disk>
 2446                 <disk type='network' device='cdrom'>
 2447                   <driver name='qemu' type='raw' cache='none' io='native'/>
 2448                   <source protocol='https' name='/dvd-image-6.iso'>
 2449                     <host name='test-srv.local' port='80'/>
 2450                   </source>
 2451                   <backingStore/>
 2452                   <target dev='hde' bus='ide'/>
 2453                   <readonly/>
 2454                 </disk>
 2455               </devices>
 2456             </domain>
 2457         """
 2458         domain_mock = self.set_mock_vm("my_vm", xml)
 2459         domain_mock.OSType.return_value = "hvm"
 2460         self.mock_conn.defineXML.return_value = True
 2461         updatedev_mock = MagicMock(return_value=0)
 2462         domain_mock.updateDeviceFlags = updatedev_mock
 2463 
 2464         ret = virt.update(
 2465             "my_vm",
 2466             disks=[
 2467                 {
 2468                     "name": "dvd1",
 2469                     "device": "cdrom",
 2470                     "source_file": None,
 2471                     "model": "ide",
 2472                 },
 2473                 {
 2474                     "name": "dvd2",
 2475                     "device": "cdrom",
 2476                     "source_file": "/srv/dvd-image-4.iso",
 2477                     "model": "ide",
 2478                 },
 2479                 {
 2480                     "name": "dvd3",
 2481                     "device": "cdrom",
 2482                     "source_file": "/srv/dvd-image-2.iso",
 2483                     "model": "ide",
 2484                 },
 2485                 {
 2486                     "name": "dvd4",
 2487                     "device": "cdrom",
 2488                     "source_file": "/srv/dvd-image-5.iso",
 2489                     "model": "ide",
 2490                 },
 2491                 {
 2492                     "name": "dvd5",
 2493                     "device": "cdrom",
 2494                     "source_file": "/srv/dvd-image-6.iso",
 2495                     "model": "ide",
 2496                 },
 2497             ],
 2498         )
 2499 
 2500         self.assertTrue(ret["definition"])
 2501         self.assertFalse(ret["disk"]["attached"])
 2502         self.assertFalse(ret["disk"]["detached"])
 2503         self.assertEqual(
 2504             [
 2505                 {
 2506                     "type": "file",
 2507                     "device": "cdrom",
 2508                     "driver": {
 2509                         "name": "qemu",
 2510                         "type": "raw",
 2511                         "cache": "none",
 2512                         "io": "native",
 2513                     },
 2514                     "backingStore": None,
 2515                     "target": {"dev": "hda", "bus": "ide"},
 2516                     "readonly": None,
 2517                     "alias": {"name": "ide0-0-0"},
 2518                     "address": {
 2519                         "type": "drive",
 2520                         "controller": "0",
 2521                         "bus": "0",
 2522                         "target": "0",
 2523                         "unit": "0",
 2524                     },
 2525                 },
 2526                 {
 2527                     "type": "file",
 2528                     "device": "cdrom",
 2529                     "driver": {
 2530                         "name": "qemu",
 2531                         "type": "raw",
 2532                         "cache": "none",
 2533                         "io": "native",
 2534                     },
 2535                     "target": {"dev": "hdb", "bus": "ide"},
 2536                     "readonly": None,
 2537                     "alias": {"name": "ide0-0-1"},
 2538                     "address": {
 2539                         "type": "drive",
 2540                         "controller": "0",
 2541                         "bus": "0",
 2542                         "target": "0",
 2543                         "unit": "1",
 2544                     },
 2545                     "source": {"file": "/srv/dvd-image-4.iso"},
 2546                 },
 2547                 {
 2548                     "type": "file",
 2549                     "device": "cdrom",
 2550                     "driver": {
 2551                         "name": "qemu",
 2552                         "type": "raw",
 2553                         "cache": "none",
 2554                         "io": "native",
 2555                     },
 2556                     "backingStore": None,
 2557                     "target": {"dev": "hdd", "bus": "ide"},
 2558                     "readonly": None,
 2559                     "alias": {"name": "ide0-0-3"},
 2560                     "address": {
 2561                         "type": "drive",
 2562                         "controller": "0",
 2563                         "bus": "0",
 2564                         "target": "0",
 2565                         "unit": "3",
 2566                     },
 2567                     "source": {"file": "/srv/dvd-image-5.iso"},
 2568                 },
 2569                 {
 2570                     "type": "file",
 2571                     "device": "cdrom",
 2572                     "driver": {
 2573                         "name": "qemu",
 2574                         "type": "raw",
 2575                         "cache": "none",
 2576                         "io": "native",
 2577                     },
 2578                     "backingStore": None,
 2579                     "target": {"dev": "hde", "bus": "ide"},
 2580                     "readonly": None,
 2581                     "source": {"file": "/srv/dvd-image-6.iso"},
 2582                 },
 2583             ],
 2584             [
 2585                 salt.utils.xmlutil.to_dict(ET.fromstring(disk), True)
 2586                 for disk in ret["disk"]["updated"]
 2587             ],
 2588         )
 2589 
 2590     def test_update_xen_boot_params(self):
 2591         """
 2592         Test virt.update() a Xen definition no boot parameter.
 2593         """
 2594         root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
 2595         xml_boot = """
 2596             <domain type='xen' id='8'>
 2597               <name>vm</name>
 2598               <memory unit='KiB'>1048576</memory>
 2599               <currentMemory unit='KiB'>1048576</currentMemory>
 2600               <vcpu placement='auto'>1</vcpu>
 2601               <os>
 2602                 <type arch='x86_64' machine='xenfv'>hvm</type>
 2603                 <loader type='rom'>/usr/lib/xen/boot/hvmloader</loader>
 2604               </os>
 2605             </domain>
 2606         """
 2607         domain_mock_boot = self.set_mock_vm("vm", xml_boot)
 2608         domain_mock_boot.OSType = MagicMock(return_value="hvm")
 2609         define_mock_boot = MagicMock(return_value=True)
 2610         define_mock_boot.setVcpusFlags = MagicMock(return_value=0)
 2611         self.mock_conn.defineXML = define_mock_boot
 2612         self.assertEqual(
 2613             {
 2614                 "cpu": False,
 2615                 "definition": True,
 2616                 "disk": {"attached": [], "detached": [], "updated": []},
 2617                 "interface": {"attached": [], "detached": []},
 2618             },
 2619             virt.update("vm", cpu=2),
 2620         )
 2621         setxml = ET.fromstring(define_mock_boot.call_args[0][0])
 2622         self.assertEqual(setxml.find("os").find("loader").attrib.get("type"), "rom")
 2623         self.assertEqual(
 2624             setxml.find("os").find("loader").text, "/usr/lib/xen/boot/hvmloader"
 2625         )
 2626 
 2627     def test_update_existing_boot_params(self):
 2628         """
 2629         Test virt.update() with existing boot parameters.
 2630         """
 2631         xml_boot = """
 2632             <domain type='kvm' id='8'>
 2633               <name>vm_with_boot_param</name>
 2634               <memory unit='KiB'>1048576</memory>
 2635               <currentMemory unit='KiB'>1048576</currentMemory>
 2636               <vcpu placement='auto'>1</vcpu>
 2637               <os>
 2638                 <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
 2639                 <kernel>/boot/oldkernel</kernel>
 2640                 <initrd>/boot/initrdold.img</initrd>
 2641                 <cmdline>console=ttyS0 ks=http://example.com/old/os/</cmdline>
 2642                 <loader>/usr/share/old/OVMF_CODE.fd</loader>
 2643                 <nvram>/usr/share/old/OVMF_VARS.ms.fd</nvram>
 2644               </os>
 2645               <devices>
 2646                 <disk type='file' device='disk'>
 2647                   <driver name='qemu' type='qcow2'/>
 2648                   <source file='{0}{1}vm_with_boot_param_system.qcow2'/>
 2649                   <backingStore/>
 2650                   <target dev='vda' bus='virtio'/>
 2651                   <alias name='virtio-disk0'/>
 2652                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
 2653                 </disk>
 2654                 <disk type='file' device='disk'>
 2655                   <driver name='qemu' type='qcow2'/>
 2656                   <source file='{0}{1}vm_with_boot_param_data.qcow2'/>
 2657                   <backingStore/>
 2658                   <target dev='vdb' bus='virtio'/>
 2659                   <alias name='virtio-disk1'/>
 2660                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/>
 2661                 </disk>
 2662                 <interface type='network'>
 2663                   <mac address='52:54:00:39:02:b1'/>
 2664                   <source network='default' bridge='virbr0'/>
 2665                   <target dev='vnet0'/>
 2666                   <model type='virtio'/>
 2667                   <alias name='net0'/>
 2668                   <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 2669                 </interface>
 2670                 <interface type='network'>
 2671                   <mac address='52:54:00:39:02:b2'/>
 2672                   <source network='oldnet' bridge='virbr1'/>
 2673                   <target dev='vnet1'/>
 2674                   <model type='virtio'/>
 2675                   <alias name='net1'/>
 2676                   <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
 2677                 </interface>
 2678                 <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'>
 2679                   <listen type='address' address='127.0.0.1'/>
 2680                 </graphics>
 2681                 <video>
 2682                   <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
 2683                   <alias name='video0'/>
 2684                   <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
 2685                 </video>
 2686               </devices>
 2687             </domain>
 2688         """
 2689         domain_mock_boot = self.set_mock_vm("vm_with_boot_param", xml_boot)
 2690         domain_mock_boot.OSType = MagicMock(return_value="hvm")
 2691         define_mock_boot = MagicMock(return_value=True)
 2692         self.mock_conn.defineXML = define_mock_boot
 2693         boot_new = {
 2694             "kernel": "/root/new-vmlinuz",
 2695             "initrd": "/root/new-initrd",
 2696             "cmdline": "console=ttyS0 ks=http://example.com/new/os/",
 2697         }
 2698 
 2699         uefi_boot_new = {
 2700             "loader": "/usr/share/new/OVMF_CODE.fd",
 2701             "nvram": "/usr/share/new/OVMF_VARS.ms.fd",
 2702         }
 2703 
 2704         self.assertEqual(
 2705             {
 2706                 "definition": True,
 2707                 "disk": {"attached": [], "detached": [], "updated": []},
 2708                 "interface": {"attached": [], "detached": []},
 2709             },
 2710             virt.update("vm_with_boot_param", boot=boot_new),
 2711         )
 2712         setxml_boot = ET.fromstring(define_mock_boot.call_args[0][0])
 2713         self.assertEqual(
 2714             setxml_boot.find("os").find("kernel").text, "/root/new-vmlinuz"
 2715         )
 2716         self.assertEqual(setxml_boot.find("os").find("initrd").text, "/root/new-initrd")
 2717         self.assertEqual(
 2718             setxml_boot.find("os").find("cmdline").text,
 2719             "console=ttyS0 ks=http://example.com/new/os/",
 2720         )
 2721 
 2722         self.assertEqual(
 2723             {
 2724                 "definition": True,
 2725                 "disk": {"attached": [], "detached": [], "updated": []},
 2726                 "interface": {"attached": [], "detached": []},
 2727             },
 2728             virt.update("vm_with_boot_param", boot=uefi_boot_new),
 2729         )
 2730 
 2731         setxml = ET.fromstring(define_mock_boot.call_args[0][0])
 2732         self.assertEqual(
 2733             setxml.find("os").find("loader").text, "/usr/share/new/OVMF_CODE.fd"
 2734         )
 2735         self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
 2736         self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
 2737         self.assertEqual(
 2738             setxml.find("os").find("nvram").attrib["template"],
 2739             "/usr/share/new/OVMF_VARS.ms.fd",
 2740         )
 2741 
 2742         kernel_none = {
 2743             "kernel": None,
 2744             "initrd": None,
 2745             "cmdline": None,
 2746         }
 2747 
 2748         uefi_none = {"loader": None, "nvram": None}
 2749 
 2750         self.assertEqual(
 2751             {
 2752                 "definition": True,
 2753                 "disk": {"attached": [], "detached": [], "updated": []},
 2754                 "interface": {"attached": [], "detached": []},
 2755             },
 2756             virt.update("vm_with_boot_param", boot=kernel_none),
 2757         )
 2758 
 2759         setxml = ET.fromstring(define_mock_boot.call_args[0][0])
 2760         self.assertEqual(setxml.find("os").find("kernel"), None)
 2761         self.assertEqual(setxml.find("os").find("initrd"), None)
 2762         self.assertEqual(setxml.find("os").find("cmdline"), None)
 2763 
 2764         self.assertEqual(
 2765             {
 2766                 "definition": True,
 2767                 "disk": {"attached": [], "detached": [], "updated": []},
 2768                 "interface": {"attached": [], "detached": []},
 2769             },
 2770             virt.update("vm_with_boot_param", boot={"efi": False}),
 2771         )
 2772         setxml = ET.fromstring(define_mock_boot.call_args[0][0])
 2773         self.assertEqual(setxml.find("os").find("nvram"), None)
 2774         self.assertEqual(setxml.find("os").find("loader"), None)
 2775 
 2776         self.assertEqual(
 2777             {
 2778                 "definition": True,
 2779                 "disk": {"attached": [], "detached": [], "updated": []},
 2780                 "interface": {"attached": [], "detached": []},
 2781             },
 2782             virt.update("vm_with_boot_param", boot=uefi_none),
 2783         )
 2784 
 2785         setxml = ET.fromstring(define_mock_boot.call_args[0][0])
 2786         self.assertEqual(setxml.find("os").find("loader"), None)
 2787         self.assertEqual(setxml.find("os").find("nvram"), None)
 2788 
 2789     def test_update_memtune_params(self):
 2790         """
 2791         Test virt.update() with memory tuning parameters.
 2792         """
 2793         xml_with_memtune_params = """
 2794             <domain type='kvm' id='8'>
 2795               <name>vm_with_boot_param</name>
 2796               <memory unit='KiB'>1048576</memory>
 2797               <currentMemory unit='KiB'>1048576</currentMemory>
 2798               <maxMemory slots="12" unit="bytes">1048576</maxMemory>
 2799               <vcpu placement='auto'>1</vcpu>
 2800               <memtune>
 2801                 <hard_limit unit="KiB">1048576</hard_limit>
 2802                 <soft_limit unit="KiB">2097152</soft_limit>
 2803                 <swap_hard_limit unit="KiB">2621440</swap_hard_limit>
 2804                 <min_guarantee unit='KiB'>671088</min_guarantee>
 2805               </memtune>
 2806               <os>
 2807                 <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
 2808               </os>
 2809             </domain>
 2810         """
 2811         domain_mock = self.set_mock_vm("vm_with_memtune_param", xml_with_memtune_params)
 2812         domain_mock.OSType = MagicMock(return_value="hvm")
 2813         define_mock = MagicMock(return_value=True)
 2814         self.mock_conn.defineXML = define_mock
 2815 
 2816         memtune_new_val = {
 2817             "boot": "0.7g",
 2818             "current": "2.5g",
 2819             "max": "3096m",
 2820             "slots": "10",
 2821             "soft_limit": "2048m",
 2822             "hard_limit": "1024",
 2823             "swap_hard_limit": "2.5g",
 2824             "min_guarantee": "1 g",
 2825         }
 2826 
 2827         domain_mock.setMemoryFlags.return_value = 0
 2828         self.assertEqual(
 2829             {
 2830                 "definition": True,
 2831                 "disk": {"attached": [], "detached": [], "updated": []},
 2832                 "interface": {"attached": [], "detached": []},
 2833                 "mem": True,
 2834             },
 2835             virt.update("vm_with_memtune_param", mem=memtune_new_val),
 2836         )
 2837         self.assertEqual(
 2838             domain_mock.setMemoryFlags.call_args[0][0], int(2.5 * 1024 ** 2)
 2839         )
 2840 
 2841         setxml = ET.fromstring(define_mock.call_args[0][0])
 2842         self.assertEqual(
 2843             setxml.find("memtune").find("soft_limit").text, str(2048 * 1024)
 2844         )
 2845         self.assertEqual(
 2846             setxml.find("memtune").find("hard_limit").text, str(1024 * 1024)
 2847         )
 2848         self.assertEqual(
 2849             setxml.find("memtune").find("swap_hard_limit").text,
 2850             str(int(2.5 * 1024 ** 2)),
 2851         )
 2852         self.assertEqual(
 2853             setxml.find("memtune").find("swap_hard_limit").get("unit"), "KiB",
 2854         )
 2855         self.assertEqual(
 2856             setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
 2857         )
 2858         self.assertEqual(
 2859             setxml.find("memtune").find("min_guarantee").attrib.get("unit"), "bytes"
 2860         )
 2861         self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
 2862         self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
 2863         self.assertEqual(setxml.find("currentMemory").text, str(int(2.5 * 1024 ** 3)))
 2864         self.assertEqual(setxml.find("memory").text, str(int(0.7 * 1024 ** 3)))
 2865 
 2866         max_slot_reverse = {
 2867             "slots": "10",
 2868             "max": "3096m",
 2869         }
 2870         self.assertEqual(
 2871             {
 2872                 "definition": True,
 2873                 "disk": {"attached": [], "detached": [], "updated": []},
 2874                 "interface": {"attached": [], "detached": []},
 2875             },
 2876             virt.update("vm_with_memtune_param", mem=max_slot_reverse),
 2877         )
 2878         setxml = ET.fromstring(define_mock.call_args[0][0])
 2879         self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
 2880         self.assertEqual(setxml.find("maxMemory").get("unit"), "bytes")
 2881         self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
 2882 
 2883         max_swap_none = {
 2884             "boot": "0.7g",
 2885             "current": "2.5g",
 2886             "max": None,
 2887             "slots": "10",
 2888             "soft_limit": "2048m",
 2889             "hard_limit": "1024",
 2890             "swap_hard_limit": None,
 2891             "min_guarantee": "1 g",
 2892         }
 2893 
 2894         domain_mock.setMemoryFlags.reset_mock()
 2895         self.assertEqual(
 2896             {
 2897                 "definition": True,
 2898                 "disk": {"attached": [], "detached": [], "updated": []},
 2899                 "interface": {"attached": [], "detached": []},
 2900                 "mem": True,
 2901             },
 2902             virt.update("vm_with_memtune_param", mem=max_swap_none),
 2903         )
 2904         self.assertEqual(
 2905             domain_mock.setMemoryFlags.call_args[0][0], int(2.5 * 1024 ** 2)
 2906         )
 2907 
 2908         setxml = ET.fromstring(define_mock.call_args[0][0])
 2909         self.assertEqual(
 2910             setxml.find("memtune").find("soft_limit").text, str(2048 * 1024)
 2911         )
 2912         self.assertEqual(
 2913             setxml.find("memtune").find("hard_limit").text, str(1024 * 1024)
 2914         )
 2915         self.assertEqual(setxml.find("memtune").find("swap_hard_limit"), None)
 2916         self.assertEqual(
 2917             setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
 2918         )
 2919         self.assertEqual(
 2920             setxml.find("memtune").find("min_guarantee").attrib.get("unit"), "bytes"
 2921         )
 2922         self.assertEqual(setxml.find("maxMemory").text, None)
 2923         self.assertEqual(setxml.find("currentMemory").text, str(int(2.5 * 1024 ** 3)))
 2924         self.assertEqual(setxml.find("memory").text, str(int(0.7 * 1024 ** 3)))
 2925 
 2926         memtune_none = {
 2927             "soft_limit": None,
 2928             "hard_limit": None,
 2929             "swap_hard_limit": None,
 2930             "min_guarantee": None,
 2931         }
 2932 
 2933         self.assertEqual(
 2934             {
 2935                 "definition": True,
 2936                 "disk": {"attached": [], "detached": [], "updated": []},
 2937                 "interface": {"attached": [], "detached": []},
 2938             },
 2939             virt.update("vm_with_memtune_param", mem=memtune_none),
 2940         )
 2941 
 2942         setxml = ET.fromstring(define_mock.call_args[0][0])
 2943         self.assertEqual(setxml.find("memtune").find("soft_limit"), None)
 2944         self.assertEqual(setxml.find("memtune").find("hard_limit"), None)
 2945         self.assertEqual(setxml.find("memtune").find("swap_hard_limit"), None)
 2946         self.assertEqual(setxml.find("memtune").find("min_guarantee"), None)
 2947 
 2948         max_none = {
 2949             "max": None,
 2950         }
 2951 
 2952         self.assertEqual(
 2953             {
 2954                 "definition": True,
 2955                 "disk": {"attached": [], "detached": [], "updated": []},
 2956                 "interface": {"attached": [], "detached": []},
 2957             },
 2958             virt.update("vm_with_memtune_param", mem=max_none),
 2959         )
 2960 
 2961         setxml = ET.fromstring(define_mock.call_args[0][0])
 2962         self.assertEqual(setxml.find("maxMemory"), None)
 2963         self.assertEqual(setxml.find("currentMemory").text, str(int(1 * 1024 ** 2)))
 2964         self.assertEqual(setxml.find("memory").text, str(int(1 * 1024 ** 2)))
 2965 
 2966     def test_handle_unit(self):
 2967         """
 2968         Test regex function for handling units
 2969         """
 2970         valid_case = [
 2971             ("2", 2097152),
 2972             ("42", 44040192),
 2973             ("5b", 5),
 2974             ("2.3Kib", 2355),
 2975             ("5.8Kb", 5800),
 2976             ("16MiB", 16777216),
 2977             ("20 GB", 20000000000),
 2978             ("16KB", 16000),
 2979             (".5k", 512),
 2980             ("2.k", 2048),
 2981         ]
 2982 
 2983         for key, val in valid_case:
 2984             self.assertEqual(virt._handle_unit(key), val)
 2985 
 2986         invalid_case = [
 2987             ("9ib", "invalid units"),
 2988             ("8byte", "invalid units"),
 2989             ("512bytes", "invalid units"),
 2990             ("4 Kbytes", "invalid units"),
 2991             ("3.4.MB", "invalid number"),
 2992             ("", "invalid number"),
 2993             ("bytes", "invalid number"),
 2994             ("2HB", "invalid units"),
 2995         ]
 2996 
 2997         for key, val in invalid_case:
 2998             with self.assertRaises(SaltInvocationError):
 2999                 virt._handle_unit(key)
 3000 
 3001     def test_mixed_dict_and_list_as_profile_objects(self):
 3002         """
 3003         Test virt._nic_profile with mixed dictionaries and lists as input.
 3004         """
 3005         yaml_config = """
 3006           virt:
 3007              nic:
 3008                 new-listonly-profile:
 3009                    - bridge: br0
 3010                      name: eth0
 3011                    - model: virtio
 3012                      name: eth1
 3013                      source: test_network
 3014                      type: network
 3015                 new-list-with-legacy-names:
 3016                    - eth0:
 3017                         bridge: br0
 3018                    - eth1:
 3019                         bridge: br1
 3020                         model: virtio
 3021                 non-default-legacy-profile:
 3022                    eth0:
 3023                       bridge: br0
 3024                    eth1:
 3025                       bridge: br1
 3026                       model: virtio
 3027         """
 3028         mock_config = salt.utils.yaml.safe_load(yaml_config)
 3029         with patch.dict(
 3030             salt.modules.config.__opts__, mock_config  # pylint: disable=no-member
 3031         ):
 3032 
 3033             for name in mock_config["virt"]["nic"].keys():
 3034                 profile = salt.modules.virt._nic_profile(name, "kvm")
 3035                 self.assertEqual(len(profile), 2)
 3036 
 3037                 interface_attrs = profile[0]
 3038                 self.assertIn("source", interface_attrs)
 3039                 self.assertIn("type", interface_attrs)
 3040                 self.assertIn("name", interface_attrs)
 3041                 self.assertIn("model", interface_attrs)
 3042                 self.assertEqual(interface_attrs["model"], "virtio")
 3043 
 3044     def test_get_xml(self):
 3045         """
 3046         Test virt.get_xml()
 3047         """
 3048         xml = """<domain type='kvm' id='7'>
 3049               <name>test-vm</name>
 3050               <devices>
 3051                 <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
 3052                   <listen type='address' address='0.0.0.0'/>
 3053                 </graphics>
 3054               </devices>
 3055             </domain>
 3056         """
 3057         domain = self.set_mock_vm("test-vm", xml)
 3058         self.assertEqual(xml, virt.get_xml("test-vm"))
 3059         self.assertEqual(xml, virt.get_xml(domain))
 3060 
 3061     def test_get_loader(self):
 3062         """
 3063         Test virt.get_loader()
 3064         """
 3065         xml = """<domain type='kvm' id='7'>
 3066               <name>test-vm</name>
 3067               <os>
 3068                 <loader readonly='yes' type='pflash'>/foo/bar</loader>
 3069               </os>
 3070             </domain>
 3071         """
 3072         self.set_mock_vm("test-vm", xml)
 3073 
 3074         loader = virt.get_loader("test-vm")
 3075         self.assertEqual("/foo/bar", loader["path"])
 3076         self.assertEqual("yes", loader["readonly"])
 3077 
 3078     def test_cpu_baseline(self):
 3079         """
 3080         Test virt.cpu_baseline()
 3081         """
 3082         capabilities_xml = dedent(
 3083             """<capabilities>
 3084                   <host>
 3085                     <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
 3086                     <cpu>
 3087                       <arch>x86_64</arch>
 3088                       <vendor>Intel</vendor>
 3089                     </cpu>
 3090                   </host>
 3091                 </capabilities>"""
 3092         )
 3093 
 3094         baseline_cpu_xml = b"""<cpu match="exact" mode="custom">
 3095                                   <vendor>Intel</vendor>
 3096                                 </cpu>"""
 3097 
 3098         self.mock_conn.getCapabilities.return_value = capabilities_xml
 3099         self.mock_conn.baselineCPU.return_value = baseline_cpu_xml
 3100         self.assertMultiLineEqual(str(baseline_cpu_xml), str(virt.cpu_baseline()))
 3101 
 3102     def test_parse_qemu_img_info(self):
 3103         """
 3104         Make sure that qemu-img info output is properly parsed
 3105         """
 3106         qemu_infos = """[{
 3107             "snapshots": [
 3108                 {
 3109                     "vm-clock-nsec": 0,
 3110                     "name": "first-snap",
 3111                     "date-sec": 1528877587,
 3112                     "date-nsec": 380589000,
 3113                     "vm-clock-sec": 0,
 3114                     "id": "1",
 3115                     "vm-state-size": 1234
 3116                 },
 3117                 {
 3118                     "vm-clock-nsec": 0,
 3119                     "name": "second snap",
 3120                     "date-sec": 1528877592,
 3121                     "date-nsec": 933509000,
 3122                     "vm-clock-sec": 0,
 3123                     "id": "2",
 3124                     "vm-state-size": 4567
 3125                 }
 3126             ],
 3127             "virtual-size": 25769803776,
 3128             "filename": "/disks/test.qcow2",
 3129             "cluster-size": 65536,
 3130             "format": "qcow2",
 3131             "actual-size": 217088,
 3132             "format-specific": {
 3133                 "type": "qcow2",
 3134                 "data": {
 3135                     "compat": "1.1",
 3136                     "lazy-refcounts": false,
 3137                     "refcount-bits": 16,
 3138                     "corrupt": false
 3139                 }
 3140             },
 3141             "full-backing-filename": "/disks/mybacking.qcow2",
 3142             "backing-filename": "mybacking.qcow2",
 3143             "dirty-flag": false
 3144         },
 3145         {
 3146             "virtual-size": 25769803776,
 3147             "filename": "/disks/mybacking.qcow2",
 3148             "cluster-size": 65536,
 3149             "format": "qcow2",
 3150             "actual-size": 393744384,
 3151             "format-specific": {
 3152                 "type": "qcow2",
 3153                 "data": {
 3154                     "compat": "1.1",
 3155                     "lazy-refcounts": false,
 3156                     "refcount-bits": 16,
 3157                     "corrupt": false
 3158                 }
 3159             },
 3160             "full-backing-filename": "/disks/root.qcow2",
 3161             "backing-filename": "root.qcow2",
 3162             "dirty-flag": false
 3163         },
 3164         {
 3165             "virtual-size": 25769803776,
 3166             "filename": "/disks/root.qcow2",
 3167             "cluster-size": 65536,
 3168             "format": "qcow2",
 3169             "actual-size": 196872192,
 3170             "format-specific": {
 3171                 "type": "qcow2",
 3172                 "data": {
 3173                     "compat": "1.1",
 3174                     "lazy-refcounts": false,
 3175                     "refcount-bits": 16,
 3176                     "corrupt": false
 3177                 }
 3178             },
 3179             "dirty-flag": false
 3180         }]"""
 3181 
 3182         self.assertEqual(
 3183             {
 3184                 "file": "/disks/test.qcow2",
 3185                 "file format": "qcow2",
 3186                 "backing file": {
 3187                     "file": "/disks/mybacking.qcow2",
 3188                     "file format": "qcow2",
 3189                     "disk size": 393744384,
 3190                     "virtual size": 25769803776,
 3191                     "cluster size": 65536,
 3192                     "backing file": {
 3193                         "file": "/disks/root.qcow2",
 3194                         "file format": "qcow2",
 3195                         "disk size": 196872192,
 3196                         "virtual size": 25769803776,
 3197                         "cluster size": 65536,
 3198                     },
 3199                 },
 3200                 "disk size": 217088,
 3201                 "virtual size": 25769803776,
 3202                 "cluster size": 65536,
 3203                 "snapshots": [
 3204                     {
 3205                         "id": "1",
 3206                         "tag": "first-snap",
 3207                         "vmsize": 1234,
 3208                         "date": datetime.datetime.fromtimestamp(
 3209                             float("{}.{}".format(1528877587, 380589000))
 3210                         ).isoformat(),
 3211                         "vmclock": "00:00:00",
 3212                     },
 3213                     {
 3214                         "id": "2",
 3215                         "tag": "second snap",
 3216                         "vmsize": 4567,
 3217                         "date": datetime.datetime.fromtimestamp(
 3218                             float("{}.{}".format(1528877592, 933509000))
 3219                         ).isoformat(),
 3220                         "vmclock": "00:00:00",
 3221                     },
 3222                 ],
 3223             },
 3224             virt._parse_qemu_img_info(qemu_infos),
 3225         )
 3226 
 3227     @patch("salt.modules.virt.stop", return_value=True)
 3228     @patch("salt.modules.virt.undefine")
 3229     @patch("os.remove")
 3230     def test_purge_default(self, mock_remove, mock_undefine, mock_stop):
 3231         """
 3232         Test virt.purge() with default parameters
 3233         """
 3234         xml = """<domain type='kvm' id='7'>
 3235               <name>test-vm</name>
 3236               <devices>
 3237                 <disk type='file' device='disk'>
 3238                 <driver name='qemu' type='qcow2'/>
 3239                 <source file='/disks/test.qcow2'/>
 3240                 <target dev='vda' bus='virtio'/>
 3241               </disk>
 3242               <disk type='file' device='cdrom'>
 3243                 <driver name='qemu' type='raw'/>
 3244                 <source file='/disks/test-cdrom.iso'/>
 3245                 <target dev='hda' bus='ide'/>
 3246                 <readonly/>
 3247               </disk>
 3248               </devices>
 3249             </domain>
 3250         """
 3251         self.set_mock_vm("test-vm", xml)
 3252 
 3253         qemu_infos = """[{
 3254             "virtual-size": 25769803776,
 3255             "filename": "/disks/test.qcow2",
 3256             "cluster-size": 65536,
 3257             "format": "qcow2",
 3258             "actual-size": 217088,
 3259             "format-specific": {
 3260                 "type": "qcow2",
 3261                 "data": {
 3262                     "compat": "1.1",
 3263                     "lazy-refcounts": false,
 3264                     "refcount-bits": 16,
 3265                     "corrupt": false
 3266                 }
 3267             },
 3268             "dirty-flag": false
 3269         }]"""
 3270 
 3271         self.mock_popen.communicate.return_value = [
 3272             qemu_infos
 3273         ]  # pylint: disable=no-member
 3274 
 3275         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=True)}):
 3276             res = virt.purge("test-vm")
 3277             self.assertTrue(res)
 3278             mock_remove.assert_called_once()
 3279             mock_remove.assert_any_call("/disks/test.qcow2")
 3280 
 3281     @patch("salt.modules.virt.stop", return_value=True)
 3282     @patch("salt.modules.virt.undefine")
 3283     def test_purge_volumes(self, mock_undefine, mock_stop):
 3284         """
 3285         Test virt.purge() with volume disks
 3286         """
 3287         xml = """<domain type='kvm' id='7'>
 3288               <name>test-vm</name>
 3289               <devices>
 3290                 <disk type='volume' device='disk'>
 3291                   <driver name='qemu' type='qcow2' cache='none' io='native'/>
 3292                   <source pool='default' volume='vm05_system'/>
 3293                   <backingStore type='file' index='1'>
 3294                     <format type='qcow2'/>
 3295                     <source file='/var/lib/libvirt/images/vm04_system.qcow2'/>
 3296                     <backingStore type='file' index='2'>
 3297                       <format type='qcow2'/>
 3298                       <source file='/var/testsuite-data/disk-image-template.qcow2'/>
 3299                       <backingStore/>
 3300                     </backingStore>
 3301                   </backingStore>
 3302                   <target dev='vda' bus='virtio'/>
 3303                   <alias name='virtio-disk0'/>
 3304                   <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
 3305                 </disk>
 3306               </devices>
 3307             </domain>
 3308         """
 3309         self.set_mock_vm("test-vm", xml)
 3310 
 3311         pool_mock = MagicMock()
 3312         pool_mock.storageVolLookupByName.return_value.info.return_value = [
 3313             0,
 3314             1234567,
 3315             12345,
 3316         ]
 3317         pool_mock.storageVolLookupByName.return_value.XMLDesc.return_value = [
 3318             """
 3319             <volume type='file'>
 3320               <name>vm05_system</name>
 3321               <target>
 3322                 <path>/var/lib/libvirt/images/vm05_system</path>
 3323                 <format type='qcow2'/>
 3324               </target>
 3325               <backingStore>
 3326                 <path>/var/lib/libvirt/images/vm04_system.qcow2</path>
 3327                 <format type='qcow2'/>
 3328               </backingStore>
 3329             </volume>
 3330             """,
 3331         ]
 3332         pool_mock.listVolumes.return_value = ["vm05_system", "vm04_system.qcow2"]
 3333         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 3334         self.mock_conn.listStoragePools.return_value = ["default"]
 3335 
 3336         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=False)}):
 3337             res = virt.purge("test-vm")
 3338             self.assertTrue(res)
 3339             pool_mock.storageVolLookupByName.return_value.delete.assert_called_once()
 3340 
 3341     @patch("salt.modules.virt.stop", return_value=True)
 3342     @patch("salt.modules.virt.undefine")
 3343     def test_purge_rbd(self, mock_undefine, mock_stop):
 3344         """
 3345         Test virt.purge() with RBD disks
 3346         """
 3347         xml = """<domain type='kvm' id='7'>
 3348               <name>test-vm</name>
 3349               <devices>
 3350                 <disk type="network" device="disk">
 3351                   <driver name='raw' type='qcow2'/>
 3352                   <source protocol='rbd' name='libvirt-pool/my_vm_data2'>
 3353                     <host name='ses2.tf.local'/>
 3354                     <host name='ses3.tf.local' port='1234'/>
 3355                     <auth username='libvirt'>
 3356                       <secret type='ceph' usage='pool_test-rbd'/>
 3357                     </auth>
 3358                   </source>
 3359                   <target dev='vdc' bus='virtio'/>
 3360                   <alias name='virtio-disk2'/>
 3361                   <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x2'/>
 3362                 </disk>
 3363               </devices>
 3364             </domain>
 3365         """
 3366         self.set_mock_vm("test-vm", xml)
 3367 
 3368         pool_mock = MagicMock()
 3369         pool_mock.storageVolLookupByName.return_value.info.return_value = [
 3370             0,
 3371             1234567,
 3372             12345,
 3373         ]
 3374         pool_mock.XMLDesc.return_value = """
 3375         <pool type='rbd'>
 3376           <name>test-ses</name>
 3377           <source>
 3378             <host name='ses2.tf.local'/>
 3379             <name>libvirt-pool</name>
 3380             <auth type='ceph' username='libvirt'>
 3381               <secret usage='pool_test-ses'/>
 3382             </auth>
 3383           </source>
 3384         </pool>
 3385         """
 3386         pool_mock.name.return_value = "test-ses"
 3387         pool_mock.storageVolLookupByName.return_value.XMLDesc.return_value = [
 3388             """
 3389             <volume type='network'>
 3390               <name>my_vm_data2</name>
 3391               <source>
 3392               </source>
 3393               <capacity unit='bytes'>536870912</capacity>
 3394               <allocation unit='bytes'>0</allocation>
 3395               <target>
 3396                 <path>libvirt-pool/my_vm_data2</path>
 3397                 <format type='raw'/>
 3398               </target>
 3399             </volume>
 3400             """,
 3401         ]
 3402         pool_mock.listVolumes.return_value = ["my_vm_data2"]
 3403         self.mock_conn.listAllStoragePools.return_value = [pool_mock]
 3404         self.mock_conn.listStoragePools.return_value = ["test-ses"]
 3405         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 3406 
 3407         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=False)}):
 3408             res = virt.purge("test-vm")
 3409             self.assertTrue(res)
 3410             pool_mock.storageVolLookupByName.return_value.delete.assert_called_once()
 3411 
 3412     @patch("salt.modules.virt.stop", return_value=True)
 3413     @patch("salt.modules.virt.undefine")
 3414     @patch("os.remove")
 3415     def test_purge_removable(self, mock_remove, mock_undefine, mock_stop):
 3416         """
 3417         Test virt.purge(removables=True)
 3418         """
 3419         xml = """<domain type="kvm" id="7">
 3420               <name>test-vm</name>
 3421               <devices>
 3422                 <disk type='file' device='disk'>
 3423                 <driver name='qemu' type='qcow2'/>
 3424                 <source file='/disks/test.qcow2'/>
 3425                 <target dev='vda' bus='virtio'/>
 3426               </disk>
 3427               <disk type='file' device='cdrom'>
 3428                 <driver name='qemu' type='raw'/>
 3429                 <source file='/disks/test-cdrom.iso'/>
 3430                 <target dev='hda' bus='ide'/>
 3431                 <readonly/>
 3432               </disk>
 3433               <disk type='file' device='floppy'>
 3434                 <driver name='qemu' type='raw'/>
 3435                 <source file='/disks/test-floppy.iso'/>
 3436                 <target dev='hdb' bus='ide'/>
 3437                 <readonly/>
 3438               </disk>
 3439               </devices>
 3440             </domain>
 3441         """
 3442         self.set_mock_vm("test-vm", xml)
 3443 
 3444         qemu_infos = """[{
 3445             "virtual-size": 25769803776,
 3446             "filename": "/disks/test.qcow2",
 3447             "cluster-size": 65536,
 3448             "format": "qcow2",
 3449             "actual-size": 217088,
 3450             "format-specific": {
 3451                 "type": "qcow2",
 3452                 "data": {
 3453                     "compat": "1.1",
 3454                     "lazy-refcounts": false,
 3455                     "refcount-bits": 16,
 3456                     "corrupt": false
 3457                 }
 3458             },
 3459             "dirty-flag": false
 3460         }]"""
 3461 
 3462         self.mock_popen.communicate.return_value = [
 3463             qemu_infos
 3464         ]  # pylint: disable=no-member
 3465 
 3466         with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=True)}):
 3467             res = virt.purge("test-vm", removables=True)
 3468             self.assertTrue(res)
 3469             mock_remove.assert_any_call("/disks/test.qcow2")
 3470             mock_remove.assert_any_call("/disks/test-cdrom.iso")
 3471 
 3472     def test_capabilities(self):
 3473         """
 3474         Test the virt.capabilities parsing
 3475         """
 3476         xml = """
 3477 <capabilities>
 3478   <host>
 3479     <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
 3480     <cpu>
 3481       <arch>x86_64</arch>
 3482       <model>Nehalem</model>
 3483       <vendor>Intel</vendor>
 3484       <microcode version='25'/>
 3485       <topology sockets='1' cores='4' threads='2'/>
 3486       <feature name='vme'/>
 3487       <feature name='ds'/>
 3488       <feature name='acpi'/>
 3489       <pages unit='KiB' size='4'/>
 3490       <pages unit='KiB' size='2048'/>
 3491     </cpu>
 3492     <power_management>
 3493       <suspend_mem/>
 3494       <suspend_disk/>
 3495       <suspend_hybrid/>
 3496     </power_management>
 3497     <migration_features>
 3498       <live/>
 3499       <uri_transports>
 3500         <uri_transport>tcp</uri_transport>
 3501         <uri_transport>rdma</uri_transport>
 3502       </uri_transports>
 3503     </migration_features>
 3504     <topology>
 3505       <cells num='1'>
 3506         <cell id='0'>
 3507           <memory unit='KiB'>12367120</memory>
 3508           <pages unit='KiB' size='4'>3091780</pages>
 3509           <pages unit='KiB' size='2048'>0</pages>
 3510           <distances>
 3511             <sibling id='0' value='10'/>
 3512           </distances>
 3513           <cpus num='8'>
 3514             <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
 3515             <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
 3516             <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
 3517             <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
 3518             <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
 3519             <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
 3520             <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
 3521             <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
 3522           </cpus>
 3523         </cell>
 3524       </cells>
 3525     </topology>
 3526     <cache>
 3527       <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
 3528     </cache>
 3529     <secmodel>
 3530       <model>apparmor</model>
 3531       <doi>0</doi>
 3532     </secmodel>
 3533     <secmodel>
 3534       <model>dac</model>
 3535       <doi>0</doi>
 3536       <baselabel type='kvm'>+487:+486</baselabel>
 3537       <baselabel type='qemu'>+487:+486</baselabel>
 3538     </secmodel>
 3539   </host>
 3540 
 3541   <guest>
 3542     <os_type>hvm</os_type>
 3543     <arch name='i686'>
 3544       <wordsize>32</wordsize>
 3545       <emulator>/usr/bin/qemu-system-i386</emulator>
 3546       <machine maxCpus='255'>pc-i440fx-2.6</machine>
 3547       <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 3548       <machine maxCpus='255'>pc-0.12</machine>
 3549       <domain type='qemu'/>
 3550       <domain type='kvm'>
 3551         <emulator>/usr/bin/qemu-kvm</emulator>
 3552         <machine maxCpus='255'>pc-i440fx-2.6</machine>
 3553         <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 3554         <machine maxCpus='255'>pc-0.12</machine>
 3555       </domain>
 3556     </arch>
 3557     <features>
 3558       <cpuselection/>
 3559       <deviceboot/>
 3560       <disksnapshot default='on' toggle='no'/>
 3561       <acpi default='off' toggle='yes'/>
 3562       <apic default='on' toggle='no'/>
 3563       <pae/>
 3564       <nonpae/>
 3565     </features>
 3566   </guest>
 3567 
 3568   <guest>
 3569     <os_type>hvm</os_type>
 3570     <arch name='x86_64'>
 3571       <wordsize>64</wordsize>
 3572       <emulator>/usr/bin/qemu-system-x86_64</emulator>
 3573       <machine maxCpus='255'>pc-i440fx-2.6</machine>
 3574       <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 3575       <machine maxCpus='255'>pc-0.12</machine>
 3576       <domain type='qemu'/>
 3577       <domain type='kvm'>
 3578         <emulator>/usr/bin/qemu-kvm</emulator>
 3579         <machine maxCpus='255'>pc-i440fx-2.6</machine>
 3580         <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 3581         <machine maxCpus='255'>pc-0.12</machine>
 3582       </domain>
 3583     </arch>
 3584     <features>
 3585       <cpuselection/>
 3586       <deviceboot/>
 3587       <disksnapshot default='on' toggle='no'/>
 3588       <acpi default='on' toggle='yes'/>
 3589       <apic default='off' toggle='no'/>
 3590     </features>
 3591   </guest>
 3592 
 3593   <guest>
 3594     <os_type>xen</os_type>
 3595     <arch name='x86_64'>
 3596       <wordsize>64</wordsize>
 3597       <emulator>/usr/bin/qemu-system-x86_64</emulator>
 3598       <machine>xenpv</machine>
 3599       <domain type='xen'/>
 3600     </arch>
 3601   </guest>
 3602 
 3603 </capabilities>
 3604         """
 3605         self.mock_conn.getCapabilities.return_value = xml  # pylint: disable=no-member
 3606         caps = virt.capabilities()
 3607 
 3608         expected = {
 3609             "host": {
 3610                 "uuid": "44454c4c-3400-105a-8033-b3c04f4b344a",
 3611                 "cpu": {
 3612                     "arch": "x86_64",
 3613                     "model": "Nehalem",
 3614                     "vendor": "Intel",
 3615                     "microcode": "25",
 3616                     "sockets": 1,
 3617                     "cores": 4,
 3618                     "threads": 2,
 3619                     "features": ["vme", "ds", "acpi"],
 3620                     "pages": [{"size": "4 KiB"}, {"size": "2048 KiB"}],
 3621                 },
 3622                 "power_management": ["suspend_mem", "suspend_disk", "suspend_hybrid"],
 3623                 "migration": {"live": True, "transports": ["tcp", "rdma"]},
 3624                 "topology": {
 3625                     "cells": [
 3626                         {
 3627                             "id": 0,
 3628                             "memory": "12367120 KiB",
 3629                             "pages": [
 3630                                 {"size": "4 KiB", "available": 3091780},
 3631                                 {"size": "2048 KiB", "available": 0},
 3632                             ],
 3633                             "distances": {0: 10},
 3634                             "cpus": [
 3635                                 {
 3636                                     "id": 0,
 3637                                     "socket_id": 0,
 3638                                     "core_id": 0,
 3639                                     "siblings": "0,4",
 3640                                 },
 3641                                 {
 3642                                     "id": 1,
 3643                                     "socket_id": 0,
 3644                                     "core_id": 1,
 3645                                     "siblings": "1,5",
 3646                                 },
 3647                                 {
 3648                                     "id": 2,
 3649                                     "socket_id": 0,
 3650                                     "core_id": 2,
 3651                                     "siblings": "2,6",
 3652                                 },
 3653                                 {
 3654                                     "id": 3,
 3655                                     "socket_id": 0,
 3656                                     "core_id": 3,
 3657                                     "siblings": "3,7",
 3658                                 },
 3659                                 {
 3660                                     "id": 4,
 3661                                     "socket_id": 0,
 3662                                     "core_id": 0,
 3663                                     "siblings": "0,4",
 3664                                 },
 3665                                 {
 3666                                     "id": 5,
 3667                                     "socket_id": 0,
 3668                                     "core_id": 1,
 3669                                     "siblings": "1,5",
 3670                                 },
 3671                                 {
 3672                                     "id": 6,
 3673                                     "socket_id": 0,
 3674                                     "core_id": 2,
 3675                                     "siblings": "2,6",
 3676                                 },
 3677                                 {
 3678                                     "id": 7,
 3679                                     "socket_id": 0,
 3680                                     "core_id": 3,
 3681                                     "siblings": "3,7",
 3682                                 },
 3683                             ],
 3684                         }
 3685                     ]
 3686                 },
 3687                 "cache": {
 3688                     "banks": [
 3689                         {
 3690                             "id": 0,
 3691                             "level": 3,
 3692                             "type": "both",
 3693                             "size": "8 MiB",
 3694                             "cpus": "0-7",
 3695                         }
 3696                     ]
 3697                 },
 3698                 "security": [
 3699                     {"model": "apparmor", "doi": "0", "baselabels": []},
 3700                     {
 3701                         "model": "dac",
 3702                         "doi": "0",
 3703                         "baselabels": [
 3704                             {"type": "kvm", "label": "+487:+486"},
 3705                             {"type": "qemu", "label": "+487:+486"},
 3706                         ],
 3707                     },
 3708                 ],
 3709             },
 3710             "guests": [
 3711                 {
 3712                     "os_type": "hvm",
 3713                     "arch": {
 3714                         "name": "i686",
 3715                         "wordsize": 32,
 3716                         "emulator": "/usr/bin/qemu-system-i386",
 3717                         "machines": {
 3718                             "pc-i440fx-2.6": {
 3719                                 "maxcpus": 255,
 3720                                 "alternate_names": ["pc"],
 3721                             },
 3722                             "pc-0.12": {"maxcpus": 255, "alternate_names": []},
 3723                         },
 3724                         "domains": {
 3725                             "qemu": {"emulator": None, "machines": {}},
 3726                             "kvm": {
 3727                                 "emulator": "/usr/bin/qemu-kvm",
 3728                                 "machines": {
 3729                                     "pc-i440fx-2.6": {
 3730                                         "maxcpus": 255,
 3731                                         "alternate_names": ["pc"],
 3732                                     },
 3733                                     "pc-0.12": {"maxcpus": 255, "alternate_names": []},
 3734                                 },
 3735                             },
 3736                         },
 3737                     },
 3738                     "features": {
 3739                         "cpuselection": {"default": True, "toggle": False},
 3740                         "deviceboot": {"default": True, "toggle": False},
 3741                         "disksnapshot": {"default": True, "toggle": False},
 3742                         "acpi": {"default": False, "toggle": True},
 3743                         "apic": {"default": True, "toggle": False},
 3744                         "pae": {"default": True, "toggle": False},
 3745                         "nonpae": {"default": True, "toggle": False},
 3746                     },
 3747                 },
 3748                 {
 3749                     "os_type": "hvm",
 3750                     "arch": {
 3751                         "name": "x86_64",
 3752                         "wordsize": 64,
 3753                         "emulator": "/usr/bin/qemu-system-x86_64",
 3754                         "machines": {
 3755                             "pc-i440fx-2.6": {
 3756                                 "maxcpus": 255,
 3757                                 "alternate_names": ["pc"],
 3758                             },
 3759                             "pc-0.12": {"maxcpus": 255, "alternate_names": []},
 3760                         },
 3761                         "domains": {
 3762                             "qemu": {"emulator": None, "machines": {}},
 3763                             "kvm": {
 3764                                 "emulator": "/usr/bin/qemu-kvm",
 3765                                 "machines": {
 3766                                     "pc-i440fx-2.6": {
 3767                                         "maxcpus": 255,
 3768                                         "alternate_names": ["pc"],
 3769                                     },
 3770                                     "pc-0.12": {"maxcpus": 255, "alternate_names": []},
 3771                                 },
 3772                             },
 3773                         },
 3774                     },
 3775                     "features": {
 3776                         "cpuselection": {"default": True, "toggle": False},
 3777                         "deviceboot": {"default": True, "toggle": False},
 3778                         "disksnapshot": {"default": True, "toggle": False},
 3779                         "acpi": {"default": True, "toggle": True},
 3780                         "apic": {"default": False, "toggle": False},
 3781                     },
 3782                 },
 3783                 {
 3784                     "os_type": "xen",
 3785                     "arch": {
 3786                         "name": "x86_64",
 3787                         "wordsize": 64,
 3788                         "emulator": "/usr/bin/qemu-system-x86_64",
 3789                         "machines": {"xenpv": {"alternate_names": []}},
 3790                         "domains": {"xen": {"emulator": None, "machines": {}}},
 3791                     },
 3792                 },
 3793             ],
 3794         }
 3795         self.assertEqual(expected, caps)
 3796 
 3797     def test_network(self):
 3798         """
 3799         Test virt._get_net_xml()
 3800         """
 3801         xml_data = virt._gen_net_xml("network", "main", "bridge", "openvswitch")
 3802         root = ET.fromstring(xml_data)
 3803         self.assertEqual(root.find("name").text, "network")
 3804         self.assertEqual(root.find("bridge").attrib["name"], "main")
 3805         self.assertEqual(root.find("forward").attrib["mode"], "bridge")
 3806         self.assertEqual(root.find("virtualport").attrib["type"], "openvswitch")
 3807 
 3808     def test_network_nat(self):
 3809         """
 3810         Test virt._get_net_xml() in a nat setup
 3811         """
 3812         xml_data = virt._gen_net_xml(
 3813             "network",
 3814             "main",
 3815             "nat",
 3816             None,
 3817             ip_configs=[
 3818                 {
 3819                     "cidr": "192.168.2.0/24",
 3820                     "dhcp_ranges": [
 3821                         {"start": "192.168.2.10", "end": "192.168.2.25"},
 3822                         {"start": "192.168.2.110", "end": "192.168.2.125"},
 3823                     ],
 3824                 }
 3825             ],
 3826         )
 3827         root = ET.fromstring(xml_data)
 3828         self.assertEqual(root.find("name").text, "network")
 3829         self.assertEqual(root.find("bridge").attrib["name"], "main")
 3830         self.assertEqual(root.find("forward").attrib["mode"], "nat")
 3831         self.assertEqual(
 3832             root.find("./ip[@address='192.168.2.0']").attrib["prefix"], "24"
 3833         )
 3834         self.assertEqual(
 3835             root.find("./ip[@address='192.168.2.0']").attrib["family"], "ipv4"
 3836         )
 3837         self.assertEqual(
 3838             root.find(
 3839                 "./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.10']"
 3840             ).attrib["end"],
 3841             "192.168.2.25",
 3842         )
 3843         self.assertEqual(
 3844             root.find(
 3845                 "./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.110']"
 3846             ).attrib["end"],
 3847             "192.168.2.125",
 3848         )
 3849 
 3850     def test_domain_capabilities(self):
 3851         """
 3852         Test the virt.domain_capabilities parsing
 3853         """
 3854         xml = """
 3855 <domainCapabilities>
 3856   <path>/usr/bin/qemu-system-aarch64</path>
 3857   <domain>kvm</domain>
 3858   <machine>virt-2.12</machine>
 3859   <arch>aarch64</arch>
 3860   <vcpu max='255'/>
 3861   <iothreads supported='yes'/>
 3862   <os supported='yes'>
 3863     <loader supported='yes'>
 3864       <value>/usr/share/AAVMF/AAVMF_CODE.fd</value>
 3865       <value>/usr/share/AAVMF/AAVMF32_CODE.fd</value>
 3866       <value>/usr/share/OVMF/OVMF_CODE.fd</value>
 3867       <enum name='type'>
 3868         <value>rom</value>
 3869         <value>pflash</value>
 3870       </enum>
 3871       <enum name='readonly'>
 3872         <value>yes</value>
 3873         <value>no</value>
 3874       </enum>
 3875     </loader>
 3876   </os>
 3877   <cpu>
 3878     <mode name='host-passthrough' supported='yes'/>
 3879     <mode name='host-model' supported='yes'>
 3880       <model fallback='forbid'>sample-cpu</model>
 3881       <vendor>ACME</vendor>
 3882       <feature policy='require' name='vme'/>
 3883       <feature policy='require' name='ss'/>
 3884     </mode>
 3885     <mode name='custom' supported='yes'>
 3886       <model usable='unknown'>pxa262</model>
 3887       <model usable='yes'>pxa270-a0</model>
 3888       <model usable='no'>arm1136</model>
 3889     </mode>
 3890   </cpu>
 3891   <devices>
 3892     <disk supported='yes'>
 3893       <enum name='diskDevice'>
 3894         <value>disk</value>
 3895         <value>cdrom</value>
 3896         <value>floppy</value>
 3897         <value>lun</value>
 3898       </enum>
 3899       <enum name='bus'>
 3900         <value>fdc</value>
 3901         <value>scsi</value>
 3902         <value>virtio</value>
 3903         <value>usb</value>
 3904         <value>sata</value>
 3905       </enum>
 3906     </disk>
 3907     <graphics supported='yes'>
 3908       <enum name='type'>
 3909         <value>sdl</value>
 3910         <value>vnc</value>
 3911       </enum>
 3912     </graphics>
 3913     <video supported='yes'>
 3914       <enum name='modelType'>
 3915         <value>vga</value>
 3916         <value>virtio</value>
 3917       </enum>
 3918     </video>
 3919     <hostdev supported='yes'>
 3920       <enum name='mode'>
 3921         <value>subsystem</value>
 3922       </enum>
 3923       <enum name='startupPolicy'>
 3924         <value>default</value>
 3925         <value>mandatory</value>
 3926         <value>requisite</value>
 3927         <value>optional</value>
 3928       </enum>
 3929       <enum name='subsysType'>
 3930         <value>usb</value>
 3931         <value>pci</value>
 3932         <value>scsi</value>
 3933       </enum>
 3934       <enum name='capsType'/>
 3935       <enum name='pciBackend'>
 3936         <value>default</value>
 3937         <value>kvm</value>
 3938         <value>vfio</value>
 3939       </enum>
 3940     </hostdev>
 3941   </devices>
 3942   <features>
 3943     <gic supported='yes'>
 3944       <enum name='version'>
 3945         <value>3</value>
 3946       </enum>
 3947     </gic>
 3948     <vmcoreinfo supported='yes'/>
 3949   </features>
 3950 </domainCapabilities>
 3951         """
 3952 
 3953         self.mock_conn.getDomainCapabilities.return_value = (
 3954             xml  # pylint: disable=no-member
 3955         )
 3956         caps = virt.domain_capabilities()
 3957 
 3958         expected = {
 3959             "emulator": "/usr/bin/qemu-system-aarch64",
 3960             "domain": "kvm",
 3961             "machine": "virt-2.12",
 3962             "arch": "aarch64",
 3963             "max_vcpus": 255,
 3964             "iothreads": True,
 3965             "os": {
 3966                 "loader": {
 3967                     "type": ["rom", "pflash"],
 3968                     "readonly": ["yes", "no"],
 3969                     "values": [
 3970                         "/usr/share/AAVMF/AAVMF_CODE.fd",
 3971                         "/usr/share/AAVMF/AAVMF32_CODE.fd",
 3972                         "/usr/share/OVMF/OVMF_CODE.fd",
 3973                     ],
 3974                 }
 3975             },
 3976             "cpu": {
 3977                 "host-passthrough": True,
 3978                 "host-model": {
 3979                     "model": {"name": "sample-cpu", "fallback": "forbid"},
 3980                     "vendor": "ACME",
 3981                     "features": {"vme": "require", "ss": "require"},
 3982                 },
 3983                 "custom": {
 3984                     "models": {"pxa262": "unknown", "pxa270-a0": "yes", "arm1136": "no"}
 3985                 },
 3986             },
 3987             "devices": {
 3988                 "disk": {
 3989                     "diskDevice": ["disk", "cdrom", "floppy", "lun"],
 3990                     "bus": ["fdc", "scsi", "virtio", "usb", "sata"],
 3991                 },
 3992                 "graphics": {"type": ["sdl", "vnc"]},
 3993                 "video": {"modelType": ["vga", "virtio"]},
 3994                 "hostdev": {
 3995                     "mode": ["subsystem"],
 3996                     "startupPolicy": ["default", "mandatory", "requisite", "optional"],
 3997                     "subsysType": ["usb", "pci", "scsi"],
 3998                     "capsType": [],
 3999                     "pciBackend": ["default", "kvm", "vfio"],
 4000                 },
 4001             },
 4002             "features": {"gic": {"version": ["3"]}, "vmcoreinfo": {}},
 4003         }
 4004 
 4005         self.assertEqual(expected, caps)
 4006 
 4007     def test_all_capabilities(self):
 4008         """
 4009         Test the virt.domain_capabilities default output
 4010         """
 4011         domainXml = """
 4012 <domainCapabilities>
 4013   <path>/usr/bin/qemu-system-x86_64</path>
 4014   <domain>kvm</domain>
 4015   <machine>virt-2.12</machine>
 4016   <arch>x86_64</arch>
 4017   <vcpu max='255'/>
 4018   <iothreads supported='yes'/>
 4019 </domainCapabilities>
 4020         """
 4021         hostXml = """
 4022 <capabilities>
 4023   <host>
 4024     <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
 4025     <cpu>
 4026       <arch>x86_64</arch>
 4027       <model>Nehalem</model>
 4028       <vendor>Intel</vendor>
 4029       <microcode version='25'/>
 4030       <topology sockets='1' cores='4' threads='2'/>
 4031     </cpu>
 4032   </host>
 4033   <guest>
 4034     <os_type>hvm</os_type>
 4035     <arch name='x86_64'>
 4036       <wordsize>64</wordsize>
 4037       <emulator>/usr/bin/qemu-system-x86_64</emulator>
 4038       <machine maxCpus='255'>pc-i440fx-2.6</machine>
 4039       <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 4040       <machine maxCpus='255'>pc-0.12</machine>
 4041       <domain type='qemu'/>
 4042       <domain type='kvm'>
 4043         <emulator>/usr/bin/qemu-kvm</emulator>
 4044         <machine maxCpus='255'>pc-i440fx-2.6</machine>
 4045         <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
 4046         <machine maxCpus='255'>pc-0.12</machine>
 4047       </domain>
 4048     </arch>
 4049   </guest>
 4050 </capabilities>
 4051         """
 4052 
 4053         # pylint: disable=no-member
 4054         self.mock_conn.getCapabilities.return_value = hostXml
 4055         self.mock_conn.getDomainCapabilities.side_effect = [
 4056             domainXml,
 4057             domainXml.replace("<domain>kvm", "<domain>qemu"),
 4058         ]
 4059         # pylint: enable=no-member
 4060 
 4061         caps = virt.all_capabilities()
 4062         self.assertEqual(
 4063             "44454c4c-3400-105a-8033-b3c04f4b344a", caps["host"]["host"]["uuid"]
 4064         )
 4065         self.assertEqual(
 4066             {"qemu", "kvm"}, {domainCaps["domain"] for domainCaps in caps["domains"]},
 4067         )
 4068 
 4069     def test_network_tag(self):
 4070         """
 4071         Test virt._get_net_xml() with VLAN tag
 4072         """
 4073         xml_data = virt._gen_net_xml("network", "main", "bridge", "openvswitch", 1001)
 4074         root = ET.fromstring(xml_data)
 4075         self.assertEqual(root.find("name").text, "network")
 4076         self.assertEqual(root.find("bridge").attrib["name"], "main")
 4077         self.assertEqual(root.find("forward").attrib["mode"], "bridge")
 4078         self.assertEqual(root.find("virtualport").attrib["type"], "openvswitch")
 4079         self.assertEqual(root.find("vlan/tag").attrib["id"], "1001")
 4080 
 4081     def test_list_networks(self):
 4082         """
 4083         Test virt.list_networks()
 4084         """
 4085         names = ["net1", "default", "net2"]
 4086         net_mocks = [MagicMock(), MagicMock(), MagicMock()]
 4087         for i, value in enumerate(names):
 4088             net_mocks[i].name.return_value = value
 4089 
 4090         self.mock_conn.listAllNetworks.return_value = (
 4091             net_mocks  # pylint: disable=no-member
 4092         )
 4093         actual = virt.list_networks()
 4094         self.assertEqual(names, actual)
 4095 
 4096     def test_network_info(self):
 4097         """
 4098         Test virt.network_info()
 4099         """
 4100         self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0
 4101         self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1
 4102 
 4103         net_mock = MagicMock()
 4104 
 4105         # pylint: disable=no-member
 4106         net_mock.name.return_value = "foo"
 4107         net_mock.UUIDString.return_value = "some-uuid"
 4108         net_mock.bridgeName.return_value = "br0"
 4109         net_mock.autostart.return_value = True
 4110         net_mock.isActive.return_value = False
 4111         net_mock.isPersistent.return_value = True
 4112         net_mock.DHCPLeases.return_value = [
 4113             {
 4114                 "iface": "virbr0",
 4115                 "expirytime": 1527757552,
 4116                 "type": 0,
 4117                 "mac": "52:54:00:01:71:bd",
 4118                 "ipaddr": "192.168.122.45",
 4119                 "prefix": 24,
 4120                 "hostname": "py3-test",
 4121                 "clientid": "01:52:54:00:01:71:bd",
 4122                 "iaid": None,
 4123             }
 4124         ]
 4125         self.mock_conn.listAllNetworks.return_value = [net_mock]
 4126         # pylint: enable=no-member
 4127 
 4128         net = virt.network_info("foo")
 4129         self.assertEqual(
 4130             {
 4131                 "foo": {
 4132                     "uuid": "some-uuid",
 4133                     "bridge": "br0",
 4134                     "autostart": True,
 4135                     "active": False,
 4136                     "persistent": True,
 4137                     "leases": [
 4138                         {
 4139                             "iface": "virbr0",
 4140                             "expirytime": 1527757552,
 4141                             "type": "ipv4",
 4142                             "mac": "52:54:00:01:71:bd",
 4143                             "ipaddr": "192.168.122.45",
 4144                             "prefix": 24,
 4145                             "hostname": "py3-test",
 4146                             "clientid": "01:52:54:00:01:71:bd",
 4147                             "iaid": None,
 4148                         }
 4149                     ],
 4150                 }
 4151             },
 4152             net,
 4153         )
 4154 
 4155     def test_network_info_all(self):
 4156         """
 4157         Test virt.network_info()
 4158         """
 4159         self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0
 4160         self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1
 4161 
 4162         net_mocks = []
 4163         # pylint: disable=no-member
 4164         for i in range(2):
 4165             net_mock = MagicMock()
 4166 
 4167             net_mock.name.return_value = "net{}".format(i)
 4168             net_mock.UUIDString.return_value = "some-uuid"
 4169             net_mock.bridgeName.return_value = "br{}".format(i)
 4170             net_mock.autostart.return_value = True
 4171             net_mock.isActive.return_value = False
 4172             net_mock.isPersistent.return_value = True
 4173             net_mock.DHCPLeases.return_value = []
 4174             net_mocks.append(net_mock)
 4175         self.mock_conn.listAllNetworks.return_value = net_mocks
 4176         # pylint: enable=no-member
 4177 
 4178         net = virt.network_info()
 4179         self.assertEqual(
 4180             {
 4181                 "net0": {
 4182                     "uuid": "some-uuid",
 4183                     "bridge": "br0",
 4184                     "autostart": True,
 4185                     "active": False,
 4186                     "persistent": True,
 4187                     "leases": [],
 4188                 },
 4189                 "net1": {
 4190                     "uuid": "some-uuid",
 4191                     "bridge": "br1",
 4192                     "autostart": True,
 4193                     "active": False,
 4194                     "persistent": True,
 4195                     "leases": [],
 4196                 },
 4197             },
 4198             net,
 4199         )
 4200 
 4201     def test_network_info_notfound(self):
 4202         """
 4203         Test virt.network_info() when the network can't be found
 4204         """
 4205         # pylint: disable=no-member
 4206         self.mock_conn.listAllNetworks.return_value = []
 4207         # pylint: enable=no-member
 4208         net = virt.network_info("foo")
 4209         self.assertEqual({}, net)
 4210 
 4211     def test_network_get_xml(self):
 4212         """
 4213         Test virt.network_get_xml
 4214         """
 4215         network_mock = MagicMock()
 4216         network_mock.XMLDesc.return_value = "<net>Raw XML</net>"
 4217         self.mock_conn.networkLookupByName.return_value = network_mock
 4218 
 4219         self.assertEqual("<net>Raw XML</net>", virt.network_get_xml("default"))
 4220 
 4221     def test_pool(self):
 4222         """
 4223         Test virt._gen_pool_xml()
 4224         """
 4225         xml_data = virt._gen_pool_xml("pool", "logical", "/dev/base")
 4226         root = ET.fromstring(xml_data)
 4227         self.assertEqual(root.find("name").text, "pool")
 4228         self.assertEqual(root.attrib["type"], "logical")
 4229         self.assertEqual(root.find("target/path").text, "/dev/base")
 4230 
 4231     def test_pool_with_source(self):
 4232         """
 4233         Test virt._gen_pool_xml() with a source device
 4234         """
 4235         xml_data = virt._gen_pool_xml(
 4236             "pool", "logical", "/dev/base", source_devices=[{"path": "/dev/sda"}]
 4237         )
 4238         root = ET.fromstring(xml_data)
 4239         self.assertEqual(root.find("name").text, "pool")
 4240         self.assertEqual(root.attrib["type"], "logical")
 4241         self.assertEqual(root.find("target/path").text, "/dev/base")
 4242         self.assertEqual(root.find("source/device").attrib["path"], "/dev/sda")
 4243 
 4244     def test_pool_with_scsi(self):
 4245         """
 4246         Test virt._gen_pool_xml() with a SCSI source
 4247         """
 4248         xml_data = virt._gen_pool_xml(
 4249             "pool",
 4250             "scsi",
 4251             "/dev/disk/by-path",
 4252             source_devices=[{"path": "/dev/sda"}],
 4253             source_adapter={
 4254                 "type": "scsi_host",
 4255                 "parent_address": {
 4256                     "unique_id": 5,
 4257                     "address": {
 4258                         "domain": "0x0000",
 4259                         "bus": "0x00",
 4260                         "slot": "0x1f",
 4261                         "function": "0x2",
 4262                     },
 4263                 },
 4264             },
 4265             source_name="srcname",
 4266         )
 4267         root = ET.fromstring(xml_data)
 4268         self.assertEqual(root.find("name").text, "pool")
 4269         self.assertEqual(root.attrib["type"], "scsi")
 4270         self.assertEqual(root.find("target/path").text, "/dev/disk/by-path")
 4271         self.assertEqual(root.find("source/device"), None)
 4272         self.assertEqual(root.find("source/name"), None)
 4273         self.assertEqual(root.find("source/adapter").attrib["type"], "scsi_host")
 4274         self.assertEqual(
 4275             root.find("source/adapter/parentaddr").attrib["unique_id"], "5"
 4276         )
 4277         self.assertEqual(
 4278             root.find("source/adapter/parentaddr/address").attrib["domain"], "0x0000"
 4279         )
 4280         self.assertEqual(
 4281             root.find("source/adapter/parentaddr/address").attrib["bus"], "0x00"
 4282         )
 4283         self.assertEqual(
 4284             root.find("source/adapter/parentaddr/address").attrib["slot"], "0x1f"
 4285         )
 4286         self.assertEqual(
 4287             root.find("source/adapter/parentaddr/address").attrib["function"], "0x2"
 4288         )
 4289 
 4290     def test_pool_with_rbd(self):
 4291         """
 4292         Test virt._gen_pool_xml() with an RBD source
 4293         """
 4294         xml_data = virt._gen_pool_xml(
 4295             "pool",
 4296             "rbd",
 4297             source_devices=[{"path": "/dev/sda"}],
 4298             source_hosts=["1.2.3.4", "my.ceph.monitor:69"],
 4299             source_auth={
 4300                 "type": "ceph",
 4301                 "username": "admin",
 4302                 "secret": {"type": "uuid", "value": "someuuid"},
 4303             },
 4304             source_name="srcname",
 4305             source_adapter={"type": "scsi_host", "name": "host0"},
 4306             source_dir="/some/dir",
 4307             source_format="fmt",
 4308         )
 4309         root = ET.fromstring(xml_data)
 4310         self.assertEqual(root.find("name").text, "pool")
 4311         self.assertEqual(root.attrib["type"], "rbd")
 4312         self.assertEqual(root.find("target"), None)
 4313         self.assertEqual(root.find("source/device"), None)
 4314         self.assertEqual(root.find("source/name").text, "srcname")
 4315         self.assertEqual(root.find("source/adapter"), None)
 4316         self.assertEqual(root.find("source/dir"), None)
 4317         self.assertEqual(root.find("source/format"), None)
 4318         self.assertEqual(root.findall("source/host")[0].attrib["name"], "1.2.3.4")
 4319         self.assertTrue("port" not in root.findall("source/host")[0].attrib)
 4320         self.assertEqual(
 4321             root.findall("source/host")[1].attrib["name"], "my.ceph.monitor"
 4322         )
 4323         self.assertEqual(root.findall("source/host")[1].attrib["port"], "69")
 4324         self.assertEqual(root.find("source/auth").attrib["type"], "ceph")
 4325         self.assertEqual(root.find("source/auth").attrib["username"], "admin")
 4326         self.assertEqual(root.find("source/auth/secret").attrib["uuid"], "someuuid")
 4327 
 4328     def test_pool_with_netfs(self):
 4329         """
 4330         Test virt._gen_pool_xml() with a netfs source
 4331         """
 4332         xml_data = virt._gen_pool_xml(
 4333             "pool",
 4334             "netfs",
 4335             target="/path/to/target",
 4336             permissions={
 4337                 "mode": "0770",
 4338                 "owner": 1000,
 4339                 "group": 100,
 4340                 "label": "seclabel",
 4341             },
 4342             source_devices=[{"path": "/dev/sda"}],
 4343             source_hosts=["nfs.host"],
 4344             source_name="srcname",
 4345             source_adapter={"type": "scsi_host", "name": "host0"},
 4346             source_dir="/some/dir",
 4347             source_format="nfs",
 4348         )
 4349         root = ET.fromstring(xml_data)
 4350         self.assertEqual(root.find("name").text, "pool")
 4351         self.assertEqual(root.attrib["type"], "netfs")
 4352         self.assertEqual(root.find("target/path").text, "/path/to/target")
 4353         self.assertEqual(root.find("target/permissions/mode").text, "0770")
 4354         self.assertEqual(root.find("target/permissions/owner").text, "1000")
 4355         self.assertEqual(root.find("target/permissions/group").text, "100")
 4356         self.assertEqual(root.find("target/permissions/label").text, "seclabel")
 4357         self.assertEqual(root.find("source/device"), None)
 4358         self.assertEqual(root.find("source/name"), None)
 4359         self.assertEqual(root.find("source/adapter"), None)
 4360         self.assertEqual(root.find("source/dir").attrib["path"], "/some/dir")
 4361         self.assertEqual(root.find("source/format").attrib["type"], "nfs")
 4362         self.assertEqual(root.find("source/host").attrib["name"], "nfs.host")
 4363         self.assertEqual(root.find("source/auth"), None)
 4364 
 4365     def test_pool_with_iscsi_direct(self):
 4366         """
 4367         Test virt._gen_pool_xml() with a iscsi-direct source
 4368         """
 4369         xml_data = virt._gen_pool_xml(
 4370             "pool",
 4371             "iscsi-direct",
 4372             source_hosts=["iscsi.example.com"],
 4373             source_devices=[{"path": "iqn.2013-06.com.example:iscsi-pool"}],
 4374             source_initiator="iqn.2013-06.com.example:iscsi-initiator",
 4375         )
 4376         root = ET.fromstring(xml_data)
 4377         self.assertEqual(root.find("name").text, "pool")
 4378         self.assertEqual(root.attrib["type"], "iscsi-direct")
 4379         self.assertEqual(root.find("target"), None)
 4380         self.assertEqual(
 4381             root.find("source/device").attrib["path"],
 4382             "iqn.2013-06.com.example:iscsi-pool",
 4383         )
 4384         self.assertEqual(
 4385             root.findall("source/host")[0].attrib["name"], "iscsi.example.com"
 4386         )
 4387         self.assertEqual(
 4388             root.find("source/initiator/iqn").attrib["name"],
 4389             "iqn.2013-06.com.example:iscsi-initiator",
 4390         )
 4391 
 4392     def test_pool_define(self):
 4393         """
 4394         Test virt.pool_define()
 4395         """
 4396         mock_pool = MagicMock()
 4397         mock_secret = MagicMock()
 4398         mock_secret_define = MagicMock(return_value=mock_secret)
 4399         self.mock_conn.secretDefineXML = mock_secret_define
 4400         self.mock_conn.storagePoolCreateXML = MagicMock(return_value=mock_pool)
 4401         self.mock_conn.storagePoolDefineXML = MagicMock(return_value=mock_pool)
 4402 
 4403         mocks = [
 4404             mock_pool,
 4405             mock_secret,
 4406             mock_secret_define,
 4407             self.mock_conn.storagePoolCreateXML,
 4408             self.mock_conn.secretDefineXML,
 4409             self.mock_conn.storagePoolDefineXML,
 4410         ]
 4411 
 4412         # Test case with already defined secret and permanent pool
 4413         self.assertTrue(
 4414             virt.pool_define(
 4415                 "default",
 4416                 "rbd",
 4417                 source_hosts=["one.example.com", "two.example.com"],
 4418                 source_name="rbdvol",
 4419                 source_auth={
 4420                     "type": "ceph",
 4421                     "username": "admin",
 4422                     "secret": {"type": "uuid", "value": "someuuid"},
 4423                 },
 4424             )
 4425         )
 4426         self.mock_conn.storagePoolDefineXML.assert_called_once()
 4427         self.mock_conn.storagePoolCreateXML.assert_not_called()
 4428         mock_pool.create.assert_called_once()
 4429         mock_secret_define.assert_not_called()
 4430 
 4431         # Test case with Ceph secret to be defined and transient pool
 4432         for mock in mocks:
 4433             mock.reset_mock()
 4434         self.assertTrue(
 4435             virt.pool_define(
 4436                 "default",
 4437                 "rbd",
 4438                 transient=True,
 4439                 source_hosts=["one.example.com", "two.example.com"],
 4440                 source_name="rbdvol",
 4441                 source_auth={"username": "admin", "password": "c2VjcmV0"},
 4442             )
 4443         )
 4444         self.mock_conn.storagePoolDefineXML.assert_not_called()
 4445 
 4446         pool_xml = self.mock_conn.storagePoolCreateXML.call_args[0][0]
 4447         root = ET.fromstring(pool_xml)
 4448         self.assertEqual(root.find("source/auth").attrib["type"], "ceph")
 4449         self.assertEqual(root.find("source/auth").attrib["username"], "admin")
 4450         self.assertEqual(
 4451             root.find("source/auth/secret").attrib["usage"], "pool_default"
 4452         )
 4453         mock_pool.create.assert_not_called()
 4454         mock_secret.setValue.assert_called_once_with(b"secret")
 4455 
 4456         secret_xml = mock_secret_define.call_args[0][0]
 4457         root = ET.fromstring(secret_xml)
 4458         self.assertEqual(root.find("usage/name").text, "pool_default")
 4459         self.assertEqual(root.find("usage").attrib["type"], "ceph")
 4460         self.assertEqual(root.attrib["private"], "yes")
 4461         self.assertEqual(
 4462             root.find("description").text, "Passphrase for default pool created by Salt"
 4463         )
 4464 
 4465         # Test case with iscsi secret not starting
 4466         for mock in mocks:
 4467             mock.reset_mock()
 4468         self.assertTrue(
 4469             virt.pool_define(
 4470                 "default",
 4471                 "iscsi",
 4472                 target="/dev/disk/by-path",
 4473                 source_hosts=["iscsi.example.com"],
 4474                 source_devices=[{"path": "iqn.2013-06.com.example:iscsi-pool"}],
 4475                 source_auth={"username": "admin", "password": "secret"},
 4476                 start=False,
 4477             )
 4478         )
 4479         self.mock_conn.storagePoolCreateXML.assert_not_called()
 4480 
 4481         pool_xml = self.mock_conn.storagePoolDefineXML.call_args[0][0]
 4482         root = ET.fromstring(pool_xml)
 4483         self.assertEqual(root.find("source/auth").attrib["type"], "chap")
 4484         self.assertEqual(root.find("source/auth").attrib["username"], "admin")
 4485         self.assertEqual(
 4486             root.find("source/auth/secret").attrib["usage"], "pool_default"
 4487         )
 4488         mock_pool.create.assert_not_called()
 4489         mock_secret.setValue.assert_called_once_with("secret")
 4490 
 4491         secret_xml = mock_secret_define.call_args[0][0]
 4492         root = ET.fromstring(secret_xml)
 4493         self.assertEqual(root.find("usage/target").text, "pool_default")
 4494         self.assertEqual(root.find("usage").attrib["type"], "iscsi")
 4495         self.assertEqual(root.attrib["private"], "yes")
 4496         self.assertEqual(
 4497             root.find("description").text, "Passphrase for default pool created by Salt"
 4498         )
 4499 
 4500     def test_list_pools(self):
 4501         """
 4502         Test virt.list_pools()
 4503         """
 4504         names = ["pool1", "default", "pool2"]
 4505         pool_mocks = [MagicMock(), MagicMock(), MagicMock()]
 4506         for i, value in enumerate(names):
 4507             pool_mocks[i].name.return_value = value
 4508 
 4509         self.mock_conn.listAllStoragePools.return_value = (
 4510             pool_mocks  # pylint: disable=no-member
 4511         )
 4512         actual = virt.list_pools()
 4513         self.assertEqual(names, actual)
 4514 
 4515     def test_pool_info(self):
 4516         """
 4517         Test virt.pool_info()
 4518         """
 4519         # pylint: disable=no-member
 4520         pool_mock = MagicMock()
 4521         pool_mock.name.return_value = "foo"
 4522         pool_mock.UUIDString.return_value = "some-uuid"
 4523         pool_mock.info.return_value = [0, 1234, 5678, 123]
 4524         pool_mock.autostart.return_value = True
 4525         pool_mock.isPersistent.return_value = True
 4526         pool_mock.XMLDesc.return_value = """<pool type='dir'>
 4527   <name>default</name>
 4528   <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid>
 4529   <capacity unit='bytes'>854374301696</capacity>
 4530   <allocation unit='bytes'>596275986432</allocation>
 4531   <available unit='bytes'>258098315264</available>
 4532   <source>
 4533   </source>
 4534   <target>
 4535     <path>/srv/vms</path>
 4536     <permissions>
 4537       <mode>0755</mode>
 4538       <owner>0</owner>
 4539       <group>0</group>
 4540     </permissions>
 4541   </target>
 4542 </pool>"""
 4543         self.mock_conn.listAllStoragePools.return_value = [pool_mock]
 4544         # pylint: enable=no-member
 4545 
 4546         pool = virt.pool_info("foo")
 4547         self.assertEqual(
 4548             {
 4549                 "foo": {
 4550                     "uuid": "some-uuid",
 4551                     "state": "inactive",
 4552                     "capacity": 1234,
 4553                     "allocation": 5678,
 4554                     "free": 123,
 4555                     "autostart": True,
 4556                     "persistent": True,
 4557                     "type": "dir",
 4558                     "target_path": "/srv/vms",
 4559                 }
 4560             },
 4561             pool,
 4562         )
 4563 
 4564     def test_pool_info_notarget(self):
 4565         """
 4566         Test virt.pool_info()
 4567         """
 4568         # pylint: disable=no-member
 4569         pool_mock = MagicMock()
 4570         pool_mock.name.return_value = "ceph"
 4571         pool_mock.UUIDString.return_value = "some-uuid"
 4572         pool_mock.info.return_value = [0, 0, 0, 0]
 4573         pool_mock.autostart.return_value = True
 4574         pool_mock.isPersistent.return_value = True
 4575         pool_mock.XMLDesc.return_value = """<pool type='rbd'>
 4576   <name>ceph</name>
 4577   <uuid>some-uuid</uuid>
 4578   <capacity unit='bytes'>0</capacity>
 4579   <allocation unit='bytes'>0</allocation>
 4580   <available unit='bytes'>0</available>
 4581   <source>
 4582     <host name='localhost' port='6789'/>
 4583     <host name='localhost' port='6790'/>
 4584     <name>rbd</name>
 4585     <auth type='ceph' username='admin'>
 4586       <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87'/>
 4587     </auth>
 4588   </source>
 4589 </pool>"""
 4590         self.mock_conn.listAllStoragePools.return_value = [pool_mock]
 4591         # pylint: enable=no-member
 4592 
 4593         pool = virt.pool_info("ceph")
 4594         self.assertEqual(
 4595             {
 4596                 "ceph": {
 4597                     "uuid": "some-uuid",
 4598                     "state": "inactive",
 4599                     "capacity": 0,
 4600                     "allocation": 0,
 4601                     "free": 0,
 4602                     "autostart": True,
 4603                     "persistent": True,
 4604                     "type": "rbd",
 4605                     "target_path": None,
 4606                 }
 4607             },
 4608             pool,
 4609         )
 4610 
 4611     def test_pool_info_notfound(self):
 4612         """
 4613         Test virt.pool_info() when the pool can't be found
 4614         """
 4615         # pylint: disable=no-member
 4616         self.mock_conn.listAllStoragePools.return_value = []
 4617         # pylint: enable=no-member
 4618         pool = virt.pool_info("foo")
 4619         self.assertEqual({}, pool)
 4620 
 4621     def test_pool_info_all(self):
 4622         """
 4623         Test virt.pool_info()
 4624         """
 4625         # pylint: disable=no-member
 4626         pool_mocks = []
 4627         for i in range(2):
 4628             pool_mock = MagicMock()
 4629             pool_mock.name.return_value = "pool{}".format(i)
 4630             pool_mock.UUIDString.return_value = "some-uuid-{}".format(i)
 4631             pool_mock.info.return_value = [0, 1234, 5678, 123]
 4632             pool_mock.autostart.return_value = True
 4633             pool_mock.isPersistent.return_value = True
 4634             pool_mock.XMLDesc.return_value = """<pool type='dir'>
 4635   <name>default</name>
 4636   <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid>
 4637   <capacity unit='bytes'>854374301696</capacity>
 4638   <allocation unit='bytes'>596275986432</allocation>
 4639   <available unit='bytes'>258098315264</available>
 4640   <source>
 4641   </source>
 4642   <target>
 4643     <path>/srv/vms</path>
 4644     <permissions>
 4645       <mode>0755</mode>
 4646       <owner>0</owner>
 4647       <group>0</group>
 4648     </permissions>
 4649   </target>
 4650 </pool>"""
 4651             pool_mocks.append(pool_mock)
 4652         self.mock_conn.listAllStoragePools.return_value = pool_mocks
 4653         # pylint: enable=no-member
 4654 
 4655         pool = virt.pool_info()
 4656         self.assertEqual(
 4657             {
 4658                 "pool0": {
 4659                     "uuid": "some-uuid-0",
 4660                     "state": "inactive",
 4661                     "capacity": 1234,
 4662                     "allocation": 5678,
 4663                     "free": 123,
 4664                     "autostart": True,
 4665                     "persistent": True,
 4666                     "type": "dir",
 4667                     "target_path": "/srv/vms",
 4668                 },
 4669                 "pool1": {
 4670                     "uuid": "some-uuid-1",
 4671                     "state": "inactive",
 4672                     "capacity": 1234,
 4673                     "allocation": 5678,
 4674                     "free": 123,
 4675                     "autostart": True,
 4676                     "persistent": True,
 4677                     "type": "dir",
 4678                     "target_path": "/srv/vms",
 4679                 },
 4680             },
 4681             pool,
 4682         )
 4683 
 4684     def test_pool_get_xml(self):
 4685         """
 4686         Test virt.pool_get_xml
 4687         """
 4688         pool_mock = MagicMock()
 4689         pool_mock.XMLDesc.return_value = "<pool>Raw XML</pool>"
 4690         self.mock_conn.storagePoolLookupByName.return_value = pool_mock
 4691 
 4692         self.assertEqual("<pool>Raw XML</pool>", virt.pool_get_xml("default"))
 4693 
 4694     def test_pool_list_volumes(self):
 4695         """
 4696         Test virt.pool_list_volumes
 4697         """
 4698         names = ["volume1", "volume2"]
 4699         mock_pool = MagicMock()
 4700         # pylint: disable=no-member
 4701         mock_pool.listVolumes.return_value = names
 4702         self.mock_conn.storagePoolLookupByName.return_value = mock_pool
 4703         # pylint: enable=no-member
 4704         self.assertEqual(names, virt.pool_list_volumes("default"))
 4705 
 4706     @patch("salt.modules.virt._is_bhyve_hyper", return_value=False)
 4707     @patch("salt.modules.virt._is_kvm_hyper", return_value=True)
 4708     @patch("salt.modules.virt._is_xen_hyper", return_value=False)
 4709     def test_get_hypervisor(self, isxen_mock, iskvm_mock, is_bhyve_mock):
 4710         """
 4711         test the virt.get_hypervisor() function
 4712         """
 4713         self.assertEqual("kvm", virt.get_hypervisor())
 4714 
 4715         iskvm_mock.return_value = False
 4716         self.assertIsNone(virt.get_hypervisor())
 4717 
 4718         is_bhyve_mock.return_value = False
 4719         self.assertIsNone(virt.get_hypervisor())
 4720 
 4721         isxen_mock.return_value = True
 4722         self.assertEqual("xen", virt.get_hypervisor())
 4723 
 4724     def test_pool_delete(self):
 4725         """
 4726         Test virt.pool_delete function
 4727         """
 4728         mock_pool = MagicMock()
 4729         mock_pool.delete = MagicMock(return_value=0)
 4730         self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mock_pool)
 4731 
 4732         res = virt.pool_delete("test-pool")
 4733         self.assertTrue(res)
 4734 
 4735         self.mock_conn.storagePoolLookupByName.assert_called_once_with("test-pool")
 4736 
 4737         # Shouldn't be called with another parameter so far since those are not implemented
 4738         # and thus throwing exceptions.
 4739         mock_pool.delete.assert_called_once_with(
 4740             self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL
 4741         )
 4742 
 4743     def test_pool_undefine_secret(self):
 4744         """
 4745         Test virt.pool_undefine function where the pool has a secret
 4746         """
 4747         mock_pool = MagicMock()
 4748         mock_pool.undefine = MagicMock(return_value=0)
 4749         mock_pool.XMLDesc.return_value = """
 4750             <pool type='rbd'>
 4751               <name>test-ses</name>
 4752               <source>
 4753                 <host name='myhost'/>
 4754                 <name>libvirt-pool</name>
 4755                 <auth type='ceph' username='libvirt'>
 4756                   <secret usage='pool_test-ses'/>
 4757                 </auth>
 4758               </source>
 4759             </pool>
 4760         """
 4761         self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mock_pool)
 4762         mock_undefine = MagicMock(return_value=0)
 4763         self.mock_conn.secretLookupByUsage.return_value.undefine = mock_undefine
 4764 
 4765         res = virt.pool_undefine("test-ses")
 4766         self.assertTrue(res)
 4767 
 4768         self.mock_conn.storagePoolLookupByName.assert_called_once_with("test-ses")
 4769         mock_pool.undefine.assert_called_once_with()
 4770 
 4771         self.mock_conn.secretLookupByUsage.assert_called_once_with(
 4772             self.mock_libvirt.VIR_SECRET_USAGE_TYPE_CEPH, "pool_test-ses"
 4773         )
 4774         mock_undefine.assert_called_once()
 4775 
 4776     def test_full_info(self):
 4777         """
 4778         Test virt.full_info
 4779         """
 4780         xml = """<domain type='kvm' id='7'>
 4781               <uuid>28deee33-4859-4f23-891c-ee239cffec94</uuid>
 4782               <name>test-vm</name>
 4783               <on_poweroff>destroy</on_poweroff>
 4784               <on_reboot>restart</on_reboot>
 4785               <on_crash>destroy</on_crash>
 4786               <devices>
 4787                 <disk type='file' device='disk'>
 4788                 <driver name='qemu' type='qcow2'/>
 4789                 <source file='/disks/test.qcow2'/>
 4790                 <target dev='vda' bus='virtio'/>
 4791               </disk>
 4792               <disk type='file' device='cdrom'>
 4793                 <driver name='qemu' type='raw'/>
 4794                 <source file='/disks/test-cdrom.iso'/>
 4795                 <target dev='hda' bus='ide'/>
 4796                 <readonly/>
 4797               </disk>
 4798               <interface type='bridge'>
 4799                 <mac address='ac:de:48:b6:8b:59'/>
 4800                 <source bridge='br0'/>
 4801                 <model type='virtio'/>
 4802                 <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
 4803               </interface>
 4804               <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
 4805                 <listen type='address' address='0.0.0.0'/>
 4806               </graphics>
 4807               </devices>
 4808             </domain>
 4809         """
 4810         self.set_mock_vm("test-vm", xml)
 4811 
 4812         qemu_infos = """[{
 4813             "virtual-size": 25769803776,
 4814             "filename": "/disks/test.qcow2",
 4815             "cluster-size": 65536,
 4816             "format": "qcow2",
 4817             "actual-size": 217088,
 4818             "format-specific": {
 4819                 "type": "qcow2",
 4820                 "data": {
 4821                     "compat": "1.1",
 4822                     "lazy-refcounts": false,
 4823                     "refcount-bits": 16,
 4824                     "corrupt": false
 4825                 }
 4826             },
 4827             "full-backing-filename": "/disks/mybacking.qcow2",
 4828             "backing-filename": "mybacking.qcow2",
 4829             "dirty-flag": false
 4830         },
 4831         {
 4832             "virtual-size": 25769803776,
 4833             "filename": "/disks/mybacking.qcow2",
 4834             "cluster-size": 65536,
 4835             "format": "qcow2",
 4836             "actual-size": 393744384,
 4837             "format-specific": {
 4838                 "type": "qcow2",
 4839                 "data": {
 4840                     "compat": "1.1",
 4841                     "lazy-refcounts": false,
 4842                     "refcount-bits&