[Kimchi-devel] [PATCH 1/2] Implement support to change guest maxMemory tag

Rodrigo Trujillo rodrigo.trujillo at linux.vnet.ibm.com
Mon Nov 23 23:18:04 UTC 2015


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 at 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




More information about the Kimchi-devel mailing list