[Kimchi-devel] [PATCH 5/6 - V2] [Memory HotPlug] Fix VM offline memory update and fix slots assignment

Aline Manera alinefm at linux.vnet.ibm.com
Mon Jun 1 12:36:38 UTC 2015



On 28/05/2015 10:59, Rodrigo Trujillo wrote:
> This patch changes the current memory update process, the static update,
> allowed when the guest is offline. Now, the update creates the new xml
> elements that will allow to hotplug memory, if necessary (MAXMEMORY, CPU,
> NUMA, NODE, etc.). It also introduce some tests to avoid errors if user
> sets more memory than the allowed in the server.
> The memory device slots count and assignment was changed to avoid erros,
> like negative or zero slots. Slots counting will have the number updated
> as the static memory is changed.
>
> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
> ---
>   src/kimchi/i18n.py       |  1 +
>   src/kimchi/model/vms.py  | 87 ++++++++++++++++++++++++++++++++++++++----------
>   src/kimchi/vmtemplate.py | 11 ++++++
>   3 files changed, 82 insertions(+), 17 deletions(-)
>
> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
> index e6e00b8..1779597 100644
> --- a/src/kimchi/i18n.py
> +++ b/src/kimchi/i18n.py
> @@ -111,6 +111,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."),
>
>       "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/kimchi/model/vms.py b/src/kimchi/model/vms.py
> index dc7f91f..5834a65 100644
> --- a/src/kimchi/model/vms.py
> +++ b/src/kimchi/model/vms.py
> @@ -59,8 +59,7 @@ DOM_STATE_MAP = {0: 'nostate',
>                    7: 'pmsuspended'}
>
>   VM_STATIC_UPDATE_PARAMS = {'name': './name',
> -                           'cpus': './vcpu',
> -                           'memory': './memory'}
> +                           'cpus': './vcpu'}
>   VM_LIVE_UPDATE_PARAMS = {}
>
>   XPATH_DOMAIN_DISK = "/domain/devices/disk[@device='disk']/source/@file"
> @@ -73,6 +72,8 @@ XPATH_DOMAIN_MEMORY = '/domain/memory'
>   XPATH_DOMAIN_MEMORY_UNIT = '/domain/memory/@unit'
>   XPATH_DOMAIN_UUID = '/domain/uuid'
>
> +XPATH_NUMA_CELL = './cpu/numa/cell'
> +
>
>   class VMsModel(object):
>       def __init__(self, **kargs):
> @@ -95,12 +96,9 @@ class VMsModel(object):
>               vm_overrides['storagepool'] = pool_uri
>               vm_overrides['fc_host_support'] = self.caps.fc_host_support
>
> -        # Setting maxMemory and slots parameter values
> -        # getInfo memory value comes in MiB, so dividing by 1024 integer,
> -        # gives the interger maximum number of slots to use in chunks of
> -        # 1 GB
> +        # Setting maxMemory of the VM, which will be equal the Host memory.
> +        # Host memory comes in MiB, so transform in KiB
>           vm_overrides['max_memory'] = self.conn.get().getInfo()[1] * 1024
> -        vm_overrides['slots'] = self.conn.get().getInfo()[1] / 1024
>
>           t = TemplateModel.get_template(t_name, self.objstore, self.conn,
>                                          vm_overrides)
> @@ -659,15 +657,15 @@ class VMModel(object):
>
>           for key, val in params.items():
>               if key in VM_STATIC_UPDATE_PARAMS:
> -                if key == 'memory':
> -                    # Libvirt saves memory in KiB. Retrieved xml has memory
> -                    # in KiB too, so new valeu must be in KiB here
> -                    val = val * 1024
>                   if type(val) == int:
>                       val = str(val)
>                   xpath = VM_STATIC_UPDATE_PARAMS[key]
>                   new_xml = xml_item_update(new_xml, xpath, val)
>
> +        # Updating memory and adds NUMA if necessary
> +        if 'memory' in params:
> +            new_xml = self._update_memory(new_xml, params)
> +
>           if 'graphics' in params:
>               new_xml = self._update_graphics(dom, new_xml, params)
>
> @@ -695,12 +693,7 @@ class VMModel(object):
>                   # Undefine old vm, only if name is going to change
>                   dom.undefine()
>
> -            root = ET.fromstring(new_xml)
> -            currentMem = root.find('.currentMemory')
> -            if currentMem is not None:
> -                root.remove(currentMem)
> -
> -            dom = conn.defineXML(ET.tostring(root, encoding="utf-8"))
> +            dom = conn.defineXML(new_xml)
>               if 'name' in params:
>                   self._redefine_snapshots(dom, snapshots_info)
>           except libvirt.libvirtError as e:
> @@ -712,6 +705,66 @@ class VMModel(object):
>                                                    'err': e.get_error_message()})
>           return dom
>
> +    def _update_memory(self, xml, params):
> +        # Checks if NUMA memory is already configured, if not, checks if CPU
> +        # element is already configured (topology). Then add NUMA element as
> +        # apropriated
> +        root = ET.fromstring(xml)
> +        numa_mem = xpath_get_text(xml, XPATH_NUMA_CELL + '/@memory')
> +        if numa_mem == []:
> +            vcpus = xpath_get_text(xml, VM_STATIC_UPDATE_PARAMS['cpus'])

> +            numa_element = E.numa(E.cell(
> +                id='0',
> +                cpus='0-' + str(vcpus - 1) if vcpus > 1 else '0',
> +                memory=str(params['memory'] << 10),
> +                unit='KiB'))

The above code is the same in vmtemplate.py file.
We have the xmlutils module to handle all the XML manipulation. I 
suggest to add it there and reuse over where.

> +            cpu = root.find('./cpu')
> +            if cpu is None:
> +                root.insert(0, E.cpu(numa_element))
> +            else:
> +                cpu.insert(0, numa_element)
> +        else:
> +            root = ET.fromstring(xml_item_update(xml, XPATH_NUMA_CELL,
> +                                                 str(params['memory'] << 10),
> +                                                 attr='memory'))
> +
> +        # Remove currentMemory, automatically set later by libvirt
> +        currentMem = root.find('.currentMemory')
> +        if currentMem is not None:
> +            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']) / 1024
> +            # Libvirt does not accepts slots <= 1
> +            if slots < 0:
> +                raise OperationFailed("KCHVM0041E")
> +            elif slots == 0:
> +                slots = 1
> +            if maxMem is None:
> +                max_mem_xml = E.maxMemory(
> +                    str(host_mem * 1024),
> +                    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')
> +            return new_xml
> +        return ET.tostring(root, encoding="utf-8")
> +
>       def _live_vm_update(self, dom, params):
>           self._vm_update_access_metadata(dom, params)
>
> diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
> index a20098d..00de7c2 100644
> --- a/src/kimchi/vmtemplate.py
> +++ b/src/kimchi/vmtemplate.py
> @@ -316,6 +316,17 @@ class VMTemplate(object):
>           else:
>               params['cdroms'] = cdrom_xml
>
> +        # Setting maximum number of slots to avoid errors when hotplug memory
> +        # Number of slots are the numbers of chunks of 1GB that fit inside
> +        # the max_memory of the host minus memory assigned to the VM
> +        params['slots'] = ((params['max_memory'] / 1024) -
> +                           params['memory']) / 1024
> +
> +        if params['slots'] < 0:
> +            raise OperationFailed("KCHVM0041E")
> +        elif params['slots'] == 0:
> +            params['slots'] = 1
> +
>           xml = """
>           <domain type='%(domain)s'>
>             %(qemu-stream-cmdline)s




More information about the Kimchi-devel mailing list