This patch implements the backend part of the support to max memory
(<maxMemory> tag) update.
Max memory is the amount of memory that a VM can reach when memory
devices are live attached (memory hotplug).
Libvirt still does not expose this tag, so, it must be updated when the
guest is offline.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/wok/plugins/kimchi/API.json | 6 ++
src/wok/plugins/kimchi/i18n.py | 6 +-
src/wok/plugins/kimchi/model/vms.py | 119 ++++++++++++++++++++----------------
3 files changed, 78 insertions(+), 53 deletions(-)
diff --git a/src/wok/plugins/kimchi/API.json b/src/wok/plugins/kimchi/API.json
index e236e51..a017c43 100644
--- a/src/wok/plugins/kimchi/API.json
+++ b/src/wok/plugins/kimchi/API.json
@@ -305,6 +305,12 @@
"type": "integer",
"minimum": 512,
"error": "KCHTMPL0013E"
+ },
+ "maxmemory": {
+ "description": "Maximum amount (MB) of memory that a
VM can reach when memory devices are live attached (memory hotplug)",
+ "type": "integer",
+ "minimum": 512,
+ "error": "KCHTMPL0013E"
}
},
"additionalProperties": false
diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py
index c69d072..4991dec 100644
--- a/src/wok/plugins/kimchi/i18n.py
+++ b/src/wok/plugins/kimchi/i18n.py
@@ -97,7 +97,7 @@ messages = {
"KCHVM0038E": _("Unable to suspend VM '%(name)s'. Details:
%(err)s"),
"KCHVM0039E": _("Cannot resume VM '%(name)s' because it is not
paused."),
"KCHVM0040E": _("Unable to resume VM '%(name)s'. Details:
%(err)s"),
- "KCHVM0041E": _("Memory assigned is higher then the maximum allowed in
the host."),
+ "KCHVM0041E": _("Memory assigned is higher then the maximum
allowed."),
"KCHVM0042E": _("VM '%(name)s' does not support live memory
update. Update the memory with the machine offline to enable this feature."),
"KCHVM0043E": _("Only increase memory is allowed in active
VMs"),
"KCHVM0044E": _("For live memory update, new memory value must be
equal old memory value plus multiples of 1024 Mib"),
@@ -125,7 +125,9 @@ messages = {
"KCHVM0068E": _("Unable to setup password-less login at remote host
%(host)s using user %(user)s. Error: %(error)s"),
"KCHVM0069E": _("Password field must be a string."),
"KCHVM0070E": _("Error creating local host ssh rsa key of user
'root'."),
- "KCHVM0071E": _("Memory value %(mem)s must be aligned to
%(alignment)sMiB."),
+ "KCHVM0071E": _("Memory or Max Memory value %(mem)s must be aligned to
%(alignment)sMiB."),
+ "KCHVM0072E": _("Cannot update maximum memory when guest is
running."),
+ "KCHVM0073E": _("Maximum memory requested is higher than amount
supported by host: %(memHost)sMiB."),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned
host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to
directly assign to VM."),
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py
index 7e12b91..cfbc05a 100644
--- a/src/wok/plugins/kimchi/model/vms.py
+++ b/src/wok/plugins/kimchi/model/vms.py
@@ -704,14 +704,19 @@ class VMModel(object):
params = copy.deepcopy(params)
name = params.get('name')
nonascii_name = None
+ state = DOM_STATE_MAP[dom.info()[0]]
+
if name is not None:
- state = DOM_STATE_MAP[dom.info()[0]]
if state != 'shutoff':
msg_args = {'name': vm_name, 'new_name':
params['name']}
raise InvalidParameter("KCHVM0003E", msg_args)
params['name'], nonascii_name = get_ascii_nonascii_name(name)
+ # You can only change <maxMemory> offline, updating guest XML
+ if 'maxmemory' in params and state != 'shutoff':
+ raise InvalidParameter("KCHVM0072E")
+
for key, val in params.items():
if key in VM_STATIC_UPDATE_PARAMS:
if type(val) == int:
@@ -721,6 +726,8 @@ class VMModel(object):
# Updating memory and NUMA if necessary, if vm is offline
if not dom.isActive():
+ if self.caps.mem_hotplug_support:
+ new_xml = self._update_maxmemory_config(new_xml, params)
if 'memory' in params:
new_xml = self._update_memory_config(new_xml, params)
elif 'cpus' in params and \
@@ -801,58 +808,68 @@ class VMModel(object):
root.remove(currentMem)
memory = root.find('.memory')
- # Update/Adds maxMemory accordingly
- if not self.caps.mem_hotplug_support:
- if memory is not None:
- memory.text = str(params['memory'] << 10)
- else:
- if memory is not None:
- root.remove(memory)
- maxMem = root.find('.maxMemory')
- host_mem = self.conn.get().getInfo()[1]
- slots = (host_mem - params['memory']) >> 10
- # Libvirt does not accepts slots <= 1
- if slots < 0:
- raise OperationFailed("KCHVM0041E")
- elif slots == 0:
- slots = 1
-
- force_max_mem_update = False
- distro, _, _ = platform.linux_distribution()
- if distro == "IBM_PowerKVM":
- # max memory 256MiB alignment
- host_mem -= (host_mem % PPC_MEM_ALIGN)
- # force max memory update if it exists but it's wrong.
- if maxMem is not None and\
- int(maxMem.text) != (host_mem << 10):
- force_max_mem_update = True
-
- # max 32 slots on Power
- if slots > 32:
- slots = 32
-
- if maxMem is None:
- max_mem_xml = E.maxMemory(
- str(host_mem << 10),
- unit='Kib',
- slots=str(slots))
- root.insert(0, max_mem_xml)
- new_xml = ET.tostring(root, encoding="utf-8")
- else:
- # Update slots only
- new_xml = xml_item_update(ET.tostring(root, encoding="utf-8"),
- './maxMemory',
- str(slots),
- attr='slots')
-
- if force_max_mem_update:
- new_xml = xml_item_update(new_xml,
- './maxMemory',
- str(host_mem << 10))
-
- return new_xml
+ if memory is not None:
+ memory.text = str(params['memory'] << 10)
return ET.tostring(root, encoding="utf-8")
+ def _update_maxmemory_config(self, xml, params):
+ # Update maxMemory or slots if necessary
+ maxMemVal = params.get('maxmemory')
+ memory = params.get('memory')
+ if maxMemVal is None and memory is None:
+ return xml
+ root = ET.fromstring(xml)
+ maxMemTag = root.find('.maxMemory')
+
+ distro, _, _ = platform.linux_distribution()
+ maxHostMem = self.conn.get().getInfo()[1]
+ # maxMem value was passed, or get it from xml, or use the total memory
+ # available in host
+ if maxMemVal is None:
+ if maxMemTag is not None:
+ maxMemVal = int(xpath_get_text(xml, './maxMemory')[0]) >>
10
+ else:
+ maxMemVal = maxHostMem
+ # Align to 256Mib
+ if distro == "IBM_PowerKVM":
+ maxMemVal -= (maxMemVal % PPC_MEM_ALIGN)
+ # Check if the host supports the amount requested
+ elif maxMemVal > maxHostMem:
+ raise InvalidParameter('KCHVM0073E', {'memHost':
str(maxHostMem)})
+
+ # Memory allocated value is passed or what is already configured
+ if memory is None:
+ memory = int(xpath_get_text(xml, XPATH_DOMAIN_MEMORY)[0]) >> 10
+
+ # Number of slots, cannot be <= 1 or 0
+ slots = (maxMemVal - memory) >> 10
+ if slots < 0:
+ raise OperationFailed("KCHVM0041E")
+ elif slots == 0:
+ slots = 1
+
+ if distro == "IBM_PowerKVM":
+ # max memory must be 256MiB alignment
+ if (maxMemVal % PPC_MEM_ALIGN) != 0:
+ raise InvalidParameter('KCHVM0071E',
+ {'mem': str(maxMemVal),
+ 'alignment': str(PPC_MEM_ALIGN)})
+ # max 32 slots on Power
+ if slots > 32:
+ slots = 32
+
+ if maxMemTag is not None:
+ root.remove(maxMemTag)
+
+ # Add max memory xml
+ max_mem_xml = E.maxMemory(
+ str(maxMemVal << 10),
+ unit='Kib',
+ slots=str(slots))
+ root.insert(0, max_mem_xml)
+ new_xml = ET.tostring(root, encoding="utf-8")
+ return new_xml
+
def _live_vm_update(self, dom, params):
self._vm_update_access_metadata(dom, params)
if 'memory' in params and dom.isActive():
--
2.1.0