dcf656e899
OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=181
6516 lines
252 KiB
Diff
6516 lines
252 KiB
Diff
From 0d606b481752d1112321046ce78d3a7f9d2a6604 Mon Sep 17 00:00:00 2001
|
|
From: Cedric Bosdonnat <cbosdonnat@suse.com>
|
|
Date: Tue, 12 Jan 2021 10:48:27 +0100
|
|
Subject: [PATCH] Open suse 3002.2 bigvm (#310)
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
* revert stop_on_reboot commits to help applying upstream patches
|
|
|
|
* libvirt domain template memory config fixes
|
|
|
|
Add unit tests for _gen_xml() on the recently added memory parameters.
|
|
Also fixes an issue with an optional attribute.
|
|
|
|
* virt: support host numa tunning capability
|
|
|
|
* fixup! precommit failure fix
|
|
|
|
* virt: support cpu model and topology
|
|
|
|
* virt: make context preprocessing more reusable in _gen_xml
|
|
|
|
Introduce mapping structures in order to help reusing the common patterns
|
|
in the virt._gen_xml() context pre processing.
|
|
|
|
* xmlutil.change_xml properly handle xpath node number
|
|
|
|
In XPath the node numbers are counted from 1 rather than 0.
|
|
Thus /foo/bar[0] is invalid and should be /foo/bar[1].
|
|
|
|
Since in the change_xml function we are getting the index from python
|
|
lists in these cases, we need to offset these.
|
|
|
|
* virt: support memory_backing
|
|
|
|
* virt: support cpu tunning and Iothread allocation
|
|
|
|
* xmlutil.change_xml: properly handle updated return value for removals
|
|
|
|
When deleting an attribute that doesn't exist in the node we should not
|
|
report a change was made.
|
|
|
|
* virt.update: properly handle nosharepages and locked elements
|
|
|
|
When updating we shouldn't set the value as text in those elements.
|
|
Libvirt seems happy with it, but it forces modifying the VM definition
|
|
even if there was no change.
|
|
|
|
* xmlutil: use a comparison function to update XML
|
|
|
|
When updating an XML file, we may need to have a more intelligent
|
|
comparison of the current and new values. This typically fits for the
|
|
case of numeric values that may have a negligible delta.
|
|
|
|
* virt.update: handle tiny difference in memory values
|
|
|
|
Libvirt may round the memory values when defining or updating a VM. That
|
|
is perfectly fine, but then the value are slightly different from the
|
|
ones passed to the virt.update() function or the virt.running state.
|
|
In those cases the state would be reapplied even though there is no real
|
|
difference with the VM.
|
|
|
|
In order to handle that case the memory parameters in the virt.update
|
|
mapping now have a comparison function that considers the tiny differences
|
|
as equal.
|
|
|
|
This commit also factorizes the creation of the memory entries in the
|
|
virt.update() mapping.
|
|
|
|
* virt.update: factorize the mapping value definition
|
|
|
|
In the mapping passed to xmlutil.change_xml() in virt.update() there are
|
|
a lot of common patterns. Extract these into helper functions. Some of
|
|
them are common enough to even be defined in the xmlutil module.
|
|
|
|
* virt: add kvm-hint-dedicated feature handling
|
|
|
|
* virt: add clock configuration for guests
|
|
|
|
* virt: add qemu guest agent channel
|
|
|
|
For libvirt to be able to communicate with the QEMU Guest Agent if
|
|
installed in the guest, a channel named org.qemu.guest_agent.0 is
|
|
needed.
|
|
|
|
Add this channel by default on all newly created KVM virtual machines.
|
|
|
|
* virt: allow using IO threads on disks
|
|
|
|
* Remove unneeded VM XML definition fragments in tests
|
|
|
|
* virt: canonicalize cpuset before comparing
|
|
|
|
Multiple libvirt cpuset notations can designate the same thing. We need
|
|
to expand those notations into an actual cpu list in order to be able to
|
|
properly compare.
|
|
|
|
For instance if the libvirt definition has '0-5,^4', and we have
|
|
'0,1,2,3,5' passed to virt.update(), those should not trigger an update
|
|
of the définition since they are defining the same thing.
|
|
|
|
* virt: only live update vcpu max if there is a change
|
|
|
|
* Add console and serial to update and running status
|
|
|
|
* virt: cleanup the consoles and serials support
|
|
|
|
* virt: add stop_on_reboot parameter in guest states and definition
|
|
|
|
It can be needed to force a VM to stop instead of rebooting. A typical
|
|
example of this is when creating a VM using a install CDROM ISO or when
|
|
using an autoinstallation profile. Forcing a shutdown allows libvirt to
|
|
pick up another XML definition for the new start to remove the
|
|
firstboot-only options.
|
|
|
|
* virt: expose live parameter in virt.defined state
|
|
|
|
Allow updating the definition of a VM without touching the live
|
|
instance. This can be helpful since live update may change the device
|
|
names in the guest.
|
|
|
|
* Ensure virt.update stop_on_reboot is updated with its default value
|
|
|
|
While all virt.update properties default values should not be used when
|
|
updating the XML definition, the stop_on_reboot default value (False)
|
|
needs to be passed still or the user will never be able to update with
|
|
this value.
|
|
|
|
Co-authored-by: gqlo <guoqing_li@pm.me>
|
|
Co-authored-by: gqlo <escita@pm.me>
|
|
Co-authored-by: marina2209 <mm.antropova@gmail.com>
|
|
---
|
|
changelog/57880.added | 1 +
|
|
changelog/58844.added | 1 +
|
|
salt/modules/virt.py | 1232 ++++++-
|
|
salt/states/virt.py | 341 +-
|
|
salt/templates/virt/libvirt_chardevs.jinja | 16 +
|
|
salt/templates/virt/libvirt_domain.jinja | 268 +-
|
|
salt/utils/xmlutil.py | 79 +-
|
|
tests/pytests/unit/modules/virt/conftest.py | 126 +
|
|
.../pytests/unit/modules/virt/test_domain.py | 335 ++
|
|
tests/pytests/unit/utils/test_xmlutil.py | 41 +
|
|
tests/unit/modules/test_virt.py | 2961 +++++++++++++++--
|
|
tests/unit/states/test_virt.py | 57 +
|
|
12 files changed, 4934 insertions(+), 524 deletions(-)
|
|
create mode 100644 changelog/57880.added
|
|
create mode 100644 changelog/58844.added
|
|
create mode 100644 salt/templates/virt/libvirt_chardevs.jinja
|
|
|
|
diff --git a/changelog/57880.added b/changelog/57880.added
|
|
new file mode 100644
|
|
index 0000000000..6fff4295fa
|
|
--- /dev/null
|
|
+++ b/changelog/57880.added
|
|
@@ -0,0 +1 @@
|
|
+CPU model, topology and NUMA node tuning
|
|
diff --git a/changelog/58844.added b/changelog/58844.added
|
|
new file mode 100644
|
|
index 0000000000..c8599125d2
|
|
--- /dev/null
|
|
+++ b/changelog/58844.added
|
|
@@ -0,0 +1 @@
|
|
+Enhance console and serial support in virt module
|
|
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
|
|
index 786bfa1e58..b852f8175d 100644
|
|
--- a/salt/modules/virt.py
|
|
+++ b/salt/modules/virt.py
|
|
@@ -788,11 +788,11 @@ def _handle_unit(s, def_unit="m"):
|
|
return int(value)
|
|
|
|
|
|
-def nesthash():
|
|
+def nesthash(value=None):
|
|
"""
|
|
create default dict that allows arbitrary level of nesting
|
|
"""
|
|
- return collections.defaultdict(nesthash)
|
|
+ return collections.defaultdict(nesthash, value or {})
|
|
|
|
|
|
def _gen_xml(
|
|
@@ -808,6 +808,11 @@ def _gen_xml(
|
|
graphics=None,
|
|
boot=None,
|
|
boot_dev=None,
|
|
+ numatune=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
**kwargs
|
|
):
|
|
@@ -817,24 +822,36 @@ def _gen_xml(
|
|
context = {
|
|
"hypervisor": hypervisor,
|
|
"name": name,
|
|
- "cpu": str(cpu),
|
|
+ "hypervisor_features": hypervisor_features or {},
|
|
+ "clock": clock or {},
|
|
"on_reboot": "destroy" if stop_on_reboot else "restart",
|
|
}
|
|
|
|
+ context["to_kib"] = lambda v: int(_handle_unit(v) / 1024)
|
|
+ context["yesno"] = lambda v: "yes" if v else "no"
|
|
+
|
|
context["mem"] = nesthash()
|
|
if isinstance(mem, int):
|
|
- mem = int(mem) * 1024 # MB
|
|
- context["mem"]["boot"] = str(mem)
|
|
- context["mem"]["current"] = str(mem)
|
|
+ context["mem"]["boot"] = mem
|
|
+ context["mem"]["current"] = mem
|
|
elif isinstance(mem, dict):
|
|
- for tag, val in mem.items():
|
|
- if val:
|
|
- if tag == "slots":
|
|
- context["mem"]["slots"] = "{}='{}'".format(tag, val)
|
|
- else:
|
|
- context["mem"][tag] = str(int(_handle_unit(val) / 1024))
|
|
+ context["mem"] = nesthash(mem)
|
|
+
|
|
+ context["cpu"] = nesthash()
|
|
+ context["cputune"] = nesthash()
|
|
+ if isinstance(cpu, int):
|
|
+ context["cpu"]["maximum"] = str(cpu)
|
|
+ elif isinstance(cpu, dict):
|
|
+ context["cpu"] = nesthash(cpu)
|
|
+
|
|
+ if clock:
|
|
+ offset = "utc" if clock.get("utc", True) else "localtime"
|
|
+ if "timezone" in clock:
|
|
+ offset = "timezone"
|
|
+ context["clock"]["offset"] = offset
|
|
|
|
if hypervisor in ["qemu", "kvm"]:
|
|
+ context["numatune"] = numatune if numatune else {}
|
|
context["controller_model"] = False
|
|
elif hypervisor == "vmware":
|
|
# TODO: make bus and model parameterized, this works for 64-bit Linux
|
|
@@ -873,18 +890,57 @@ def _gen_xml(
|
|
context["boot"]["kernel"] = "/usr/lib/grub2/x86_64-xen/grub.xen"
|
|
context["boot_dev"] = []
|
|
|
|
- if "serial_type" in kwargs:
|
|
- context["serial_type"] = kwargs["serial_type"]
|
|
- if "serial_type" in context and context["serial_type"] == "tcp":
|
|
- if "telnet_port" in kwargs:
|
|
- context["telnet_port"] = kwargs["telnet_port"]
|
|
- else:
|
|
- context["telnet_port"] = 23023 # FIXME: use random unused port
|
|
- if "serial_type" in context:
|
|
- if "console" in kwargs:
|
|
- context["console"] = kwargs["console"]
|
|
- else:
|
|
- context["console"] = True
|
|
+ default_port = 23023
|
|
+ default_chardev_type = "tcp"
|
|
+
|
|
+ chardev_types = ["serial", "console"]
|
|
+ for chardev_type in chardev_types:
|
|
+ context[chardev_type + "s"] = []
|
|
+ parameter_value = locals()[chardev_type + "s"]
|
|
+ if parameter_value is not None:
|
|
+ for chardev in parameter_value:
|
|
+ chardev_context = chardev
|
|
+ chardev_context["type"] = chardev.get("type", default_chardev_type)
|
|
+
|
|
+ if chardev_context["type"] == "tcp":
|
|
+ chardev_context["port"] = chardev.get("port", default_port)
|
|
+ chardev_context["protocol"] = chardev.get("protocol", "telnet")
|
|
+ context[chardev_type + "s"].append(chardev_context)
|
|
+
|
|
+ # processing of deprecated parameters
|
|
+ old_port = kwargs.get("telnet_port")
|
|
+ if old_port:
|
|
+ salt.utils.versions.warn_until(
|
|
+ "Phosphorus",
|
|
+ "'telnet_port' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
|
|
+ "'telnet_port' parameter has been deprecated, use the 'serials' parameter with a value "
|
|
+ "like ``{{{{'type': 'tcp', 'protocol': 'telnet', 'port': {}}}}}`` instead and a similar `consoles` parameter. "
|
|
+ "It will be removed in {{version}}.".format(old_port),
|
|
+ )
|
|
+
|
|
+ old_serial_type = kwargs.get("serial_type")
|
|
+ if old_serial_type:
|
|
+ salt.utils.versions.warn_until(
|
|
+ "Phosphorus",
|
|
+ "'serial_type' parameter has been deprecated, use the 'serials' parameter with a value "
|
|
+ "like ``{{{{'type': '{}', 'protocol': 'telnet' }}}}`` instead and a similar `consoles` parameter. "
|
|
+ "It will be removed in {{version}}.".format(old_serial_type),
|
|
+ )
|
|
+ serial_context = {"type": old_serial_type}
|
|
+ if serial_context["type"] == "tcp":
|
|
+ serial_context["port"] = old_port or default_port
|
|
+ serial_context["protocol"] = "telnet"
|
|
+ context["serials"].append(serial_context)
|
|
+
|
|
+ old_console = kwargs.get("console")
|
|
+ if old_console:
|
|
+ salt.utils.versions.warn_until(
|
|
+ "Phosphorus",
|
|
+ "'console' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
|
|
+ "It will be removed in {version}.",
|
|
+ )
|
|
+ if old_console is True:
|
|
+ context["consoles"].append(serial_context)
|
|
|
|
context["disks"] = []
|
|
disk_bus_map = {"virtio": "vd", "xen": "xvd", "fdc": "fd", "ide": "hd"}
|
|
@@ -897,6 +953,7 @@ def _gen_xml(
|
|
"disk_bus": disk["model"],
|
|
"format": disk.get("format", "raw"),
|
|
"index": str(i),
|
|
+ "io": "threads" if disk.get("iothreads", False) else "native",
|
|
}
|
|
targets.append(disk_context["target_dev"])
|
|
if disk.get("source_file"):
|
|
@@ -946,7 +1003,6 @@ def _gen_xml(
|
|
|
|
context["os_type"] = os_type
|
|
context["arch"] = arch
|
|
-
|
|
fn_ = "libvirt_domain.jinja"
|
|
try:
|
|
template = JINJA.get_template(fn_)
|
|
@@ -1751,6 +1807,11 @@ def init(
|
|
arch=None,
|
|
boot=None,
|
|
boot_dev=None,
|
|
+ numatune=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
**kwargs
|
|
):
|
|
@@ -1758,13 +1819,126 @@ def init(
|
|
Initialize a new vm
|
|
|
|
:param name: name of the virtual machine to create
|
|
- :param cpu: Number of virtual CPUs to assign to the virtual machine
|
|
- :param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, a dictionary can be used to
|
|
+ :param cpu:
|
|
+ Number of virtual CPUs to assign to the virtual machine or a dictionary with detailed information to configure
|
|
+ cpu model and topology, numa node tuning, cpu tuning and iothreads allocation. The structure of the dictionary is
|
|
+ documented in :ref:`init-cpu-def`.
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ cpu:
|
|
+ placement: static
|
|
+ cpuset: 0-11
|
|
+ current: 5
|
|
+ maximum: 12
|
|
+ vcpus:
|
|
+ 0:
|
|
+ enabled: True
|
|
+ hotpluggable: False
|
|
+ order: 1
|
|
+ 1:
|
|
+ enabled: False
|
|
+ hotpluggable: True
|
|
+ match: minimum
|
|
+ mode: custom
|
|
+ check: full
|
|
+ vendor: Intel
|
|
+ model:
|
|
+ name: core2duo
|
|
+ fallback: allow
|
|
+ vendor_id: GenuineIntel
|
|
+ topology:
|
|
+ sockets: 1
|
|
+ cores: 12
|
|
+ threads: 1
|
|
+ cache:
|
|
+ level: 3
|
|
+ mode: emulate
|
|
+ features:
|
|
+ lahf: optional
|
|
+ pcid: require
|
|
+ numa:
|
|
+ 0:
|
|
+ cpus: 0-3
|
|
+ memory: 1g
|
|
+ discard: True
|
|
+ distances:
|
|
+ 0: 10 # sibling id : value
|
|
+ 1: 21
|
|
+ 2: 31
|
|
+ 3: 41
|
|
+ 1:
|
|
+ cpus: 4-6
|
|
+ memory: 1g
|
|
+ memAccess: shared
|
|
+ distances:
|
|
+ 0: 21
|
|
+ 1: 10
|
|
+ 2: 21
|
|
+ 3: 31
|
|
+ tuning:
|
|
+ vcpupin:
|
|
+ 0: 1-4,^2 # vcpuid : cpuset
|
|
+ 1: 0,1
|
|
+ 2: 2,3
|
|
+ 3: 0,4
|
|
+ emulatorpin: 1-3
|
|
+ iothreadpin:
|
|
+ 1: 5,6 # iothread id: cpuset
|
|
+ 2: 7,8
|
|
+ shares: 2048
|
|
+ period: 1000000
|
|
+ quota: -1
|
|
+ global_period: 1000000
|
|
+ global_quota: -1
|
|
+ emulator_period: 1000000
|
|
+ emulator_quota: -1
|
|
+ iothread_period: 1000000
|
|
+ iothread_quota: -1
|
|
+ vcpusched:
|
|
+ - scheduler: fifo
|
|
+ priority: 1
|
|
+ vcpus: 0,3-5
|
|
+ - scheduler: rr
|
|
+ priority: 3
|
|
+ iothreadsched:
|
|
+ - scheduler: idle
|
|
+ - scheduler: batch
|
|
+ iothreads: 2,3
|
|
+ emulatorsched:
|
|
+ - scheduler: batch
|
|
+ cachetune:
|
|
+ 0-3: # vcpus set
|
|
+ 0: # cache id
|
|
+ level: 3
|
|
+ type: both
|
|
+ size: 4
|
|
+ 1:
|
|
+ level: 3
|
|
+ type: both
|
|
+ size: 6
|
|
+ monitor:
|
|
+ 1: 3
|
|
+ 0-3: 3
|
|
+ 4-5:
|
|
+ monitor:
|
|
+ 4: 3 # vcpus: level
|
|
+ 5: 3
|
|
+ memorytune:
|
|
+ 0-3: # vcpus set
|
|
+ 0: 60 # node id: bandwidth
|
|
+ 4-5:
|
|
+ 0: 60
|
|
+ iothreads: 4
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ :param mem: Amount of memory to allocate to the virtual machine in MiB. Since 3002, a dictionary can be used to
|
|
contain detailed configuration which support memory allocation or tuning. Supported parameters are ``boot``,
|
|
- ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit`` and ``min_guarantee``. The
|
|
- structure of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported.
|
|
- Detail unit specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be
|
|
- an integer.
|
|
+ ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit``, ``min_guarantee``,
|
|
+ ``hugepages`` , ``nosharepages``, ``locked``, ``source``, ``access``, ``allocation`` and ``discard``. The structure
|
|
+ of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported. Detail unit
|
|
+ specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be an integer.
|
|
|
|
.. code-block:: python
|
|
|
|
@@ -1773,10 +1947,17 @@ def init(
|
|
'current': 1g,
|
|
'max': 1g,
|
|
'slots': 10,
|
|
- 'hard_limit': '1024'
|
|
- 'soft_limit': '512m'
|
|
- 'swap_hard_limit': '1g'
|
|
- 'min_guarantee': '512mib'
|
|
+ 'hard_limit': '1024',
|
|
+ 'soft_limit': '512m',
|
|
+ 'swap_hard_limit': '1g',
|
|
+ 'min_guarantee': '512mib',
|
|
+ 'hugepages': [{'nodeset': '0-3,^2', 'size': '1g'}, {'nodeset': '2', 'size': '2m'}],
|
|
+ 'nosharepages': True,
|
|
+ 'locked': True,
|
|
+ 'source': 'file',
|
|
+ 'access': 'shared',
|
|
+ 'allocation': 'immediate',
|
|
+ 'discard': True
|
|
}
|
|
|
|
.. versionchanged:: Magnesium
|
|
@@ -1872,6 +2053,232 @@ def init(
|
|
|
|
By default, the value will ``"hd"``.
|
|
|
|
+ :param numatune:
|
|
+ The optional numatune element provides details of how to tune the performance of a NUMA host via controlling NUMA
|
|
+ policy for domain process. The optional ``memory`` element specifies how to allocate memory for the domain process
|
|
+ on a NUMA host. ``memnode`` elements can specify memory allocation policies per each guest NUMA node. The definition
|
|
+ used in the dictionary can be found at :ref:`init-cpu-def`.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: python
|
|
+
|
|
+ {
|
|
+ 'memory': {'mode': 'strict', 'nodeset': '0-11'},
|
|
+ 'memnodes': {0: {'mode': 'strict', 'nodeset': 1}, 1: {'mode': 'preferred', 'nodeset': 2}}
|
|
+ }
|
|
+
|
|
+ :param hypervisor_features:
|
|
+ Enable or disable hypervisor-specific features on the virtual machine.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ hypervisor_features:
|
|
+ kvm-hint-dedicated: True
|
|
+
|
|
+ :param clock:
|
|
+ Configure the guest clock.
|
|
+ The value is a dictionary with the following keys:
|
|
+
|
|
+ adjustment
|
|
+ time adjustment in seconds or ``reset``
|
|
+
|
|
+ utc
|
|
+ set to ``False`` to use the host local time as the guest clock. Defaults to ``True``.
|
|
+
|
|
+ timezone
|
|
+ synchronize the guest to the correspding timezone
|
|
+
|
|
+ timers
|
|
+ a dictionary associating the timer name with its configuration.
|
|
+ This configuration is a dictionary with the properties ``track``, ``tickpolicy``,
|
|
+ ``catchup``, ``frequency``, ``mode``, ``present``, ``slew``, ``threshold`` and ``limit``.
|
|
+ See `libvirt time keeping documentation <https://libvirt.org/formatdomain.html#time-keeping>`_ for the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ Set the clock to local time using an offset in seconds
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ adjustment: 3600
|
|
+ utc: False
|
|
+
|
|
+ Set the clock to a specific time zone:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timezone: CEST
|
|
+
|
|
+ Tweak guest timers:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timers:
|
|
+ tsc:
|
|
+ frequency: 3504000000
|
|
+ mode: native
|
|
+ rtc:
|
|
+ track: wall
|
|
+ tickpolicy: catchup
|
|
+ slew: 4636
|
|
+ threshold: 123
|
|
+ limit: 2342
|
|
+ hpet:
|
|
+ present: False
|
|
+
|
|
+ :param serials:
|
|
+ Dictionary providing details on the serials connection to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ :param consoles:
|
|
+ Dictionary providing details on the consoles device to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. _init-cpu-def:
|
|
+
|
|
+ .. rubric:: cpu parameters definition
|
|
+
|
|
+ The cpu parameters dictionary can contain the following properties:
|
|
+
|
|
+ cpuset
|
|
+ a comma-separated list of physical CPU numbers that domain process and virtual CPUs can be pinned to by default.
|
|
+ eg. ``1-4,^3`` cpuset 3 is excluded.
|
|
+
|
|
+ current
|
|
+ the number of virtual cpus available at startup
|
|
+
|
|
+ placement
|
|
+ indicate the CPU placement mode for domain process. the value can be either ``static`` or ``auto``
|
|
+
|
|
+ vcpus
|
|
+ specify the state of individual vcpu. Possible attribute for each individual vcpu include: ``id``, ``enabled``,
|
|
+ ``hotpluggable`` and ``order``. Valid ``ids`` are from 0 to the maximum vCPU count minus 1. ``enabled`` takes
|
|
+ boolean values which controls the state of the vcpu. ``hotpluggable`` take boolean value which controls whether
|
|
+ given vCPU can be hotplugged and hotunplugged. ``order`` takes an integer value which specifies the order to add
|
|
+ the online vCPUs.
|
|
+
|
|
+ match
|
|
+ The cpu attribute ``match`` attribute specifies how strictly the virtual CPU provided to the guest matches the CPU
|
|
+ requirements, possible values are ``minimum``, ``exact`` or ``strict``.
|
|
+
|
|
+ check
|
|
+ Optional cpu attribute ``check`` attribute can be used to request a specific way of checking whether the virtual
|
|
+ CPU matches the specification, possible values are ``none``, ``partial`` and ``full``.
|
|
+
|
|
+ mode
|
|
+ Optional cpu attribute ``mode`` attribute may be used to make it easier to configure a guest CPU to be as close
|
|
+ to host CPU as possible, possible values are ``custom``, ``host-model`` and ``host-passthrough``.
|
|
+
|
|
+ model
|
|
+ specifies CPU model requested by the guest. An optional ``fallback`` attribute can be used to forbid libvirt falls
|
|
+ back to the closest model supported by the hypervisor, possible values are ``allow`` or ``forbid``. ``vendor_id``
|
|
+ attribute can be used to set the vendor id seen by the guest, the length must be exactly 12 characters long.
|
|
+
|
|
+ vendor
|
|
+ specifies CPU vendor requested by the guest.
|
|
+
|
|
+ topology
|
|
+ specifies requested topology of virtual CPU provided to the guest. Four possible attributes , ``sockets``, ``dies``,
|
|
+ ``cores``, and ``threads``, accept non-zero positive integer values. They refer to the number of CPU sockets per
|
|
+ NUMA node, number of dies per socket, number of cores per die, and number of threads per core, respectively.
|
|
+
|
|
+ features
|
|
+ A dictionary conains a set of cpu features to fine-tune features provided by the selected CPU model. Use cpu
|
|
+ feature ``name`` as the key and the ``policy`` as the value. ``policy`` Attribute takes ``force``, ``require``,
|
|
+ ``optional``, ``disable`` or ``forbid``.
|
|
+
|
|
+ cache
|
|
+ describes the virtual CPU cache. Optional attribute ``level`` takes an integer value which describes cache level
|
|
+ ``mode`` attribute supported three possible values: ``emulate``, ``passthrough``, ``disable``
|
|
+
|
|
+ numa
|
|
+ specify the guest numa topology. ``cell`` element specifies a NUMA cell or a NUMA node, ``cpus`` specifies the
|
|
+ CPU or range of CPUs that are part of the node, ``memory`` specifies the size of the node memory. All cells
|
|
+ should have ``id`` attribute in case referring to some cell is necessary in the code. optional attribute
|
|
+ ``memAccess`` control whether the memory is to be mapped as ``shared`` or ``private``, ``discard`` attribute which
|
|
+ fine tunes the discard feature for given numa node, possible values are ``True`` or ``False``. ``distances``
|
|
+ element define the distance between NUMA cells and ``sibling`` sub-element is used to specify the distance value
|
|
+ between sibling NUMA cells.
|
|
+
|
|
+ vcpupin
|
|
+ The optional vcpupin element specifies which of host's physical CPUs the domain vCPU will be pinned to.
|
|
+
|
|
+ emulatorpin
|
|
+ The optional emulatorpin element specifies which of host physical CPUs the "emulator", a subset of a domain not
|
|
+ including vCPU or iothreads will be pinned to.
|
|
+
|
|
+ iothreadpin
|
|
+ The optional iothreadpin element specifies which of host physical CPUs the IOThreads will be pinned to.
|
|
+
|
|
+ shares
|
|
+ The optional shares element specifies the proportional weighted share for the domain.
|
|
+
|
|
+ period
|
|
+ The optional period element specifies the enforcement interval (unit: microseconds).
|
|
+
|
|
+ quota
|
|
+ The optional quota element specifies the maximum allowed bandwidth (unit: microseconds).
|
|
+
|
|
+ global_period
|
|
+ The optional global_period element specifies the enforcement CFS scheduler interval (unit: microseconds) for the
|
|
+ whole domain in contrast with period which enforces the interval per vCPU.
|
|
+
|
|
+ global_quota
|
|
+ The optional global_quota element specifies the maximum allowed bandwidth (unit: microseconds) within a period
|
|
+ for the whole domain.
|
|
+
|
|
+ emulator_period
|
|
+ The optional emulator_period element specifies the enforcement interval (unit: microseconds).
|
|
+
|
|
+ emulator_quota
|
|
+ The optional emulator_quota element specifies the maximum allowed bandwidth (unit: microseconds) for domain's
|
|
+ emulator threads (those excluding vCPUs).
|
|
+
|
|
+ iothread_period
|
|
+ The optional iothread_period element specifies the enforcement interval (unit: microseconds) for IOThreads.
|
|
+
|
|
+ iothread_quota
|
|
+ The optional iothread_quota element specifies the maximum allowed bandwidth (unit: microseconds) for IOThreads.
|
|
+
|
|
+ vcpusched
|
|
+ specify the scheduler type for vCPUs.
|
|
+ The value is a list of dictionaries with the ``scheduler`` key (values ``batch``, ``idle``, ``fifo``, ``rr``)
|
|
+ and the optional ``priority`` and ``vcpus`` keys. The ``priority`` value usually is a positive integer and the
|
|
+ ``vcpus`` value is a cpu set like ``1-4,^3,6`` or simply the vcpu id.
|
|
+
|
|
+ iothreadsched
|
|
+ specify the scheduler type for IO threads.
|
|
+ The value is a list of dictionaries with the ``scheduler`` key (values ``batch``, ``idle``, ``fifo``, ``rr``)
|
|
+ and the optional ``priority`` and ``vcpus`` keys. The ``priority`` value usually is a positive integer and the
|
|
+ ``vcpus`` value is a cpu set like ``1-4,^3,6`` or simply the vcpu id.
|
|
+
|
|
+ emulatorsched
|
|
+ specify the scheduler type (values batch, idle, fifo, rr) for particular the emulator.
|
|
+ The value is a dictionary with the ``scheduler`` key (values ``batch``, ``idle``, ``fifo``, ``rr``)
|
|
+ and the optional ``priority`` and ``vcpus`` keys. The ``priority`` value usually is a positive integer.
|
|
+
|
|
+ cachetune
|
|
+ Optional cachetune element can control allocations for CPU caches using the resctrl on the host.
|
|
+
|
|
+ monitor
|
|
+ The optional element monitor creates the cache monitor(s) for current cache allocation.
|
|
+
|
|
+ memorytune
|
|
+ Optional memorytune element can control allocations for memory bandwidth using the resctrl on the host.
|
|
+
|
|
+ iothreads
|
|
+ Number of threads for supported disk devices to perform I/O requests. iothread id will be numbered from 1 to
|
|
+ the provided number (Default: None).
|
|
+
|
|
.. _init-boot-def:
|
|
|
|
.. rubric:: Boot parameters definition
|
|
@@ -1932,6 +2339,33 @@ def init(
|
|
min_guarantee
|
|
the guaranteed minimum memory allocation for the guest
|
|
|
|
+ hugepages
|
|
+ memory allocated using ``hugepages`` instead of the normal native page size. It takes a list of
|
|
+ dictionaries with ``nodeset`` and ``size`` keys.
|
|
+ For example ``"hugepages": [{"nodeset": "1-4,^3", "size": "2m"}, {"nodeset": "3", "size": "1g"}]``.
|
|
+
|
|
+ nosharepages
|
|
+ boolean value to instruct hypervisor to disable shared pages (memory merge, KSM) for this domain
|
|
+
|
|
+ locked
|
|
+ boolean value that allows memory pages belonging to the domain will be locked in host's memory and the host will
|
|
+ not be allowed to swap them out, which might be required for some workloads such as real-time.
|
|
+
|
|
+ source
|
|
+ possible values are ``file`` which utilizes file memorybacking, ``anonymous`` by default and ``memfd`` backing.
|
|
+ (QEMU/KVM only)
|
|
+
|
|
+ access
|
|
+ specify if the memory is to be ``shared`` or ``private``. This can be overridden per numa node by memAccess.
|
|
+
|
|
+ allocation
|
|
+ specify when to allocate the memory by supplying either ``immediate`` or ``ondemand``.
|
|
+
|
|
+ discard
|
|
+ boolean value to ensure the memory content is discarded just before guest shuts down (or when DIMM module is
|
|
+ unplugged). Please note that this is just an optimization and is not guaranteed to work in all cases
|
|
+ (e.g. when hypervisor crashes). (QEMU/KVM only)
|
|
+
|
|
.. _init-nic-def:
|
|
|
|
.. rubric:: Network Interfaces Definitions
|
|
@@ -2051,6 +2485,10 @@ def init(
|
|
hostname_property: virt:hostname
|
|
sparse_volume: True
|
|
|
|
+ iothreads
|
|
+ When ``True`` dedicated threads will be used for the I/O of the disk.
|
|
+ (Default: ``False``)
|
|
+
|
|
.. _init-graphics-def:
|
|
|
|
.. rubric:: Graphics Definition
|
|
@@ -2077,6 +2515,42 @@ def init(
|
|
By default, not setting the ``listen`` part of the dictionary will default to
|
|
listen on all addresses.
|
|
|
|
+ .. _init-chardevs-def:
|
|
+
|
|
+ .. rubric:: Serials and Consoles Definitions
|
|
+
|
|
+ Serial dictionaries can contain the following properties:
|
|
+
|
|
+ type
|
|
+ Type of the serial connection, like ``'tcp'``, ``'pty'``, ``'file'``, ``'udp'``, ``'dev'``,
|
|
+ ``'pipe'``, ``'unix'``.
|
|
+
|
|
+ path
|
|
+ Path to the source device. Can be a log file, a host character device to pass through,
|
|
+ a unix socket, a named pipe path.
|
|
+
|
|
+ host
|
|
+ The serial UDP or TCP host name.
|
|
+ (Default: 23023)
|
|
+
|
|
+ port
|
|
+ The serial UDP or TCP port number.
|
|
+ (Default: 23023)
|
|
+
|
|
+ protocol
|
|
+ Name of the TCP connection protocol.
|
|
+ (Default: telnet)
|
|
+
|
|
+ tls
|
|
+ Boolean value indicating whether to use hypervisor TLS certificates environment for TCP devices.
|
|
+
|
|
+ target_port
|
|
+ The guest device port number starting from 0
|
|
+
|
|
+ target_type
|
|
+ The guest device type. Common values are ``serial``, ``virtio`` or ``usb-serial``, but more are documented in
|
|
+ `the libvirt documentation <https://libvirt.org/formatdomain.html#consoles-serial-parallel-channel-devices>`_.
|
|
+
|
|
.. rubric:: CLI Example
|
|
|
|
.. code-block:: bash
|
|
@@ -2226,6 +2700,11 @@ def init(
|
|
graphics,
|
|
boot,
|
|
boot_dev,
|
|
+ numatune,
|
|
+ hypervisor_features,
|
|
+ clock,
|
|
+ serials,
|
|
+ consoles,
|
|
stop_on_reboot,
|
|
**kwargs
|
|
)
|
|
@@ -2249,19 +2728,15 @@ def _disks_equal(disk1, disk2):
|
|
"""
|
|
target1 = disk1.find("target")
|
|
target2 = disk2.find("target")
|
|
- source1 = (
|
|
- disk1.find("source")
|
|
- if disk1.find("source") is not None
|
|
- else ElementTree.Element("source")
|
|
- )
|
|
- source2 = (
|
|
- disk2.find("source")
|
|
- if disk2.find("source") is not None
|
|
- else ElementTree.Element("source")
|
|
- )
|
|
|
|
- source1_dict = xmlutil.to_dict(source1, True)
|
|
- source2_dict = xmlutil.to_dict(source2, True)
|
|
+ disk1_dict = xmlutil.to_dict(disk1, True)
|
|
+ disk2_dict = xmlutil.to_dict(disk2, True)
|
|
+
|
|
+ source1_dict = disk1_dict.get("source", {})
|
|
+ source2_dict = disk2_dict.get("source", {})
|
|
+
|
|
+ io1 = disk1_dict.get("driver", {}).get("io", "native")
|
|
+ io2 = disk2_dict.get("driver", {}).get("io", "native")
|
|
|
|
# Remove the index added by libvirt in the source for backing chain
|
|
if source1_dict:
|
|
@@ -2276,6 +2751,7 @@ def _disks_equal(disk1, disk2):
|
|
and target1.get("bus") == target2.get("bus")
|
|
and disk1.get("device", "disk") == disk2.get("device", "disk")
|
|
and target1.get("dev") == target2.get("dev")
|
|
+ and io1 == io2
|
|
)
|
|
|
|
|
|
@@ -2443,6 +2919,101 @@ def _diff_graphics_lists(old, new):
|
|
return _diff_lists(old, new, _graphics_equal)
|
|
|
|
|
|
+def _expand_cpuset(cpuset):
|
|
+ """
|
|
+ Expand the libvirt cpuset and nodeset values into a list of cpu/node IDs
|
|
+ """
|
|
+ if cpuset is None:
|
|
+ return None
|
|
+
|
|
+ if isinstance(cpuset, int):
|
|
+ return str(cpuset)
|
|
+
|
|
+ result = set()
|
|
+ toremove = set()
|
|
+ for part in cpuset.split(","):
|
|
+ m = re.match("([0-9]+)-([0-9]+)", part)
|
|
+ if m:
|
|
+ result |= set(range(int(m.group(1)), int(m.group(2)) + 1))
|
|
+ elif part.startswith("^"):
|
|
+ toremove.add(int(part[1:]))
|
|
+ else:
|
|
+ result.add(int(part))
|
|
+ cpus = list(result - toremove)
|
|
+ cpus.sort()
|
|
+ cpus = [str(cpu) for cpu in cpus]
|
|
+ return ",".join(cpus)
|
|
+
|
|
+
|
|
+def _normalize_cpusets(desc, data):
|
|
+ """
|
|
+ Expand the cpusets that can't be expanded by the change_xml() function,
|
|
+ namely the ones that are used as keys and in the middle of the XPath expressions.
|
|
+ """
|
|
+ # Normalize the cpusets keys in the XML
|
|
+ xpaths = ["cputune/cachetune", "cputune/cachetune/monitor", "cputune/memorytune"]
|
|
+ for xpath in xpaths:
|
|
+ nodes = desc.findall(xpath)
|
|
+ for node in nodes:
|
|
+ node.set("vcpus", _expand_cpuset(node.get("vcpus")))
|
|
+
|
|
+ # data paths to change:
|
|
+ # - cpu:tuning:cachetune:{id}:monitor:{sid}
|
|
+ # - cpu:tuning:memorytune:{id}
|
|
+ if not isinstance(data.get("cpu"), dict):
|
|
+ return
|
|
+ tuning = data["cpu"].get("tuning", {})
|
|
+ for child in ["cachetune", "memorytune"]:
|
|
+ if tuning.get(child):
|
|
+ new_item = dict()
|
|
+ for cpuset, value in tuning[child].items():
|
|
+ if child == "cachetune" and value.get("monitor"):
|
|
+ value["monitor"] = {
|
|
+ _expand_cpuset(monitor_cpus): monitor
|
|
+ for monitor_cpus, monitor in value["monitor"].items()
|
|
+ }
|
|
+ new_item[_expand_cpuset(cpuset)] = value
|
|
+ tuning[child] = new_item
|
|
+
|
|
+
|
|
+def _serial_or_concole_equal(old, new):
|
|
+ def _filter_serial_or_concole(item):
|
|
+ """
|
|
+ Filter out elements to ignore when comparing items
|
|
+ """
|
|
+ return {
|
|
+ "type": item.attrib["type"],
|
|
+ "port": item.find("source").attrib["service"]
|
|
+ if item.find("source") is not None
|
|
+ else None,
|
|
+ "protocol": item.find("protocol").attrib["type"]
|
|
+ if item.find("protocol") is not None
|
|
+ else None,
|
|
+ }
|
|
+
|
|
+ return _filter_serial_or_concole(old) == _filter_serial_or_concole(new)
|
|
+
|
|
+
|
|
+def _diff_serial_list(old, new):
|
|
+ """
|
|
+ Compare serial definitions to extract the changes
|
|
+
|
|
+ :param old: list of ElementTree nodes representing the old serials
|
|
+ :param new: list of ElementTree nodes representing the new serials
|
|
+ """
|
|
+ return _diff_lists(old, new, _serial_or_concole_equal)
|
|
+
|
|
+
|
|
+def _diff_console_list(old, new):
|
|
+ """
|
|
+ Compare console definitions to extract the changes
|
|
+
|
|
+ :param old: list of ElementTree nodes representing the old consoles
|
|
+ :param new: list of ElementTree nodes representing the new consoles
|
|
+ """
|
|
+ return _diff_lists(old, new, _serial_or_concole_equal)
|
|
+
|
|
+
|
|
def update(
|
|
name,
|
|
cpu=0,
|
|
@@ -2454,8 +3025,13 @@ def update(
|
|
graphics=None,
|
|
live=True,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=False,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
**kwargs
|
|
):
|
|
@@ -2463,13 +3039,20 @@ def update(
|
|
Update the definition of an existing domain.
|
|
|
|
:param name: Name of the domain to update
|
|
- :param cpu: Number of virtual CPUs to assign to the virtual machine
|
|
- :param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, a dictionary can be used to
|
|
+ :param cpu:
|
|
+ Number of virtual CPUs to assign to the virtual machine or a dictionary with detailed information to configure
|
|
+ cpu model and topology, numa node tuning, cpu tuning and iothreads allocation. The structure of the dictionary is
|
|
+ documented in :ref:`init-cpu-def`.
|
|
+
|
|
+ To update any cpu parameters specify the new values to the corresponding tag. To remove any element or attribute,
|
|
+ specify ``None`` object. Please note that ``None`` object is mapped to ``null`` in yaml, use ``null`` in sls file
|
|
+ instead.
|
|
+ :param mem: Amount of memory to allocate to the virtual machine in MiB. Since 3002, a dictionary can be used to
|
|
contain detailed configuration which support memory allocation or tuning. Supported parameters are ``boot``,
|
|
- ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit`` and ``min_guarantee``. The
|
|
- structure of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported.
|
|
- Detail unit specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be
|
|
- an integer.
|
|
+ ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit``, ``min_guarantee``,
|
|
+ ``hugepages`` , ``nosharepages``, ``locked``, ``source``, ``access``, ``allocation`` and ``discard``. The structure
|
|
+ of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported. Detail unit
|
|
+ specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be an integer.
|
|
|
|
To remove any parameters, pass a None object, for instance: 'soft_limit': ``None``. Please note that ``None``
|
|
is mapped to ``null`` in sls file, pass ``null`` in sls file instead.
|
|
@@ -2538,6 +3121,30 @@ def update(
|
|
|
|
.. versionadded:: Magnesium
|
|
|
|
+ :param numatune:
|
|
+ The optional numatune element provides details of how to tune the performance of a NUMA host via controlling NUMA
|
|
+ policy for domain process. The optional ``memory`` element specifies how to allocate memory for the domain process
|
|
+ on a NUMA host. ``memnode`` elements can specify memory allocation policies per each guest NUMA node. The definition
|
|
+ used in the dictionary can be found at :ref:`init-cpu-def`.
|
|
+
|
|
+ To update any numatune parameters, specify the new value. To remove any ``numatune`` parameters, pass a None object,
|
|
+ for instance: 'numatune': ``None``. Please note that ``None`` is mapped to ``null`` in sls file, pass ``null`` in
|
|
+ sls file instead.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ :param serials:
|
|
+ Dictionary providing details on the serials connection to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ :param consoles:
|
|
+ Dictionary providing details on the consoles device to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
:param stop_on_reboot:
|
|
If set to ``True`` the guest will stop instead of rebooting.
|
|
This is specially useful when creating a virtual machine with an installation cdrom or
|
|
@@ -2550,6 +3157,69 @@ def update(
|
|
|
|
.. versionadded:: sodium
|
|
|
|
+ :param hypervisor_features:
|
|
+ Enable or disable hypervisor-specific features on the virtual machine.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ hypervisor_features:
|
|
+ kvm-hint-dedicated: True
|
|
+
|
|
+ :param clock:
|
|
+ Configure the guest clock.
|
|
+ The value is a dictionary with the following keys:
|
|
+
|
|
+ adjustment
|
|
+ time adjustment in seconds or ``reset``
|
|
+
|
|
+ utc
|
|
+ set to ``False`` to use the host local time as the guest clock. Defaults to ``True``.
|
|
+
|
|
+ timezone
|
|
+ synchronize the guest to the correspding timezone
|
|
+
|
|
+ timers
|
|
+ a dictionary associating the timer name with its configuration.
|
|
+ This configuration is a dictionary with the properties ``track``, ``tickpolicy``,
|
|
+ ``catchup``, ``frequency``, ``mode``, ``present``, ``slew``, ``threshold`` and ``limit``.
|
|
+ See `libvirt time keeping documentation <https://libvirt.org/formatdomain.html#time-keeping>`_ for the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ Set the clock to local time using an offset in seconds
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ adjustment: 3600
|
|
+ utc: False
|
|
+
|
|
+ Set the clock to a specific time zone:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timezone: CEST
|
|
+
|
|
+ Tweak guest timers:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timers:
|
|
+ tsc:
|
|
+ frequency: 3504000000
|
|
+ mode: native
|
|
+ rtc:
|
|
+ track: wall
|
|
+ tickpolicy: catchup
|
|
+ slew: 4636
|
|
+ threshold: 123
|
|
+ limit: 2342
|
|
+ hpet:
|
|
+ present: False
|
|
+
|
|
:return:
|
|
|
|
Returns a dictionary indicating the status of what has been done. It is structured in
|
|
@@ -2595,12 +3265,11 @@ def update(
|
|
boot = _handle_remote_boot_params(boot)
|
|
if boot.get("efi", None) is not None:
|
|
need_update = _handle_efi_param(boot, desc)
|
|
-
|
|
new_desc = ElementTree.fromstring(
|
|
_gen_xml(
|
|
conn,
|
|
name,
|
|
- cpu or 0,
|
|
+ cpu,
|
|
mem or 0,
|
|
all_disks,
|
|
_get_merged_nics(hypervisor, nic_profile, interfaces),
|
|
@@ -2610,17 +3279,19 @@ def update(
|
|
graphics,
|
|
boot,
|
|
boot_dev,
|
|
- stop_on_reboot,
|
|
+ numatune,
|
|
+ serial=serials,
|
|
+ consoles=consoles,
|
|
+ stop_on_reboot=stop_on_reboot,
|
|
**kwargs
|
|
)
|
|
)
|
|
|
|
- # Update the cpu
|
|
- cpu_node = desc.find("vcpu")
|
|
- if cpu and int(cpu_node.text) != cpu:
|
|
- cpu_node.text = str(cpu)
|
|
- cpu_node.set("current", str(cpu))
|
|
- need_update = True
|
|
+ if clock:
|
|
+ offset = "utc" if clock.get("utc", True) else "localtime"
|
|
+ if "timezone" in clock:
|
|
+ offset = "timezone"
|
|
+ clock["offset"] = offset
|
|
|
|
def _set_loader(node, value):
|
|
salt.utils.xmlutil.set_node_text(node, value)
|
|
@@ -2631,20 +3302,110 @@ def update(
|
|
def _set_nvram(node, value):
|
|
node.set("template", value)
|
|
|
|
- def _set_with_byte_unit(node, value):
|
|
- node.text = str(value)
|
|
- node.set("unit", "bytes")
|
|
+ def _set_with_byte_unit(attr_name=None):
|
|
+ def _setter(node, value):
|
|
+ if attr_name:
|
|
+ node.set(attr_name, str(value))
|
|
+ else:
|
|
+ node.text = str(value)
|
|
+ node.set("unit", "bytes")
|
|
+
|
|
+ return _setter
|
|
|
|
def _get_with_unit(node):
|
|
unit = node.get("unit", "KiB")
|
|
# _handle_unit treats bytes as invalid unit for the purpose of consistency
|
|
unit = unit if unit != "bytes" else "b"
|
|
- value = node.get("memory") or node.text
|
|
+ value = node.get("memory") or node.get("size") or node.text
|
|
return _handle_unit("{}{}".format(value, unit)) if value else None
|
|
|
|
+ def _set_vcpu(node, value):
|
|
+ node.text = str(value)
|
|
+ node.set("current", str(value))
|
|
+
|
|
old_mem = int(_get_with_unit(desc.find("memory")) / 1024)
|
|
+ old_cpu = int(desc.find("./vcpu").text)
|
|
+
|
|
+ def _almost_equal(current, new):
|
|
+ if current is None or new is None:
|
|
+ return False
|
|
+ return abs(current - new) / current < 1e-03
|
|
+
|
|
+ def _yesno_attribute(path, xpath, attr_name, ignored=None):
|
|
+ return xmlutil.attribute(
|
|
+ path, xpath, attr_name, ignored, lambda v: "yes" if v else "no"
|
|
+ )
|
|
+
|
|
+ def _memory_parameter(path, xpath, attr_name=None, ignored=None):
|
|
+ entry = {
|
|
+ "path": path,
|
|
+ "xpath": xpath,
|
|
+ "convert": _handle_unit,
|
|
+ "get": _get_with_unit,
|
|
+ "set": _set_with_byte_unit(attr_name),
|
|
+ "equals": _almost_equal,
|
|
+ }
|
|
+ if attr_name:
|
|
+ entry["del"] = salt.utils.xmlutil.del_attribute(attr_name, ignored)
|
|
+ return entry
|
|
+
|
|
+ def _cpuset_parameter(path, xpath, attr_name=None, ignored=None):
|
|
+ def _set_cpuset(node, value):
|
|
+ if attr_name:
|
|
+ node.set(attr_name, value)
|
|
+ else:
|
|
+ node.text = value
|
|
+
|
|
+ entry = {
|
|
+ "path": path,
|
|
+ "xpath": xpath,
|
|
+ "convert": _expand_cpuset,
|
|
+ "get": lambda n: _expand_cpuset(n.get(attr_name) if attr_name else n.text),
|
|
+ "set": _set_cpuset,
|
|
+ }
|
|
+ if attr_name:
|
|
+ entry["del"] = salt.utils.xmlutil.del_attribute(attr_name, ignored)
|
|
+ return entry
|
|
|
|
# Update the kernel boot parameters
|
|
+ data = {k: v for k, v in locals().items() if bool(v)}
|
|
+ data["stop_on_reboot"] = stop_on_reboot
|
|
+ if boot_dev:
|
|
+ data["boot_dev"] = boot_dev.split()
|
|
+
|
|
+ # Set the missing optional attributes and timers to None in timers to help cleaning up
|
|
+ timer_names = [
|
|
+ "platform",
|
|
+ "hpet",
|
|
+ "kvmclock",
|
|
+ "pit",
|
|
+ "rtc",
|
|
+ "tsc",
|
|
+ "hypervclock",
|
|
+ "armvtimer",
|
|
+ ]
|
|
+ if data.get("clock", {}).get("timers"):
|
|
+ attributes = [
|
|
+ "track",
|
|
+ "tickpolicy",
|
|
+ "frequency",
|
|
+ "mode",
|
|
+ "present",
|
|
+ "slew",
|
|
+ "threshold",
|
|
+ "limit",
|
|
+ ]
|
|
+ for timer in data["clock"]["timers"].values():
|
|
+ for attribute in attributes:
|
|
+ if attribute not in timer:
|
|
+ timer[attribute] = None
|
|
+
|
|
+ for timer_name in timer_names:
|
|
+ if timer_name not in data["clock"]["timers"]:
|
|
+ data["clock"]["timers"][timer_name] = None
|
|
+
|
|
+ _normalize_cpusets(desc, data)
|
|
+
|
|
params_mapping = [
|
|
{
|
|
"path": "stop_on_reboot",
|
|
@@ -2657,89 +3418,251 @@ def update(
|
|
{"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
|
|
+ _memory_parameter("mem", "memory"),
|
|
+ _memory_parameter("mem", "currentMemory"),
|
|
+ _memory_parameter("mem:max", "maxMemory"),
|
|
+ _memory_parameter("mem:boot", "memory"),
|
|
+ _memory_parameter("mem:current", "currentMemory"),
|
|
+ xmlutil.attribute("mem:slots", "maxMemory", "slots", ["unit"]),
|
|
+ _memory_parameter("mem:hard_limit", "memtune/hard_limit"),
|
|
+ _memory_parameter("mem:soft_limit", "memtune/soft_limit"),
|
|
+ _memory_parameter("mem:swap_hard_limit", "memtune/swap_hard_limit"),
|
|
+ _memory_parameter("mem:min_guarantee", "memtune/min_guarantee"),
|
|
+ xmlutil.attribute("boot_dev:{dev}", "os/boot[$dev]", "dev"),
|
|
+ _memory_parameter(
|
|
+ "mem:hugepages:{id}:size",
|
|
+ "memoryBacking/hugepages/page[$id]",
|
|
+ "size",
|
|
+ ["unit", "nodeset"],
|
|
+ ),
|
|
+ _cpuset_parameter(
|
|
+ "mem:hugepages:{id}:nodeset", "memoryBacking/hugepages/page[$id]", "nodeset"
|
|
+ ),
|
|
{
|
|
- "path": "mem",
|
|
- "xpath": "memory",
|
|
- "convert": _handle_unit,
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem",
|
|
- "xpath": "currentMemory",
|
|
- "convert": _handle_unit,
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem:max",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "maxMemory",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
+ "path": "mem:nosharepages",
|
|
+ "xpath": "memoryBacking/nosharepages",
|
|
+ "get": lambda n: n is not None,
|
|
+ "set": lambda n, v: None,
|
|
},
|
|
{
|
|
- "path": "mem:boot",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "memory",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem:current",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "currentMemory",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
+ "path": "mem:locked",
|
|
+ "xpath": "memoryBacking/locked",
|
|
+ "get": lambda n: n is not None,
|
|
+ "set": lambda n, v: None,
|
|
},
|
|
+ xmlutil.attribute("mem:source", "memoryBacking/source", "type"),
|
|
+ xmlutil.attribute("mem:access", "memoryBacking/access", "mode"),
|
|
+ xmlutil.attribute("mem:allocation", "memoryBacking/allocation", "mode"),
|
|
+ {"path": "mem:discard", "xpath": "memoryBacking/discard"},
|
|
{
|
|
- "path": "mem:slots",
|
|
- "xpath": "maxMemory",
|
|
- "get": lambda n: n.get("slots"),
|
|
- "set": lambda n, v: n.set("slots", str(v)),
|
|
- "del": salt.utils.xmlutil.del_attribute("slots", ["unit"]),
|
|
- },
|
|
- {
|
|
- "path": "mem:hard_limit",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "memtune/hard_limit",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem:soft_limit",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "memtune/soft_limit",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem:swap_hard_limit",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "memtune/swap_hard_limit",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_unit,
|
|
- },
|
|
- {
|
|
- "path": "mem:min_guarantee",
|
|
- "convert": _handle_unit,
|
|
- "xpath": "memtune/min_guarantee",
|
|
- "get": _get_with_unit,
|
|
- "set": _set_with_byte_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"),
|
|
+ "path": "cpu",
|
|
+ "xpath": "vcpu",
|
|
+ "get": lambda n: int(n.text),
|
|
+ "set": _set_vcpu,
|
|
},
|
|
+ {"path": "cpu:maximum", "xpath": "vcpu", "get": lambda n: int(n.text)},
|
|
+ xmlutil.attribute("cpu:placement", "vcpu", "placement"),
|
|
+ _cpuset_parameter("cpu:cpuset", "vcpu", "cpuset"),
|
|
+ xmlutil.attribute("cpu:current", "vcpu", "current"),
|
|
+ xmlutil.attribute("cpu:match", "cpu", "match"),
|
|
+ xmlutil.attribute("cpu:mode", "cpu", "mode"),
|
|
+ xmlutil.attribute("cpu:check", "cpu", "check"),
|
|
+ {"path": "cpu:model:name", "xpath": "cpu/model"},
|
|
+ xmlutil.attribute("cpu:model:fallback", "cpu/model", "fallback"),
|
|
+ xmlutil.attribute("cpu:model:vendor_id", "cpu/model", "vendor_id"),
|
|
+ {"path": "cpu:vendor", "xpath": "cpu/vendor"},
|
|
+ xmlutil.attribute("cpu:topology:sockets", "cpu/topology", "sockets"),
|
|
+ xmlutil.attribute("cpu:topology:cores", "cpu/topology", "cores"),
|
|
+ xmlutil.attribute("cpu:topology:threads", "cpu/topology", "threads"),
|
|
+ xmlutil.attribute("cpu:cache:level", "cpu/cache", "level"),
|
|
+ xmlutil.attribute("cpu:cache:mode", "cpu/cache", "mode"),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:features:{id}", "cpu/feature[@name='$id']", "policy", ["name"]
|
|
+ ),
|
|
+ _yesno_attribute(
|
|
+ "cpu:vcpus:{id}:enabled", "vcpus/vcpu[@id='$id']", "enabled", ["id"]
|
|
+ ),
|
|
+ _yesno_attribute(
|
|
+ "cpu:vcpus:{id}:hotpluggable",
|
|
+ "vcpus/vcpu[@id='$id']",
|
|
+ "hotpluggable",
|
|
+ ["id"],
|
|
+ ),
|
|
+ xmlutil.int_attribute(
|
|
+ "cpu:vcpus:{id}:order", "vcpus/vcpu[@id='$id']", "order", ["id"]
|
|
+ ),
|
|
+ _cpuset_parameter(
|
|
+ "cpu:numa:{id}:cpus", "cpu/numa/cell[@id='$id']", "cpus", ["id"]
|
|
+ ),
|
|
+ _memory_parameter(
|
|
+ "cpu:numa:{id}:memory", "cpu/numa/cell[@id='$id']", "memory", ["id"]
|
|
+ ),
|
|
+ _yesno_attribute(
|
|
+ "cpu:numa:{id}:discard", "cpu/numa/cell[@id='$id']", "discard", ["id"]
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:numa:{id}:memAccess", "cpu/numa/cell[@id='$id']", "memAccess", ["id"]
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:numa:{id}:distances:{sid}",
|
|
+ "cpu/numa/cell[@id='$id']/distances/sibling[@id='$sid']",
|
|
+ "value",
|
|
+ ["id"],
|
|
+ ),
|
|
+ {"path": "cpu:iothreads", "xpath": "iothreads"},
|
|
+ {"path": "cpu:tuning:shares", "xpath": "cputune/shares"},
|
|
+ {"path": "cpu:tuning:period", "xpath": "cputune/period"},
|
|
+ {"path": "cpu:tuning:quota", "xpath": "cputune/quota"},
|
|
+ {"path": "cpu:tuning:global_period", "xpath": "cputune/global_period"},
|
|
+ {"path": "cpu:tuning:global_quota", "xpath": "cputune/global_quota"},
|
|
+ {"path": "cpu:tuning:emulator_period", "xpath": "cputune/emulator_period"},
|
|
+ {"path": "cpu:tuning:emulator_quota", "xpath": "cputune/emulator_quota"},
|
|
+ {"path": "cpu:tuning:iothread_period", "xpath": "cputune/iothread_period"},
|
|
+ {"path": "cpu:tuning:iothread_quota", "xpath": "cputune/iothread_quota"},
|
|
+ _cpuset_parameter(
|
|
+ "cpu:tuning:vcpupin:{id}",
|
|
+ "cputune/vcpupin[@vcpu='$id']",
|
|
+ "cpuset",
|
|
+ ["vcpu"],
|
|
+ ),
|
|
+ _cpuset_parameter("cpu:tuning:emulatorpin", "cputune/emulatorpin", "cpuset"),
|
|
+ _cpuset_parameter(
|
|
+ "cpu:tuning:iothreadpin:{id}",
|
|
+ "cputune/iothreadpin[@iothread='$id']",
|
|
+ "cpuset",
|
|
+ ["iothread"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:vcpusched:{id}:scheduler",
|
|
+ "cputune/vcpusched[$id]",
|
|
+ "scheduler",
|
|
+ ["priority", "vcpus"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:vcpusched:{id}:priority", "cputune/vcpusched[$id]", "priority"
|
|
+ ),
|
|
+ _cpuset_parameter(
|
|
+ "cpu:tuning:vcpusched:{id}:vcpus", "cputune/vcpusched[$id]", "vcpus"
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:iothreadsched:{id}:scheduler",
|
|
+ "cputune/iothreadsched[$id]",
|
|
+ "scheduler",
|
|
+ ["priority", "iothreads"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:iothreadsched:{id}:priority",
|
|
+ "cputune/iothreadsched[$id]",
|
|
+ "priority",
|
|
+ ),
|
|
+ _cpuset_parameter(
|
|
+ "cpu:tuning:iothreadsched:{id}:iothreads",
|
|
+ "cputune/iothreadsched[$id]",
|
|
+ "iothreads",
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:emulatorsched:scheduler",
|
|
+ "cputune/emulatorsched",
|
|
+ "scheduler",
|
|
+ ["priority"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:emulatorsched:priority", "cputune/emulatorsched", "priority"
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:cachetune:{id}:monitor:{sid}",
|
|
+ "cputune/cachetune[@vcpus='$id']/monitor[@vcpus='$sid']",
|
|
+ "level",
|
|
+ ["vcpus"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:memorytune:{id}:{sid}",
|
|
+ "cputune/memorytune[@vcpus='$id']/node[@id='$sid']",
|
|
+ "bandwidth",
|
|
+ ["id", "vcpus"],
|
|
+ ),
|
|
+ xmlutil.attribute("clock:offset", "clock", "offset"),
|
|
+ xmlutil.attribute("clock:adjustment", "clock", "adjustment", convert=str),
|
|
+ xmlutil.attribute("clock:timezone", "clock", "timezone"),
|
|
]
|
|
|
|
- data = {k: v for k, v in locals().items() if bool(v)}
|
|
- data["stop_on_reboot"] = stop_on_reboot
|
|
- if boot_dev:
|
|
- data["boot_dev"] = {i + 1: dev for i, dev in enumerate(boot_dev.split())}
|
|
+ for timer in timer_names:
|
|
+ params_mapping += [
|
|
+ xmlutil.attribute(
|
|
+ "clock:timers:{}:track".format(timer),
|
|
+ "clock/timer[@name='{}']".format(timer),
|
|
+ "track",
|
|
+ ["name"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "clock:timers:{}:tickpolicy".format(timer),
|
|
+ "clock/timer[@name='{}']".format(timer),
|
|
+ "tickpolicy",
|
|
+ ["name"],
|
|
+ ),
|
|
+ xmlutil.int_attribute(
|
|
+ "clock:timers:{}:frequency".format(timer),
|
|
+ "clock/timer[@name='{}']".format(timer),
|
|
+ "frequency",
|
|
+ ["name"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "clock:timers:{}:mode".format(timer),
|
|
+ "clock/timer[@name='{}']".format(timer),
|
|
+ "mode",
|
|
+ ["name"],
|
|
+ ),
|
|
+ _yesno_attribute(
|
|
+ "clock:timers:{}:present".format(timer),
|
|
+ "clock/timer[@name='{}']".format(timer),
|
|
+ "present",
|
|
+ ["name"],
|
|
+ ),
|
|
+ ]
|
|
+ for attr in ["slew", "threshold", "limit"]:
|
|
+ params_mapping.append(
|
|
+ xmlutil.int_attribute(
|
|
+ "clock:timers:{}:{}".format(timer, attr),
|
|
+ "clock/timer[@name='{}']/catchup".format(timer),
|
|
+ attr,
|
|
+ )
|
|
+ )
|
|
+
|
|
+ for attr in ["level", "type", "size"]:
|
|
+ params_mapping.append(
|
|
+ xmlutil.attribute(
|
|
+ "cpu:tuning:cachetune:{id}:{sid}:" + attr,
|
|
+ "cputune/cachetune[@vcpus='$id']/cache[@id='$sid']",
|
|
+ attr,
|
|
+ ["id", "unit", "vcpus"],
|
|
+ )
|
|
+ )
|
|
+
|
|
+ # update NUMA host policy
|
|
+ if hypervisor in ["qemu", "kvm"]:
|
|
+ params_mapping += [
|
|
+ xmlutil.attribute("numatune:memory:mode", "numatune/memory", "mode"),
|
|
+ _cpuset_parameter("numatune:memory:nodeset", "numatune/memory", "nodeset"),
|
|
+ xmlutil.attribute(
|
|
+ "numatune:memnodes:{id}:mode",
|
|
+ "numatune/memnode[@cellid='$id']",
|
|
+ "mode",
|
|
+ ["cellid"],
|
|
+ ),
|
|
+ _cpuset_parameter(
|
|
+ "numatune:memnodes:{id}:nodeset",
|
|
+ "numatune/memnode[@cellid='$id']",
|
|
+ "nodeset",
|
|
+ ["cellid"],
|
|
+ ),
|
|
+ xmlutil.attribute(
|
|
+ "hypervisor_features:kvm-hint-dedicated",
|
|
+ "features/kvm/hint-dedicated",
|
|
+ "state",
|
|
+ convert=lambda v: "on" if v else "off",
|
|
+ ),
|
|
+ ]
|
|
+
|
|
need_update = (
|
|
salt.utils.xmlutil.change_xml(desc, data, params_mapping) or need_update
|
|
)
|
|
@@ -2750,6 +3673,8 @@ def update(
|
|
"disk": ["disks", "disk_profile"],
|
|
"interface": ["interfaces", "nic_profile"],
|
|
"graphics": ["graphics"],
|
|
+ "serial": ["serial"],
|
|
+ "console": ["console"],
|
|
}
|
|
changes = {}
|
|
for dev_type in parameters:
|
|
@@ -2787,7 +3712,6 @@ def update(
|
|
_qemu_image_create(all_disks[idx])
|
|
elif item in changes["disk"]["new"] and not source_file:
|
|
_disk_volume_create(conn, all_disks[idx])
|
|
-
|
|
if not test:
|
|
xml_desc = ElementTree.tostring(desc)
|
|
log.debug("Update virtual machine definition: %s", xml_desc)
|
|
@@ -2803,14 +3727,18 @@ def update(
|
|
commands = []
|
|
removable_changes = []
|
|
if domain.isActive() and live:
|
|
- if cpu:
|
|
- commands.append(
|
|
- {
|
|
- "device": "cpu",
|
|
- "cmd": "setVcpusFlags",
|
|
- "args": [cpu, libvirt.VIR_DOMAIN_AFFECT_LIVE],
|
|
- }
|
|
- )
|
|
+ if cpu and (
|
|
+ isinstance(cpu, int) or isinstance(cpu, dict) and cpu.get("maximum")
|
|
+ ):
|
|
+ new_cpu = cpu.get("maximum") if isinstance(cpu, dict) else cpu
|
|
+ if old_cpu != new_cpu and new_cpu is not None:
|
|
+ commands.append(
|
|
+ {
|
|
+ "device": "cpu",
|
|
+ "cmd": "setVcpusFlags",
|
|
+ "args": [new_cpu, libvirt.VIR_DOMAIN_AFFECT_LIVE],
|
|
+ }
|
|
+ )
|
|
if mem:
|
|
if isinstance(mem, dict):
|
|
# setMemoryFlags takes memory amount in KiB
|
|
@@ -2822,7 +3750,7 @@ def update(
|
|
elif isinstance(mem, int):
|
|
new_mem = int(mem * 1024)
|
|
|
|
- if old_mem != new_mem and new_mem is not None:
|
|
+ if not _almost_equal(old_mem, new_mem) and new_mem is not None:
|
|
commands.append(
|
|
{
|
|
"device": "mem",
|
|
@@ -4402,7 +5330,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
|
|
diff --git a/salt/states/virt.py b/salt/states/virt.py
|
|
index 20ea1c25f1..784cdca73c 100644
|
|
--- a/salt/states/virt.py
|
|
+++ b/salt/states/virt.py
|
|
@@ -287,8 +287,13 @@ def defined(
|
|
os_type=None,
|
|
arch=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
update=True,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
live=True,
|
|
):
|
|
@@ -298,26 +303,151 @@ def defined(
|
|
.. versionadded:: sodium
|
|
|
|
:param name: name of the virtual machine to run
|
|
- :param cpu: number of CPUs for the virtual machine to create
|
|
- :param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, a dictionary can be used to
|
|
+ :param cpu:
|
|
+ Number of virtual CPUs to assign to the virtual machine or a dictionary with detailed information to configure
|
|
+ cpu model and topology, numa node tuning, cpu tuning and iothreads allocation. The structure of the dictionary is
|
|
+ documented in :ref:`init-cpu-def`.
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ cpu:
|
|
+ placement: static
|
|
+ cpuset: 0-11
|
|
+ current: 5
|
|
+ maximum: 12
|
|
+ vcpus:
|
|
+ 0:
|
|
+ enabled: 'yes'
|
|
+ hotpluggable: 'no'
|
|
+ order: 1
|
|
+ 1:
|
|
+ enabled: 'no'
|
|
+ hotpluggable: 'yes'
|
|
+ match: minimum
|
|
+ mode: custom
|
|
+ check: full
|
|
+ vendor: Intel
|
|
+ model:
|
|
+ name: core2duo
|
|
+ fallback: allow
|
|
+ vendor_id: GenuineIntel
|
|
+ topology:
|
|
+ sockets: 1
|
|
+ cores: 12
|
|
+ threads: 1
|
|
+ cache:
|
|
+ level: 3
|
|
+ mode: emulate
|
|
+ feature:
|
|
+ policy: optional
|
|
+ name: lahf_lm
|
|
+ numa:
|
|
+ 0:
|
|
+ cpus: 0-3
|
|
+ memory: 1g
|
|
+ discard: 'yes'
|
|
+ distances:
|
|
+ 0: 10 # sibling id : value
|
|
+ 1: 21
|
|
+ 2: 31
|
|
+ 3: 41
|
|
+ 1:
|
|
+ cpus: 4-6
|
|
+ memory: 1g
|
|
+ memAccess: shared
|
|
+ distances:
|
|
+ 0: 21
|
|
+ 1: 10
|
|
+ 2: 21
|
|
+ 3: 31
|
|
+ tuning:
|
|
+ vcpupin:
|
|
+ 0: 1-4,^2 # vcpuid : cpuset
|
|
+ 1: 0,1
|
|
+ 2: 2,3
|
|
+ 3: 0,4
|
|
+ emulatorpin: 1-3
|
|
+ iothreadpin:
|
|
+ 1: 5,6 # iothread id: cpuset
|
|
+ 2: 7,8
|
|
+ shares: 2048
|
|
+ period: 1000000
|
|
+ quota: -1
|
|
+ global_period: 1000000
|
|
+ global_quota: -1
|
|
+ emulator_period: 1000000
|
|
+ emulator_quota: -1
|
|
+ iothread_period: 1000000
|
|
+ iothread_quota: -1
|
|
+ vcpusched:
|
|
+ - scheduler: fifo
|
|
+ priority: 1
|
|
+ - scheduler: fifo
|
|
+ priority: 2
|
|
+ vcpus: 1-3
|
|
+ - scheduler: rr
|
|
+ priority: 3
|
|
+ vcpus: 4
|
|
+ iothreadsched:
|
|
+ - scheduler: batch
|
|
+ iothreads: 2
|
|
+ emulatorsched:
|
|
+ scheduler: idle
|
|
+ cachetune:
|
|
+ 0-3: # vcpus set
|
|
+ 0: # cache id
|
|
+ level: 3
|
|
+ type: both
|
|
+ size: 4
|
|
+ 1:
|
|
+ level: 3
|
|
+ type: both
|
|
+ size: 6
|
|
+ monitor:
|
|
+ 1: 3
|
|
+ 0-3: 3
|
|
+ 4-5:
|
|
+ monitor:
|
|
+ 4: 3 # vcpus: level
|
|
+ 5: 3
|
|
+ memorytune:
|
|
+ 0-3: # vcpus set
|
|
+ 0: 60 # node id: bandwidth
|
|
+ 4-5:
|
|
+ 0: 60
|
|
+ iothreads: 4
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ :param mem: Amount of memory to allocate to the virtual machine in MiB. Since 3002, a dictionary can be used to
|
|
contain detailed configuration which support memory allocation or tuning. Supported parameters are ``boot``,
|
|
- ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit`` and ``min_guarantee``. The
|
|
- structure of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported.
|
|
- Detail unit specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be
|
|
- an integer.
|
|
+ ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit``, ``min_guarantee``,
|
|
+ ``hugepages`` , ``nosharepages``, ``locked``, ``source``, ``access``, ``allocation`` and ``discard``. The structure
|
|
+ of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported. Detail unit
|
|
+ specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be an integer.
|
|
|
|
- .. code-block:: python
|
|
+ .. code-block:: yaml
|
|
|
|
- {
|
|
- 'boot': 1g,
|
|
- 'current': 1g,
|
|
- 'max': 1g,
|
|
- 'slots': 10,
|
|
- 'hard_limit': '1024'
|
|
- 'soft_limit': '512m'
|
|
- 'swap_hard_limit': '1g'
|
|
- 'min_guarantee': '512mib'
|
|
- }
|
|
+ boot: 1g
|
|
+ current: 1g
|
|
+ max: 1g
|
|
+ slots: 10
|
|
+ hard_limit: 1024
|
|
+ soft_limit: 512m
|
|
+ swap_hard_limit: 1g
|
|
+ min_guarantee: 512mib
|
|
+ hugepages:
|
|
+ - size: 2m
|
|
+ - nodeset: 0-2
|
|
+ size: 1g
|
|
+ - nodeset: 3
|
|
+ size: 2g
|
|
+ nosharepages: True
|
|
+ locked: True
|
|
+ source: file
|
|
+ access: shared
|
|
+ allocation: immediate
|
|
+ discard: True
|
|
|
|
.. versionchanged:: Magnesium
|
|
|
|
@@ -380,6 +510,77 @@ def defined(
|
|
|
|
.. versionadded:: Magnesium
|
|
|
|
+ :param numatune:
|
|
+ The optional numatune element provides details of how to tune the performance of a NUMA host via controlling NUMA
|
|
+ policy for domain process. The optional ``memory`` element specifies how to allocate memory for the domain process
|
|
+ on a NUMA host. ``memnode`` elements can specify memory allocation policies per each guest NUMA node. The definition
|
|
+ used in the dictionary can be found at :ref:`init-cpu-def`.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: python
|
|
+
|
|
+ {
|
|
+ 'memory': {'mode': 'strict', 'nodeset': '0-11'},
|
|
+ 'memnodes': {0: {'mode': 'strict', 'nodeset': 1}, 1: {'mode': 'preferred', 'nodeset': 2}}
|
|
+ }
|
|
+
|
|
+ :param hypervisor_features:
|
|
+ Enable or disable hypervisor-specific features on the virtual machine.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ hypervisor_features:
|
|
+ kvm-hint-dedicated: True
|
|
+
|
|
+ :param clock:
|
|
+ Configure the guest clock.
|
|
+ The value is a dictionary with the following keys:
|
|
+
|
|
+ adjustment
|
|
+ time adjustment in seconds or ``reset``
|
|
+
|
|
+ utc
|
|
+ set to ``False`` to use the host local time as the guest clock. Defaults to ``True``.
|
|
+
|
|
+ timezone
|
|
+ synchronize the guest to the correspding timezone
|
|
+
|
|
+ timers
|
|
+ a dictionary associating the timer name with its configuration.
|
|
+ This configuration is a dictionary with the properties ``track``, ``tickpolicy``,
|
|
+ ``catchup``, ``frequency``, ``mode``, ``present``, ``slew``, ``threshold`` and ``limit``.
|
|
+ See `libvirt time keeping documentation <https://libvirt.org/formatdomain.html#time-keeping>`_ for the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ Set the clock to local time using an offset in seconds
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ adjustment: 3600
|
|
+ utc: False
|
|
+
|
|
+ Set the clock to a specific time zone:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timezone: CEST
|
|
+
|
|
+ :param serials:
|
|
+ Dictionary providing details on the serials connection to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+ :param consoles:
|
|
+ Dictionary providing details on the consoles device to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
:param stop_on_reboot:
|
|
If set to ``True`` the guest will stop instead of rebooting.
|
|
This is specially useful when creating a virtual machine with an installation cdrom or
|
|
@@ -456,8 +657,13 @@ def defined(
|
|
username=username,
|
|
password=password,
|
|
boot=boot,
|
|
+ numatune=numatune,
|
|
+ serials=serials,
|
|
+ consoles=consoles,
|
|
test=__opts__["test"],
|
|
boot_dev=boot_dev,
|
|
+ hypervisor_features=hypervisor_features,
|
|
+ clock=clock,
|
|
stop_on_reboot=stop_on_reboot,
|
|
)
|
|
ret["changes"][name] = status
|
|
@@ -492,8 +698,13 @@ def defined(
|
|
username=username,
|
|
password=password,
|
|
boot=boot,
|
|
+ numatune=numatune,
|
|
+ serials=serials,
|
|
+ consoles=consoles,
|
|
start=False,
|
|
boot_dev=boot_dev,
|
|
+ hypervisor_features=hypervisor_features,
|
|
+ clock=clock,
|
|
stop_on_reboot=stop_on_reboot,
|
|
)
|
|
ret["changes"][name] = {"definition": True}
|
|
@@ -528,6 +739,11 @@ def running(
|
|
arch=None,
|
|
boot=None,
|
|
boot_dev=None,
|
|
+ numatune=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
):
|
|
"""
|
|
@@ -536,13 +752,20 @@ def running(
|
|
.. versionadded:: 2016.3.0
|
|
|
|
:param name: name of the virtual machine to run
|
|
- :param cpu: number of CPUs for the virtual machine to create
|
|
- :param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, a dictionary can be used to
|
|
+ :param cpu:
|
|
+ Number of virtual CPUs to assign to the virtual machine or a dictionary with detailed information to configure
|
|
+ cpu model and topology, numa node tuning, cpu tuning and iothreads allocation. The structure of the dictionary is
|
|
+ documented in :ref:`init-cpu-def`.
|
|
+
|
|
+ To update any cpu parameters specify the new values to the corresponding tag. To remove any element or attribute,
|
|
+ specify ``None`` object. Please note that ``None`` object is mapped to ``null`` in yaml, use ``null`` in sls file
|
|
+ instead.
|
|
+ :param mem: Amount of memory to allocate to the virtual machine in MiB. Since 3002, a dictionary can be used to
|
|
contain detailed configuration which support memory allocation or tuning. Supported parameters are ``boot``,
|
|
- ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit`` and ``min_guarantee``. The
|
|
- structure of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported.
|
|
- Detail unit specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be
|
|
- an integer.
|
|
+ ``current``, ``max``, ``slots``, ``hard_limit``, ``soft_limit``, ``swap_hard_limit``, ``min_guarantee``,
|
|
+ ``hugepages`` , ``nosharepages``, ``locked``, ``source``, ``access``, ``allocation`` and ``discard``. The structure
|
|
+ of the dictionary is documented in :ref:`init-mem-def`. Both decimal and binary base are supported. Detail unit
|
|
+ specification is documented in :ref:`virt-units`. Please note that the value for ``slots`` must be an integer.
|
|
|
|
To remove any parameters, pass a None object, for instance: 'soft_limit': ``None``. Please note that ``None``
|
|
is mapped to ``null`` in sls file, pass ``null`` in sls file instead.
|
|
@@ -638,6 +861,16 @@ def running(
|
|
pass a None object, for instance: 'kernel': ``None``.
|
|
|
|
.. versionadded:: 3000
|
|
+ :param serials:
|
|
+ Dictionary providing details on the serials connection to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+ :param consoles:
|
|
+ Dictionary providing details on the consoles device to create. (Default: ``None``)
|
|
+ See :ref:`init-chardevs-def` for more details on the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
|
|
:param boot:
|
|
Specifies kernel for the virtual machine, as well as boot parameters
|
|
@@ -664,6 +897,18 @@ def running(
|
|
|
|
.. versionadded:: Magnesium
|
|
|
|
+ :param numatune:
|
|
+ The optional numatune element provides details of how to tune the performance of a NUMA host via controlling NUMA
|
|
+ policy for domain process. The optional ``memory`` element specifies how to allocate memory for the domain process
|
|
+ on a NUMA host. ``memnode`` elements can specify memory allocation policies per each guest NUMA node. The definition
|
|
+ used in the dictionary can be found at :ref:`init-cpu-def`.
|
|
+
|
|
+ To update any numatune parameters, specify the new value. To remove any ``numatune`` parameters, pass a None object,
|
|
+ for instance: 'numatune': ``None``. Please note that ``None`` is mapped to ``null`` in sls file, pass ``null`` in
|
|
+ sls file instead.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
:param stop_on_reboot:
|
|
If set to ``True`` the guest will stop instead of rebooting.
|
|
This is specially useful when creating a virtual machine with an installation cdrom or
|
|
@@ -672,6 +917,51 @@ def running(
|
|
|
|
.. versionadded:: Aluminium
|
|
|
|
+ :param hypervisor_features:
|
|
+ Enable or disable hypervisor-specific features on the virtual machine.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ hypervisor_features:
|
|
+ kvm-hint-dedicated: True
|
|
+
|
|
+ :param clock:
|
|
+ Configure the guest clock.
|
|
+ The value is a dictionary with the following keys:
|
|
+
|
|
+ adjustment
|
|
+ time adjustment in seconds or ``reset``
|
|
+
|
|
+ utc
|
|
+ set to ``False`` to use the host local time as the guest clock. Defaults to ``True``.
|
|
+
|
|
+ timezone
|
|
+ synchronize the guest to the correspding timezone
|
|
+
|
|
+ timers
|
|
+ a dictionary associating the timer name with its configuration.
|
|
+ This configuration is a dictionary with the properties ``track``, ``tickpolicy``,
|
|
+ ``catchup``, ``frequency``, ``mode``, ``present``, ``slew``, ``threshold`` and ``limit``.
|
|
+ See `libvirt time keeping documentation <https://libvirt.org/formatdomain.html#time-keeping>`_ for the possible values.
|
|
+
|
|
+ .. versionadded:: Aluminium
|
|
+
|
|
+ Set the clock to local time using an offset in seconds
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ adjustment: 3600
|
|
+ utc: False
|
|
+
|
|
+ Set the clock to a specific time zone:
|
|
+
|
|
+ .. code-block:: yaml
|
|
+
|
|
+ clock:
|
|
+ timezone: CEST
|
|
+
|
|
.. rubric:: Example States
|
|
|
|
Make sure an already-defined virtual machine called ``domain_name`` is running:
|
|
@@ -740,10 +1030,15 @@ def running(
|
|
boot=boot,
|
|
update=update,
|
|
boot_dev=boot_dev,
|
|
+ numatune=numatune,
|
|
+ hypervisor_features=hypervisor_features,
|
|
+ clock=clock,
|
|
stop_on_reboot=stop_on_reboot,
|
|
connection=connection,
|
|
username=username,
|
|
password=password,
|
|
+ serials=serials,
|
|
+ consoles=consoles,
|
|
)
|
|
|
|
result = True if not __opts__["test"] else None
|
|
diff --git a/salt/templates/virt/libvirt_chardevs.jinja b/salt/templates/virt/libvirt_chardevs.jinja
|
|
new file mode 100644
|
|
index 0000000000..1795277180
|
|
--- /dev/null
|
|
+++ b/salt/templates/virt/libvirt_chardevs.jinja
|
|
@@ -0,0 +1,16 @@
|
|
+{% macro chardev(dev) -%}
|
|
+ {% if dev.type == "unix" -%}
|
|
+ <source mode="bind" path="{{ dev.path }}"/>
|
|
+ {% elif dev.type in ["udp", "tcp"] -%}
|
|
+ <source mode="bind" host="{{ dev.get('host', '0.0.0.0') }}" service="{{ dev.port }}"
|
|
+ {% if dev.get('tls') is not none %}tls="{{'yes' if dev.tls else 'no'}}"{% endif %}/>
|
|
+ {% elif dev.type in ["pipe", "dev", "pty", "file"] and dev.path -%}
|
|
+ <source path="{{ dev.path }}"/>
|
|
+ {%- endif %}
|
|
+ {% if dev.type == "tcp" -%}
|
|
+ <protocol type="{{ dev.protocol }}"/>
|
|
+ {%- endif %}
|
|
+ {% if "target_port" in dev or "target_type" in dev -%}
|
|
+ <target port="{{ dev.get('target_port', 0) }}" {% if dev.target_type %}type="{{ dev.target_type }}"{% endif %}/>
|
|
+ {%- endif %}
|
|
+{%- endmacro %}
|
|
diff --git a/salt/templates/virt/libvirt_domain.jinja b/salt/templates/virt/libvirt_domain.jinja
|
|
index fb4c9f40d0..6ac3e867b9 100644
|
|
--- a/salt/templates/virt/libvirt_domain.jinja
|
|
+++ b/salt/templates/virt/libvirt_domain.jinja
|
|
@@ -1,32 +1,220 @@
|
|
{%- import 'libvirt_disks.jinja' as libvirt_disks -%}
|
|
+{%- macro opt_attribute(obj, name, conv=none) %}
|
|
+{%- if obj.get(name) is not none %} {{ name }}='{{ obj[name] if conv is none else conv(obj[name]) }}'{% endif -%}
|
|
+{%- endmacro %}
|
|
+{%- import 'libvirt_chardevs.jinja' as libvirt_chardevs -%}
|
|
<domain type='{{ hypervisor }}'>
|
|
<name>{{ name }}</name>
|
|
- <vcpu>{{ cpu }}</vcpu>
|
|
+ {%- if cpu %}
|
|
+ <vcpu {{ opt_attribute(cpu, 'placement') }} {{ opt_attribute(cpu, 'cpuset') }} {{ opt_attribute(cpu, 'current') }}>{{ cpu.get('maximum', '') }}</vcpu>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.get('vcpus') %}
|
|
+ <vcpus>
|
|
+ {%- for vcpu_id in cpu["vcpus"].keys() %}
|
|
+ <vcpu id='{{ vcpu_id }}' {{ opt_attribute(cpu.vcpus[vcpu_id], 'enabled', yesno) }} {{ opt_attribute(cpu.vcpus[vcpu_id], 'hotpluggable', yesno) }} {{ opt_attribute(cpu.vcpus[vcpu_id], 'order') }}/>
|
|
+ {%- endfor %}
|
|
+ </vcpus>
|
|
+ {%- endif %}
|
|
+ {%- if cpu %}
|
|
+ <cpu {{ opt_attribute(cpu, 'match') }} {{ opt_attribute(cpu, 'mode') }} {{ opt_attribute(cpu, 'check') }} >
|
|
+ {%- if cpu.model %}
|
|
+ <model {{ opt_attribute(cpu.model, 'fallback') }} {{ opt_attribute(cpu.model, 'vendor_id') }}>{{ cpu.model.get('name', '') }}</model>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.vendor %}
|
|
+ <vendor>{{ cpu.get('vendor', '') }}</vendor>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.topology %}
|
|
+ <topology {{ opt_attribute(cpu.topology, 'sockets') }} {{ opt_attribute(cpu.topology, 'dies') }} {{ opt_attribute(cpu.topology, 'cores') }} {{ opt_attribute(cpu.topology, 'threads') }}/>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.cache %}
|
|
+ <cache {{ opt_attribute(cpu.cache, 'level') }} {{ opt_attribute(cpu.cache, 'mode') }}/>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.features %}
|
|
+ {%- for k, v in cpu.features.items() %}
|
|
+ <feature policy='{{ v }}' name='{{ k }}'/>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.numa %}
|
|
+ <numa>
|
|
+ {%- for numa_id in cpu.numa.keys() %}
|
|
+ {%- if cpu.numa.get(numa_id) %}
|
|
+ <cell id='{{ numa_id }}' {{ opt_attribute(cpu.numa[numa_id], 'cpus') }} {{ opt_attribute(cpu.numa[numa_id], 'memory', to_kib) }} {{ opt_attribute(cpu.numa[numa_id], 'discard', yesno) }} {{ opt_attribute(cpu.numa[numa_id], 'memAccess') }}>
|
|
+ {%- if cpu.numa[numa_id].distances %}
|
|
+ <distances>
|
|
+ {%- for sibling_id in cpu.numa[numa_id].distances %}
|
|
+ <sibling id='{{ sibling_id }}' value='{{ cpu.numa[numa_id].distances[sibling_id] }}'/>
|
|
+ {%- endfor %}
|
|
+ </distances>
|
|
+ {%- endif %}
|
|
+ </cell>
|
|
+ {%- endif %}
|
|
+ {%- endfor %}
|
|
+ </numa>
|
|
+ {%- endif %}
|
|
+ </cpu>
|
|
+ {%- if cpu.iothreads %}
|
|
+ <iothreads>{{ cpu.iothreads }}</iothreads>
|
|
+ {%- endif %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning %}
|
|
+ <cputune>
|
|
+ {%- if cpu.tuning.vcpupin %}
|
|
+ {%- for vcpu_id, cpuset in cpu.tuning.vcpupin.items() %}
|
|
+ <vcpupin vcpu='{{ vcpu_id }}' cpuset='{{ cpuset }}'/>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.emulatorpin %}
|
|
+ <emulatorpin cpuset="{{ cpu.tuning.emulatorpin }}"/>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.iothreadpin %}
|
|
+ {%- for thread_id, cpuset in cpu.tuning.iothreadpin.items() %}
|
|
+ <iothreadpin iothread='{{ thread_id }}' cpuset='{{ cpuset }}'/>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.shares %}
|
|
+ <shares>{{ cpu.tuning.shares }}</shares>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.period %}
|
|
+ <period>{{ cpu.tuning.period }}</period>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.quota %}
|
|
+ <quota>{{ cpu.tuning.quota }}</quota>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.global_period %}
|
|
+ <global_period>{{ cpu.tuning.global_period }}</global_period>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.global_quota %}
|
|
+ <global_quota>{{ cpu.tuning.global_quota }}</global_quota>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.emulator_period %}
|
|
+ <emulator_period>{{ cpu.tuning.emulator_period }}</emulator_period>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.emulator_quota %}
|
|
+ <emulator_quota>{{ cpu.tuning.emulator_quota }}</emulator_quota>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.iothread_period %}
|
|
+ <iothread_period>{{ cpu.tuning.iothread_period }}</iothread_period>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.iothread_quota %}
|
|
+ <iothread_quota>{{ cpu.tuning.iothread_quota }}</iothread_quota>
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.vcpusched %}
|
|
+ {%- for sched in cpu.tuning.vcpusched %}
|
|
+ <vcpusched scheduler='{{ sched.scheduler }}'
|
|
+ {%- if sched.get("vcpus") %} vcpus='{{ sched.get("vcpus") }}'{% endif -%}
|
|
+ {%- if sched.get("priority") is not none %} priority='{{ sched.get("priority") }}'{% endif -%}
|
|
+ />
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.iothreadsched %}
|
|
+ {%- for sched in cpu.tuning.iothreadsched %}
|
|
+ <iothreadsched scheduler='{{ sched.scheduler }}'
|
|
+ {%- if sched.get("iothreads") %} iothreads='{{ sched.get("iothreads") }}'{% endif -%}
|
|
+ {%- if sched.get("priority") is not none %} priority='{{ sched.get("priority") }}'{% endif -%}
|
|
+ />
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.emulatorsched %}
|
|
+ <emulatorsched scheduler='{{ cpu.tuning.emulatorsched.scheduler }}'
|
|
+ {%- if cpu.tuning.emulatorsched.get("priority") is not none %} priority='{{ cpu.tuning.emulatorsched.get("priority") }}'{% endif -%}
|
|
+ />
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.cachetune %}
|
|
+ {%- for k, v in cpu.tuning.cachetune.items() %}
|
|
+ <cachetune vcpus='{{ k }}'>
|
|
+ {%- for e, atrs in v.items() %}
|
|
+ {%- if e is number and atrs %}
|
|
+ <cache id='{{ e }}' {%- for atr, val in atrs.items() %} {{ atr }}='{{ val }}' {%- endfor %} />
|
|
+ {%- elif e is not number %}
|
|
+ {%- for atr, val in atrs.items() %}
|
|
+ <monitor level='{{ val }}' vcpus='{{ atr }}'/>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- endfor %}
|
|
+ </cachetune>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ {%- if cpu.tuning.memorytune %}
|
|
+ {%- for vcpus, nodes in cpu.tuning.memorytune.items() %}
|
|
+ <memorytune vcpus='{{ vcpus}}'>
|
|
+ {%- for id, bandwidth in nodes.items() %}
|
|
+ <node id='{{ id }}' bandwidth='{{ bandwidth }}'/>
|
|
+ {%- endfor %}
|
|
+ </memorytune>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ </cputune>
|
|
+ {%- endif %}
|
|
{%- if mem.max %}
|
|
- <maxMemory {{ mem.slots }} unit='KiB'> {{ mem.max }}</maxMemory>
|
|
+ <maxMemory {{ opt_attribute(mem, 'slots') }} unit='KiB'>{{ to_kib(mem.max) }}</maxMemory>
|
|
{%- endif %}
|
|
{%- if mem.boot %}
|
|
- <memory unit='KiB'>{{ mem.boot }}</memory>
|
|
+ <memory unit='KiB'>{{ to_kib(mem.boot) }}</memory>
|
|
{%- endif %}
|
|
{%- if mem.current %}
|
|
- <currentMemory unit='KiB'>{{ mem.current }}</currentMemory>
|
|
+ <currentMemory unit='KiB'>{{ to_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>
|
|
+ <hard_limit unit="KiB">{{ to_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>
|
|
+ <soft_limit unit="KiB">{{ to_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>
|
|
+ <swap_hard_limit unit="KiB">{{ to_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>
|
|
+ <min_guarantee unit="KiB">{{ to_kib(mem.min_guarantee) }}</min_guarantee>
|
|
{%- endif %}
|
|
</memtune>
|
|
{%- endif %}
|
|
+ {%- if numatune %}
|
|
+ <numatune>
|
|
+ {%- if 'memory' in numatune and numatune.memory %}
|
|
+ <memory mode='{{ numatune.memory.mode }}'
|
|
+ {%- if numatune.memory.nodeset %} nodeset='{{ numatune.memory.nodeset }}'{%- endif %}
|
|
+ />
|
|
+ {%- endif %}
|
|
+ {%- if 'memnodes' in numatune and numatune.memnodes %}
|
|
+ {%- for cell_id in numatune['memnodes'] %}
|
|
+ <memnode cellid='{{ cell_id }}' mode='{{ numatune.memnodes[cell_id].mode }}' nodeset='{{ numatune.memnodes[cell_id].nodeset }}'/>
|
|
+ {%- endfor %}
|
|
+ {%- endif %}
|
|
+ </numatune>
|
|
+ {%- endif %}
|
|
+ {%- if mem %}
|
|
+ <memoryBacking>
|
|
+ {%- if mem.hugepages %}
|
|
+ <hugepages>
|
|
+ {%- for page in mem.hugepages %}
|
|
+ <page size="{{ to_kib(page.get("size")) }}" unit="KiB"
|
|
+ {%- if page.get("nodeset") or page.get("nodeset") == 0 %} nodeset='{{ page.get("nodeset") }}'{% endif -%}
|
|
+ />
|
|
+ {%- endfor %}
|
|
+ </hugepages>
|
|
+ {%- if mem.nosharepages %}
|
|
+ <nosharepages/>
|
|
+ {%- endif %}
|
|
+ {%- if mem.locked %}
|
|
+ <locked/>
|
|
+ {%- endif %}
|
|
+ {%- if mem.source %}
|
|
+ <source type="{{ mem.source }}"/>
|
|
+ {%- endif %}
|
|
+ {%- if mem.access %}
|
|
+ <access mode="{{ mem.access }}"/>
|
|
+ {%- endif %}
|
|
+ {%- if mem.allocation %}
|
|
+ <allocation mode="{{ mem.allocation }}"/>
|
|
+ {%- endif %}
|
|
+ {%- if mem.discard %}
|
|
+ <discard/>
|
|
+ {%- endif %}
|
|
+ {%- endif %}
|
|
+ </memoryBacking>
|
|
+ {%- endif %}
|
|
<os {{ boot.os_attrib }}>
|
|
<type arch='{{ arch }}'>{{ os_type }}</type>
|
|
{% if boot %}
|
|
@@ -50,6 +238,18 @@
|
|
<boot dev='{{ dev }}' />
|
|
{% endfor %}
|
|
</os>
|
|
+{%- if clock %}
|
|
+ <clock offset="{{ clock.offset }}"{{ opt_attribute(clock, "adjustment") }}{{ opt_attribute(clock, "timezone") }}>
|
|
+ {%- for timer_name in clock.timers %}
|
|
+ {%- set timer = clock.timers[timer_name] %}
|
|
+ <timer name='{{ timer_name }}'{{ opt_attribute(timer, "track") }}{{ opt_attribute(timer, "tickpolicy") }}{{ opt_attribute(timer, "frequency") }}{{ opt_attribute(timer, "mode") }}{{ opt_attribute(timer, "present", yesno) }}>
|
|
+ {%- if "threshold" in timer or "slew" in timer or "limit" in timer %}
|
|
+ <catchup{{ opt_attribute(timer, "slew") }}{{ opt_attribute(timer, "threshold") }}{{ opt_attribute(timer, "limit") }}/>
|
|
+ {%- endif %}
|
|
+ </timer>
|
|
+ {%- endfor %}
|
|
+ </clock>
|
|
+{%- endif %}
|
|
<on_reboot>{{ on_reboot }}</on_reboot>
|
|
<devices>
|
|
{% for disk in disks %}
|
|
@@ -69,7 +269,7 @@
|
|
<address type='drive' controller='0' bus='0' target='0' unit='{{ disk.index }}' />
|
|
{% endif %}
|
|
{% if disk.driver -%}
|
|
- <driver name='qemu' type='{{ disk.format}}' cache='none' io='native'/>
|
|
+ <driver name='qemu' type='{{ disk.format}}' cache='none' io='{{ disk.io }}'/>
|
|
{% endif %}
|
|
</disk>
|
|
{% endfor %}
|
|
@@ -104,35 +304,39 @@
|
|
address='{{ graphics.listen.address }}'
|
|
{% endif %}/>
|
|
</graphics>
|
|
- {% endif %}
|
|
- {% if serial_type == 'pty' %}
|
|
- <serial type='pty'>
|
|
- <target port='0'/>
|
|
- </serial>
|
|
- {% if console %}
|
|
- <console type='pty'>
|
|
- <target type='serial' port='0'/>
|
|
- </console>
|
|
- {% endif %}
|
|
+
|
|
+ {% if graphics.type == "spice" -%}
|
|
+ <channel type='spicevmc'>
|
|
+ <target type='virtio' name='com.redhat.spice.0'/>
|
|
+ </channel>
|
|
+ {%- endif %}
|
|
{% endif %}
|
|
|
|
- {% if serial_type == 'tcp' %}
|
|
- <serial type='tcp'>
|
|
- <source mode='bind' host='' service='{{ telnet_port }}'/>
|
|
- <protocol type='telnet'/>
|
|
- <target port='0'/>
|
|
+ {%- for serial in serials %}
|
|
+ <serial type='{{ serial.type }}'>
|
|
+ {{ libvirt_chardevs.chardev(serial) }}
|
|
</serial>
|
|
- {% if console %}
|
|
- <console type='tcp'>
|
|
- <source mode='bind' host='' service='{{ telnet_port }}'/>
|
|
- <protocol type='telnet'/>
|
|
- <target type='serial' port='0'/>
|
|
- </console>
|
|
- {% endif %}
|
|
- {% endif %}
|
|
+ {%- endfor %}
|
|
|
|
+ {%- for console in consoles %}
|
|
+ <console type='{{ console.type }}'>
|
|
+ {{ libvirt_chardevs.chardev(console) }}
|
|
+ </console>
|
|
+ {% endfor %}
|
|
+{%- if hypervisor in ["qemu", "kvm"] %}
|
|
+ <channel type='unix'>
|
|
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
|
|
+ </channel>
|
|
+{%- endif %}
|
|
</devices>
|
|
<features>
|
|
<acpi />
|
|
+ <apic />
|
|
+ <pae />
|
|
+{%- if hypervisor_features.get("kvm-hint-dedicated") %}
|
|
+ <kvm>
|
|
+ <hint-dedicated state="on"/>
|
|
+ </kvm>
|
|
+{%- endif %}
|
|
</features>
|
|
</domain>
|
|
diff --git a/salt/utils/xmlutil.py b/salt/utils/xmlutil.py
|
|
index d25f5c8da5..5c187ca7e5 100644
|
|
--- a/salt/utils/xmlutil.py
|
|
+++ b/salt/utils/xmlutil.py
|
|
@@ -157,18 +157,24 @@ def clean_node(parent_map, node, ignored=None):
|
|
:param parent_map: dictionary mapping each node to its parent
|
|
:param node: the node to clean
|
|
:param ignored: a list of ignored attributes.
|
|
+ :return: True if anything has been removed, False otherwise
|
|
"""
|
|
has_text = node.text is not None and node.text.strip()
|
|
parent = parent_map.get(node)
|
|
+ removed = False
|
|
if (
|
|
len(set(node.attrib.keys()) - set(ignored or [])) == 0
|
|
and not list(node)
|
|
and not has_text
|
|
+ and parent
|
|
):
|
|
parent.remove(node)
|
|
+ removed = True
|
|
# Clean parent nodes if needed
|
|
if parent is not None:
|
|
- clean_node(parent_map, parent, ignored)
|
|
+ parent_cleaned = clean_node(parent_map, parent, ignored)
|
|
+ removed = removed or parent_cleaned
|
|
+ return removed
|
|
|
|
|
|
def del_text(parent_map, node):
|
|
@@ -180,6 +186,7 @@ def del_text(parent_map, node):
|
|
parent = parent_map[node]
|
|
parent.remove(node)
|
|
clean_node(parent, node)
|
|
+ return True
|
|
|
|
|
|
def del_attribute(attribute, ignored=None):
|
|
@@ -197,13 +204,54 @@ def del_attribute(attribute, ignored=None):
|
|
|
|
def _do_delete(parent_map, node):
|
|
if attribute not in node.keys():
|
|
- return
|
|
+ return False
|
|
node.attrib.pop(attribute)
|
|
clean_node(parent_map, node, ignored)
|
|
+ return True
|
|
|
|
return _do_delete
|
|
|
|
|
|
+def attribute(path, xpath, attr_name, ignored=None, convert=None):
|
|
+ """
|
|
+ Helper function creating a change_xml mapping entry for a text XML attribute.
|
|
+
|
|
+ :param path: the path to the value in the data
|
|
+ :param xpath: the xpath to the node holding the attribute
|
|
+ :param attr_name: the attribute name
|
|
+ :param ignored: the list of attributes to ignore when cleaning up the node
|
|
+ :param convert: a function used to convert the value
|
|
+ """
|
|
+ entry = {
|
|
+ "path": path,
|
|
+ "xpath": xpath,
|
|
+ "get": lambda n: n.get(attr_name),
|
|
+ "set": lambda n, v: n.set(attr_name, str(v)),
|
|
+ "del": salt.utils.xmlutil.del_attribute(attr_name, ignored),
|
|
+ }
|
|
+ if convert:
|
|
+ entry["convert"] = convert
|
|
+ return entry
|
|
+
|
|
+
|
|
+def int_attribute(path, xpath, attr_name, ignored=None):
|
|
+ """
|
|
+ Helper function creating a change_xml mapping entry for a text XML integer attribute.
|
|
+
|
|
+ :param path: the path to the value in the data
|
|
+ :param xpath: the xpath to the node holding the attribute
|
|
+ :param attr_name: the attribute name
|
|
+ :param ignored: the list of attributes to ignore when cleaning up the node
|
|
+ """
|
|
+ return {
|
|
+ "path": path,
|
|
+ "xpath": xpath,
|
|
+ "get": lambda n: int(n.get(attr_name)) if n.get(attr_name) else None,
|
|
+ "set": lambda n, v: n.set(attr_name, str(v)),
|
|
+ "del": salt.utils.xmlutil.del_attribute(attr_name, ignored),
|
|
+ }
|
|
+
|
|
+
|
|
def change_xml(doc, data, mapping):
|
|
"""
|
|
Change an XML ElementTree document according.
|
|
@@ -237,6 +285,7 @@ def change_xml(doc, data, mapping):
|
|
del
|
|
function deleting the value in the XML.
|
|
Takes two parameters for the parent node and the node matched by the XPath.
|
|
+ Returns True if anything was removed, False otherwise.
|
|
Default is to remove the text value.
|
|
More cleanup may be performed, see the :py:func:`clean_node` function for details.
|
|
|
|
@@ -281,8 +330,17 @@ def change_xml(doc, data, mapping):
|
|
continue
|
|
|
|
if new_value is not None:
|
|
+ # We need to increment ids from arrays since xpath starts at 1
|
|
+ converters = {
|
|
+ p: (lambda n: n + 1)
|
|
+ if "[${}]".format(p) in xpath
|
|
+ else (lambda n: n)
|
|
+ for p in placeholders
|
|
+ }
|
|
ctx = {
|
|
- placeholder: value_item.get(placeholder, "")
|
|
+ placeholder: converters[placeholder](
|
|
+ value_item.get(placeholder, "")
|
|
+ )
|
|
for placeholder in placeholders
|
|
}
|
|
node_xpath = string.Template(xpath).substitute(ctx)
|
|
@@ -299,7 +357,9 @@ def change_xml(doc, data, mapping):
|
|
if convert_fn:
|
|
new_value = convert_fn(new_value)
|
|
|
|
- if str(current_value) != str(new_value):
|
|
+ # Allow custom comparison. Can be useful for almost equal numeric values
|
|
+ compare_fn = param.get("equals", lambda o, n: str(o) == str(n))
|
|
+ if not compare_fn(current_value, new_value):
|
|
set_fn(node, new_value)
|
|
need_update = True
|
|
else:
|
|
@@ -307,17 +367,16 @@ def change_xml(doc, data, mapping):
|
|
del_fn = param.get("del", del_text)
|
|
parent_map = {c: p for p in doc.iter() for c in p}
|
|
for node in nodes:
|
|
- del_fn(parent_map, node)
|
|
- need_update = True
|
|
+ deleted = del_fn(parent_map, node)
|
|
+ need_update = need_update or deleted
|
|
|
|
# Clean the left over XML elements if there were placeholders
|
|
- if placeholders and values[0].get("value") != []:
|
|
+ if placeholders and [v for v in values if v.get("value") != []]:
|
|
all_nodes = set(doc.findall(all_nodes_xpath))
|
|
to_remove = all_nodes - kept_nodes
|
|
del_fn = param.get("del", del_text)
|
|
parent_map = {c: p for p in doc.iter() for c in p}
|
|
for node in to_remove:
|
|
- del_fn(parent_map, node)
|
|
- need_update = True
|
|
-
|
|
+ deleted = del_fn(parent_map, node)
|
|
+ need_update = need_update or deleted
|
|
return need_update
|
|
diff --git a/tests/pytests/unit/modules/virt/conftest.py b/tests/pytests/unit/modules/virt/conftest.py
|
|
index 1c32ae12eb..ec56bdff24 100644
|
|
--- a/tests/pytests/unit/modules/virt/conftest.py
|
|
+++ b/tests/pytests/unit/modules/virt/conftest.py
|
|
@@ -189,3 +189,129 @@ def make_mock_storage_pool():
|
|
return mocked_pool
|
|
|
|
return _make_mock_storage_pool
|
|
+
|
|
+
|
|
+@pytest.fixture
|
|
+def make_capabilities():
|
|
+ def _make_capabilities():
|
|
+ mocked_conn = virt.libvirt.openAuth.return_value
|
|
+ mocked_conn.getCapabilities.return_value = """
|
|
+<capabilities>
|
|
+ <host>
|
|
+ <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
|
|
+ <cpu>
|
|
+ <arch>x86_64</arch>
|
|
+ <model>Nehalem</model>
|
|
+ <vendor>Intel</vendor>
|
|
+ <microcode version='25'/>
|
|
+ <topology sockets='1' cores='4' threads='2'/>
|
|
+ <feature name='vme'/>
|
|
+ <feature name='ds'/>
|
|
+ <feature name='acpi'/>
|
|
+ <pages unit='KiB' size='4'/>
|
|
+ <pages unit='KiB' size='2048'/>
|
|
+ </cpu>
|
|
+ <power_management>
|
|
+ <suspend_mem/>
|
|
+ <suspend_disk/>
|
|
+ <suspend_hybrid/>
|
|
+ </power_management>
|
|
+ <migration_features>
|
|
+ <live/>
|
|
+ <uri_transports>
|
|
+ <uri_transport>tcp</uri_transport>
|
|
+ <uri_transport>rdma</uri_transport>
|
|
+ </uri_transports>
|
|
+ </migration_features>
|
|
+ <topology>
|
|
+ <cells num='1'>
|
|
+ <cell id='0'>
|
|
+ <memory unit='KiB'>12367120</memory>
|
|
+ <pages unit='KiB' size='4'>3091780</pages>
|
|
+ <pages unit='KiB' size='2048'>0</pages>
|
|
+ <distances>
|
|
+ <sibling id='0' value='10'/>
|
|
+ </distances>
|
|
+ <cpus num='8'>
|
|
+ <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
|
|
+ <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
|
|
+ <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
|
|
+ <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
|
|
+ <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
|
|
+ <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
|
|
+ <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
|
|
+ <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
|
|
+ </cpus>
|
|
+ </cell>
|
|
+ </cells>
|
|
+ </topology>
|
|
+ <cache>
|
|
+ <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
|
|
+ </cache>
|
|
+ <secmodel>
|
|
+ <model>apparmor</model>
|
|
+ <doi>0</doi>
|
|
+ </secmodel>
|
|
+ <secmodel>
|
|
+ <model>dac</model>
|
|
+ <doi>0</doi>
|
|
+ <baselabel type='kvm'>+487:+486</baselabel>
|
|
+ <baselabel type='qemu'>+487:+486</baselabel>
|
|
+ </secmodel>
|
|
+ </host>
|
|
+
|
|
+ <guest>
|
|
+ <os_type>hvm</os_type>
|
|
+ <arch name='i686'>
|
|
+ <wordsize>32</wordsize>
|
|
+ <emulator>/usr/bin/qemu-system-i386</emulator>
|
|
+ <machine maxCpus='255'>pc-i440fx-2.6</machine>
|
|
+ <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
|
+ <machine maxCpus='255'>pc-0.12</machine>
|
|
+ <domain type='qemu'/>
|
|
+ <domain type='kvm'>
|
|
+ <emulator>/usr/bin/qemu-kvm</emulator>
|
|
+ <machine maxCpus='255'>pc-i440fx-2.6</machine>
|
|
+ <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
|
+ <machine maxCpus='255'>pc-0.12</machine>
|
|
+ </domain>
|
|
+ </arch>
|
|
+ <features>
|
|
+ <cpuselection/>
|
|
+ <deviceboot/>
|
|
+ <disksnapshot default='on' toggle='no'/>
|
|
+ <acpi default='on' toggle='yes'/>
|
|
+ <apic default='on' toggle='no'/>
|
|
+ <pae/>
|
|
+ <nonpae/>
|
|
+ </features>
|
|
+ </guest>
|
|
+
|
|
+ <guest>
|
|
+ <os_type>hvm</os_type>
|
|
+ <arch name='x86_64'>
|
|
+ <wordsize>64</wordsize>
|
|
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
|
|
+ <machine maxCpus='255'>pc-i440fx-2.6</machine>
|
|
+ <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
|
+ <machine maxCpus='255'>pc-0.12</machine>
|
|
+ <domain type='qemu'/>
|
|
+ <domain type='kvm'>
|
|
+ <emulator>/usr/bin/qemu-kvm</emulator>
|
|
+ <machine maxCpus='255'>pc-i440fx-2.6</machine>
|
|
+ <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
|
+ <machine maxCpus='255'>pc-0.12</machine>
|
|
+ </domain>
|
|
+ </arch>
|
|
+ <features>
|
|
+ <cpuselection/>
|
|
+ <deviceboot/>
|
|
+ <disksnapshot default='on' toggle='no'/>
|
|
+ <acpi default='on' toggle='yes'/>
|
|
+ <apic default='on' toggle='no'/>
|
|
+ </features>
|
|
+ </guest>
|
|
+
|
|
+</capabilities>"""
|
|
+
|
|
+ return _make_capabilities
|
|
diff --git a/tests/pytests/unit/modules/virt/test_domain.py b/tests/pytests/unit/modules/virt/test_domain.py
|
|
index 5f9b45ec9a..347c3bcd88 100644
|
|
--- a/tests/pytests/unit/modules/virt/test_domain.py
|
|
+++ b/tests/pytests/unit/modules/virt/test_domain.py
|
|
@@ -254,3 +254,338 @@ def test_get_disk_convert_volumes(make_mock_vm, make_mock_storage_pool):
|
|
"virtual size": 214748364800,
|
|
},
|
|
} == virt.get_disks("srv01")
|
|
+
|
|
+
|
|
+def test_update_approx_mem(make_mock_vm):
|
|
+ """
|
|
+ test virt.update with memory parameter unchanged thought not exactly equals to the current value.
|
|
+ This may happen since libvirt sometimes rounds the memory value.
|
|
+ """
|
|
+ xml_def = """
|
|
+ <domain type="kvm">
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>3177680</memory>
|
|
+ <currentMemory unit='KiB'>3177680</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <os>
|
|
+ <type arch='x86_64'>hvm</type>
|
|
+ </os>
|
|
+ <on_reboot>restart</on_reboot>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+
|
|
+ ret = virt.update("my_vm", mem={"boot": "3253941043B", "current": "3253941043B"})
|
|
+ assert not ret["definition"]
|
|
+
|
|
+
|
|
+def test_gen_hypervisor_features():
|
|
+ """
|
|
+ Test the virt._gen_xml hypervisor_features handling
|
|
+ """
|
|
+ xml_data = virt._gen_xml(
|
|
+ virt.libvirt.openAuth.return_value,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ {},
|
|
+ {},
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ hypervisor_features={"kvm-hint-dedicated": True},
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ assert "on" == root.find("features/kvm/hint-dedicated").attrib["state"]
|
|
+
|
|
+
|
|
+def test_update_hypervisor_features(make_mock_vm):
|
|
+ """
|
|
+ Test changing the hypervisor features of a guest
|
|
+ """
|
|
+ xml_def = """
|
|
+ <domain type="kvm">
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>524288</memory>
|
|
+ <currentMemory unit='KiB'>524288</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <os>
|
|
+ <type arch='x86_64'>linux</type>
|
|
+ <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
|
|
+ </os>
|
|
+ <features>
|
|
+ <kvm>
|
|
+ <hint-dedicated state="on"/>
|
|
+ </kvm>
|
|
+ </features>
|
|
+ <on_reboot>restart</on_reboot>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+
|
|
+ # Update with no change to the features
|
|
+ ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": True})
|
|
+ assert not ret["definition"]
|
|
+
|
|
+ # Alter the features
|
|
+ ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": False})
|
|
+ assert ret["definition"]
|
|
+ setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
|
|
+ assert "off" == setxml.find("features/kvm/hint-dedicated").get("state")
|
|
+
|
|
+ # Add the features
|
|
+ xml_def = """
|
|
+ <domain type="kvm">
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>524288</memory>
|
|
+ <currentMemory unit='KiB'>524288</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <os>
|
|
+ <type arch='x86_64'>linux</type>
|
|
+ <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
|
|
+ </os>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+ ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": True})
|
|
+ assert ret["definition"]
|
|
+ setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
|
|
+ assert "on" == setxml.find("features/kvm/hint-dedicated").get("state")
|
|
+
|
|
+
|
|
+def test_gen_clock():
|
|
+ """
|
|
+ Test the virt._gen_xml clock property
|
|
+ """
|
|
+ # Localtime with adjustment
|
|
+ xml_data = virt._gen_xml(
|
|
+ virt.libvirt.openAuth.return_value,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ {},
|
|
+ {},
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ clock={"adjustment": 3600, "utc": False},
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ assert "localtime" == root.find("clock").get("offset")
|
|
+ assert "3600" == root.find("clock").get("adjustment")
|
|
+
|
|
+ # Specific timezone
|
|
+ xml_data = virt._gen_xml(
|
|
+ virt.libvirt.openAuth.return_value,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ {},
|
|
+ {},
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ clock={"timezone": "CEST"},
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ assert "timezone" == root.find("clock").get("offset")
|
|
+ assert "CEST" == root.find("clock").get("timezone")
|
|
+
|
|
+ # UTC
|
|
+ xml_data = virt._gen_xml(
|
|
+ virt.libvirt.openAuth.return_value,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ {},
|
|
+ {},
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ clock={"utc": True},
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ assert "utc" == root.find("clock").get("offset")
|
|
+
|
|
+ # Timers
|
|
+ xml_data = virt._gen_xml(
|
|
+ virt.libvirt.openAuth.return_value,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ {},
|
|
+ {},
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ clock={
|
|
+ "timers": {
|
|
+ "tsc": {"frequency": 3504000000, "mode": "native"},
|
|
+ "rtc": {
|
|
+ "tickpolicy": "catchup",
|
|
+ "slew": 4636,
|
|
+ "threshold": 123,
|
|
+ "limit": 2342,
|
|
+ },
|
|
+ "hpet": {"present": False},
|
|
+ },
|
|
+ },
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ assert "utc" == root.find("clock").get("offset")
|
|
+ assert "3504000000" == root.find("clock/timer[@name='tsc']").get("frequency")
|
|
+ assert "native" == root.find("clock/timer[@name='tsc']").get("mode")
|
|
+ assert "catchup" == root.find("clock/timer[@name='rtc']").get("tickpolicy")
|
|
+ assert {"slew": "4636", "threshold": "123", "limit": "2342"} == root.find(
|
|
+ "clock/timer[@name='rtc']/catchup"
|
|
+ ).attrib
|
|
+ assert "no" == root.find("clock/timer[@name='hpet']").get("present")
|
|
+
|
|
+
|
|
+def test_update_clock(make_mock_vm):
|
|
+ """
|
|
+ test virt.update with clock parameter
|
|
+ """
|
|
+ xml_def = """
|
|
+ <domain type="kvm">
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>524288</memory>
|
|
+ <currentMemory unit='KiB'>524288</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <os>
|
|
+ <type arch='x86_64'>linux</type>
|
|
+ <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
|
|
+ </os>
|
|
+ <clock offset="localtime" adjustment="-3600">
|
|
+ <timer name="tsc" frequency="3504000000" mode="native" />
|
|
+ <timer name="kvmclock" present="no" />
|
|
+ </clock>
|
|
+ <on_reboot>restart</on_reboot>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+
|
|
+ # Update with no change to the features
|
|
+ ret = virt.update(
|
|
+ "my_vm",
|
|
+ clock={
|
|
+ "utc": False,
|
|
+ "adjustment": -3600,
|
|
+ "timers": {
|
|
+ "tsc": {"frequency": 3504000000, "mode": "native"},
|
|
+ "kvmclock": {"present": False},
|
|
+ },
|
|
+ },
|
|
+ )
|
|
+ assert not ret["definition"]
|
|
+
|
|
+ # Update
|
|
+ ret = virt.update(
|
|
+ "my_vm",
|
|
+ clock={
|
|
+ "timezone": "CEST",
|
|
+ "timers": {
|
|
+ "rtc": {
|
|
+ "track": "wall",
|
|
+ "tickpolicy": "catchup",
|
|
+ "slew": 4636,
|
|
+ "threshold": 123,
|
|
+ "limit": 2342,
|
|
+ },
|
|
+ "hpet": {"present": True},
|
|
+ },
|
|
+ },
|
|
+ )
|
|
+ assert ret["definition"]
|
|
+ setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
|
|
+ assert "timezone" == setxml.find("clock").get("offset")
|
|
+ assert "CEST" == setxml.find("clock").get("timezone")
|
|
+ assert {"rtc", "hpet"} == {t.get("name") for t in setxml.findall("clock/timer")}
|
|
+ assert "catchup" == setxml.find("clock/timer[@name='rtc']").get("tickpolicy")
|
|
+ assert "wall" == setxml.find("clock/timer[@name='rtc']").get("track")
|
|
+ assert {"slew": "4636", "threshold": "123", "limit": "2342"} == setxml.find(
|
|
+ "clock/timer[@name='rtc']/catchup"
|
|
+ ).attrib
|
|
+ assert "yes" == setxml.find("clock/timer[@name='hpet']").get("present")
|
|
+
|
|
+ # Revert to UTC
|
|
+ ret = virt.update("my_vm", clock={"utc": True, "adjustment": None, "timers": None})
|
|
+ assert ret["definition"]
|
|
+ setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
|
|
+ assert {"offset": "utc"} == setxml.find("clock").attrib
|
|
+ assert setxml.find("clock/timer") is None
|
|
+
|
|
+
|
|
+def test_update_stop_on_reboot_reset(make_mock_vm):
|
|
+ """
|
|
+ Test virt.update to remove the on_reboot=destroy flag
|
|
+ """
|
|
+ xml_def = """
|
|
+ <domain type='kvm'>
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>524288</memory>
|
|
+ <currentMemory unit='KiB'>524288</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <on_reboot>destroy</on_reboot>
|
|
+ <os>
|
|
+ <type arch='x86_64'>hvm</type>
|
|
+ </os>
|
|
+ </domain>"""
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+
|
|
+ ret = virt.update("my_vm")
|
|
+
|
|
+ assert ret["definition"]
|
|
+ define_mock = virt.libvirt.openAuth().defineXML
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ assert "restart" == setxml.find("./on_reboot").text
|
|
+
|
|
+
|
|
+def test_update_stop_on_reboot(make_mock_vm):
|
|
+ """
|
|
+ Test virt.update to add the on_reboot=destroy flag
|
|
+ """
|
|
+ xml_def = """
|
|
+ <domain type='kvm'>
|
|
+ <name>my_vm</name>
|
|
+ <memory unit='KiB'>524288</memory>
|
|
+ <currentMemory unit='KiB'>524288</currentMemory>
|
|
+ <vcpu placement='static'>1</vcpu>
|
|
+ <os>
|
|
+ <type arch='x86_64'>hvm</type>
|
|
+ </os>
|
|
+ </domain>"""
|
|
+ domain_mock = make_mock_vm(xml_def)
|
|
+
|
|
+ ret = virt.update("my_vm", stop_on_reboot=True)
|
|
+
|
|
+ assert ret["definition"]
|
|
+ define_mock = virt.libvirt.openAuth().defineXML
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ assert "destroy" == setxml.find("./on_reboot").text
|
|
+
|
|
+
|
|
+def test_init_no_stop_on_reboot(make_capabilities):
|
|
+ """
|
|
+ Test virt.init to add the on_reboot=restart flag
|
|
+ """
|
|
+ make_capabilities()
|
|
+ with patch.dict(virt.os.__dict__, {"chmod": MagicMock(), "makedirs": MagicMock()}):
|
|
+ with patch.dict(virt.__salt__, {"cmd.run": MagicMock()}):
|
|
+ virt.init("test_vm", 2, 2048, start=False)
|
|
+ define_mock = virt.libvirt.openAuth().defineXML
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ assert "restart" == setxml.find("./on_reboot").text
|
|
+
|
|
+
|
|
+def test_init_stop_on_reboot(make_capabilities):
|
|
+ """
|
|
+ Test virt.init to add the on_reboot=destroy flag
|
|
+ """
|
|
+ make_capabilities()
|
|
+ with patch.dict(virt.os.__dict__, {"chmod": MagicMock(), "makedirs": MagicMock()}):
|
|
+ with patch.dict(virt.__salt__, {"cmd.run": MagicMock()}):
|
|
+ virt.init("test_vm", 2, 2048, stop_on_reboot=True, start=False)
|
|
+ define_mock = virt.libvirt.openAuth().defineXML
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ assert "destroy" == setxml.find("./on_reboot").text
|
|
diff --git a/tests/pytests/unit/utils/test_xmlutil.py b/tests/pytests/unit/utils/test_xmlutil.py
|
|
index 081cc64193..2bcaff3a17 100644
|
|
--- a/tests/pytests/unit/utils/test_xmlutil.py
|
|
+++ b/tests/pytests/unit/utils/test_xmlutil.py
|
|
@@ -16,6 +16,11 @@ def xml_doc():
|
|
<vcpus>
|
|
<vcpu enabled="yes" id="1"/>
|
|
</vcpus>
|
|
+ <memtune>
|
|
+ <hugepages>
|
|
+ <page size="128"/>
|
|
+ </hugepages>
|
|
+ </memtune>
|
|
</domain>
|
|
"""
|
|
)
|
|
@@ -36,6 +41,22 @@ def test_change_xml_text_nochange(xml_doc):
|
|
assert not ret
|
|
|
|
|
|
+def test_change_xml_equals_nochange(xml_doc):
|
|
+ ret = xml.change_xml(
|
|
+ xml_doc,
|
|
+ {"mem": 1023},
|
|
+ [
|
|
+ {
|
|
+ "path": "mem",
|
|
+ "xpath": "memory",
|
|
+ "get": lambda n: int(n.text),
|
|
+ "equals": lambda o, n: abs(o - n) <= 1,
|
|
+ }
|
|
+ ],
|
|
+ )
|
|
+ assert not ret
|
|
+
|
|
+
|
|
def test_change_xml_text_notdefined(xml_doc):
|
|
ret = xml.change_xml(xml_doc, {}, [{"path": "name", "xpath": "name"}])
|
|
assert not ret
|
|
@@ -167,3 +188,23 @@ def test_change_xml_template_remove(xml_doc):
|
|
)
|
|
assert ret
|
|
assert xml_doc.find("vcpus") is None
|
|
+
|
|
+
|
|
+def test_change_xml_template_list(xml_doc):
|
|
+ ret = xml.change_xml(
|
|
+ xml_doc,
|
|
+ {"memtune": {"hugepages": [{"size": "1024"}, {"size": "512"}]}},
|
|
+ [
|
|
+ {
|
|
+ "path": "memtune:hugepages:{id}:size",
|
|
+ "xpath": "memtune/hugepages/page[$id]",
|
|
+ "get": lambda n: n.get("size"),
|
|
+ "set": lambda n, v: n.set("size", v),
|
|
+ "del": xml.del_attribute("size"),
|
|
+ },
|
|
+ ],
|
|
+ )
|
|
+ assert ret
|
|
+ assert ["1024", "512"] == [
|
|
+ n.get("size") for n in xml_doc.findall("memtune/hugepages/page")
|
|
+ ]
|
|
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
|
|
index 83152eda6e..91dee2098d 100644
|
|
--- a/tests/unit/modules/test_virt.py
|
|
+++ b/tests/unit/modules/test_virt.py
|
|
@@ -106,6 +106,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
mock_domain.name.return_value = name
|
|
return mock_domain
|
|
|
|
+ def assertEqualUnit(self, actual, expected, unit="KiB"):
|
|
+ self.assertEqual(actual.get("unit"), unit)
|
|
+ self.assertEqual(actual.text, str(expected))
|
|
+
|
|
def test_disk_profile_merge(self):
|
|
"""
|
|
Test virt._disk_profile() when merging with user-defined disks
|
|
@@ -215,16 +219,14 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="pty",
|
|
- console=True,
|
|
+ serials=[{"type": "pty"}],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
|
|
- self.assertEqual(root.find("devices/console").attrib["type"], "pty")
|
|
|
|
- def test_gen_xml_for_serial_console(self):
|
|
+ def test_gen_xml_for_telnet_serial(self):
|
|
"""
|
|
- Test virt._gen_xml() serial console
|
|
+ Test virt._gen_xml() telnet serial
|
|
"""
|
|
diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
nicp = virt._nic_profile("default", "kvm")
|
|
@@ -238,11 +240,134 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="pty",
|
|
- console=True,
|
|
+ serials=[{"type": "tcp", "port": 22223, "protocol": "telnet"}],
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
|
|
+ self.assertEqual(root.find("devices/serial/source").attrib["service"], "22223")
|
|
+ self.assertEqual(root.find("devices/serial/protocol").attrib["type"], "telnet")
|
|
+
|
|
+ def test_gen_xml_for_telnet_serial_unspecified_port(self):
|
|
+ """
|
|
+ Test virt._gen_xml() telnet serial without any specified port
|
|
+ """
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ serials=[{"type": "tcp"}],
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
|
|
+ self.assertEqual(root.find("devices/serial/source").attrib["service"], "23023")
|
|
+ self.assertFalse("tls" in root.find("devices/serial/source").keys())
|
|
+ self.assertEqual(root.find("devices/serial/protocol").attrib["type"], "telnet")
|
|
+
|
|
+ def test_gen_xml_for_chardev_types(self):
|
|
+ """
|
|
+ Test virt._gen_xml() consoles and serials of various types
|
|
+ """
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ consoles=[
|
|
+ {"type": "pty", "path": "/dev/pts/2", "target_port": 2},
|
|
+ {"type": "pty", "target_type": "usb-serial"},
|
|
+ {"type": "stdio"},
|
|
+ {"type": "file", "path": "/path/to/serial.log"},
|
|
+ ],
|
|
+ serials=[
|
|
+ {"type": "pipe", "path": "/tmp/mypipe"},
|
|
+ {"type": "udp", "host": "127.0.0.1", "port": 1234},
|
|
+ {"type": "tcp", "port": 22223, "protocol": "raw", "tls": True},
|
|
+ {"type": "unix", "path": "/path/to/socket"},
|
|
+ ],
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+
|
|
+ self.assertEqual(root.find("devices/console[1]").attrib["type"], "pty")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/console[1]/source").attrib["path"], "/dev/pts/2"
|
|
+ )
|
|
+ self.assertEqual(root.find("devices/console[1]/target").attrib["port"], "2")
|
|
+
|
|
+ self.assertEqual(root.find("devices/console[2]").attrib["type"], "pty")
|
|
+ self.assertIsNone(root.find("devices/console[2]/source"))
|
|
+ self.assertEqual(
|
|
+ root.find("devices/console[2]/target").attrib["type"], "usb-serial"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(root.find("devices/console[3]").attrib["type"], "stdio")
|
|
+ self.assertIsNone(root.find("devices/console[3]/source"))
|
|
+
|
|
+ self.assertEqual(root.find("devices/console[4]").attrib["type"], "file")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/console[4]/source").attrib["path"], "/path/to/serial.log"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(root.find("devices/serial[1]").attrib["type"], "pipe")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/serial[1]/source").attrib["path"], "/tmp/mypipe"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(root.find("devices/serial[2]").attrib["type"], "udp")
|
|
+ self.assertEqual(root.find("devices/serial[2]/source").attrib["mode"], "bind")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/serial[2]/source").attrib["service"], "1234"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("devices/serial[2]/source").attrib["host"], "127.0.0.1"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(root.find("devices/serial[3]").attrib["type"], "tcp")
|
|
+ self.assertEqual(root.find("devices/serial[3]/source").attrib["mode"], "bind")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/serial[3]/source").attrib["service"], "22223"
|
|
+ )
|
|
+ self.assertEqual(root.find("devices/serial[3]/source").attrib["tls"], "yes")
|
|
+ self.assertEqual(root.find("devices/serial[3]/protocol").attrib["type"], "raw")
|
|
+
|
|
+ self.assertEqual(root.find("devices/serial[4]").attrib["type"], "unix")
|
|
+ self.assertEqual(
|
|
+ root.find("devices/serial[4]/source").attrib["path"], "/path/to/socket"
|
|
+ )
|
|
+
|
|
+ def test_gen_xml_no_nic_console(self):
|
|
+ """
|
|
+ Test virt._gen_xml() console
|
|
+ """
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ 1,
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ consoles=[{"type": "pty"}],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
- self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
|
|
self.assertEqual(root.find("devices/console").attrib["type"], "pty")
|
|
|
|
def test_gen_xml_for_telnet_console(self):
|
|
@@ -261,14 +386,12 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="tcp",
|
|
- console=True,
|
|
- telnet_port=22223,
|
|
+ consoles=[{"type": "tcp", "port": 22223, "protocol": "telnet"}],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
- self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
|
|
self.assertEqual(root.find("devices/console").attrib["type"], "tcp")
|
|
self.assertEqual(root.find("devices/console/source").attrib["service"], "22223")
|
|
+ self.assertEqual(root.find("devices/console/protocol").attrib["type"], "telnet")
|
|
|
|
def test_gen_xml_for_telnet_console_unspecified_port(self):
|
|
"""
|
|
@@ -286,15 +409,12 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="tcp",
|
|
- console=True,
|
|
+ consoles=[{"type": "tcp"}],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
- self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
|
|
self.assertEqual(root.find("devices/console").attrib["type"], "tcp")
|
|
- self.assertIsInstance(
|
|
- int(root.find("devices/console/source").attrib["service"]), int
|
|
- )
|
|
+ self.assertEqual(root.find("devices/console/source").attrib["service"], "23023")
|
|
+ self.assertEqual(root.find("devices/console/protocol").attrib["type"], "telnet")
|
|
|
|
def test_gen_xml_for_serial_no_console(self):
|
|
"""
|
|
@@ -312,8 +432,8 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="pty",
|
|
- console=False,
|
|
+ serials=[{"type": "pty"}],
|
|
+ consoles=[],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
self.assertEqual(root.find("devices/serial").attrib["type"], "pty")
|
|
@@ -335,8 +455,8 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"kvm",
|
|
"hvm",
|
|
"x86_64",
|
|
- serial_type="tcp",
|
|
- console=False,
|
|
+ serials=[{"type": "tcp", "port": 22223, "protocol": "telnet"}],
|
|
+ consoles=[],
|
|
)
|
|
root = ET.fromstring(xml_data)
|
|
self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
|
|
@@ -459,109 +579,493 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "none")
|
|
self.assertFalse("address" in root.find("devices/graphics/listen").attrib)
|
|
|
|
- def test_default_disk_profile_hypervisor_esxi(self):
|
|
+ def test_gen_xml_memory(self):
|
|
"""
|
|
- Test virt._disk_profile() default ESXi profile
|
|
+ Test virt._gen_xml() with advanced memory settings
|
|
"""
|
|
- mock = MagicMock(return_value={})
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._disk_profile(
|
|
- self.mock_conn, "nonexistent", "vmware", None, "test-vm"
|
|
- )
|
|
- self.assertTrue(len(ret) == 1)
|
|
- found = [disk for disk in ret if disk["name"] == "system"]
|
|
- self.assertTrue(bool(found))
|
|
- system = found[0]
|
|
- self.assertEqual(system["format"], "vmdk")
|
|
- self.assertEqual(system["model"], "scsi")
|
|
- self.assertTrue(int(system["size"]) >= 1)
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ 1,
|
|
+ {
|
|
+ "boot": "512m",
|
|
+ "current": "256m",
|
|
+ "max": "1g",
|
|
+ "hard_limit": "1024",
|
|
+ "soft_limit": "512m",
|
|
+ "swap_hard_limit": "1g",
|
|
+ "min_guarantee": "256m",
|
|
+ "hugepages": [
|
|
+ {"nodeset": "", "size": "128m"},
|
|
+ {"nodeset": "0", "size": "256m"},
|
|
+ {"nodeset": "1", "size": "512m"},
|
|
+ ],
|
|
+ "nosharepages": True,
|
|
+ "locked": True,
|
|
+ "source": "file",
|
|
+ "access": "shared",
|
|
+ "allocation": "immediate",
|
|
+ "discard": True,
|
|
+ },
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqualUnit(root.find("memory"), 512 * 1024)
|
|
+ self.assertEqualUnit(root.find("currentMemory"), 256 * 1024)
|
|
+ self.assertEqualUnit(root.find("maxMemory"), 1024 * 1024)
|
|
+ self.assertFalse("slots" in root.find("maxMemory").keys())
|
|
+ self.assertEqualUnit(root.find("memtune/hard_limit"), 1024 * 1024)
|
|
+ self.assertEqualUnit(root.find("memtune/soft_limit"), 512 * 1024)
|
|
+ self.assertEqualUnit(root.find("memtune/swap_hard_limit"), 1024 ** 2)
|
|
+ self.assertEqualUnit(root.find("memtune/min_guarantee"), 256 * 1024)
|
|
+ self.assertEqual(
|
|
+ [
|
|
+ {"nodeset": page.get("nodeset"), "size": page.get("size")}
|
|
+ for page in root.findall("memoryBacking/hugepages/page")
|
|
+ ],
|
|
+ [
|
|
+ {"nodeset": None, "size": str(128 * 1024)},
|
|
+ {"nodeset": "0", "size": str(256 * 1024)},
|
|
+ {"nodeset": "1", "size": str(512 * 1024)},
|
|
+ ],
|
|
+ )
|
|
+ self.assertIsNotNone(root.find("memoryBacking/nosharepages"))
|
|
+ self.assertIsNotNone(root.find("memoryBacking/locked"))
|
|
+ self.assertIsNotNone(root.find("memoryBacking/discard"))
|
|
+ self.assertEqual(root.find("memoryBacking/source").get("type"), "file")
|
|
+ self.assertEqual(root.find("memoryBacking/access").get("mode"), "shared")
|
|
+ self.assertEqual(root.find("memoryBacking/allocation").get("mode"), "immediate")
|
|
|
|
- def test_default_disk_profile_hypervisor_kvm(self):
|
|
+ def test_gen_xml_cpu(self):
|
|
"""
|
|
- Test virt._disk_profile() default KVM profile
|
|
+ Test virt._gen_xml() with CPU advanced properties
|
|
"""
|
|
- mock = MagicMock(side_effect=[{}, "/images/dir"])
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._disk_profile(
|
|
- self.mock_conn, "nonexistent", "kvm", None, "test-vm"
|
|
- )
|
|
- self.assertTrue(len(ret) == 1)
|
|
- found = [disk for disk in ret if disk["name"] == "system"]
|
|
- self.assertTrue(bool(found))
|
|
- system = found[0]
|
|
- self.assertEqual(system["format"], "qcow2")
|
|
- self.assertEqual(system["model"], "virtio")
|
|
- self.assertTrue(int(system["size"]) >= 1)
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ {
|
|
+ "maximum": 12,
|
|
+ "placement": "static",
|
|
+ "cpuset": "0-11",
|
|
+ "current": 5,
|
|
+ "mode": "custom",
|
|
+ "match": "minimum",
|
|
+ "check": "full",
|
|
+ "vendor": "Intel",
|
|
+ "model": {
|
|
+ "name": "core2duo",
|
|
+ "fallback": "allow",
|
|
+ "vendor_id": "GenuineIntel",
|
|
+ },
|
|
+ "cache": {"level": 3, "mode": "emulate"},
|
|
+ "features": {"lahf": "optional", "vmx": "require"},
|
|
+ "vcpus": {
|
|
+ 0: {"enabled": True, "hotpluggable": True},
|
|
+ 1: {"enabled": False},
|
|
+ },
|
|
+ },
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqual(root.find("vcpu").get("current"), "5")
|
|
+ self.assertEqual(root.find("vcpu").get("placement"), "static")
|
|
+ self.assertEqual(root.find("vcpu").get("cpuset"), "0-11")
|
|
+ self.assertEqual(root.find("vcpu").text, "12")
|
|
+ self.assertEqual(root.find("cpu").get("match"), "minimum")
|
|
+ self.assertEqual(root.find("cpu").get("mode"), "custom")
|
|
+ self.assertEqual(root.find("cpu").get("check"), "full")
|
|
+ self.assertEqual(root.find("cpu/vendor").text, "Intel")
|
|
+ self.assertEqual(root.find("cpu/model").text, "core2duo")
|
|
+ self.assertEqual(root.find("cpu/model").get("fallback"), "allow")
|
|
+ self.assertEqual(root.find("cpu/model").get("vendor_id"), "GenuineIntel")
|
|
+ self.assertEqual(root.find("cpu/cache").get("level"), "3")
|
|
+ self.assertEqual(root.find("cpu/cache").get("mode"), "emulate")
|
|
+ self.assertEqual(
|
|
+ {f.get("name"): f.get("policy") for f in root.findall("cpu/feature")},
|
|
+ {"lahf": "optional", "vmx": "require"},
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ v.get("id"): {
|
|
+ "enabled": v.get("enabled"),
|
|
+ "hotpluggable": v.get("hotpluggable"),
|
|
+ }
|
|
+ for v in root.findall("vcpus/vcpu")
|
|
+ },
|
|
+ {
|
|
+ "0": {"enabled": "yes", "hotpluggable": "yes"},
|
|
+ "1": {"enabled": "no", "hotpluggable": None},
|
|
+ },
|
|
+ )
|
|
|
|
- def test_default_disk_profile_hypervisor_xen(self):
|
|
+ def test_gen_xml_cpu_topology(self):
|
|
"""
|
|
- Test virt._disk_profile() default XEN profile
|
|
+ Test virt._gen_xml() with CPU topology
|
|
"""
|
|
- mock = MagicMock(side_effect=[{}, "/images/dir"])
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._disk_profile(
|
|
- self.mock_conn, "nonexistent", "xen", None, "test-vm"
|
|
- )
|
|
- self.assertTrue(len(ret) == 1)
|
|
- found = [disk for disk in ret if disk["name"] == "system"]
|
|
- self.assertTrue(bool(found))
|
|
- system = found[0]
|
|
- self.assertEqual(system["format"], "qcow2")
|
|
- self.assertEqual(system["model"], "xen")
|
|
- self.assertTrue(int(system["size"]) >= 1)
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ {"maximum": 1, "topology": {"sockets": 4, "cores": 16, "threads": 2}},
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqual(root.find("cpu/topology").get("sockets"), "4")
|
|
+ self.assertEqual(root.find("cpu/topology").get("cores"), "16")
|
|
+ self.assertEqual(root.find("cpu/topology").get("threads"), "2")
|
|
|
|
- def test_default_nic_profile_hypervisor_esxi(self):
|
|
+ def test_gen_xml_cpu_numa(self):
|
|
"""
|
|
- Test virt._nic_profile() default ESXi profile
|
|
+ Test virt._gen_xml() with CPU numa settings
|
|
"""
|
|
- mock = MagicMock(return_value={})
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._nic_profile("nonexistent", "vmware")
|
|
- self.assertTrue(len(ret) == 1)
|
|
- eth0 = ret[0]
|
|
- self.assertEqual(eth0["name"], "eth0")
|
|
- self.assertEqual(eth0["type"], "bridge")
|
|
- self.assertEqual(eth0["source"], "DEFAULT")
|
|
- self.assertEqual(eth0["model"], "e1000")
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ {
|
|
+ "maximum": 1,
|
|
+ "numa": {
|
|
+ 0: {
|
|
+ "cpus": "0-3",
|
|
+ "memory": "1g",
|
|
+ "discard": True,
|
|
+ "distances": {0: 10, 1: 20},
|
|
+ },
|
|
+ 1: {"cpus": "4-7", "memory": "2g", "distances": {0: 20, 1: 10}},
|
|
+ },
|
|
+ },
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ cell0 = root.find("cpu/numa/cell[@id='0']")
|
|
+ self.assertEqual(cell0.get("cpus"), "0-3")
|
|
+ self.assertIsNone(cell0.get("unit"))
|
|
+ self.assertEqual(cell0.get("memory"), str(1024 ** 2))
|
|
+ self.assertEqual(cell0.get("discard"), "yes")
|
|
+ self.assertEqual(
|
|
+ {d.get("id"): d.get("value") for d in cell0.findall("distances/sibling")},
|
|
+ {"0": "10", "1": "20"},
|
|
+ )
|
|
|
|
- def test_default_nic_profile_hypervisor_kvm(self):
|
|
- """
|
|
- Test virt._nic_profile() default KVM profile
|
|
- """
|
|
- mock = MagicMock(return_value={})
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._nic_profile("nonexistent", "kvm")
|
|
- self.assertTrue(len(ret) == 1)
|
|
- eth0 = ret[0]
|
|
- self.assertEqual(eth0["name"], "eth0")
|
|
- self.assertEqual(eth0["type"], "bridge")
|
|
- self.assertEqual(eth0["source"], "br0")
|
|
- self.assertEqual(eth0["model"], "virtio")
|
|
+ cell1 = root.find("cpu/numa/cell[@id='1']")
|
|
+ self.assertEqual(cell1.get("cpus"), "4-7")
|
|
+ self.assertIsNone(cell0.get("unit"))
|
|
+ self.assertEqual(cell1.get("memory"), str(2 * 1024 ** 2))
|
|
+ self.assertFalse("discard" in cell1.keys())
|
|
+ self.assertEqual(
|
|
+ {d.get("id"): d.get("value") for d in cell1.findall("distances/sibling")},
|
|
+ {"0": "20", "1": "10"},
|
|
+ )
|
|
|
|
- def test_default_nic_profile_hypervisor_xen(self):
|
|
+ def test_gen_xml_cputune(self):
|
|
"""
|
|
- Test virt._nic_profile() default XEN profile
|
|
+ Test virt._gen_xml() with CPU tuning
|
|
"""
|
|
- mock = MagicMock(return_value={})
|
|
- with patch.dict(
|
|
- virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
- ):
|
|
- ret = virt._nic_profile("nonexistent", "xen")
|
|
- self.assertTrue(len(ret) == 1)
|
|
- eth0 = ret[0]
|
|
- self.assertEqual(eth0["name"], "eth0")
|
|
- self.assertEqual(eth0["type"], "bridge")
|
|
- self.assertEqual(eth0["source"], "br0")
|
|
+ diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
|
|
+ nicp = virt._nic_profile("default", "kvm")
|
|
+ cputune = {
|
|
+ "shares": 2048,
|
|
+ "period": 122000,
|
|
+ "quota": -1,
|
|
+ "global_period": 1000000,
|
|
+ "global_quota": -3,
|
|
+ "emulator_period": 1200000,
|
|
+ "emulator_quota": -10,
|
|
+ "iothread_period": 133000,
|
|
+ "iothread_quota": -1,
|
|
+ "vcpupin": {0: "1-4,^2", 1: "0,1", 2: "2,3", 3: "0,4"},
|
|
+ "emulatorpin": "1-3",
|
|
+ "iothreadpin": {1: "5-6", 2: "7-8"},
|
|
+ "vcpusched": [
|
|
+ {"scheduler": "fifo", "priority": 1, "vcpus": "0"},
|
|
+ {"scheduler": "fifo", "priority": 2, "vcpus": "1"},
|
|
+ {"scheduler": "idle", "priority": 3, "vcpus": "2"},
|
|
+ ],
|
|
+ "iothreadsched": [
|
|
+ {"scheduler": "idle"},
|
|
+ {"scheduler": "batch", "iothreads": "5-7", "priority": 1},
|
|
+ ],
|
|
+ "emulatorsched": {"scheduler": "rr", "priority": 2},
|
|
+ "cachetune": {
|
|
+ "0-3": {
|
|
+ 0: {"level": 3, "type": "both", "size": 3},
|
|
+ 1: {"level": 3, "type": "both", "size": 3},
|
|
+ "monitor": {1: 3, "0-3": 3},
|
|
+ },
|
|
+ "4-5": {"monitor": {4: 3, 5: 2}},
|
|
+ },
|
|
+ "memorytune": {"0-2": {0: 60}, "3-4": {0: 50, 1: 70}},
|
|
+ }
|
|
+ xml_data = virt._gen_xml(
|
|
+ self.mock_conn,
|
|
+ "hello",
|
|
+ {"maximum": 1, "tuning": cputune, "iothreads": 2},
|
|
+ 512,
|
|
+ diskp,
|
|
+ nicp,
|
|
+ "kvm",
|
|
+ "hvm",
|
|
+ "x86_64",
|
|
+ )
|
|
+ root = ET.fromstring(xml_data)
|
|
+ self.assertEqual(root.find("cputune").find("shares").text, "2048")
|
|
+ self.assertEqual(root.find("cputune").find("period").text, "122000")
|
|
+ self.assertEqual(root.find("cputune").find("quota").text, "-1")
|
|
+ self.assertEqual(root.find("cputune").find("global_period").text, "1000000")
|
|
+ self.assertEqual(root.find("cputune").find("global_quota").text, "-3")
|
|
+ self.assertEqual(root.find("cputune").find("emulator_period").text, "1200000")
|
|
+ self.assertEqual(root.find("cputune").find("emulator_quota").text, "-10")
|
|
+ self.assertEqual(root.find("cputune").find("iothread_period").text, "133000")
|
|
+ self.assertEqual(root.find("cputune").find("iothread_quota").text, "-1")
|
|
+ self.assertEqual(
|
|
+ root.find("cputune").find("vcpupin[@vcpu='0']").attrib.get("cpuset"),
|
|
+ "1-4,^2",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune").find("vcpupin[@vcpu='1']").attrib.get("cpuset"), "0,1",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune").find("vcpupin[@vcpu='2']").attrib.get("cpuset"), "2,3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune").find("vcpupin[@vcpu='3']").attrib.get("cpuset"), "0,4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune").find("emulatorpin").attrib.get("cpuset"), "1-3"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune")
|
|
+ .find("iothreadpin[@iothread='1']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "5-6",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("cputune")
|
|
+ .find("iothreadpin[@iothread='2']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "7-8",
|
|
+ )
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ s.get("vcpus"): {
|
|
+ "scheduler": s.get("scheduler"),
|
|
+ "priority": s.get("priority"),
|
|
+ }
|
|
+ for s in root.findall("cputune/vcpusched")
|
|
+ },
|
|
+ {
|
|
+ "0": {"scheduler": "fifo", "priority": "1"},
|
|
+ "1": {"scheduler": "fifo", "priority": "2"},
|
|
+ "2": {"scheduler": "idle", "priority": "3"},
|
|
+ },
|
|
+ )
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ s.get("iothreads"): {
|
|
+ "scheduler": s.get("scheduler"),
|
|
+ "priority": s.get("priority"),
|
|
+ }
|
|
+ for s in root.findall("cputune/iothreadsched")
|
|
+ },
|
|
+ {
|
|
+ None: {"scheduler": "idle", "priority": None},
|
|
+ "5-7": {"scheduler": "batch", "priority": "1"},
|
|
+ },
|
|
+ )
|
|
+ self.assertEqual(root.find("cputune/emulatorsched").get("scheduler"), "rr")
|
|
+ self.assertEqual(root.find("cputune/emulatorsched").get("priority"), "2")
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='0-3']").attrib.get("vcpus"), "0-3"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='0-3']/cache[@id='0']").attrib.get(
|
|
+ "level"
|
|
+ ),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='0-3']/cache[@id='0']").attrib.get(
|
|
+ "type"
|
|
+ ),
|
|
+ "both",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find(
|
|
+ "./cputune/cachetune[@vcpus='0-3']/monitor[@vcpus='1']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='0-3']/monitor[@vcpus='1']"), None
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='4-5']").attrib.get("vcpus"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/cachetune[@vcpus='4-5']/cache[@id='0']"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find(
|
|
+ "./cputune/cachetune[@vcpus='4-5']/monitor[@vcpus='4']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find(
|
|
+ "./cputune/cachetune[@vcpus='4-5']/monitor[@vcpus='5']"
|
|
+ ).attrib.get("level"),
|
|
+ "2",
|
|
+ )
|
|
+ self.assertNotEqual(root.find("./cputune/memorytune[@vcpus='0-2']"), None)
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/memorytune[@vcpus='0-2']/node[@id='0']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "60",
|
|
+ )
|
|
+ self.assertNotEqual(root.find("./cputune/memorytune[@vcpus='3-4']"), None)
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/memorytune[@vcpus='3-4']/node[@id='0']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "50",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ root.find("./cputune/memorytune[@vcpus='3-4']/node[@id='1']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "70",
|
|
+ )
|
|
+ self.assertEqual(root.find("iothreads").text, "2")
|
|
+
|
|
+ def test_default_disk_profile_hypervisor_esxi(self):
|
|
+ """
|
|
+ Test virt._disk_profile() default ESXi profile
|
|
+ """
|
|
+ mock = MagicMock(return_value={})
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._disk_profile(
|
|
+ self.mock_conn, "nonexistent", "vmware", None, "test-vm"
|
|
+ )
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ found = [disk for disk in ret if disk["name"] == "system"]
|
|
+ self.assertTrue(bool(found))
|
|
+ system = found[0]
|
|
+ self.assertEqual(system["format"], "vmdk")
|
|
+ self.assertEqual(system["model"], "scsi")
|
|
+ self.assertTrue(int(system["size"]) >= 1)
|
|
+
|
|
+ def test_default_disk_profile_hypervisor_kvm(self):
|
|
+ """
|
|
+ Test virt._disk_profile() default KVM profile
|
|
+ """
|
|
+ mock = MagicMock(side_effect=[{}, "/images/dir"])
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._disk_profile(
|
|
+ self.mock_conn, "nonexistent", "kvm", None, "test-vm"
|
|
+ )
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ found = [disk for disk in ret if disk["name"] == "system"]
|
|
+ self.assertTrue(bool(found))
|
|
+ system = found[0]
|
|
+ self.assertEqual(system["format"], "qcow2")
|
|
+ self.assertEqual(system["model"], "virtio")
|
|
+ self.assertTrue(int(system["size"]) >= 1)
|
|
+
|
|
+ def test_default_disk_profile_hypervisor_xen(self):
|
|
+ """
|
|
+ Test virt._disk_profile() default XEN profile
|
|
+ """
|
|
+ mock = MagicMock(side_effect=[{}, "/images/dir"])
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._disk_profile(
|
|
+ self.mock_conn, "nonexistent", "xen", None, "test-vm"
|
|
+ )
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ found = [disk for disk in ret if disk["name"] == "system"]
|
|
+ self.assertTrue(bool(found))
|
|
+ system = found[0]
|
|
+ self.assertEqual(system["format"], "qcow2")
|
|
+ self.assertEqual(system["model"], "xen")
|
|
+ self.assertTrue(int(system["size"]) >= 1)
|
|
+
|
|
+ def test_default_nic_profile_hypervisor_esxi(self):
|
|
+ """
|
|
+ Test virt._nic_profile() default ESXi profile
|
|
+ """
|
|
+ mock = MagicMock(return_value={})
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._nic_profile("nonexistent", "vmware")
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ eth0 = ret[0]
|
|
+ self.assertEqual(eth0["name"], "eth0")
|
|
+ self.assertEqual(eth0["type"], "bridge")
|
|
+ self.assertEqual(eth0["source"], "DEFAULT")
|
|
+ self.assertEqual(eth0["model"], "e1000")
|
|
+
|
|
+ def test_default_nic_profile_hypervisor_kvm(self):
|
|
+ """
|
|
+ Test virt._nic_profile() default KVM profile
|
|
+ """
|
|
+ mock = MagicMock(return_value={})
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._nic_profile("nonexistent", "kvm")
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ eth0 = ret[0]
|
|
+ self.assertEqual(eth0["name"], "eth0")
|
|
+ self.assertEqual(eth0["type"], "bridge")
|
|
+ self.assertEqual(eth0["source"], "br0")
|
|
+ self.assertEqual(eth0["model"], "virtio")
|
|
+
|
|
+ def test_default_nic_profile_hypervisor_xen(self):
|
|
+ """
|
|
+ Test virt._nic_profile() default XEN profile
|
|
+ """
|
|
+ mock = MagicMock(return_value={})
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"config.get": mock} # pylint: disable=no-member
|
|
+ ):
|
|
+ ret = virt._nic_profile("nonexistent", "xen")
|
|
+ self.assertTrue(len(ret) == 1)
|
|
+ eth0 = ret[0]
|
|
+ self.assertEqual(eth0["name"], "eth0")
|
|
+ self.assertEqual(eth0["type"], "bridge")
|
|
+ self.assertEqual(eth0["source"], "br0")
|
|
self.assertFalse(eth0["model"])
|
|
|
|
def test_gen_vol_xml_esx(self):
|
|
@@ -1836,6 +2340,8 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
<alias name='video0'/>
|
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
|
</video>
|
|
+ <serial type='pty'/>
|
|
+ <console type='pty'/>
|
|
</devices>
|
|
</domain>
|
|
""".format(
|
|
@@ -1896,10 +2402,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
),
|
|
)
|
|
|
|
- # Update vcpus case
|
|
+ # test cpu passed as an integer case
|
|
setvcpus_mock = MagicMock(return_value=0)
|
|
domain_mock.setVcpusFlags = setvcpus_mock
|
|
self.assertEqual(
|
|
@@ -1914,142 +2421,400 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
self.assertEqual(setxml.find("vcpu").text, "2")
|
|
self.assertEqual(setvcpus_mock.call_args[0][0], 2)
|
|
+ define_mock.reset_mock()
|
|
|
|
- boot = {
|
|
- "kernel": "/root/f8-i386-vmlinuz",
|
|
- "initrd": "/root/f8-i386-initrd",
|
|
- "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/",
|
|
+ # test updating vcpu attribute
|
|
+ vcpu = {
|
|
+ "placement": "static",
|
|
+ "cpuset": "0-11",
|
|
+ "current": 5,
|
|
+ "maximum": 12,
|
|
}
|
|
-
|
|
- # Update boot devices case
|
|
- define_mock.reset_mock()
|
|
self.assertEqual(
|
|
{
|
|
"definition": True,
|
|
+ "cpu": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", boot_dev="cdrom network hd"),
|
|
+ virt.update("my_vm", cpu=vcpu),
|
|
)
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("vcpu").text, "12")
|
|
+ self.assertEqual(setxml.find("vcpu").attrib["placement"], "static")
|
|
self.assertEqual(
|
|
- ["cdrom", "network", "hd"],
|
|
- [node.get("dev") for node in setxml.findall("os/boot")],
|
|
+ setxml.find("vcpu").attrib["cpuset"], "0,1,2,3,4,5,6,7,8,9,10,11"
|
|
)
|
|
+ self.assertEqual(setxml.find("vcpu").attrib["current"], "5")
|
|
|
|
- # Update unchanged boot devices case
|
|
- define_mock.reset_mock()
|
|
+ # test adding vcpus elements
|
|
+ vcpus = {
|
|
+ "vcpus": {
|
|
+ "0": {"enabled": True, "hotpluggable": False, "order": 1},
|
|
+ "1": {"enabled": False, "hotpluggable": True},
|
|
+ }
|
|
+ }
|
|
self.assertEqual(
|
|
{
|
|
- "definition": False,
|
|
+ "definition": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", boot_dev="hd"),
|
|
+ virt.update("my_vm", cpu=vcpus),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["id"], "0")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["enabled"], "yes")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='0']").attrib["hotpluggable"], "no"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["order"], "1")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='1']").attrib["id"], "1")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='1']").attrib["enabled"], "no")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='1']").attrib["hotpluggable"], "yes"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='1']").attrib.get("order"), None
|
|
)
|
|
- define_mock.assert_not_called()
|
|
|
|
- # Update with boot parameter case
|
|
- define_mock.reset_mock()
|
|
+ # test adding cpu attribute
|
|
+ cpu_atr = {"mode": "custom", "match": "exact", "check": "full"}
|
|
self.assertEqual(
|
|
{
|
|
"definition": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", boot=boot),
|
|
- )
|
|
- setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
- self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
|
|
- self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
|
|
- self.assertEqual(
|
|
- setxml.find("os").find("cmdline").text,
|
|
- "console=ttyS0 ks=http://example.com/f8-i386/os/",
|
|
+ virt.update("my_vm", cpu=cpu_atr),
|
|
)
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
- self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
|
|
- self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
|
|
- self.assertEqual(
|
|
- setxml.find("os").find("cmdline").text,
|
|
- "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(setxml.find("cpu").attrib["mode"], "custom")
|
|
+ self.assertEqual(setxml.find("cpu").attrib["match"], "exact")
|
|
+ self.assertEqual(setxml.find("cpu").attrib["check"], "full")
|
|
+
|
|
+ # test adding cpu model
|
|
+ cpu_model = {
|
|
+ "model": {
|
|
+ "name": "coreduo",
|
|
+ "fallback": "allow",
|
|
+ "vendor_id": "Genuine20201",
|
|
+ }
|
|
}
|
|
-
|
|
self.assertEqual(
|
|
{
|
|
"definition": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", boot=boot_uefi),
|
|
+ virt.update("my_vm", cpu=cpu_model),
|
|
)
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
self.assertEqual(
|
|
- setxml.find("os").find("loader").text, "/usr/share/OVMF/OVMF_CODE.fd"
|
|
+ setxml.find("cpu").find("model").attrib.get("vendor_id"), "Genuine20201"
|
|
)
|
|
- self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
|
|
- self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
|
|
self.assertEqual(
|
|
- setxml.find("os").find("nvram").attrib["template"],
|
|
- "/usr/share/OVMF/OVMF_VARS.ms.fd",
|
|
+ setxml.find("cpu").find("model").attrib.get("fallback"), "allow"
|
|
)
|
|
+ self.assertEqual(setxml.find("cpu").find("model").text, "coreduo")
|
|
|
|
+ # test adding cpu vendor
|
|
+ cpu_vendor = {"vendor": "Intel"}
|
|
self.assertEqual(
|
|
{
|
|
"definition": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", boot={"efi": True}),
|
|
+ virt.update("my_vm", cpu=cpu_vendor),
|
|
)
|
|
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 memtune parameter case
|
|
- memtune = {
|
|
- "soft_limit": "0.5g",
|
|
- "hard_limit": "1024",
|
|
- "swap_hard_limit": "2048m",
|
|
- "min_guarantee": "1 g",
|
|
- }
|
|
+ self.assertEqual(setxml.find("cpu").find("vendor").text, "Intel")
|
|
|
|
+ # test adding cpu topology
|
|
+ cpu_topology = {"topology": {"sockets": 1, "cores": 12, "threads": 1}}
|
|
self.assertEqual(
|
|
{
|
|
"definition": True,
|
|
"disk": {"attached": [], "detached": [], "updated": []},
|
|
"interface": {"attached": [], "detached": []},
|
|
},
|
|
- virt.update("my_vm", mem=memtune),
|
|
+ virt.update("my_vm", cpu=cpu_topology),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("sockets"), "1")
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("cores"), "12")
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("threads"), "1")
|
|
+
|
|
+ # test adding cache
|
|
+ cpu_cache = {"cache": {"mode": "emulate", "level": 3}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", cpu=cpu_cache),
|
|
)
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("cache").attrib.get("level"), "3")
|
|
+ self.assertEqual(setxml.find("cpu").find("cache").attrib.get("mode"), "emulate")
|
|
|
|
+ # test adding feature
|
|
+ cpu_feature = {"features": {"lahf": "optional", "pcid": "disable"}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", cpu=cpu_feature),
|
|
+ )
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
self.assertEqual(
|
|
- setxml.find("memtune").find("soft_limit").text, str(int(0.5 * 1024 ** 3))
|
|
+ setxml.find("./cpu/feature[@name='pcid']").attrib.get("policy"), "disable"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/feature[@name='lahf']").attrib.get("policy"), "optional"
|
|
)
|
|
- self.assertEqual(setxml.find("memtune").find("soft_limit").get("unit"), "bytes")
|
|
+
|
|
+ # test adding numa cell
|
|
+ numa_cell = {
|
|
+ "numa": {
|
|
+ "0": {
|
|
+ "cpus": "0-3",
|
|
+ "memory": "1g",
|
|
+ "discard": True,
|
|
+ "distances": {0: 10, 1: 21, 2: 31, 3: 41},
|
|
+ },
|
|
+ "1": {
|
|
+ "cpus": "4-6",
|
|
+ "memory": "0.5g",
|
|
+ "discard": False,
|
|
+ "memAccess": "shared",
|
|
+ "distances": {0: 21, 1: 10, 2: 15, 3: 30},
|
|
+ },
|
|
+ }
|
|
+ }
|
|
self.assertEqual(
|
|
- setxml.find("memtune").find("hard_limit").text, str(1024 * 1024 ** 2)
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", cpu=numa_cell),
|
|
)
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
self.assertEqual(
|
|
- setxml.find("memtune").find("swap_hard_limit").text, str(2048 * 1024 ** 2)
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["cpus"], "0,1,2,3"
|
|
)
|
|
self.assertEqual(
|
|
- setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["memory"], str(1024 ** 3)
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./cpu/numa/cell/[@id='0']").get("unit"), "bytes")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["discard"], "yes"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "10",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "21",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "31",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='3']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "41",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["cpus"], "4,5,6"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memory"],
|
|
+ str(int(1024 ** 3 / 2)),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").get("unit"), "bytes",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["discard"], "no"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memAccess"], "shared"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "21",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "10",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "15",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='3']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "30",
|
|
+ )
|
|
+
|
|
+ # Update boot parameter case
|
|
+ boot = {
|
|
+ "kernel": "/root/f8-i386-vmlinuz",
|
|
+ "initrd": "/root/f8-i386-initrd",
|
|
+ "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/",
|
|
+ }
|
|
+
|
|
+ # 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")],
|
|
+ )
|
|
+
|
|
+ # 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,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", boot=boot),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
|
|
+ self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("cmdline").text,
|
|
+ "console=ttyS0 ks=http://example.com/f8-i386/os/",
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz")
|
|
+ self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd")
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("cmdline").text,
|
|
+ "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,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", boot=boot_uefi),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("loader").text, "/usr/share/OVMF/OVMF_CODE.fd"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
|
|
+ self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("nvram").attrib["template"],
|
|
+ "/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 memtune parameter case
|
|
+ memtune = {
|
|
+ "soft_limit": "0.5g",
|
|
+ "hard_limit": "1024",
|
|
+ "swap_hard_limit": "2048m",
|
|
+ "min_guarantee": "1 g",
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", mem=memtune),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqualUnit(
|
|
+ setxml.find("memtune").find("soft_limit"), int(0.5 * 1024 ** 3), "bytes"
|
|
+ )
|
|
+ self.assertEqualUnit(
|
|
+ setxml.find("memtune").find("hard_limit"), 1024 * 1024 ** 2, "bytes"
|
|
+ )
|
|
+ self.assertEqualUnit(
|
|
+ setxml.find("memtune").find("swap_hard_limit"), 2048 * 1024 ** 2, "bytes"
|
|
+ )
|
|
+ self.assertEqualUnit(
|
|
+ setxml.find("memtune").find("min_guarantee"), 1 * 1024 ** 3, "bytes"
|
|
)
|
|
|
|
invalid_unit = {"soft_limit": "2HB"}
|
|
@@ -2064,6 +2829,50 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
with self.assertRaises(SaltInvocationError):
|
|
virt.update("my_vm", mem=invalid_number)
|
|
|
|
+ # Update numatune case
|
|
+ numatune = {
|
|
+ "memory": {"mode": "strict", "nodeset": 1},
|
|
+ "memnodes": {
|
|
+ 0: {"mode": "strict", "nodeset": 1},
|
|
+ 1: {"mode": "preferred", "nodeset": 2},
|
|
+ },
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", numatune=numatune),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("mode"), "strict"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("nodeset"), "1"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("mode"), "strict"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("nodeset"), "1"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='1']").attrib.get("mode"),
|
|
+ "preferred",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='1']").attrib.get("nodeset"), "2"
|
|
+ )
|
|
+
|
|
# Update memory case
|
|
setmem_mock = MagicMock(return_value=0)
|
|
domain_mock.setMemoryFlags = setmem_mock
|
|
@@ -2115,37 +2924,250 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
|
|
self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
|
|
|
|
- # Update disks case
|
|
- devattach_mock = MagicMock(return_value=0)
|
|
- devdetach_mock = MagicMock(return_value=0)
|
|
- domain_mock.attachDevice = devattach_mock
|
|
- domain_mock.detachDevice = devdetach_mock
|
|
- mock_chmod = MagicMock()
|
|
- mock_run = MagicMock()
|
|
- with patch.dict(
|
|
- os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()}
|
|
- ): # pylint: disable=no-member
|
|
- with patch.dict(
|
|
- virt.__salt__, {"cmd.run": mock_run}
|
|
- ): # pylint: disable=no-member
|
|
- ret = virt.update(
|
|
- "my_vm",
|
|
- disk_profile="default",
|
|
- disks=[
|
|
- {
|
|
- "name": "cddrive",
|
|
- "device": "cdrom",
|
|
- "source_file": None,
|
|
- "model": "ide",
|
|
- },
|
|
- {"name": "added", "size": 2048},
|
|
- ],
|
|
- )
|
|
- added_disk_path = os.path.join(
|
|
- virt.__salt__["config.get"]("virt:images"), "my_vm_added.qcow2"
|
|
- ) # pylint: disable=no-member
|
|
- self.assertEqual(
|
|
- mock_run.call_args[0][0],
|
|
+ # update memory backing case
|
|
+ mem_back = {
|
|
+ "hugepages": [
|
|
+ {"nodeset": "1-5,^4", "size": "1g"},
|
|
+ {"nodeset": "4", "size": "2g"},
|
|
+ ],
|
|
+ "nosharepages": True,
|
|
+ "locked": True,
|
|
+ "source": "file",
|
|
+ "access": "shared",
|
|
+ "allocation": "immediate",
|
|
+ "discard": True,
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", mem=mem_back),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ p.get("nodeset"): {"size": p.get("size"), "unit": p.get("unit")}
|
|
+ for p in setxml.findall("memoryBacking/hugepages/page")
|
|
+ },
|
|
+ {
|
|
+ "1,2,3,5": {"size": str(1024 ** 3), "unit": "bytes"},
|
|
+ "4": {"size": str(2 * 1024 ** 3), "unit": "bytes"},
|
|
+ },
|
|
+ )
|
|
+ self.assertNotEqual(setxml.find("./memoryBacking/nosharepages"), None)
|
|
+ self.assertIsNone(setxml.find("./memoryBacking/nosharepages").text)
|
|
+ self.assertEqual([], setxml.find("./memoryBacking/nosharepages").keys())
|
|
+ self.assertNotEqual(setxml.find("./memoryBacking/locked"), None)
|
|
+ self.assertIsNone(setxml.find("./memoryBacking/locked").text)
|
|
+ self.assertEqual([], setxml.find("./memoryBacking/locked").keys())
|
|
+ self.assertEqual(setxml.find("./memoryBacking/source").attrib["type"], "file")
|
|
+ self.assertEqual(setxml.find("./memoryBacking/access").attrib["mode"], "shared")
|
|
+ self.assertNotEqual(setxml.find("./memoryBacking/discard"), None)
|
|
+
|
|
+ # test adding iothreads
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", cpu={"iothreads": 5}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("iothreads").text, "5")
|
|
+
|
|
+ # test adding cpu tune parameters
|
|
+ cputune = {
|
|
+ "shares": 2048,
|
|
+ "period": 122000,
|
|
+ "quota": -1,
|
|
+ "global_period": 1000000,
|
|
+ "global_quota": -3,
|
|
+ "emulator_period": 1200000,
|
|
+ "emulator_quota": -10,
|
|
+ "iothread_period": 133000,
|
|
+ "iothread_quota": -1,
|
|
+ "vcpupin": {0: "1-4,^2", 1: "0,1", 2: "2,3", 3: "0,4"},
|
|
+ "emulatorpin": "1-3",
|
|
+ "iothreadpin": {1: "5-6", 2: "7-8"},
|
|
+ "vcpusched": [
|
|
+ {"scheduler": "fifo", "priority": 1, "vcpus": "0"},
|
|
+ {"scheduler": "fifo", "priotity": 2, "vcpus": "1"},
|
|
+ {"scheduler": "idle", "priotity": 3, "vcpus": "2"},
|
|
+ ],
|
|
+ "iothreadsched": [{"scheduler": "batch", "iothreads": "7"}],
|
|
+ "cachetune": {
|
|
+ "0-3": {
|
|
+ 0: {"level": 3, "type": "both", "size": 3},
|
|
+ 1: {"level": 3, "type": "both", "size": 3},
|
|
+ "monitor": {1: 3, "0-3": 3},
|
|
+ },
|
|
+ "4-5": {"monitor": {4: 3, 5: 2}},
|
|
+ },
|
|
+ "memorytune": {"0-2": {0: 60}, "3-4": {0: 50, 1: 70}},
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", cpu={"tuning": cputune}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cputune").find("shares").text, "2048")
|
|
+ self.assertEqual(setxml.find("cputune").find("period").text, "122000")
|
|
+ self.assertEqual(setxml.find("cputune").find("quota").text, "-1")
|
|
+ self.assertEqual(setxml.find("cputune").find("global_period").text, "1000000")
|
|
+ self.assertEqual(setxml.find("cputune").find("global_quota").text, "-3")
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_period").text, "1200000")
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_quota").text, "-10")
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_period").text, "133000")
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_quota").text, "-1")
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='0']").attrib.get("cpuset"),
|
|
+ "1,3,4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='1']").attrib.get("cpuset"),
|
|
+ "0,1",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='2']").attrib.get("cpuset"),
|
|
+ "2,3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='3']").attrib.get("cpuset"),
|
|
+ "0,4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("emulatorpin").attrib.get("cpuset"), "1,2,3"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("iothreadpin[@iothread='1']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "5,6",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("iothreadpin[@iothread='2']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "7,8",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpusched[@vcpus='0']").attrib.get("priority"),
|
|
+ "1",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("vcpusched[@vcpus='0']")
|
|
+ .attrib.get("scheduler"),
|
|
+ "fifo",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("iothreadsched").attrib.get("iothreads"), "7"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("iothreadsched").attrib.get("scheduler"),
|
|
+ "batch",
|
|
+ )
|
|
+ self.assertIsNotNone(setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']"))
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("type"),
|
|
+ "both",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='1']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='1']"),
|
|
+ None,
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='4,5']").attrib.get("vcpus"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='4,5']/cache[@id='0']"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='4,5']/monitor[@vcpus='4']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='4,5']/monitor[@vcpus='5']"
|
|
+ ).attrib.get("level"),
|
|
+ "2",
|
|
+ )
|
|
+ self.assertNotEqual(setxml.find("./cputune/memorytune[@vcpus='0,1,2']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/memorytune[@vcpus='0,1,2']/node[@id='0']"
|
|
+ ).attrib.get("bandwidth"),
|
|
+ "60",
|
|
+ )
|
|
+ self.assertNotEqual(setxml.find("./cputune/memorytune[@vcpus='3,4']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='0']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "50",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='1']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "70",
|
|
+ )
|
|
+
|
|
+ # Update disks case
|
|
+ devattach_mock = MagicMock(return_value=0)
|
|
+ devdetach_mock = MagicMock(return_value=0)
|
|
+ domain_mock.attachDevice = devattach_mock
|
|
+ domain_mock.detachDevice = devdetach_mock
|
|
+ mock_chmod = MagicMock()
|
|
+ mock_run = MagicMock()
|
|
+ with patch.dict(
|
|
+ os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()}
|
|
+ ): # pylint: disable=no-member
|
|
+ with patch.dict(
|
|
+ virt.__salt__, {"cmd.run": mock_run}
|
|
+ ): # pylint: disable=no-member
|
|
+ ret = virt.update(
|
|
+ "my_vm",
|
|
+ disk_profile="default",
|
|
+ disks=[
|
|
+ {
|
|
+ "name": "cddrive",
|
|
+ "device": "cdrom",
|
|
+ "source_file": None,
|
|
+ "model": "ide",
|
|
+ },
|
|
+ {"name": "added", "size": 2048, "iothreads": True},
|
|
+ ],
|
|
+ )
|
|
+ added_disk_path = os.path.join(
|
|
+ virt.__salt__["config.get"]("virt:images"), "my_vm_added.qcow2"
|
|
+ ) # pylint: disable=no-member
|
|
+ self.assertEqual(
|
|
+ mock_run.call_args[0][0],
|
|
'qemu-img create -f qcow2 "{}" 2048M'.format(added_disk_path),
|
|
)
|
|
self.assertEqual(mock_chmod.call_args[0][0], added_disk_path)
|
|
@@ -2170,6 +3192,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
self.assertEqual(devattach_mock.call_count, 2)
|
|
self.assertEqual(devdetach_mock.call_count, 2)
|
|
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ "threads", setxml.find("devices/disk[3]/driver").get("io")
|
|
+ )
|
|
+
|
|
# Update nics case
|
|
yaml_config = """
|
|
virt:
|
|
@@ -2244,6 +3271,19 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
self.assertEqual("vnc", setxml.find("devices/graphics").get("type"))
|
|
|
|
+ # Serial and console test case
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": False,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("my_vm", serials=[{"type": "tcp"}], consoles=[{"type": "tcp"}]),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("devices/serial").attrib["type"], "pty")
|
|
+ self.assertEqual(setxml.find("devices/console").attrib["type"], "pty")
|
|
+
|
|
# Update with no diff case
|
|
pool_mock = MagicMock()
|
|
default_pool_desc = "<pool type='dir'></pool>"
|
|
@@ -2644,48 +3684,6 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
<loader>/usr/share/old/OVMF_CODE.fd</loader>
|
|
<nvram>/usr/share/old/OVMF_VARS.ms.fd</nvram>
|
|
</os>
|
|
- <devices>
|
|
- <disk type='file' device='disk'>
|
|
- <driver name='qemu' type='qcow2'/>
|
|
- <source file='{0}{1}vm_with_boot_param_system.qcow2'/>
|
|
- <backingStore/>
|
|
- <target dev='vda' bus='virtio'/>
|
|
- <alias name='virtio-disk0'/>
|
|
- <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
|
|
- </disk>
|
|
- <disk type='file' device='disk'>
|
|
- <driver name='qemu' type='qcow2'/>
|
|
- <source file='{0}{1}vm_with_boot_param_data.qcow2'/>
|
|
- <backingStore/>
|
|
- <target dev='vdb' bus='virtio'/>
|
|
- <alias name='virtio-disk1'/>
|
|
- <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/>
|
|
- </disk>
|
|
- <interface type='network'>
|
|
- <mac address='52:54:00:39:02:b1'/>
|
|
- <source network='default' bridge='virbr0'/>
|
|
- <target dev='vnet0'/>
|
|
- <model type='virtio'/>
|
|
- <alias name='net0'/>
|
|
- <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
|
|
- </interface>
|
|
- <interface type='network'>
|
|
- <mac address='52:54:00:39:02:b2'/>
|
|
- <source network='oldnet' bridge='virbr1'/>
|
|
- <target dev='vnet1'/>
|
|
- <model type='virtio'/>
|
|
- <alias name='net1'/>
|
|
- <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
|
|
- </interface>
|
|
- <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'>
|
|
- <listen type='address' address='127.0.0.1'/>
|
|
- </graphics>
|
|
- <video>
|
|
- <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
|
|
- <alias name='video0'/>
|
|
- <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
|
- </video>
|
|
- </devices>
|
|
</domain>
|
|
"""
|
|
domain_mock_boot = self.set_mock_vm("vm_with_boot_param", xml_boot)
|
|
@@ -2722,71 +3720,909 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
)
|
|
|
|
self.assertEqual(
|
|
- {
|
|
- "definition": True,
|
|
- "disk": {"attached": [], "detached": [], "updated": []},
|
|
- "interface": {"attached": [], "detached": []},
|
|
- },
|
|
- virt.update("vm_with_boot_param", boot=uefi_boot_new),
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_boot_param", boot=uefi_boot_new),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("loader").text, "/usr/share/new/OVMF_CODE.fd"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
|
|
+ self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
|
|
+ self.assertEqual(
|
|
+ setxml.find("os").find("nvram").attrib["template"],
|
|
+ "/usr/share/new/OVMF_VARS.ms.fd",
|
|
+ )
|
|
+
|
|
+ kernel_none = {
|
|
+ "kernel": None,
|
|
+ "initrd": None,
|
|
+ "cmdline": None,
|
|
+ }
|
|
+
|
|
+ uefi_none = {"loader": None, "nvram": None}
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_boot_param", boot=kernel_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("os").find("kernel"), None)
|
|
+ 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,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_boot_param", boot=uefi_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("os").find("loader"), None)
|
|
+ self.assertEqual(setxml.find("os").find("nvram"), None)
|
|
+
|
|
+ def test_update_existing_numatune_params(self):
|
|
+ """
|
|
+ Test virt.update() with existing numatune parameters.
|
|
+ """
|
|
+ xml_numatune = """
|
|
+ <domain type='kvm' id='8'>
|
|
+ <name>vm_with_numatune_param</name>
|
|
+ <memory unit='KiB'>1048576</memory>
|
|
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|
+ <maxMemory slots="12" unit="bytes">1048576</maxMemory>
|
|
+ <vcpu placement='auto'>1</vcpu>
|
|
+ <numatune>
|
|
+ <memory mode="strict" nodeset="0-11"/>
|
|
+ <memnode cellid="1" mode="strict" nodeset="3"/>
|
|
+ <memnode cellid="3" mode="preferred" nodeset="7"/>
|
|
+ </numatune>
|
|
+ <os>
|
|
+ <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
|
+ </os>
|
|
+ <on_reboot>restart</on_reboot>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = self.set_mock_vm("vm_with_numatune_param", xml_numatune)
|
|
+ domain_mock.OSType = MagicMock(return_value="hvm")
|
|
+ define_mock = MagicMock(return_value=True)
|
|
+ self.mock_conn.defineXML = define_mock
|
|
+
|
|
+ # test update existing numatune node
|
|
+ numatune = {
|
|
+ "memory": {"mode": "preferred", "nodeset": "0-5"},
|
|
+ "memnodes": {
|
|
+ 0: {"mode": "strict", "nodeset": "4"},
|
|
+ 3: {"mode": "preferred", "nodeset": "7"},
|
|
+ 4: {"mode": "strict", "nodeset": "6"},
|
|
+ },
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_numatune_param", numatune=numatune),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("mode"), "preferred"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("nodeset"),
|
|
+ ",".join([str(i) for i in range(0, 6)]),
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("mode"), "strict"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("nodeset"), "4"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(setxml.find("./numatune/memnode/[@cellid='1']"), None)
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='3']").attrib.get("mode"),
|
|
+ "preferred",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='3']").attrib.get("nodeset"), "7"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='4']").attrib.get("mode"), "strict"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='4']").attrib.get("nodeset"), "6"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(setxml.find("./numatune/memnode/[@cellid='2']"), None)
|
|
+
|
|
+ numatune_mem_none = {
|
|
+ "memory": None,
|
|
+ "memnodes": {
|
|
+ 0: {"mode": "strict", "nodeset": "4"},
|
|
+ 3: {"mode": "preferred", "nodeset": "7"},
|
|
+ 4: {"mode": "strict", "nodeset": "6"},
|
|
+ },
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_numatune_param", numatune=numatune_mem_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("numatune").find("memory"), None)
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("mode"), "strict"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='0']").attrib.get("nodeset"), "4"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='3']").attrib.get("mode"),
|
|
+ "preferred",
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./numatune/memnode/[@cellid='3']").attrib.get("nodeset"), "7"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(setxml.find("./numatune/memnode/[@cellid='2']"), None)
|
|
+
|
|
+ numatune_mnodes_none = {
|
|
+ "memory": {"mode": "preferred", "nodeset": "0-5"},
|
|
+ "memnodes": None,
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_numatune_param", numatune=numatune_mnodes_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("mode"), "preferred"
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("numatune").find("memory").attrib.get("nodeset"),
|
|
+ ",".join([str(i) for i in range(0, 6)]),
|
|
+ )
|
|
+
|
|
+ self.assertEqual(setxml.find("./numatune/memnode"), None)
|
|
+
|
|
+ numatune_without_change = {
|
|
+ "memory": {"mode": "strict", "nodeset": "0-5,6,7-11"},
|
|
+ "memnodes": {
|
|
+ 1: {"mode": "strict", "nodeset": "3"},
|
|
+ 3: {"mode": "preferred", "nodeset": "7"},
|
|
+ },
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": False,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_numatune_param", numatune=numatune_without_change),
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update(
|
|
+ "vm_with_numatune_param", numatune={"memory": None, "memnodes": None}
|
|
+ ),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("numatune"), None)
|
|
+
|
|
+ def test_update_existing_cpu_params(self):
|
|
+ """
|
|
+ Test virt.update() with existing cpu-related parameters.
|
|
+ """
|
|
+ xml_with_existing_params = """
|
|
+ <domain type='kvm' id='8'>
|
|
+ <name>vm_with_boot_param</name>
|
|
+ <memory unit='KiB'>1048576</memory>
|
|
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|
+ <vcpu placement="static" cpuset="0-11" current="3">6</vcpu>
|
|
+ <vcpus>
|
|
+ <vcpu id="0" enabled="yes" hotpluggable="no" order="1"/>
|
|
+ <vcpu id="1" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="2" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="3" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="4" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="5" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="6" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="7" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="8" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="9" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="10" enabled="no" hotpluggable="yes"/>
|
|
+ <vcpu id="11" enabled="no" hotpluggable="yes"/>
|
|
+ </vcpus>
|
|
+ <cpu mode="custom" match="exact" check="full">
|
|
+ <model fallback="allow" vendor_id="Genuine20201">core2duo</model>
|
|
+ <vendor>Intel</vendor>
|
|
+ <topology sockets="2" cores="5" threads="2"/>
|
|
+ <cache level="3" mode="emulate"/>
|
|
+ <feature policy="optional" name="lahf_lm"/>
|
|
+ <feature policy="require" name="pcid"/>
|
|
+ <numa>
|
|
+ <cell id="0" cpus="0-3" memory="1073741824" unit="KiB" discard="no">
|
|
+ <distances>
|
|
+ <sibling id="0" value="10"/>
|
|
+ <sibling id="1" value="21"/>
|
|
+ <sibling id="2" value="31"/>
|
|
+ <sibling id="3" value="41"/>
|
|
+ </distances>
|
|
+ </cell>
|
|
+ <cell id="1" cpus="4-6" memory="1073741824" unit="KiB" memAccess="private">
|
|
+ <distances>
|
|
+ <sibling id="0" value="21"/>
|
|
+ <sibling id="1" value="10"/>
|
|
+ <sibling id="2" value="21"/>
|
|
+ <sibling id="3" value="31"/>
|
|
+ </distances>
|
|
+ </cell>
|
|
+ </numa>
|
|
+ </cpu>
|
|
+ <os>
|
|
+ <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
|
+ </os>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = self.set_mock_vm(
|
|
+ "vm_with_existing_param", xml_with_existing_params
|
|
+ )
|
|
+ domain_mock.OSType = MagicMock(return_value="hvm")
|
|
+ define_mock = MagicMock(return_value=True)
|
|
+ self.mock_conn.defineXML = define_mock
|
|
+
|
|
+ # test update vcpu with existing attributes case
|
|
+ setvcpus_mock = MagicMock(return_value=0)
|
|
+ domain_mock.setVcpusFlags = setvcpus_mock
|
|
+
|
|
+ cpu_attr = {"placement": "static", "cpuset": "0-5", "current": 3, "maximum": 5}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "cpu": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_attr),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("vcpu").text, "5")
|
|
+ self.assertEqual(setxml.find("vcpu").attrib["placement"], "static")
|
|
+ self.assertEqual(
|
|
+ setxml.find("vcpu").attrib["cpuset"],
|
|
+ ",".join([str(i) for i in range(0, 6)]),
|
|
+ )
|
|
+ self.assertEqual(setxml.find("vcpu").attrib["current"], "3")
|
|
+
|
|
+ # test removing vcpu attribute
|
|
+ cpu_none = {"placement": "auto", "cpuset": None, "current": 2, "maximum": 5}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "cpu": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("vcpu").text, "5")
|
|
+ self.assertEqual(setxml.find("vcpu").attrib["placement"], "auto")
|
|
+ self.assertEqual(setxml.find("vcpu").attrib.get("cpuset"), None)
|
|
+ self.assertEqual(setxml.find("vcpu").attrib.get("current"), "2")
|
|
+
|
|
+ # test update individual vcpu with exisiting attributes
|
|
+ vcpus = {
|
|
+ "vcpus": {
|
|
+ "0": {"enabled": False, "hotpluggable": True, "order": 5},
|
|
+ "3": {"enabled": True, "hotpluggable": False, "order": 3},
|
|
+ "7": {"enabled": True, "hotpluggable": False},
|
|
+ }
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=vcpus),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["id"], "0")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["enabled"], "no")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='0']").attrib["hotpluggable"], "yes"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']").attrib["order"], "5")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='3']").attrib["id"], "3")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='3']").attrib["enabled"], "yes")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='3']").attrib["hotpluggable"], "no"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='3']").attrib["order"], "3")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='7']").attrib["id"], "7")
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='7']").attrib["enabled"], "yes")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='7']").attrib["hotpluggable"], "no"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='7']").attrib.get("order"), None
|
|
+ )
|
|
+
|
|
+ # test removing vcpu element
|
|
+ ind_vcpu = {
|
|
+ "vcpus": {"3": {"enabled": True, "hotpluggable": False, "order": None}}
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=ind_vcpu),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='0']"), None)
|
|
+ self.assertEqual(setxml.find("./vcpus/vcpu/[@id='3']").attrib["enabled"], "yes")
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='3']").attrib["hotpluggable"], "no"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./vcpus/vcpu/[@id='3']").attrib.get("order"), None
|
|
+ )
|
|
+
|
|
+ # test removing vcpus element
|
|
+ vcpus_none = {"vcpus": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=vcpus_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("vcpus"), None)
|
|
+
|
|
+ # test removing cpu attrbutes
|
|
+ cpu_atr_none = {"match": None, "mode": None, "check": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_atr_none),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").attrib, {})
|
|
+
|
|
+ cpu_atr_mn = {"match": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_atr_mn),
|
|
+ )
|
|
+
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").attrib.get("match"), None)
|
|
+ self.assertEqual(setxml.find("cpu").attrib.get("mode"), "custom")
|
|
+ self.assertEqual(setxml.find("cpu").attrib.get("check"), "full")
|
|
+
|
|
+ # test update existing cpu model
|
|
+ cpu_model_none = {"model": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_model_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("model"), None)
|
|
+
|
|
+ cpu_model_atr_none = {
|
|
+ "model": {"name": "coresolo", "fallback": "forbid", "vendor_id": None}
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_model_atr_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("model").attrib.get("vendor_id"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("model").attrib.get("fallback"), "forbid"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cpu").find("model").text, "coresolo")
|
|
+
|
|
+ cpu_model_atr = {
|
|
+ "model": {
|
|
+ "name": "coresolo",
|
|
+ "fallback": "forbid",
|
|
+ "vendor_id": "AuthenticAMD",
|
|
+ }
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_model_atr),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("model").attrib.get("fallback"), "forbid"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("model").attrib.get("vendor_id"), "AuthenticAMD"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cpu").find("model").text, "coresolo")
|
|
+
|
|
+ # test update existing cpu vendor
|
|
+ cpu_vendor = {"vendor": "AMD"}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_vendor),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("vendor").text, "AMD")
|
|
+
|
|
+ cpu_vendor_none = {"vendor": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_vendor_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("vendor"), None)
|
|
+
|
|
+ # test update exisiting cpu topology
|
|
+ cpu_topology = {"topology": {"sockets": 1, "cores": 12, "threads": 1}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_topology),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("sockets"), "1")
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("cores"), "12")
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("threads"), "1")
|
|
+
|
|
+ cpu_topology_atr_none = {
|
|
+ "topology": {"sockets": None, "cores": 12, "threads": 1}
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_topology_atr_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("topology").attrib.get("sockets"), None
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("cores"), "12")
|
|
+ self.assertEqual(setxml.find("cpu").find("topology").attrib.get("threads"), "1")
|
|
+
|
|
+ cpu_topology_none = {"topology": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_topology_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("topology"), None)
|
|
+
|
|
+ # test update existing cache
|
|
+ cpu_cache = {"cache": {"mode": "passthrough", "level": 2}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_cache),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("cache").attrib.get("level"), "2")
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("cache").attrib.get("mode"), "passthrough"
|
|
+ )
|
|
+
|
|
+ cpu_cache_atr_none = {"cache": {"mode": "passthrough", "level": None}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_cache_atr_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("cache").attrib.get("level"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("cpu").find("cache").attrib.get("mode"), "passthrough"
|
|
+ )
|
|
+
|
|
+ cpu_cache_none = {"cache": None}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_cache_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cpu").find("cache"), None)
|
|
+
|
|
+ # test update existing feature
|
|
+ cpu_feature = {"features": {"lahf_lm": "require", "pcid": "optional"}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_feature),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/feature[@name='pcid']").attrib.get("policy"), "optional"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/feature[@name='lahf_lm']").attrib.get("policy"),
|
|
+ "require",
|
|
+ )
|
|
+
|
|
+ cpu_feature_atr_none = {"features": {"pcid": "optional", "lahf_lm": "disable"}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_feature_atr_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/feature[@name='lahf_lm']").attrib.get("policy"),
|
|
+ "disable",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/feature[@name='pcid']").attrib.get("policy"), "optional"
|
|
+ )
|
|
+
|
|
+ cpu_feature_none = {"features": {"lahf_lm": None, "pcid": None}}
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=cpu_feature_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("./cpu/feature"), None)
|
|
+
|
|
+ # test update existing numa cell
|
|
+ numa_cell = {
|
|
+ "numa": {
|
|
+ 0: {
|
|
+ "cpus": "0-6",
|
|
+ "memory": "512m",
|
|
+ "discard": True,
|
|
+ "distances": {0: 15, 1: 16, 2: 17, 3: 18},
|
|
+ },
|
|
+ 1: {
|
|
+ "cpus": "7-12",
|
|
+ "memory": "2g",
|
|
+ "discard": True,
|
|
+ "memAccess": "shared",
|
|
+ "distances": {0: 23, 1: 24, 2: 25, 3: 26},
|
|
+ },
|
|
+ }
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=numa_cell),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["cpus"],
|
|
+ ",".join([str(i) for i in range(0, 7)]),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["memory"],
|
|
+ str(512 * 1024 ** 2),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").get("unit"), "bytes",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["discard"], "yes"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "15",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "16",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "17",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='3']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "18",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["cpus"],
|
|
+ ",".join([str(i) for i in range(7, 13)]),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memory"],
|
|
+ str(int(2 * 1024 ** 3)),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").get("unit"), "bytes",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["discard"], "yes"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memAccess"], "shared"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "23",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "24",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "25",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='3']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "26",
|
|
+ )
|
|
+
|
|
+ numa_cell_atr_none = {
|
|
+ "numa": {
|
|
+ "0": {
|
|
+ "cpus": "0-6",
|
|
+ "memory": "512m",
|
|
+ "discard": False,
|
|
+ "distances": {0: 15, 2: 17, 3: 18},
|
|
+ },
|
|
+ "1": {
|
|
+ "cpus": "7-12",
|
|
+ "memory": "2g",
|
|
+ "discard": True,
|
|
+ "distances": {0: 23, 1: 24, 2: 25},
|
|
+ },
|
|
+ }
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_existing_param", cpu=numa_cell_atr_none),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["cpus"],
|
|
+ ",".join([str(i) for i in range(0, 7)]),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib["memory"],
|
|
+ str(512 * 1024 ** 2),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").get("unit"), "bytes",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']").attrib.get("discard"), "no"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "15",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='1']"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "17",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='0']/distances/sibling/[@id='3']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "18",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["cpus"],
|
|
+ ",".join([str(i) for i in range(7, 13)]),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memory"],
|
|
+ str(int(2 * 1024 ** 3)),
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["discard"], "yes"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "23",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "24",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "25",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='3']"), None
|
|
+ )
|
|
+
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["cpus"],
|
|
+ ",".join([str(i) for i in range(7, 13)]),
|
|
)
|
|
-
|
|
- setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
self.assertEqual(
|
|
- setxml.find("os").find("loader").text, "/usr/share/new/OVMF_CODE.fd"
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["memory"],
|
|
+ str(int(1024 ** 3 * 2)),
|
|
)
|
|
- self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes")
|
|
- self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash")
|
|
self.assertEqual(
|
|
- setxml.find("os").find("nvram").attrib["template"],
|
|
- "/usr/share/new/OVMF_VARS.ms.fd",
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']").attrib["discard"], "yes"
|
|
)
|
|
-
|
|
- kernel_none = {
|
|
- "kernel": None,
|
|
- "initrd": None,
|
|
- "cmdline": None,
|
|
- }
|
|
-
|
|
- uefi_none = {"loader": None, "nvram": None}
|
|
-
|
|
self.assertEqual(
|
|
- {
|
|
- "definition": True,
|
|
- "disk": {"attached": [], "detached": [], "updated": []},
|
|
- "interface": {"attached": [], "detached": []},
|
|
- },
|
|
- virt.update("vm_with_boot_param", boot=kernel_none),
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='0']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "23",
|
|
)
|
|
-
|
|
- setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
- self.assertEqual(setxml.find("os").find("kernel"), None)
|
|
- 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.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='1']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "24",
|
|
)
|
|
- 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,
|
|
- "disk": {"attached": [], "detached": [], "updated": []},
|
|
- "interface": {"attached": [], "detached": []},
|
|
- },
|
|
- virt.update("vm_with_boot_param", boot=uefi_none),
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='2']").attrib[
|
|
+ "value"
|
|
+ ],
|
|
+ "25",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cpu/numa/cell/[@id='1']/distances/sibling/[@id='3']"), None,
|
|
)
|
|
-
|
|
- setxml = ET.fromstring(define_mock_boot.call_args[0][0])
|
|
- self.assertEqual(setxml.find("os").find("loader"), None)
|
|
- self.assertEqual(setxml.find("os").find("nvram"), None)
|
|
|
|
def test_update_memtune_params(self):
|
|
"""
|
|
@@ -2965,6 +4801,517 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
self.assertEqual(setxml.find("currentMemory").text, str(int(1 * 1024 ** 2)))
|
|
self.assertEqual(setxml.find("memory").text, str(int(1 * 1024 ** 2)))
|
|
|
|
+ def test_update_exist_memorybacking_params(self):
|
|
+ """
|
|
+ Test virt.update() with memory backing parameters.
|
|
+ """
|
|
+ xml_with_memback_params = """
|
|
+ <domain type='kvm' id='8'>
|
|
+ <name>vm_with_memback_param</name>
|
|
+ <memory unit='KiB'>1048576</memory>
|
|
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|
+ <maxMemory slots="12" unit="KiB">1048576</maxMemory>
|
|
+ <vcpu placement='auto'>1</vcpu>
|
|
+ <memoryBacking>
|
|
+ <hugepages>
|
|
+ <page size="2048" unit="KiB"/>
|
|
+ <page size="3145728" nodeset="1-4,^3" unit="KiB"/>
|
|
+ <page size="1048576" nodeset="3" unit="KiB"/>
|
|
+ </hugepages>
|
|
+ <nosharepages/>
|
|
+ <locked/>
|
|
+ <source type="file"/>
|
|
+ <access mode="shared"/>
|
|
+ <discard/>
|
|
+ </memoryBacking>
|
|
+ <os>
|
|
+ <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
|
+ </os>
|
|
+ <on_reboot>restart</on_reboot>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = self.set_mock_vm("vm_with_memback_param", xml_with_memback_params)
|
|
+ domain_mock.OSType = MagicMock(return_value="hvm")
|
|
+ define_mock = MagicMock(return_value=True)
|
|
+ self.mock_conn.defineXML = define_mock
|
|
+
|
|
+ # update memory backing case
|
|
+ mem_back_param = {
|
|
+ "hugepages": [
|
|
+ {"nodeset": "1-4,^3", "size": "1g"},
|
|
+ {"nodeset": "3", "size": "2g"},
|
|
+ ],
|
|
+ "nosharepages": None,
|
|
+ "locked": None,
|
|
+ "source": "anonymous",
|
|
+ "access": "private",
|
|
+ "allocation": "ondemand",
|
|
+ "discard": None,
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_memback_param", mem=mem_back_param),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ p.get("nodeset"): {"size": p.get("size"), "unit": p.get("unit")}
|
|
+ for p in setxml.findall("memoryBacking/hugepages/page")
|
|
+ },
|
|
+ {
|
|
+ "1,2,4": {"size": str(1024 ** 3), "unit": "bytes"},
|
|
+ "3": {"size": str(2 * 1024 ** 3), "unit": "bytes"},
|
|
+ },
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./memoryBacking/nosharepages"), None)
|
|
+ self.assertEqual(setxml.find("./memoryBacking/locked"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("./memoryBacking/source").attrib["type"], "anonymous"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./memoryBacking/access").attrib["mode"], "private"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./memoryBacking/allocation").attrib["mode"], "ondemand"
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./memoryBacking/discard"), None)
|
|
+
|
|
+ unchanged_page = {
|
|
+ "hugepages": [
|
|
+ {"size": "2m"},
|
|
+ {"nodeset": "1-4,^3", "size": "3g"},
|
|
+ {"nodeset": "3", "size": "1g"},
|
|
+ ],
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": False,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("vm_with_memback_param", mem=unchanged_page),
|
|
+ )
|
|
+
|
|
+ def test_update_iothreads_params(self):
|
|
+ """
|
|
+ Test virt.update() with iothreads parameters.
|
|
+ """
|
|
+ xml_with_iothreads_params = """
|
|
+ <domain type='kvm' id='8'>
|
|
+ <name>xml_with_iothreads_params</name>
|
|
+ <memory unit='KiB'>1048576</memory>
|
|
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|
+ <maxMemory slots="12" unit="KiB">1048576</maxMemory>
|
|
+ <vcpu placement='auto'>1</vcpu>
|
|
+ <iothreads>6</iothreads>
|
|
+ <os>
|
|
+ <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
|
+ </os>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = self.set_mock_vm(
|
|
+ "xml_with_iothreads_params", xml_with_iothreads_params
|
|
+ )
|
|
+ domain_mock.OSType = MagicMock(return_value="hvm")
|
|
+ define_mock = MagicMock(return_value=True)
|
|
+ self.mock_conn.defineXML = define_mock
|
|
+
|
|
+ # test updating existing iothreads
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("xml_with_iothreads_params", cpu={"iothreads": 7}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("iothreads").text, "7")
|
|
+
|
|
+ def test_update_cputune_paramters(self):
|
|
+ """
|
|
+ Test virt.update() with cputune parameters.
|
|
+ """
|
|
+ xml_with_cputune_params = """
|
|
+ <domain type='kvm' id='8'>
|
|
+ <name>xml_with_cputune_params</name>
|
|
+ <memory unit='KiB'>1048576</memory>
|
|
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|
+ <maxMemory slots="12" unit="KiB">1048576</maxMemory>
|
|
+ <vcpu placement='auto'>1</vcpu>
|
|
+ <iothreads>4</iothreads>
|
|
+ <cputune>
|
|
+ <shares>2048</shares>
|
|
+ <period>1000000</period>
|
|
+ <quota>-1</quota>
|
|
+ <global_period>1000000</global_period>
|
|
+ <global_quota>-1</global_quota>
|
|
+ <emulator_period>1000000</emulator_period>
|
|
+ <emulator_quota>-1</emulator_quota>
|
|
+ <iothread_period>1000000</iothread_period>
|
|
+ <iothread_quota>-1</iothread_quota>
|
|
+ <vcpupin vcpu="0" cpuset="0-2"/>
|
|
+ <vcpupin vcpu="1" cpuset="3"/>
|
|
+ <vcpupin vcpu="2" cpuset="4"/>
|
|
+ <vcpupin vcpu="3" cpuset="5-7"/>
|
|
+ <emulatorpin cpuset="1-2"/>
|
|
+ <iothreadpin iothread="1" cpuset="1-5"/>
|
|
+ <iothreadpin iothread="2" cpuset="6-7"/>
|
|
+ <vcpusched vcpus="0" scheduler="idle" priority="3"/>
|
|
+ <vcpusched vcpus="1" scheduler="rr" priority="1"/>
|
|
+ <vcpusched vcpus="2" scheduler="fifo" priority="2"/>
|
|
+ <iothreadsched iothreads="4" scheduler="fifo"/>
|
|
+ <emulatorsched scheduler="idle"/>
|
|
+ <cachetune vcpus="0-4">
|
|
+ <cache id="0" level="2" type="both" size="4" unit="KiB"/>
|
|
+ <cache id="1" level="2" type="both" size="4" unit="KiB"/>
|
|
+ <monitor level="5" vcpus="0-2"/>
|
|
+ <monitor level="6" vcpus="1-3"/>
|
|
+ </cachetune>
|
|
+ <cachetune vcpus="5-8">
|
|
+ <monitor level="5" vcpus="5-6"/>
|
|
+ <monitor level="3" vcpus="7-8"/>
|
|
+ </cachetune>
|
|
+ <memorytune vcpus="0-6">
|
|
+ <node id="0" bandwidth="45"/>
|
|
+ </memorytune>
|
|
+ <memorytune vcpus="7-8">
|
|
+ <node id="0" bandwidth="120"/>
|
|
+ </memorytune>
|
|
+ </cputune>
|
|
+ <os>
|
|
+ <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
|
+ </os>
|
|
+ </domain>
|
|
+ """
|
|
+ domain_mock = self.set_mock_vm(
|
|
+ "xml_with_cputune_params", xml_with_cputune_params
|
|
+ )
|
|
+ domain_mock.OSType = MagicMock(return_value="hvm")
|
|
+ define_mock = MagicMock(return_value=True)
|
|
+ self.mock_conn.defineXML = define_mock
|
|
+
|
|
+ # test updating existing cputune parameters
|
|
+ cputune = {
|
|
+ "shares": 1024,
|
|
+ "period": 5000,
|
|
+ "quota": -20,
|
|
+ "global_period": 4000,
|
|
+ "global_quota": -30,
|
|
+ "emulator_period": 3000,
|
|
+ "emulator_quota": -4,
|
|
+ "iothread_period": 7000,
|
|
+ "iothread_quota": -5,
|
|
+ "vcpupin": {0: "1-4,^2", 1: "0,1", 2: "2,3", 3: "0,4"},
|
|
+ "emulatorpin": "1-3",
|
|
+ "iothreadpin": {1: "5-6", 2: "7-8"},
|
|
+ "vcpusched": [
|
|
+ {"scheduler": "fifo", "priority": 1, "vcpus": "0"},
|
|
+ {"scheduler": "fifo", "priority": 2, "vcpus": "1"},
|
|
+ {"scheduler": "idle", "priority": 3, "vcpus": "2"},
|
|
+ ],
|
|
+ "iothreadsched": [
|
|
+ {"scheduler": "batch", "iothreads": "5-7", "priority": 1}
|
|
+ ],
|
|
+ "emulatorsched": {"scheduler": "rr", "priority": 2},
|
|
+ "cachetune": {
|
|
+ "0-3": {
|
|
+ 0: {"level": 3, "type": "both", "size": 3},
|
|
+ 1: {"level": 3, "type": "both", "size": 3},
|
|
+ "monitor": {1: 3, "0-3": 3},
|
|
+ },
|
|
+ "4-5": {"monitor": {4: 3, 5: 2}},
|
|
+ },
|
|
+ "memorytune": {"0-2": {0: 60}, "3-4": {0: 50, 1: 70}},
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("xml_with_cputune_params", cpu={"tuning": cputune}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cputune").find("shares").text, "1024")
|
|
+ self.assertEqual(setxml.find("cputune").find("period").text, "5000")
|
|
+ self.assertEqual(setxml.find("cputune").find("quota").text, "-20")
|
|
+ self.assertEqual(setxml.find("cputune").find("global_period").text, "4000")
|
|
+ self.assertEqual(setxml.find("cputune").find("global_quota").text, "-30")
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_period").text, "3000")
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_quota").text, "-4")
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_period").text, "7000")
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_quota").text, "-5")
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='0']").attrib.get("cpuset"),
|
|
+ "1,3,4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='1']").attrib.get("cpuset"),
|
|
+ "0,1",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='2']").attrib.get("cpuset"),
|
|
+ "2,3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='3']").attrib.get("cpuset"),
|
|
+ "0,4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("emulatorpin").attrib.get("cpuset"), "1,2,3"
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("iothreadpin[@iothread='1']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "5,6",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("iothreadpin[@iothread='2']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "7,8",
|
|
+ )
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ s.get("vcpus"): {
|
|
+ "scheduler": s.get("scheduler"),
|
|
+ "priority": s.get("priority"),
|
|
+ }
|
|
+ for s in setxml.findall("cputune/vcpusched")
|
|
+ },
|
|
+ {
|
|
+ "0": {"scheduler": "fifo", "priority": "1"},
|
|
+ "1": {"scheduler": "fifo", "priority": "2"},
|
|
+ "2": {"scheduler": "idle", "priority": "3"},
|
|
+ },
|
|
+ )
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ s.get("iothreads"): {
|
|
+ "scheduler": s.get("scheduler"),
|
|
+ "priority": s.get("priority"),
|
|
+ }
|
|
+ for s in setxml.findall("cputune/iothreadsched")
|
|
+ },
|
|
+ {"5,6,7": {"scheduler": "batch", "priority": "1"}},
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cputune/emulatorsched").get("scheduler"), "rr")
|
|
+ self.assertEqual(setxml.find("cputune/emulatorsched").get("priority"), "2")
|
|
+ self.assertIsNotNone(setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']"))
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("type"),
|
|
+ "both",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='1']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='1']"),
|
|
+ None,
|
|
+ )
|
|
+ self.assertNotEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='4,5']").attrib.get("vcpus"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='4,5']/cache[@id='0']"), None
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='4,5']/monitor[@vcpus='4']"
|
|
+ ).attrib.get("level"),
|
|
+ "3",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='4,5']/monitor[@vcpus='5']"
|
|
+ ).attrib.get("level"),
|
|
+ "2",
|
|
+ )
|
|
+ self.assertNotEqual(setxml.find("./cputune/memorytune[@vcpus='0,1,2']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/memorytune[@vcpus='0,1,2']/node[@id='0']"
|
|
+ ).attrib.get("bandwidth"),
|
|
+ "60",
|
|
+ )
|
|
+ self.assertNotEqual(setxml.find("./cputune/memorytune[@vcpus='3,4']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='0']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "50",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='1']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "70",
|
|
+ )
|
|
+
|
|
+ # test removing cputune attributes and sub elements
|
|
+ cputune = {
|
|
+ "shares": None,
|
|
+ "period": 20000,
|
|
+ "quota": None,
|
|
+ "global_period": 5000,
|
|
+ "global_quota": None,
|
|
+ "emulator_period": 2000,
|
|
+ "emulator_quota": -4,
|
|
+ "iothread_period": None,
|
|
+ "iothread_quota": -5,
|
|
+ "vcpupin": {0: "1-4,^2", 2: "2,4"},
|
|
+ "emulatorpin": None,
|
|
+ "iothreadpin": {1: "5-6"},
|
|
+ "vcpusched": [{"scheduler": "idle", "priority": 5, "vcpus": "1"}],
|
|
+ "iothreadsched": None,
|
|
+ "cachetune": {
|
|
+ "0-3": {
|
|
+ 0: {"level": 4, "type": "data", "size": 7},
|
|
+ "monitor": {"1-2": 11},
|
|
+ },
|
|
+ },
|
|
+ "memorytune": {"3-4": {0: 37, 1: 73}},
|
|
+ }
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("xml_with_cputune_params", cpu={"tuning": cputune}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cputune").find("shares"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("period").text, "20000")
|
|
+ self.assertEqual(setxml.find("cputune").find("quota"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("global_period").text, "5000")
|
|
+ self.assertEqual(setxml.find("cputune").find("global_quota"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_period").text, "2000")
|
|
+ self.assertEqual(setxml.find("cputune").find("emulator_quota").text, "-4")
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_period"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("iothread_quota").text, "-5")
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='0']").attrib.get("cpuset"),
|
|
+ "1,3,4",
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cputune").find("vcpupin[@vcpu='1']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("vcpupin[@vcpu='2']").attrib.get("cpuset"),
|
|
+ "2,4",
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cputune").find("vcpupin[@vcpu='3']"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("emulatorpin"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune")
|
|
+ .find("iothreadpin[@iothread='1']")
|
|
+ .attrib.get("cpuset"),
|
|
+ "5,6",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("cputune").find("iothreadpin[@iothread='2']"), None
|
|
+ )
|
|
+ self.assertDictEqual(
|
|
+ {
|
|
+ s.get("vcpus"): {
|
|
+ "scheduler": s.get("scheduler"),
|
|
+ "priority": s.get("priority"),
|
|
+ }
|
|
+ for s in setxml.findall("cputune/vcpusched")
|
|
+ },
|
|
+ {"1": {"scheduler": "idle", "priority": "5"}},
|
|
+ )
|
|
+ self.assertEqual(setxml.find("cputune").find("iothreadsched"), None)
|
|
+ self.assertIsNotNone(setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']"))
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("size"),
|
|
+ "7",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("level"),
|
|
+ "4",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='0']"
|
|
+ ).attrib.get("type"),
|
|
+ "data",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find(
|
|
+ "./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='1,2']"
|
|
+ ).attrib.get("level"),
|
|
+ "11",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']/monitor[@vcpus='3,4']"),
|
|
+ None,
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/cachetune[@vcpus='0,1,2,3']/cache[@id='1']"), None
|
|
+ )
|
|
+ self.assertEqual(setxml.find("./cputune/cachetune[@vcpus='4,5']"), None)
|
|
+ self.assertEqual(setxml.find("./cputune/memorytune[@vcpus='0,1,2']"), None)
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='0']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "37",
|
|
+ )
|
|
+ self.assertEqual(
|
|
+ setxml.find("./cputune/memorytune[@vcpus='3,4']/node[@id='1']").attrib.get(
|
|
+ "bandwidth"
|
|
+ ),
|
|
+ "73",
|
|
+ )
|
|
+
|
|
+ cputune_subelement = {
|
|
+ "vcpupin": None,
|
|
+ "iothreadpin": None,
|
|
+ "vcpusched": None,
|
|
+ "iothreadsched": None,
|
|
+ "cachetune": None,
|
|
+ "memorytune": None,
|
|
+ }
|
|
+
|
|
+ self.assertEqual(
|
|
+ {
|
|
+ "definition": True,
|
|
+ "disk": {"attached": [], "detached": [], "updated": []},
|
|
+ "interface": {"attached": [], "detached": []},
|
|
+ },
|
|
+ virt.update("xml_with_cputune_params", cpu={"tuning": cputune_subelement}),
|
|
+ )
|
|
+ setxml = ET.fromstring(define_mock.call_args[0][0])
|
|
+ self.assertEqual(setxml.find("cputune").find("vcpupin"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("iothreadpin"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("vcpusched"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("iothreadsched"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("cachetune"), None)
|
|
+ self.assertEqual(setxml.find("cputune").find("memorytune"), None)
|
|
+
|
|
def test_handle_unit(self):
|
|
"""
|
|
Test regex function for handling units
|
|
diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py
|
|
index 1923ae5c0f..dadc6dd08e 100644
|
|
--- a/tests/unit/states/test_virt.py
|
|
+++ b/tests/unit/states/test_virt.py
|
|
@@ -327,6 +327,14 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
"type": "spice",
|
|
"listen": {"type": "address", "address": "192.168.0.1"},
|
|
}
|
|
+ serials = [
|
|
+ {"type": "tcp", "port": 22223, "protocol": "telnet"},
|
|
+ {"type": "pty"},
|
|
+ ]
|
|
+ consoles = [
|
|
+ {"type": "tcp", "port": 22223, "protocol": "telnet"},
|
|
+ {"type": "pty"},
|
|
+ ]
|
|
self.assertDictEqual(
|
|
virt.defined(
|
|
"myvm",
|
|
@@ -345,10 +353,14 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
install=False,
|
|
pub_key="/path/to/key.pub",
|
|
priv_key="/path/to/key",
|
|
+ hypervisor_features={"kvm-hint-dedicated": True},
|
|
+ clock={"utc": True},
|
|
stop_on_reboot=True,
|
|
connection="someconnection",
|
|
username="libvirtuser",
|
|
password="supersecret",
|
|
+ serials=serials,
|
|
+ consoles=consoles,
|
|
),
|
|
ret,
|
|
)
|
|
@@ -367,14 +379,19 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
hypervisor="qemu",
|
|
seed=False,
|
|
boot=None,
|
|
+ numatune=None,
|
|
install=False,
|
|
start=False,
|
|
pub_key="/path/to/key.pub",
|
|
priv_key="/path/to/key",
|
|
+ hypervisor_features={"kvm-hint-dedicated": True},
|
|
+ clock={"utc": True},
|
|
stop_on_reboot=True,
|
|
connection="someconnection",
|
|
username="libvirtuser",
|
|
password="supersecret",
|
|
+ serials=serials,
|
|
+ consoles=consoles,
|
|
)
|
|
|
|
# Working update case when running
|
|
@@ -484,7 +501,12 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=False,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
|
|
@@ -597,8 +619,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=True,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
|
|
@@ -633,8 +660,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=True,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
|
|
@@ -701,6 +733,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
os_type=None,
|
|
arch=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
disk=None,
|
|
disks=[{"name": "system", "image": "/path/to/img.qcow2"}],
|
|
nic=None,
|
|
@@ -713,10 +746,14 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
pub_key=None,
|
|
priv_key=None,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
stop_on_reboot=False,
|
|
connection=None,
|
|
username=None,
|
|
password=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
)
|
|
start_mock.assert_called_with(
|
|
"myvm", connection=None, username=None, password=None
|
|
@@ -797,15 +834,20 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
hypervisor="qemu",
|
|
seed=False,
|
|
boot=None,
|
|
+ numatune=None,
|
|
install=False,
|
|
start=False,
|
|
pub_key="/path/to/key.pub",
|
|
priv_key="/path/to/key",
|
|
boot_dev="network hd",
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
stop_on_reboot=True,
|
|
connection="someconnection",
|
|
username="libvirtuser",
|
|
password="supersecret",
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
)
|
|
start_mock.assert_called_with(
|
|
"myvm",
|
|
@@ -946,8 +988,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=False,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
|
|
@@ -1067,8 +1114,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=True,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
start_mock.assert_not_called()
|
|
@@ -1105,8 +1157,13 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
|
|
username=None,
|
|
password=None,
|
|
boot=None,
|
|
+ numatune=None,
|
|
test=True,
|
|
boot_dev=None,
|
|
+ hypervisor_features=None,
|
|
+ clock=None,
|
|
+ serials=None,
|
|
+ consoles=None,
|
|
stop_on_reboot=False,
|
|
)
|
|
|
|
--
|
|
2.29.2
|
|
|
|
|