[Kimchi-devel] [PATCH 5/6 - V2] [Memory HotPlug] Fix VM offline memory update and fix slots assignment
Rodrigo Trujillo
rodrigo.trujillo at linux.vnet.ibm.com
Mon Jun 1 19:38:04 UTC 2015
On 06/01/2015 09:36 AM, Aline Manera wrote:
>
>
> 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.
Sure, thanks
>
>> + 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
>
> _______________________________________________
> Kimchi-devel mailing list
> Kimchi-devel at ovirt.org
> http://lists.ovirt.org/mailman/listinfo/kimchi-devel
>
More information about the Kimchi-devel
mailing list