mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
virt: add VM memory tunning support
This commit is contained in:
parent
6554e5ac3d
commit
667880b954
5 changed files with 594 additions and 24 deletions
1
changelog/57639.added
Normal file
1
changelog/57639.added
Normal file
|
@ -0,0 +1 @@
|
|||
Memory Tuning Support which allows much greater control of memory allocation
|
|
@ -71,12 +71,57 @@ The calls not using the libvirt connection setup are:
|
|||
- `libvirt URI format <http://libvirt.org/uri.html#URI_config>`_
|
||||
- `libvirt authentication configuration <http://libvirt.org/auth.html#Auth_client_config>`_
|
||||
|
||||
Units
|
||||
==========
|
||||
.. _virt-units:
|
||||
.. rubric:: Units specification
|
||||
.. versionadded:: Magnesium
|
||||
|
||||
The string should contain a number optionally followed
|
||||
by a unit. The number may have a decimal fraction. If
|
||||
the unit is not given then MiB are set by default.
|
||||
Units can optionally be given in IEC style (such as MiB),
|
||||
although the standard single letter style (such as M) is
|
||||
more convenient.
|
||||
|
||||
Valid units include:
|
||||
|
||||
========== ===== ========== ========== ======
|
||||
Standard IEC Standard IEC
|
||||
Unit Unit Name Name Factor
|
||||
========== ===== ========== ========== ======
|
||||
B Bytes 1
|
||||
K KiB Kilobytes Kibibytes 2**10
|
||||
M MiB Megabytes Mebibytes 2**20
|
||||
G GiB Gigabytes Gibibytes 2**30
|
||||
T TiB Terabytes Tebibytes 2**40
|
||||
P PiB Petabytes Pebibytes 2**50
|
||||
E EiB Exabytes Exbibytes 2**60
|
||||
Z ZiB Zettabytes Zebibytes 2**70
|
||||
Y YiB Yottabytes Yobibytes 2**80
|
||||
========== ===== ========== ========== ======
|
||||
|
||||
Additional decimal based units:
|
||||
|
||||
====== =======
|
||||
Unit Factor
|
||||
====== =======
|
||||
KB 10**3
|
||||
MB 10**6
|
||||
GB 10**9
|
||||
TB 10**12
|
||||
PB 10**15
|
||||
EB 10**18
|
||||
ZB 10**21
|
||||
YB 10**24
|
||||
====== =======
|
||||
"""
|
||||
# Special Thanks to Michael Dehann, many of the concepts, and a few structures
|
||||
# of his in the virt func module have been used
|
||||
|
||||
|
||||
import base64
|
||||
import collections
|
||||
import copy
|
||||
import datetime
|
||||
import logging
|
||||
|
@ -841,6 +886,39 @@ def _disk_from_pool(conn, pool, pool_xml, volume_name):
|
|||
return disk_context
|
||||
|
||||
|
||||
def _handle_unit(s, def_unit="m"):
|
||||
"""
|
||||
Handle the unit conversion, return the value in bytes
|
||||
"""
|
||||
m = re.match(r"(?P<value>[0-9.]*)\s*(?P<unit>.*)$", str(s).strip())
|
||||
value = m.group("value")
|
||||
# default unit
|
||||
unit = m.group("unit").lower() or def_unit
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
raise SaltInvocationError("invalid number")
|
||||
# flag for base ten
|
||||
dec = False
|
||||
if re.match(r"[kmgtpezy]b$", unit):
|
||||
dec = True
|
||||
elif not re.match(r"(b|[kmgtpezy](ib)?)$", unit):
|
||||
raise SaltInvocationError("invalid units")
|
||||
p = "bkmgtpezy".index(unit[0])
|
||||
value *= 10 ** (p * 3) if dec else 2 ** (p * 10)
|
||||
return int(value)
|
||||
|
||||
|
||||
def nesthash():
|
||||
"""
|
||||
create default dict that allows arbitrary level of nesting
|
||||
"""
|
||||
return collections.defaultdict(nesthash)
|
||||
|
||||
|
||||
def _gen_xml(
|
||||
conn,
|
||||
name,
|
||||
|
@ -859,13 +937,25 @@ def _gen_xml(
|
|||
"""
|
||||
Generate the XML string to define a libvirt VM
|
||||
"""
|
||||
mem = int(mem) * 1024 # MB
|
||||
context = {
|
||||
"hypervisor": hypervisor,
|
||||
"name": name,
|
||||
"cpu": str(cpu),
|
||||
"mem": str(mem),
|
||||
}
|
||||
|
||||
context["mem"] = nesthash()
|
||||
if isinstance(mem, int):
|
||||
mem = int(mem) * 1024 # MB
|
||||
context["mem"]["boot"] = str(mem)
|
||||
context["mem"]["current"] = str(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))
|
||||
|
||||
if hypervisor in ["qemu", "kvm"]:
|
||||
context["controller_model"] = False
|
||||
elif hypervisor == "vmware":
|
||||
|
@ -985,7 +1075,6 @@ def _gen_xml(
|
|||
except jinja2.exceptions.TemplateNotFound:
|
||||
log.error("Could not load template %s", fn_)
|
||||
return ""
|
||||
|
||||
return template.render(**context)
|
||||
|
||||
|
||||
|
@ -1793,7 +1882,28 @@ def init(
|
|||
|
||||
: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.
|
||||
:param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, 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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
'boot': 1g,
|
||||
'current': 1g,
|
||||
'max': 1g,
|
||||
'slots': 10,
|
||||
'hard_limit': '1024'
|
||||
'soft_limit': '512m'
|
||||
'swap_hard_limit': '1g'
|
||||
'min_guarantee': '512mib'
|
||||
}
|
||||
|
||||
.. versionchanged:: Magnesium
|
||||
|
||||
:param nic: NIC profile to use (Default: ``'default'``).
|
||||
The profile interfaces can be customized / extended with the interfaces parameter.
|
||||
If set to ``None``, no profile will be used.
|
||||
|
@ -1906,6 +2016,36 @@ def init(
|
|||
|
||||
.. versionadded:: sodium
|
||||
|
||||
.. _init-mem-def:
|
||||
|
||||
.. rubric:: Memory parameter definition
|
||||
|
||||
Memory parameter can contain the following properties:
|
||||
|
||||
boot
|
||||
The maximum allocation of memory for the guest at boot time
|
||||
|
||||
current
|
||||
The actual allocation of memory for the guest
|
||||
|
||||
max
|
||||
The run time maximum memory allocation of the guest
|
||||
|
||||
slots
|
||||
specifies the number of slots available for adding memory to the guest
|
||||
|
||||
hard_limit
|
||||
the maximum memory the guest can use
|
||||
|
||||
soft_limit
|
||||
memory limit to enforce during memory contention
|
||||
|
||||
swap_hard_limit
|
||||
the maximum memory plus swap the guest can use
|
||||
|
||||
min_guarantee
|
||||
the guaranteed minimum memory allocation for the guest
|
||||
|
||||
.. _init-nic-def:
|
||||
|
||||
.. rubric:: Network Interfaces Definitions
|
||||
|
@ -2436,7 +2576,24 @@ def update(
|
|||
|
||||
: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.
|
||||
:param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, 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.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- mem:
|
||||
hard_limit: null
|
||||
soft_limit: null
|
||||
|
||||
.. versionchanged:: Magnesium
|
||||
|
||||
:param disk_profile: disk profile to use
|
||||
:param disks:
|
||||
Disk definitions as documented in the :func:`init` function.
|
||||
|
@ -2576,9 +2733,16 @@ def update(
|
|||
def _set_nvram(node, value):
|
||||
node.set("template", value)
|
||||
|
||||
def _set_with_mib_unit(node, value):
|
||||
def _set_with_byte_unit(node, value):
|
||||
node.text = str(value)
|
||||
node.set("unit", "MiB")
|
||||
node.set("unit", "bytes")
|
||||
|
||||
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
|
||||
return _handle_unit("{}{}".format(value, unit)) if value else None
|
||||
|
||||
# Update the kernel boot parameters
|
||||
params_mapping = [
|
||||
|
@ -2591,14 +2755,72 @@ def update(
|
|||
{
|
||||
"path": "mem",
|
||||
"xpath": "memory",
|
||||
"get": lambda n: int(n.text) / 1024,
|
||||
"set": _set_with_mib_unit,
|
||||
"convert": _handle_unit,
|
||||
"get": _get_with_unit,
|
||||
"set": _set_with_byte_unit,
|
||||
},
|
||||
{
|
||||
"path": "mem",
|
||||
"xpath": "currentMemory",
|
||||
"get": lambda n: int(n.text) / 1024,
|
||||
"set": _set_with_mib_unit,
|
||||
"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: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: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}",
|
||||
|
@ -2684,11 +2906,15 @@ def update(
|
|||
}
|
||||
)
|
||||
if mem:
|
||||
if isinstance(mem, dict):
|
||||
mem = str(_handle_unit(mem.get("current", 0)))
|
||||
elif isinstance(mem, int):
|
||||
mem = str(mem * 1024)
|
||||
commands.append(
|
||||
{
|
||||
"device": "mem",
|
||||
"cmd": "setMemoryFlags",
|
||||
"args": [mem * 1024, libvirt.VIR_DOMAIN_AFFECT_LIVE],
|
||||
"args": [mem, libvirt.VIR_DOMAIN_AFFECT_LIVE],
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -296,7 +296,28 @@ def defined(
|
|||
|
||||
: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 in MiB for the new virtual machine
|
||||
:param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, 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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
'boot': 1g,
|
||||
'current': 1g,
|
||||
'max': 1g,
|
||||
'slots': 10,
|
||||
'hard_limit': '1024'
|
||||
'soft_limit': '512m'
|
||||
'swap_hard_limit': '1g'
|
||||
'min_guarantee': '512mib'
|
||||
}
|
||||
|
||||
.. versionchanged:: Magnesium
|
||||
|
||||
:param vm_type: force virtual machine type for the new VM. The default value is taken from
|
||||
the host capabilities. This could be useful for example to use ``'qemu'`` type instead
|
||||
of the ``'kvm'`` one.
|
||||
|
@ -496,7 +517,23 @@ def running(
|
|||
|
||||
: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 in MiB for the new virtual machine
|
||||
:param mem: Amount of memory to allocate to the virtual machine in MiB. Since Magnesium, 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.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- mem:
|
||||
hard_limit: null
|
||||
soft_limit: null
|
||||
|
||||
.. versionchanged:: Magnesium
|
||||
:param vm_type: force virtual machine type for the new VM. The default value is taken from
|
||||
the host capabilities. This could be useful for example to use ``'qemu'`` type instead
|
||||
of the ``'kvm'`` one.
|
||||
|
|
|
@ -2,9 +2,32 @@
|
|||
<domain type='{{ hypervisor }}'>
|
||||
<name>{{ name }}</name>
|
||||
<vcpu>{{ cpu }}</vcpu>
|
||||
<memory unit='KiB'>{{ mem }}</memory>
|
||||
<currentMemory unit='KiB'>{{ mem }}</currentMemory>
|
||||
<os {{boot.os_attrib}}>
|
||||
{%- if mem.max %}
|
||||
<maxMemory {{ mem.slots }} unit='KiB'> {{ mem.max }}</maxMemory>
|
||||
{%- endif %}
|
||||
{%- if mem.boot %}
|
||||
<memory unit='KiB'>{{ mem.boot }}</memory>
|
||||
{%- endif %}
|
||||
{%- if mem.current %}
|
||||
<currentMemory unit='KiB'>{{ mem.current }}</currentMemory>
|
||||
{%- endif %}
|
||||
{%- if mem %}
|
||||
<memtune>
|
||||
{%- if 'hard_limit' in mem and mem.hard_limit %}
|
||||
<hard_limit unit="KiB">{{ mem.hard_limit }}</hard_limit>
|
||||
{%- endif %}
|
||||
{%- if 'soft_limit' in mem and mem.soft_limit %}
|
||||
<soft_limit unit="KiB">{{ mem.soft_limit }}</soft_limit>
|
||||
{%- endif %}
|
||||
{%- if 'swap_hard_limit' in mem and mem.swap_hard_limit %}
|
||||
<swap_hard_limit unit="KiB">{{ mem.swap_hard_limit }}</swap_hard_limit>
|
||||
{%- endif %}
|
||||
{%- if 'min_guarantee' in mem and mem.min_guarantee %}
|
||||
<min_guarantee unit="KiB">{{ mem.min_guarantee }}</min_guarantee>
|
||||
{%- endif %}
|
||||
</memtune>
|
||||
{%- endif %}
|
||||
<os {{ boot.os_attrib }}>
|
||||
<type arch='{{ arch }}'>{{ os_type }}</type>
|
||||
{% if boot %}
|
||||
{% if 'kernel' in boot %}
|
||||
|
|
|
@ -2016,6 +2016,51 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|||
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": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("my_vm", mem=memtune),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("soft_limit").text, str(int(0.5 * 1024 ** 3))
|
||||
)
|
||||
self.assertEqual(setxml.find("memtune").find("soft_limit").get("unit"), "bytes")
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("hard_limit").text, str(1024 * 1024 ** 2)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("swap_hard_limit").text, str(2048 * 1024 ** 2)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
|
||||
)
|
||||
|
||||
invalid_unit = {"soft_limit": "2HB"}
|
||||
|
||||
with self.assertRaises(SaltInvocationError):
|
||||
virt.update("my_vm", mem=invalid_unit)
|
||||
|
||||
invalid_number = {
|
||||
"soft_limit": "3.4.MB",
|
||||
}
|
||||
|
||||
with self.assertRaises(SaltInvocationError):
|
||||
virt.update("my_vm", mem=invalid_number)
|
||||
|
||||
# Update memory case
|
||||
setmem_mock = MagicMock(return_value=0)
|
||||
domain_mock.setMemoryFlags = setmem_mock
|
||||
|
@ -2030,9 +2075,43 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|||
virt.update("my_vm", mem=2048),
|
||||
)
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(setxml.find("memory").text, "2048")
|
||||
self.assertEqual(setxml.find("memory").get("unit"), "MiB")
|
||||
self.assertEqual(setmem_mock.call_args[0][0], 2048 * 1024)
|
||||
self.assertEqual(setxml.find("memory").text, str(2048 * 1024 ** 2))
|
||||
self.assertEqual(setxml.find("memory").get("unit"), "bytes")
|
||||
self.assertEqual(setmem_mock.call_args[0][0], str(2048 * 1024))
|
||||
|
||||
mem_dict = {"boot": "0.5g", "current": "2g", "max": "1g", "slots": 12}
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"mem": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
},
|
||||
virt.update("my_vm", mem=mem_dict),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(setxml.find("memory").get("unit"), "bytes")
|
||||
self.assertEqual(setxml.find("memory").text, str(int(0.5 * 1024 ** 3)))
|
||||
self.assertEqual(setxml.find("maxMemory").text, str(1 * 1024 ** 3))
|
||||
self.assertEqual(setxml.find("currentMemory").text, str(2 * 1024 ** 3))
|
||||
|
||||
max_slot_reverse = {
|
||||
"slots": "10",
|
||||
"max": "3096m",
|
||||
}
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": True,
|
||||
},
|
||||
virt.update("my_vm", mem=max_slot_reverse),
|
||||
)
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
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)
|
||||
|
@ -2548,7 +2627,6 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|||
"""
|
||||
Test virt.update() with existing boot parameters.
|
||||
"""
|
||||
root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images")
|
||||
xml_boot = """
|
||||
<domain type='kvm' id='8'>
|
||||
<name>vm_with_boot_param</name>
|
||||
|
@ -2606,9 +2684,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|||
</video>
|
||||
</devices>
|
||||
</domain>
|
||||
""".format(
|
||||
root_dir, os.sep
|
||||
)
|
||||
"""
|
||||
domain_mock_boot = self.set_mock_vm("vm_with_boot_param", xml_boot)
|
||||
domain_mock_boot.OSType = MagicMock(return_value="hvm")
|
||||
define_mock_boot = MagicMock(return_value=True)
|
||||
|
@ -2709,6 +2785,213 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.assertEqual(setxml.find("os").find("loader"), None)
|
||||
self.assertEqual(setxml.find("os").find("nvram"), None)
|
||||
|
||||
def test_update_memtune_params(self):
|
||||
"""
|
||||
Test virt.update() with memory tuning parameters.
|
||||
"""
|
||||
xml_with_memtune_params = """
|
||||
<domain type='kvm' id='8'>
|
||||
<name>vm_with_boot_param</name>
|
||||
<memory unit='KiB'>1048576</memory>
|
||||
<currentMemory unit='KiB'>1048576</currentMemory>
|
||||
<maxMemory slots="12" unit="bytes">1048576</maxMemory>
|
||||
<vcpu placement='auto'>1</vcpu>
|
||||
<memtune>
|
||||
<hard_limit unit="KiB">1048576</hard_limit>
|
||||
<soft_limit unit="KiB">2097152</soft_limit>
|
||||
<swap_hard_limit unit="KiB">2621440</swap_hard_limit>
|
||||
<min_guarantee unit='KiB'>671088</min_guarantee>
|
||||
</memtune>
|
||||
<os>
|
||||
<type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
|
||||
</os>
|
||||
</domain>
|
||||
"""
|
||||
domain_mock = self.set_mock_vm("vm_with_memtune_param", xml_with_memtune_params)
|
||||
domain_mock.OSType = MagicMock(return_value="hvm")
|
||||
define_mock = MagicMock(return_value=True)
|
||||
self.mock_conn.defineXML = define_mock
|
||||
|
||||
memtune_new_val = {
|
||||
"boot": "0.7g",
|
||||
"current": "2.5g",
|
||||
"max": "3096m",
|
||||
"slots": "10",
|
||||
"soft_limit": "2048m",
|
||||
"hard_limit": "1024",
|
||||
"swap_hard_limit": "2.5g",
|
||||
"min_guarantee": "1 g",
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("vm_with_memtune_param", mem=memtune_new_val),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("soft_limit").text, str(2048 * 1024)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("hard_limit").text, str(1024 * 1024)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("swap_hard_limit").text,
|
||||
str(int(2.5 * 1024 ** 2)),
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("swap_hard_limit").get("unit"), "KiB",
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("min_guarantee").attrib.get("unit"), "bytes"
|
||||
)
|
||||
self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
|
||||
self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
|
||||
self.assertEqual(setxml.find("currentMemory").text, str(int(2.5 * 1024 ** 3)))
|
||||
self.assertEqual(setxml.find("memory").text, str(int(0.7 * 1024 ** 3)))
|
||||
|
||||
max_slot_reverse = {
|
||||
"slots": "10",
|
||||
"max": "3096m",
|
||||
}
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("vm_with_memtune_param", mem=max_slot_reverse),
|
||||
)
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(setxml.find("maxMemory").text, str(3096 * 1024 ** 2))
|
||||
self.assertEqual(setxml.find("maxMemory").get("unit"), "bytes")
|
||||
self.assertEqual(setxml.find("maxMemory").attrib.get("slots"), "10")
|
||||
|
||||
max_swap_none = {
|
||||
"boot": "0.7g",
|
||||
"current": "2.5g",
|
||||
"max": None,
|
||||
"slots": "10",
|
||||
"soft_limit": "2048m",
|
||||
"hard_limit": "1024",
|
||||
"swap_hard_limit": None,
|
||||
"min_guarantee": "1 g",
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("vm_with_memtune_param", mem=max_swap_none),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("soft_limit").text, str(2048 * 1024)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("hard_limit").text, str(1024 * 1024)
|
||||
)
|
||||
self.assertEqual(setxml.find("memtune").find("swap_hard_limit"), None)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("min_guarantee").text, str(1 * 1024 ** 3)
|
||||
)
|
||||
self.assertEqual(
|
||||
setxml.find("memtune").find("min_guarantee").attrib.get("unit"), "bytes"
|
||||
)
|
||||
self.assertEqual(setxml.find("maxMemory").text, None)
|
||||
self.assertEqual(setxml.find("currentMemory").text, str(int(2.5 * 1024 ** 3)))
|
||||
self.assertEqual(setxml.find("memory").text, str(int(0.7 * 1024 ** 3)))
|
||||
|
||||
memtune_none = {
|
||||
"soft_limit": None,
|
||||
"hard_limit": None,
|
||||
"swap_hard_limit": None,
|
||||
"min_guarantee": None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("vm_with_memtune_param", mem=memtune_none),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(setxml.find("memtune").find("soft_limit"), None)
|
||||
self.assertEqual(setxml.find("memtune").find("hard_limit"), None)
|
||||
self.assertEqual(setxml.find("memtune").find("swap_hard_limit"), None)
|
||||
self.assertEqual(setxml.find("memtune").find("min_guarantee"), None)
|
||||
|
||||
max_none = {
|
||||
"max": None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"definition": True,
|
||||
"disk": {"attached": [], "detached": [], "updated": []},
|
||||
"interface": {"attached": [], "detached": []},
|
||||
"mem": False,
|
||||
},
|
||||
virt.update("vm_with_memtune_param", mem=max_none),
|
||||
)
|
||||
|
||||
setxml = ET.fromstring(define_mock.call_args[0][0])
|
||||
self.assertEqual(setxml.find("maxMemory"), None)
|
||||
self.assertEqual(setxml.find("currentMemory").text, str(int(1 * 1024 ** 2)))
|
||||
self.assertEqual(setxml.find("memory").text, str(int(1 * 1024 ** 2)))
|
||||
|
||||
def test_handle_unit(self):
|
||||
"""
|
||||
Test regex function for handling units
|
||||
"""
|
||||
valid_case = [
|
||||
("2", 2097152),
|
||||
("42", 44040192),
|
||||
("5b", 5),
|
||||
("2.3Kib", 2355),
|
||||
("5.8Kb", 5800),
|
||||
("16MiB", 16777216),
|
||||
("20 GB", 20000000000),
|
||||
("16KB", 16000),
|
||||
(".5k", 512),
|
||||
("2.k", 2048),
|
||||
]
|
||||
|
||||
for key, val in valid_case:
|
||||
self.assertEqual(virt._handle_unit(key), val)
|
||||
|
||||
invalid_case = [
|
||||
("9ib", "invalid units"),
|
||||
("8byte", "invalid units"),
|
||||
("512bytes", "invalid units"),
|
||||
("4 Kbytes", "invalid units"),
|
||||
("3.4.MB", "invalid number"),
|
||||
("", "invalid number"),
|
||||
("bytes", "invalid number"),
|
||||
("2HB", "invalid units"),
|
||||
]
|
||||
|
||||
for key, val in invalid_case:
|
||||
with self.assertRaises(SaltInvocationError):
|
||||
virt._handle_unit(key)
|
||||
|
||||
def test_mixed_dict_and_list_as_profile_objects(self):
|
||||
"""
|
||||
Test virt._nic_profile with mixed dictionaries and lists as input.
|
||||
|
|
Loading…
Add table
Reference in a new issue