
From: CrÃstian Deives <cristiandeives@gmail.com> Currently, the VCPU count is updated by editing the configuration XML (tag: <vcpu>). However, in some cases, editing that tag will only update the maximum VCPU count, not the current one. For example, if the VM has the following XML tag: <vcpu current='2'>8</vcpu> and the user updates the VCPU value to 10, Kimchi will update the XML tag to: <vcpu current='2'>10</vcpu> which is probably not what the user wanted. Use the libvirt function "setVcpusFlags" to update the current and the maximum VCPU values to the one specified by the user. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> Signed-off-by: CrÃstian Deives <cristiandeives@gmail.com> Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 48 ++++++++++++++++++------------------------------ src/kimchi/model/vms.py | 23 ++++++++++++++++++----- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 34bcfc9..3080868 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -25,6 +25,7 @@ import time import kimchi.model.cpuinfo +from collections import defaultdict from lxml import objectify from lxml.builder import E @@ -54,10 +55,9 @@ mockmodel_defaults = {'storagepool': '/storagepools/default-pool', class MockModel(Model): - _mock_vms = {} + _mock_vms = defaultdict(list) _mock_snapshots = {} _XMLDesc = libvirt.virDomain.XMLDesc - _defineXML = libvirt.virConnect.defineXML _undefineDomain = libvirt.virDomain.undefine _libvirt_get_vol_path = LibvirtVMTemplate._get_volume_path @@ -76,7 +76,6 @@ class MockModel(Model): kimchi.model.cpuinfo.get_topo_capabilities = \ MockModel.get_topo_capabilities - libvirt.virConnect.defineXML = MockModel.domainDefineXML libvirt.virDomain.XMLDesc = MockModel.domainXMLDesc libvirt.virDomain.undefine = MockModel.undefineDomain libvirt.virDomain.attachDeviceFlags = MockModel.attachDeviceFlags @@ -119,7 +118,7 @@ class MockModel(Model): imageinfo.probe_image = self._probe_image def reset(self): - MockModel._mock_vms = {} + MockModel._mock_vms = defaultdict(list) MockModel._mock_snapshots = {} self._mock_swupdate = MockSoftwareUpdate() self._mock_repositories = MockRepositories() @@ -154,21 +153,15 @@ class MockModel(Model): return ET.fromstring(xml) @staticmethod - def domainDefineXML(conn, xml): - name = objectify.fromstring(xml).name.text - try: - dom = conn.lookupByName(name) - if not dom.isActive(): - MockModel._mock_vms[name] = xml - except: - pass + def domainXMLDesc(dom, flags=0): + xml = MockModel._XMLDesc(dom, flags) + root = objectify.fromstring(xml) - return MockModel._defineXML(conn, xml) + for dev_xml in MockModel._mock_vms.get(dom.name(), []): + dev = objectify.fromstring(dev_xml) + root.devices.append(dev) - @staticmethod - def domainXMLDesc(dom, flags=0): - return MockModel._mock_vms.get(dom.name(), - MockModel._XMLDesc(dom, flags)) + return ET.tostring(root, encoding="utf-8") @staticmethod def undefineDomain(dom): @@ -179,12 +172,7 @@ class MockModel(Model): @staticmethod def attachDeviceFlags(dom, xml, flags=0): - old_xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) - root = objectify.fromstring(old_xml) - dev = objectify.fromstring(xml) - root.devices.append(dev) - - MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8") + MockModel._mock_vms[dom.name()].append(xml) @staticmethod def _get_device_node(dom, xml): @@ -210,16 +198,16 @@ class MockModel(Model): @staticmethod def detachDeviceFlags(dom, xml, flags=0): - root, dev = MockModel._get_device_node(dom, xml) - root.devices.remove(dev) - - MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8") + if xml in MockModel._mock_vms[dom.name()]: + MockModel._mock_vms[dom.name()].remove(xml) @staticmethod def updateDeviceFlags(dom, xml, flags=0): - root, old_dev = MockModel._get_device_node(dom, xml) - root.devices.replace(old_dev, objectify.fromstring(xml)) - MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8") + _, old_dev = MockModel._get_device_node(dom, xml) + old_xml = ET.tostring(old_dev, encoding="utf-8") + if old_xml in MockModel._mock_vms[dom.name()]: + MockModel._mock_vms[dom.name()].remove(old_xml) + MockModel._mock_vms[dom.name()].append(xml) @staticmethod def volResize(vol, size, flags=0): diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index b579712..0dd8099 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -63,8 +63,8 @@ DOM_STATE_MAP = {0: 'nostate', 6: 'crashed', 7: 'pmsuspended'} -VM_STATIC_UPDATE_PARAMS = {'name': './name', - 'cpus': './vcpu'} +VM_STATIC_UPDATE_PARAMS = {'name': './name'} + VM_LIVE_UPDATE_PARAMS = {} XPATH_DOMAIN_DISK = "/domain/devices/disk[@device='disk']/source/@file" @@ -226,8 +226,8 @@ class VMModel(object): with lock: dom = self.get_vm(name, self.conn) - vm_name, dom = self._static_vm_update(name, dom, params) self._live_vm_update(dom, params) + vm_name, dom = self._static_vm_update(name, dom, params) return vm_name def clone(self, name): @@ -751,8 +751,7 @@ class VMModel(object): vcpus = params.get('cpus') if numa_mem == []: if vcpus is None: - vcpus = int(xpath_get_text(xml, - VM_STATIC_UPDATE_PARAMS['cpus'])[0]) + vcpus = int(xpath_get_text(xml, 'vcpu')[0]) cpu = root.find('./cpu') if cpu is None: cpu = get_cpu_xml(vcpus, params['memory'] << 10) @@ -813,6 +812,20 @@ class VMModel(object): if 'memory' in params and dom.isActive(): self._update_memory_live(dom, params) + if 'cpus' in params: + cpus = params['cpus'] + + try: + # set maximum VCPU count + dom.setVcpusFlags(cpus, libvirt.VIR_DOMAIN_AFFECT_CONFIG | + libvirt.VIR_DOMAIN_VCPU_MAXIMUM) + + # set current VCPU count + dom.setVcpusFlags(cpus, libvirt.VIR_DOMAIN_AFFECT_CONFIG) + except libvirt.libvirtError, e: + raise OperationFailed('KCHVM0008E', {'name': dom.name(), + 'err': e.message}) + def _update_memory_live(self, dom, params): # Check if host supports memory device if not self.caps.mem_hotplug_support: -- 1.9.1