SHA256
1
0
forked from pool/salt
salt/backport-virt-patches-from-3001-256.patch

1660 lines
58 KiB
Diff
Raw Normal View History

From 32559016ba2bd306a3a027a2191857f24258fc46 Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cbosdonnat@suse.com>
Date: Mon, 7 Sep 2020 15:00:40 +0200
Subject: [PATCH] Backport virt patches from 3001+ (#256)
* Fix various spelling mistakes in master branch (#55954)
* Fix typo of additional
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of against
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of amount
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of argument
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of attempt
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of bandwidth
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of caught
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of compatibility
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of consistency
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of conversions
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of corresponding
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of dependent
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of dictionary
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of disabled
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of adapters
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of disassociates
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of changes
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of command
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of communicate
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of community
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of configuration
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of default
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of absence
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of attribute
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of container
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of described
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of existence
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of explicit
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of formatted
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of guarantees
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of hexadecimal
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of hierarchy
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of initialize
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of label
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of management
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of mismatch
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of don't
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of manually
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of getting
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of information
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of meant
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of nonexistent
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of occur
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of omitted
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of normally
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of overridden
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of repository
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of separate
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of separator
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of specific
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of successful
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of succeeded
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of support
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of version
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of that's
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of "will be removed"
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of release
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of synchronize
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of python
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of usually
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of override
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of running
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of whether
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of package
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of persist
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of preferred
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of present
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix typo of run
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of "allows someone to..."
"Allows to" is not correct English. It must either be "allows someone
to" or "allows doing".
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of "number of times"
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of msgpack
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of daemonized
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of daemons
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of extemporaneous
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of instead
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix spelling mistake of returning
Signed-off-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
* Fix literal comparissons
* virt: Convert cpu_baseline ElementTree to string
In commit 0f5184c (Remove minidom use in virt module) the value
of `cpu` become `xml.etree.ElementTree.Element` and no longer
has a method `toxml()`. This results in the following error:
$ salt '*' virt.cpu_baseline
host2:
The minion function caused an exception: Traceback (most recent call last):
File "/usr/lib/python3.7/site-packages/salt/minion.py", line 1675, in _thread_return
return_data = minion_instance.executors[fname](opts, data, func, args, kwargs)
File "/usr/lib/python3.7/site-packages/salt/executors/direct_call.py", line 12, in execute
return func(*args, **kwargs)
File "/usr/lib/python3.7/site-packages/salt/modules/virt.py", line 4410, in cpu_baseline
return cpu.toxml()
AttributeError: 'xml.etree.ElementTree.Element' object has no attribute 'toxml'
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
* PR#57374 backport
virt: pool secret should be undefined in pool_undefine not pool_delete
virt: handle build differently depending on the pool type
virt: don't fail if the pool secret has been removed
* PR #57396 backport
add firmware auto select feature
* virt: Update dependencies
Closes: #57641
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
* use null in sls file to map None object
add sls file example
reword doc
* Update virt module and states and their tests to python3
* PR #57545 backport
Move virt.init boot_dev parameter away from the kwargs
virt: handle boot device in virt.update()
virt: add boot_dev parameter to virt.running state
* PR #57431 backport
virt: Handle no available hypervisors
virt: Remove unused imports
* Blacken salt
* Add method to remove circular references in data objects and add test (#54930)
* Add method to remove circular references in data objects and add test
* remove trailing whitespace
* Blacken changed files
Co-authored-by: xeacott <jeacott@saltstack.com>
Co-authored-by: Frode Gundersen <fgundersen@saltstack.com>
Co-authored-by: Daniel A. Wozniak <dwozniak@saltstack.com>
* PR #58332 backport
virt: add debug log with VM XML definition
Add xmlutil.get_xml_node() helper function
Add salt.utils.data.get_value function
Add change_xml() function to xmlutil
virt.update: refactor the XML diffing code
virt.test_update: move some code to make test more readable
Co-authored-by: Benjamin Drung <benjamin.drung@cloud.ionos.com>
Co-authored-by: Pedro Algarvio <pedro@algarvio.me>
Co-authored-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
Co-authored-by: Firefly <leevn2011@hotmail.com>
Co-authored-by: Blacken Salt <jenkins@saltstack.com>
Co-authored-by: Joe Eacott <31625359+xeacott@users.noreply.github.com>
Co-authored-by: xeacott <jeacott@saltstack.com>
Co-authored-by: Frode Gundersen <fgundersen@saltstack.com>
Co-authored-by: Daniel A. Wozniak <dwozniak@saltstack.com>
---
changelog/56454.fixed | 1 +
changelog/57544.added | 1 +
changelog/58331.fixed | 1 +
salt/modules/virt.py | 270 +++++++++++++----------
salt/states/virt.py | 88 ++++++--
salt/templates/virt/libvirt_domain.jinja | 29 +--
salt/utils/xmlutil.py | 4 +-
tests/unit/modules/test_virt.py | 159 +++++++++----
tests/unit/states/test_virt.py | 93 +++++++-
tests/unit/utils/test_data.py | 32 ---
10 files changed, 441 insertions(+), 237 deletions(-)
create mode 100644 changelog/56454.fixed
create mode 100644 changelog/57544.added
create mode 100644 changelog/58331.fixed
diff --git a/changelog/56454.fixed b/changelog/56454.fixed
new file mode 100644
index 0000000000..978b4b6e03
--- /dev/null
+++ b/changelog/56454.fixed
@@ -0,0 +1 @@
+Better handle virt.pool_rebuild in virt.pool_running and virt.pool_defined states
diff --git a/changelog/57544.added b/changelog/57544.added
new file mode 100644
index 0000000000..52071cf2c7
--- /dev/null
+++ b/changelog/57544.added
@@ -0,0 +1 @@
+Allow setting VM boot devices order in virt.running and virt.defined states
diff --git a/changelog/58331.fixed b/changelog/58331.fixed
new file mode 100644
index 0000000000..4b8f78dd53
--- /dev/null
+++ b/changelog/58331.fixed
@@ -0,0 +1 @@
+Leave boot parameters untouched if boot parameter is set to None in virt.update
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index fb27397baa..ec40f08359 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -94,17 +94,13 @@ from xml.sax import saxutils
import jinja2.exceptions
import salt.utils.files
import salt.utils.json
-import salt.utils.network
import salt.utils.path
import salt.utils.stringutils
import salt.utils.templates
-import salt.utils.validate.net
-import salt.utils.versions
import salt.utils.xmlutil as xmlutil
import salt.utils.yaml
from salt._compat import ipaddress
from salt.exceptions import CommandExecutionError, SaltInvocationError
-from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
from salt.ext.six.moves.urllib.parse import urlparse, urlunparse
from salt.utils.virt import check_remote, download_remote
@@ -647,6 +643,7 @@ def _gen_xml(
arch,
graphics=None,
boot=None,
+ boot_dev=None,
**kwargs
):
"""
@@ -680,15 +677,17 @@ def _gen_xml(
graphics = None
context["graphics"] = graphics
- if "boot_dev" in kwargs:
- context["boot_dev"] = []
- for dev in kwargs["boot_dev"].split():
- context["boot_dev"].append(dev)
- else:
- context["boot_dev"] = ["hd"]
+ context["boot_dev"] = boot_dev.split() if boot_dev is not None else ["hd"]
context["boot"] = boot if boot else {}
+ # if efi parameter is specified, prepare os_attrib
+ efi_value = context["boot"].get("efi", None) if boot else None
+ if efi_value is True:
+ context["boot"]["os_attrib"] = "firmware='efi'"
+ elif efi_value is not None and type(efi_value) != bool:
+ raise SaltInvocationError("Invalid efi value")
+
if os_type == "xen":
# Compute the Xen PV boot method
if __grains__["os_family"] == "Suse":
@@ -1519,17 +1518,24 @@ def _handle_remote_boot_params(orig_boot):
new_boot = orig_boot.copy()
keys = orig_boot.keys()
cases = [
+ {"efi"},
+ {"kernel", "initrd", "efi"},
+ {"kernel", "initrd", "cmdline", "efi"},
{"loader", "nvram"},
{"kernel", "initrd"},
{"kernel", "initrd", "cmdline"},
- {"loader", "nvram", "kernel", "initrd"},
- {"loader", "nvram", "kernel", "initrd", "cmdline"},
+ {"kernel", "initrd", "loader", "nvram"},
+ {"kernel", "initrd", "cmdline", "loader", "nvram"},
]
try:
if keys in cases:
for key in keys:
- if orig_boot.get(key) is not None and check_remote(orig_boot.get(key)):
+ if key == "efi" and type(orig_boot.get(key)) == bool:
+ new_boot[key] = orig_boot.get(key)
+ elif orig_boot.get(key) is not None and check_remote(
+ orig_boot.get(key)
+ ):
if saltinst_dir is None:
os.makedirs(CACHE_DIR)
saltinst_dir = CACHE_DIR
@@ -1537,12 +1543,41 @@ def _handle_remote_boot_params(orig_boot):
return new_boot
else:
raise SaltInvocationError(
- "Invalid boot parameters, (kernel, initrd) or/and (loader, nvram) must be both present"
+ "Invalid boot parameters,It has to follow this combination: [(kernel, initrd) or/and cmdline] or/and [(loader, nvram) or efi]"
)
except Exception as err: # pylint: disable=broad-except
raise err
+def _handle_efi_param(boot, desc):
+ """
+ Checks if boot parameter contains efi boolean value, if so, handles the firmware attribute.
+ :param boot: The boot parameters passed to the init or update functions.
+ :param desc: The XML description of that domain.
+ :return: A boolean value.
+ """
+ efi_value = boot.get("efi", None) if boot else None
+ parent_tag = desc.find("os")
+ os_attrib = parent_tag.attrib
+
+ # newly defined vm without running, loader tag might not be filled yet
+ if efi_value is False and os_attrib != {}:
+ parent_tag.attrib.pop("firmware", None)
+ return True
+
+ # check the case that loader tag might be present. This happens after the vm ran
+ elif type(efi_value) == bool and os_attrib == {}:
+ if efi_value is True and parent_tag.find("loader") is None:
+ parent_tag.set("firmware", "efi")
+ if efi_value is False and parent_tag.find("loader") is not None:
+ parent_tag.remove(parent_tag.find("loader"))
+ parent_tag.remove(parent_tag.find("nvram"))
+ return True
+ elif type(efi_value) != bool:
+ raise SaltInvocationError("Invalid efi value")
+ return False
+
+
def init(
name,
cpu,
@@ -1563,6 +1598,7 @@ def init(
os_type=None,
arch=None,
boot=None,
+ boot_dev=None,
**kwargs
):
"""
@@ -1632,7 +1668,8 @@ def init(
This is an optional parameter, all of the keys are optional within the dictionary. The structure of
the dictionary is documented in :ref:`init-boot-def`. If a remote path is provided to kernel or initrd,
salt will handle the downloading of the specified remote file and modify the XML accordingly.
- To boot VM with UEFI, specify loader and nvram path.
+ To boot VM with UEFI, specify loader and nvram path or specify 'efi': ``True`` if your libvirtd version
+ is >= 5.2.0 and QEMU >= 3.0.0.
.. versionadded:: 3000
@@ -1646,6 +1683,12 @@ def init(
'nvram': '/usr/share/OVMF/OVMF_VARS.ms.fd'
}
+ :param boot_dev:
+ Space separated list of devices to boot from sorted by decreasing priority.
+ Values can be ``hd``, ``fd``, ``cdrom`` or ``network``.
+
+ By default, the value will ``"hd"``.
+
.. _init-boot-def:
.. rubric:: Boot parameters definition
@@ -1671,6 +1714,11 @@ def init(
.. versionadded:: sodium
+ efi
+ A boolean value.
+
+ .. versionadded:: sodium
+
.. _init-nic-def:
.. rubric:: Network Interfaces Definitions
@@ -1855,6 +1903,8 @@ def init(
for x in y
}
)
+ if len(hypervisors) == 0:
+ raise SaltInvocationError("No supported hypervisors were found")
virt_hypervisor = "kvm" if "kvm" in hypervisors else hypervisors[0]
# esxi used to be a possible value for the hypervisor: map it to vmware since it's the same
@@ -1962,8 +2012,10 @@ def init(
arch,
graphics,
boot,
+ boot_dev,
**kwargs
)
+ log.debug("New virtual machine definition: %s", vm_xml)
conn.defineXML(vm_xml)
except libvirt.libvirtError as err:
conn.close()
@@ -2189,6 +2241,7 @@ def update(
live=True,
boot=None,
test=False,
+ boot_dev=None,
**kwargs
):
"""
@@ -2248,11 +2301,28 @@ def update(
Refer to :ref:`init-boot-def` for the complete boot parameter description.
- To update any boot parameters, specify the new path for each. To remove any boot parameters,
- pass a None object, for instance: 'kernel': ``None``.
+ To update any boot parameters, specify the new path for each. To remove any boot parameters, pass ``None`` object,
+ for instance: 'kernel': ``None``. To switch back to BIOS boot, specify ('loader': ``None`` and 'nvram': ``None``)
+ or 'efi': ``False``. Please note that ``None`` is mapped to ``null`` in sls file, pass ``null`` in sls file instead.
+
+ SLS file Example:
+
+ .. code-block:: yaml
+
+ - boot:
+ loader: null
+ nvram: null
.. versionadded:: 3000
+ :param boot_dev:
+ Space separated list of devices to boot from sorted by decreasing priority.
+ Values can be ``hd``, ``fd``, ``cdrom`` or ``network``.
+
+ By default, the value will ``"hd"``.
+
+ .. versionadded:: Magnesium
+
:param test: run in dry-run mode if set to True
.. versionadded:: sodium
@@ -2327,67 +2397,54 @@ def update(
cpu_node.set("current", str(cpu))
need_update = True
- # Update the kernel boot parameters
- boot_tags = ["kernel", "initrd", "cmdline", "loader", "nvram"]
- parent_tag = desc.find("os")
-
- # We need to search for each possible subelement, and update it.
- for tag in boot_tags:
- # The Existing Tag...
- found_tag = parent_tag.find(tag)
-
- # The new value
- boot_tag_value = boot.get(tag, None) if boot else None
-
- # Existing tag is found and values don't match
- if found_tag is not None and found_tag.text != boot_tag_value:
-
- # If the existing tag is found, but the new value is None
- # remove it. If the existing tag is found, and the new value
- # doesn't match update it. In either case, mark for update.
- if boot_tag_value is None and boot is not None and parent_tag is not None:
- parent_tag.remove(found_tag)
- else:
- found_tag.text = boot_tag_value
-
- # If the existing tag is loader or nvram, we need to update the corresponding attribute
- if found_tag.tag == "loader" and boot_tag_value is not None:
- found_tag.set("readonly", "yes")
- found_tag.set("type", "pflash")
-
- if found_tag.tag == "nvram" and boot_tag_value is not None:
- found_tag.set("template", found_tag.text)
- found_tag.text = None
+ def _set_loader(node, value):
+ salt.utils.xmlutil.set_node_text(node, value)
+ if value is not None:
+ node.set("readonly", "yes")
+ node.set("type", "pflash")
- need_update = True
+ def _set_nvram(node, value):
+ node.set("template", value)
- # Need to check for parent tag, and add it if it does not exist.
- # Add a subelement and set the value to the new value, and then
- # mark for update.
- if parent_tag is not None:
- child_tag = ElementTree.SubElement(parent_tag, tag)
- else:
- new_parent_tag = ElementTree.Element("os")
- child_tag = ElementTree.SubElement(new_parent_tag, tag)
-
- child_tag.text = boot_tag_value
-
- # If the newly created tag is loader or nvram, we need to update the corresponding attribute
- if child_tag.tag == "loader":
- child_tag.set("readonly", "yes")
- child_tag.set("type", "pflash")
+ def _set_with_mib_unit(node, value):
+ node.text = str(value)
+ node.set("unit", "MiB")
- if child_tag.tag == "nvram":
- child_tag.set("template", child_tag.text)
- child_tag.text = None
+ # Update the kernel boot parameters
+ params_mapping = [
+ {"path": "boot:kernel", "xpath": "os/kernel"},
+ {"path": "boot:initrd", "xpath": "os/initrd"},
+ {"path": "boot:cmdline", "xpath": "os/cmdline"},
+ {"path": "boot:loader", "xpath": "os/loader", "set": _set_loader},
+ {"path": "boot:nvram", "xpath": "os/nvram", "set": _set_nvram},
+ # Update the memory, note that libvirt outputs all memory sizes in KiB
+ {
+ "path": "mem",
+ "xpath": "memory",
+ "get": lambda n: int(n.text) / 1024,
+ "set": _set_with_mib_unit,
+ },
+ {
+ "path": "mem",
+ "xpath": "currentMemory",
+ "get": lambda n: int(n.text) / 1024,
+ "set": _set_with_mib_unit,
+ },
+ {
+ "path": "boot_dev:{dev}",
+ "xpath": "os/boot[$dev]",
+ "get": lambda n: n.get("dev"),
+ "set": lambda n, v: n.set("dev", v),
+ "del": salt.utils.xmlutil.del_attribute("dev"),
+ },
+ ]
- # Update the memory, note that libvirt outputs all memory sizes in KiB
- for mem_node_name in ["memory", "currentMemory"]:
- mem_node = desc.find(mem_node_name)
- if mem and int(mem_node.text) != mem * 1024:
- mem_node.text = str(mem)
- mem_node.set("unit", "MiB")
- need_update = True
+ data = {k: v for k, v in locals().items() if bool(v)}
+ if boot_dev:
+ data["boot_dev"] = {i + 1: dev for i, dev in enumerate(boot_dev.split())}
+ need_update = need_update or salt.utils.xmlutil.change_xml(
+ desc, data, params_mapping
+ )
# Update the XML definition with the new disks and diff changes
devices_node = desc.find("devices")
@@ -2434,9 +2491,9 @@ def update(
_disk_volume_create(conn, all_disks[idx])
if not test:
- conn.defineXML(
- salt.utils.stringutils.to_str(ElementTree.tostring(desc))
- )
+ xml_desc = ElementTree.tostring(desc)
+ log.debug("Update virtual machine definition: %s", xml_desc)
+ conn.defineXML(salt.utils.stringutils.to_str(xml_desc))
status["definition"] = True
except libvirt.libvirtError as err:
conn.close()
@@ -3218,24 +3275,19 @@ def get_profiles(hypervisor=None, **kwargs):
for x in y
}
)
- default_hypervisor = "kvm" if "kvm" in hypervisors else hypervisors[0]
+ if len(hypervisors) == 0:
+ raise SaltInvocationError("No supported hypervisors were found")
if not hypervisor:
- hypervisor = default_hypervisor
+ hypervisor = "kvm" if "kvm" in hypervisors else hypervisors[0]
virtconf = __salt__["config.get"]("virt", {})
for typ in ["disk", "nic"]:
_func = getattr(sys.modules[__name__], "_{}_profile".format(typ))
- ret[typ] = {
- "default": _func(
- "default", hypervisor if hypervisor else default_hypervisor
- )
- }
+ ret[typ] = {"default": _func("default", hypervisor)}
if typ in virtconf:
ret.setdefault(typ, {})
for prf in virtconf[typ]:
- ret[typ][prf] = _func(
- prf, hypervisor if hypervisor else default_hypervisor
- )
+ ret[typ][prf] = _func(prf, hypervisor)
return ret
@@ -4043,7 +4095,7 @@ def purge(vm_, dirs=False, removables=False, **kwargs):
directories.add(os.path.dirname(disks[disk]["file"]))
else:
# We may have a volume to delete here
- matcher = re.match("^(?P<pool>[^/]+)/(?P<volume>.*)$", disks[disk]["file"],)
+ matcher = re.match("^(?P<pool>[^/]+)/(?P<volume>.*)$", disks[disk]["file"])
if matcher:
pool_name = matcher.group("pool")
pool = None
@@ -5431,7 +5483,7 @@ def list_networks(**kwargs):
def network_info(name=None, **kwargs):
"""
- Return informations on a virtual network provided its name.
+ Return information on a virtual network provided its name.
:param name: virtual network name
:param connection: libvirt connection URI, overriding defaults
@@ -6028,15 +6080,19 @@ def _pool_set_secret(
if secret_type:
# Get the previously defined secret if any
secret = None
- if usage:
- usage_type = (
- libvirt.VIR_SECRET_USAGE_TYPE_CEPH
- if secret_type == "ceph"
- else libvirt.VIR_SECRET_USAGE_TYPE_ISCSI
- )
- secret = conn.secretLookupByUsage(usage_type, usage)
- elif uuid:
- secret = conn.secretLookupByUUIDString(uuid)
+ try:
+ if usage:
+ usage_type = (
+ libvirt.VIR_SECRET_USAGE_TYPE_CEPH
+ if secret_type == "ceph"
+ else libvirt.VIR_SECRET_USAGE_TYPE_ISCSI
+ )
+ secret = conn.secretLookupByUsage(usage_type, usage)
+ elif uuid:
+ secret = conn.secretLookupByUUIDString(uuid)
+ except libvirt.libvirtError as err:
+ # For some reason the secret has been removed. Don't fail since we'll recreate it
+ log.info("Secret not found: %s", err.get_error_message())
# Create secret if needed
if not secret:
@@ -6288,7 +6344,7 @@ def list_pools(**kwargs):
def pool_info(name=None, **kwargs):
"""
- Return informations on a storage pool provided its name.
+ Return information on a storage pool provided its name.
:param name: libvirt storage pool name
:param connection: libvirt connection URI, overriding defaults
@@ -6505,22 +6561,6 @@ def pool_delete(name, **kwargs):
conn = __get_conn(**kwargs)
try:
pool = conn.storagePoolLookupByName(name)
- desc = ElementTree.fromstring(pool.XMLDesc())
-
- # Is there a secret that we generated and would need to be removed?
- # Don't remove the other secrets
- auth_node = desc.find("source/auth")
- if auth_node is not None:
- auth_types = {
- "ceph": libvirt.VIR_SECRET_USAGE_TYPE_CEPH,
- "iscsi": libvirt.VIR_SECRET_USAGE_TYPE_ISCSI,
- }
- secret_type = auth_types[auth_node.get("type")]
- secret_usage = auth_node.find("secret").get("usage")
- if secret_type and "pool_{}".format(name) == secret_usage:
- secret = conn.secretLookupByUsage(secret_type, secret_usage)
- secret.undefine()
-
return not bool(pool.delete(libvirt.VIR_STORAGE_POOL_DELETE_NORMAL))
finally:
conn.close()
diff --git a/salt/states/virt.py b/salt/states/virt.py
index cb15d57d8f..b45cf72ed3 100644
--- a/salt/states/virt.py
+++ b/salt/states/virt.py
@@ -33,6 +33,8 @@ except ImportError:
__virtualname__ = "virt"
+log = logging.getLogger(__name__)
+
def __virtual__():
"""
@@ -285,6 +287,7 @@ def defined(
arch=None,
boot=None,
update=True,
+ boot_dev=None,
):
"""
Starts an existing guest, or defines and starts a new VM with specified arguments.
@@ -345,6 +348,14 @@ def defined(
.. deprecated:: sodium
+ :param boot_dev:
+ Space separated list of devices to boot from sorted by decreasing priority.
+ Values can be ``hd``, ``fd``, ``cdrom`` or ``network``.
+
+ By default, the value will ``"hd"``.
+
+ .. versionadded:: Magnesium
+
.. rubric:: Example States
Make sure a virtual machine called ``domain_name`` is defined:
@@ -355,6 +366,7 @@ def defined(
virt.defined:
- cpu: 2
- mem: 2048
+ - boot_dev: network hd
- disk_profile: prod
- disks:
- name: system
@@ -407,6 +419,7 @@ def defined(
password=password,
boot=boot,
test=__opts__["test"],
+ boot_dev=boot_dev,
)
ret["changes"][name] = status
if not status.get("definition"):
@@ -441,6 +454,7 @@ def defined(
password=password,
boot=boot,
start=False,
+ boot_dev=boot_dev,
)
ret["changes"][name] = {"definition": True}
ret["comment"] = "Domain {} defined".format(name)
@@ -473,6 +487,7 @@ def running(
os_type=None,
arch=None,
boot=None,
+ boot_dev=None,
):
"""
Starts an existing guest, or defines and starts a new VM with specified arguments.
@@ -584,6 +599,14 @@ def running(
.. versionadded:: 3000
+ :param boot_dev:
+ Space separated list of devices to boot from sorted by decreasing priority.
+ Values can be ``hd``, ``fd``, ``cdrom`` or ``network``.
+
+ By default, the value will ``"hd"``.
+
+ .. versionadded:: Magnesium
+
.. rubric:: Example States
Make sure an already-defined virtual machine called ``domain_name`` is running:
@@ -651,6 +674,7 @@ def running(
arch=arch,
boot=boot,
update=update,
+ boot_dev=boot_dev,
connection=connection,
username=username,
password=password,
@@ -1218,14 +1242,24 @@ def pool_defined(
action = ""
if info[name]["state"] != "running":
- if not __opts__["test"]:
- __salt__["virt.pool_build"](
- name,
- connection=connection,
- username=username,
- password=password,
- )
- action = ", built"
+ if ptype in BUILDABLE_POOL_TYPES:
+ if not __opts__["test"]:
+ # Storage pools build like disk or logical will fail if the disk or LV group
+ # was already existing. Since we can't easily figure that out, just log the
+ # possible libvirt error.
+ try:
+ __salt__["virt.pool_build"](
+ name,
+ connection=connection,
+ username=username,
+ password=password,
+ )
+ except libvirt.libvirtError as err:
+ log.warning(
+ "Failed to build libvirt storage pool: %s",
+ err.get_error_message(),
+ )
+ action = ", built"
action = (
"{}, autostart flag changed".format(action)
@@ -1261,9 +1295,22 @@ def pool_defined(
password=password,
)
- __salt__["virt.pool_build"](
- name, connection=connection, username=username, password=password
- )
+ if ptype in BUILDABLE_POOL_TYPES:
+ # Storage pools build like disk or logical will fail if the disk or LV group
+ # was already existing. Since we can't easily figure that out, just log the
+ # possible libvirt error.
+ try:
+ __salt__["virt.pool_build"](
+ name,
+ connection=connection,
+ username=username,
+ password=password,
+ )
+ except libvirt.libvirtError as err:
+ log.warning(
+ "Failed to build libvirt storage pool: %s",
+ err.get_error_message(),
+ )
if needs_autostart:
ret["changes"][name] = "Pool defined, marked for autostart"
ret["comment"] = "Pool {} defined, marked for autostart".format(name)
@@ -1370,7 +1417,7 @@ def pool_running(
is_running = info.get(name, {}).get("state", "stopped") == "running"
if is_running:
if updated:
- action = "built, restarted"
+ action = "restarted"
if not __opts__["test"]:
__salt__["virt.pool_stop"](
name,
@@ -1378,13 +1425,16 @@ def pool_running(
username=username,
password=password,
)
- if not __opts__["test"]:
- __salt__["virt.pool_build"](
- name,
- connection=connection,
- username=username,
- password=password,
- )
+ # if the disk or LV group is already existing build will fail (issue #56454)
+ if ptype in BUILDABLE_POOL_TYPES - {"disk", "logical"}:
+ if not __opts__["test"]:
+ __salt__["virt.pool_build"](
+ name,
+ connection=connection,
+ username=username,
+ password=password,
+ )
+ action = "built, {}".format(action)
else:
action = "already running"
result = True
diff --git a/salt/templates/virt/libvirt_domain.jinja b/salt/templates/virt/libvirt_domain.jinja
index 439ed83f7f..2a2f5e4141 100644
--- a/salt/templates/virt/libvirt_domain.jinja
+++ b/salt/templates/virt/libvirt_domain.jinja
@@ -2,32 +2,9 @@
<domain type='{{ hypervisor }}'>
<name>{{ name }}</name>
<vcpu>{{ cpu }}</vcpu>
- {%- if mem.max %}
- <maxMemory {{ mem.slots }} unit='KiB'> {{ mem.max }}</maxMemory>
- {%- endif %}
- {%- if mem.boot %}
- <memory unit='KiB'>{{ mem.boot }}</memory>
- {%- endif %}
- {%- if mem.current %}
- <currentMemory unit='KiB'>{{ mem.current }}</currentMemory>
- {%- endif %}
- {%- if mem %}
- <memtune>
- {%- if 'hard_limit' in mem and mem.hard_limit %}
- <hard_limit unit="KiB">{{ mem.hard_limit }}</hard_limit>
- {%- endif %}
- {%- if 'soft_limit' in mem and mem.soft_limit %}
- <soft_limit unit="KiB">{{ mem.soft_limit }}</soft_limit>
- {%- endif %}
- {%- if 'swap_hard_limit' in mem and mem.swap_hard_limit %}
- <swap_hard_limit unit="KiB">{{ mem.swap_hard_limit }}</swap_hard_limit>
- {%- endif %}
- {%- if 'min_guarantee' in mem and mem.min_guarantee %}
- <min_guarantee unit="KiB">{{ mem.min_guarantee }}</min_guarantee>
- {%- endif %}
- </memtune>
- {%- endif %}
- <os {{ boot.os_attrib }}>
+ <memory unit='KiB'>{{ mem }}</memory>
+ <currentMemory unit='KiB'>{{ mem }}</currentMemory>
+ <os {{boot.os_attrib}}>
<type arch='{{ arch }}'>{{ os_type }}</type>
{% if boot %}
{% if 'kernel' in boot %}
diff --git a/salt/utils/xmlutil.py b/salt/utils/xmlutil.py
index e5c8ad4eec..b9f047820b 100644
--- a/salt/utils/xmlutil.py
+++ b/salt/utils/xmlutil.py
@@ -25,7 +25,7 @@ def _to_dict(xmltree):
"""
Converts an XML ElementTree to a dictionary that only contains items.
This is the default behavior in version 2017.7. This will default to prevent
- unexpected parsing issues on modules dependent on this.
+ unexpected parsing issues on modules dependant on this.
"""
# If this object has no children, the for..loop below will return nothing
# for it, so just return a single dict representing it.
@@ -298,7 +298,7 @@ def change_xml(doc, data, mapping):
if convert_fn:
new_value = convert_fn(new_value)
- if str(current_value) != str(new_value):
+ if current_value != new_value:
set_fn(node, new_value)
need_update = True
else:
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
index f53b4a85c1..4775fec31f 100644
--- a/tests/unit/modules/test_virt.py
+++ b/tests/unit/modules/test_virt.py
@@ -1843,17 +1843,36 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
"cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/",
}
- boot_uefi = {
- "loader": "/usr/share/OVMF/OVMF_CODE.fd",
- "nvram": "/usr/share/OVMF/OVMF_VARS.ms.fd",
- }
+ # Update boot devices case
+ define_mock.reset_mock()
+ self.assertEqual(
+ {
+ "definition": True,
+ "disk": {"attached": [], "detached": [], "updated": []},
+ "interface": {"attached": [], "detached": []},
+ },
+ virt.update("my_vm", boot_dev="cdrom network hd"),
+ )
+ setxml = ET.fromstring(define_mock.call_args[0][0])
+ self.assertEqual(
+ ["cdrom", "network", "hd"],
+ [node.get("dev") for node in setxml.findall("os/boot")],
+ )
- invalid_boot = {
- "loader": "/usr/share/OVMF/OVMF_CODE.fd",
- "initrd": "/root/f8-i386-initrd",
- }
+ # Update unchanged boot devices case
+ define_mock.reset_mock()
+ self.assertEqual(
+ {
+ "definition": False,
+ "disk": {"attached": [], "detached": [], "updated": []},
+ "interface": {"attached": [], "detached": []},
+ },
+ virt.update("my_vm", boot_dev="hd"),
+ )
+ define_mock.assert_not_called()
# Update with boot parameter case
+ define_mock.reset_mock()
self.assertEqual(
{
"definition": True,
@@ -1877,6 +1896,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
"console=ttyS0 ks=http://example.com/f8-i386/os/",
)
+ boot_uefi = {
+ "loader": "/usr/share/OVMF/OVMF_CODE.fd",
+ "nvram": "/usr/share/OVMF/OVMF_VARS.ms.fd",
+ }
+
self.assertEqual(
{
"definition": True,
@@ -1896,9 +1920,28 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
"/usr/share/OVMF/OVMF_VARS.ms.fd",
)
+ self.assertEqual(
+ {
+ "definition": True,
+ "disk": {"attached": [], "detached": [], "updated": []},
+ "interface": {"attached": [], "detached": []},
+ },
+ virt.update("my_vm", boot={"efi": True}),
+ )
+ setxml = ET.fromstring(define_mock.call_args[0][0])
+ self.assertEqual(setxml.find("os").attrib.get("firmware"), "efi")
+
+ invalid_boot = {
+ "loader": "/usr/share/OVMF/OVMF_CODE.fd",
+ "initrd": "/root/f8-i386-initrd",
+ }
+
with self.assertRaises(SaltInvocationError):
virt.update("my_vm", boot=invalid_boot)
+ with self.assertRaises(SaltInvocationError):
+ virt.update("my_vm", boot={"efi": "Not a boolean value"})
+
# Update memory case
setmem_mock = MagicMock(return_value=0)
domain_mock.setMemoryFlags = setmem_mock
@@ -2390,6 +2433,43 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
],
)
+ def test_update_xen_boot_params(self):
+ """
+ Test virt.update() a Xen definition no boot parameter.
+ """
+ root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
+ xml_boot = """
+ <domain type='xen' id='8'>
+ <name>vm</name>
+ <memory unit='KiB'>1048576</memory>
+ <currentMemory unit='KiB'>1048576</currentMemory>
+ <vcpu placement='auto'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='xenfv'>hvm</type>
+ <loader type='rom'>/usr/lib/xen/boot/hvmloader</loader>
+ </os>
+ </domain>
+ """
+ domain_mock_boot = self.set_mock_vm("vm", xml_boot)
+ domain_mock_boot.OSType = MagicMock(return_value="hvm")
+ define_mock_boot = MagicMock(return_value=True)
+ define_mock_boot.setVcpusFlags = MagicMock(return_value=0)
+ self.mock_conn.defineXML = define_mock_boot
+ self.assertEqual(
+ {
+ "cpu": False,
+ "definition": True,
+ "disk": {"attached": [], "detached": [], "updated": []},
+ "interface": {"attached": [], "detached": []},
+ },
+ virt.update("vm", cpu=2),
+ )
+ setxml = ET.fromstring(define_mock_boot.call_args[0][0])
+ self.assertEqual(setxml.find("os").find("loader").attrib.get("type"), "rom")
+ self.assertEqual(
+ setxml.find("os").find("loader").text, "/usr/lib/xen/boot/hvmloader"
+ )
+
def test_update_existing_boot_params(self):
"""
Test virt.update() with existing boot parameters.
@@ -2530,6 +2610,18 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
self.assertEqual(setxml.find("os").find("initrd"), None)
self.assertEqual(setxml.find("os").find("cmdline"), None)
+ self.assertEqual(
+ {
+ "definition": True,
+ "disk": {"attached": [], "detached": [], "updated": []},
+ "interface": {"attached": [], "detached": []},
+ },
+ virt.update("vm_with_boot_param", boot={"efi": False}),
+ )
+ setxml = ET.fromstring(define_mock_boot.call_args[0][0])
+ self.assertEqual(setxml.find("os").find("nvram"), None)
+ self.assertEqual(setxml.find("os").find("loader"), None)
+
self.assertEqual(
{
"definition": True,
@@ -4248,7 +4340,6 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
"""
mock_pool = MagicMock()
mock_pool.delete = MagicMock(return_value=0)
- mock_pool.XMLDesc.return_value = "<pool type='dir'/>"
self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mock_pool)
res = virt.pool_delete("test-pool")
@@ -4262,12 +4353,12 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL
)
- def test_pool_delete_secret(self):
+ def test_pool_undefine_secret(self):
"""
- Test virt.pool_delete function where the pool has a secret
+ Test virt.pool_undefine function where the pool has a secret
"""
mock_pool = MagicMock()
- mock_pool.delete = MagicMock(return_value=0)
+ mock_pool.undefine = MagicMock(return_value=0)
mock_pool.XMLDesc.return_value = """
<pool type='rbd'>
<name>test-ses</name>
@@ -4284,16 +4375,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
mock_undefine = MagicMock(return_value=0)
self.mock_conn.secretLookupByUsage.return_value.undefine = mock_undefine
- res = virt.pool_delete("test-ses")
+ res = virt.pool_undefine("test-ses")
self.assertTrue(res)
self.mock_conn.storagePoolLookupByName.assert_called_once_with("test-ses")
-
- # Shouldn't be called with another parameter so far since those are not implemented
- # and thus throwing exceptions.
- mock_pool.delete.assert_called_once_with(
- self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL
- )
+ mock_pool.undefine.assert_called_once_with()
self.mock_conn.secretLookupByUsage.assert_called_once_with(
self.mock_libvirt.VIR_SECRET_USAGE_TYPE_CEPH, "pool_test-ses"
@@ -4562,24 +4648,6 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
</source>
</pool>"""
- expected_xml = (
- '<pool type="rbd">'
- "<name>default</name>"
- "<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>"
- '<capacity unit="bytes">1999421108224</capacity>'
- '<allocation unit="bytes">713207042048</allocation>'
- '<available unit="bytes">1286214066176</available>'
- "<source>"
- '<host name="ses4.tf.local" />'
- '<host name="ses5.tf.local" />'
- '<auth type="ceph" username="libvirt">'
- '<secret uuid="14e9a0f1-8fbf-4097-b816-5b094c182212" />'
- "</auth>"
- "<name>iscsi-images</name>"
- "</source>"
- "</pool>"
- )
-
mock_secret = MagicMock()
self.mock_conn.secretLookupByUUIDString = MagicMock(return_value=mock_secret)
@@ -4600,6 +4668,23 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
self.mock_conn.storagePoolDefineXML.assert_not_called()
mock_secret.setValue.assert_called_once_with(b"secret")
+ # Case where the secret can't be found
+ self.mock_conn.secretLookupByUUIDString = MagicMock(
+ side_effect=self.mock_libvirt.libvirtError("secret not found")
+ )
+ self.assertFalse(
+ virt.pool_update(
+ "default",
+ "rbd",
+ source_name="iscsi-images",
+ source_hosts=["ses4.tf.local", "ses5.tf.local"],
+ source_auth={"username": "libvirt", "password": "c2VjcmV0"},
+ )
+ )
+ self.mock_conn.storagePoolDefineXML.assert_not_called()
+ self.mock_conn.secretDefineXML.assert_called_once()
+ mock_secret.setValue.assert_called_once_with(b"secret")
+
def test_pool_update_password_create(self):
"""
Test the pool_update function, where the password only is changed
diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py
index 6d38829870..8fe892f607 100644
--- a/tests/unit/states/test_virt.py
+++ b/tests/unit/states/test_virt.py
@@ -333,6 +333,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
"myvm",
cpu=2,
mem=2048,
+ boot_dev="cdrom hd",
os_type="linux",
arch="i686",
vm_type="qemu",
@@ -355,6 +356,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
"myvm",
cpu=2,
mem=2048,
+ boot_dev="cdrom hd",
os_type="linux",
arch="i686",
disk="prod",
@@ -463,10 +465,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
"comment": "Domain myvm updated with live update(s) failures",
}
)
- self.assertDictEqual(virt.defined("myvm", cpu=2), ret)
+ self.assertDictEqual(
+ virt.defined("myvm", cpu=2, boot_dev="cdrom hd"), ret
+ )
update_mock.assert_called_with(
"myvm",
cpu=2,
+ boot_dev="cdrom hd",
mem=None,
disk_profile=None,
disks=None,
@@ -590,6 +595,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password=None,
boot=None,
test=True,
+ boot_dev=None,
)
# No changes case
@@ -624,6 +630,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password=None,
boot=None,
test=True,
+ boot_dev=None,
)
def test_running(self):
@@ -700,6 +707,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
install=True,
pub_key=None,
priv_key=None,
+ boot_dev=None,
connection=None,
username=None,
password=None,
@@ -761,6 +769,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
install=False,
pub_key="/path/to/key.pub",
priv_key="/path/to/key",
+ boot_dev="network hd",
connection="someconnection",
username="libvirtuser",
password="supersecret",
@@ -785,6 +794,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
start=False,
pub_key="/path/to/key.pub",
priv_key="/path/to/key",
+ boot_dev="network hd",
connection="someconnection",
username="libvirtuser",
password="supersecret",
@@ -929,6 +939,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password=None,
boot=None,
test=False,
+ boot_dev=None,
)
# Failed definition update case
@@ -1047,6 +1058,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password=None,
boot=None,
test=True,
+ boot_dev=None,
)
start_mock.assert_not_called()
@@ -1083,6 +1095,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password=None,
boot=None,
test=True,
+ boot_dev=None,
)
def test_stopped(self):
@@ -1970,6 +1983,72 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
password="secret",
)
+ # Define a pool that doesn't handle build
+ for mock in mocks:
+ mocks[mock].reset_mock()
+ with patch.dict(
+ virt.__salt__,
+ { # pylint: disable=no-member
+ "virt.pool_info": MagicMock(
+ side_effect=[
+ {},
+ {"mypool": {"state": "stopped", "autostart": True}},
+ ]
+ ),
+ "virt.pool_define": mocks["define"],
+ "virt.pool_build": mocks["build"],
+ "virt.pool_set_autostart": mocks["autostart"],
+ },
+ ):
+ ret.update(
+ {
+ "changes": {"mypool": "Pool defined, marked for autostart"},
+ "comment": "Pool mypool defined, marked for autostart",
+ }
+ )
+ self.assertDictEqual(
+ virt.pool_defined(
+ "mypool",
+ ptype="rbd",
+ source={
+ "name": "libvirt-pool",
+ "hosts": ["ses2.tf.local", "ses3.tf.local"],
+ "auth": {
+ "username": "libvirt",
+ "password": "AQAz+PRdtquBBRAASMv7nlMZYfxIyLw3St65Xw==",
+ },
+ },
+ autostart=True,
+ ),
+ ret,
+ )
+ mocks["define"].assert_called_with(
+ "mypool",
+ ptype="rbd",
+ target=None,
+ permissions=None,
+ source_devices=None,
+ source_dir=None,
+ source_adapter=None,
+ source_hosts=["ses2.tf.local", "ses3.tf.local"],
+ source_auth={
+ "username": "libvirt",
+ "password": "AQAz+PRdtquBBRAASMv7nlMZYfxIyLw3St65Xw==",
+ },
+ source_name="libvirt-pool",
+ source_format=None,
+ source_initiator=None,
+ start=False,
+ transient=False,
+ connection=None,
+ username=None,
+ password=None,
+ )
+ mocks["autostart"].assert_called_with(
+ "mypool", state="on", connection=None, username=None, password=None,
+ )
+ mocks["build"].assert_not_called()
+
mocks["update"] = MagicMock(return_value=False)
for mock in mocks:
mocks[mock].reset_mock()
@@ -2019,6 +2098,9 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
for mock in mocks:
mocks[mock].reset_mock()
mocks["update"] = MagicMock(return_value=True)
+ mocks["build"] = MagicMock(
+ side_effect=self.mock_libvirt.libvirtError("Existing VG")
+ )
with patch.dict(
virt.__salt__,
{ # pylint: disable=no-member
@@ -2122,6 +2204,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
),
ret,
)
+ mocks["build"].assert_not_called()
mocks["update"].assert_called_with(
"mypool",
ptype="logical",
@@ -2469,8 +2552,8 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
):
ret.update(
{
- "changes": {"mypool": "Pool updated, built, restarted"},
- "comment": "Pool mypool updated, built, restarted",
+ "changes": {"mypool": "Pool updated, restarted"},
+ "comment": "Pool mypool updated, restarted",
"result": True,
}
)
@@ -2496,9 +2579,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
mocks["start"].assert_called_with(
"mypool", connection=None, username=None, password=None
)
- mocks["build"].assert_called_with(
- "mypool", connection=None, username=None, password=None
- )
+ mocks["build"].assert_not_called()
mocks["update"].assert_called_with(
"mypool",
ptype="logical",
diff --git a/tests/unit/utils/test_data.py b/tests/unit/utils/test_data.py
index 9206979284..aff7384232 100644
--- a/tests/unit/utils/test_data.py
+++ b/tests/unit/utils/test_data.py
@@ -220,38 +220,6 @@ class DataTestCase(TestCase):
),
)
- # Traverse and match integer key in a nested dict
- # https://github.com/saltstack/salt/issues/56444
- self.assertEqual(
- "it worked",
- salt.utils.data.traverse_dict_and_list(
- {"foo": {1234: "it worked"}}, "foo:1234", "it didn't work",
- ),
- )
- # Make sure that we properly return the default value when the initial
- # attempt fails and YAML-loading the target key doesn't change its
- # value.
- self.assertEqual(
- "default",
- salt.utils.data.traverse_dict_and_list(
- {"foo": {"baz": "didn't work"}}, "foo:bar", "default",
- ),
- )
-
- def test_issue_39709(self):
- test_two_level_dict_and_list = {
- "foo": ["bar", "baz", {"lorem": {"ipsum": [{"dolor": "sit"}]}}]
- }
-
- self.assertEqual(
- "sit",
- salt.utils.data.traverse_dict_and_list(
- test_two_level_dict_and_list,
- ["foo", "lorem", "ipsum", "dolor"],
- {"not_found": "not_found"},
- ),
- )
-
def test_compare_dicts(self):
ret = salt.utils.data.compare_dicts(old={"foo": "bar"}, new={"foo": "bar"})
self.assertEqual(ret, {})
--
2.29.2