65598582f5
OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=179
1660 lines
58 KiB
Diff
1660 lines
58 KiB
Diff
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
|
|
|
|
|