[Kimchi-devel] [PATCH] Update VCPU by using libvirt function
Aline Manera
alinefm at linux.vnet.ibm.com
Mon Nov 30 11:28:01 UTC 2015
Rodrigo, are you going to send a new patch to cover all that or Ziviani
should send V2?
On 26/11/2015 18:59, Rodrigo Trujillo wrote:
> There is a problem that this patch is not fixing:
> If you try to edit a Guest (offline) and pass: a good CPU value (2),
> and a wrong Memory value (4324243224), you are going to receive
> an error about the memory. But, the CPU is going to be updated.
> Offline updates should be atomic.
>
> Rodrigo Trujillo
>
> On 11/26/2015 02:16 PM, Jose Ricardo Ziviani wrote:
>> From: Crístian Deives <cristiandeives at 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>
>>
>> Use the libvirt function "setVcpusFlags" to update the current and the
>> maximum VCPU values to the one specified by the user.
>>
>> * This patch also changes vcpu setting when guest is being
>> created. Now 'current' is always set and is the total of cpus
>> from the Template and maxVcpu is the >= 255 or (sockets * cores
>> * threads) in PPC. In x86 it is the value of libvirt getMaxVcpu.
>>
>> * Change NUMA management. Sets all cpus to 0. There is not
>> impact to the guest.
>>
>> Signed-off-by: Aline Manera <alinefm at linux.vnet.ibm.com>
>> Signed-off-by: Crístian Deives <cristiandeives at gmail.com>
>> Signed-off-by: Jose Ricardo Ziviani <joserz at linux.vnet.ibm.com>
>> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
>> ---
>> src/wok/plugins/kimchi/i18n.py | 3 +
>> src/wok/plugins/kimchi/mockmodel.py | 51 +++++------
>> src/wok/plugins/kimchi/model/vms.py | 144
>> +++++++++++++++++++++++++++---
>> src/wok/plugins/kimchi/tests/test_rest.py | 10 ++-
>> src/wok/plugins/kimchi/vmtemplate.py | 26 +++++-
>> 5 files changed, 186 insertions(+), 48 deletions(-)
>>
>> diff --git a/src/wok/plugins/kimchi/i18n.py
>> b/src/wok/plugins/kimchi/i18n.py
>> index de7962a..29a7222 100644
>> --- a/src/wok/plugins/kimchi/i18n.py
>> +++ b/src/wok/plugins/kimchi/i18n.py
>> @@ -128,6 +128,9 @@ messages = {
>> "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."),
>> + "KCHVM0072E": _("Unable to update the following parameters while
>> the VM is offline: %(params)s"),
>> + "KCHVM0073E": _("Unable to update the following parameters while
>> the VM is online: %(params)s"),
>> + "KCHVM0074E": _("Cannot change VCPU value because '%(vm)s' has a
>> topology defined - sockets: %(sockets)s, cores: %(cores)s, threads:
>> %(threads)s."),
>> "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/mockmodel.py
>> b/src/wok/plugins/kimchi/mockmodel.py
>> index 9186f78..cca38e5 100644
>> --- a/src/wok/plugins/kimchi/mockmodel.py
>> +++ b/src/wok/plugins/kimchi/mockmodel.py
>> @@ -21,6 +21,8 @@ import libvirt
>> import lxml.etree as ET
>> import os
>> import time
>> +
>> +from collections import defaultdict
>> from lxml import objectify
>> from lxml.builder import E
>> @@ -60,10 +62,9 @@ storagevolumes.VALID_RAW_CONTENT = ['dos/mbr
>> boot sector',
>> 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
>> @@ -81,7 +82,6 @@ class MockModel(Model):
>> cpuinfo.get_topo_capabilities =
>> MockModel.get_topo_capabilities
>> vmifaces.getDHCPLeases = MockModel.getDHCPLeases
>> - libvirt.virConnect.defineXML = MockModel.domainDefineXML
>> libvirt.virDomain.XMLDesc = MockModel.domainXMLDesc
>> libvirt.virDomain.undefine = MockModel.undefineDomain
>> libvirt.virDomain.attachDeviceFlags =
>> MockModel.attachDeviceFlags
>> @@ -124,7 +124,7 @@ class MockModel(Model):
>> imageinfo.probe_image = self._probe_image
>> def reset(self):
>> - MockModel._mock_vms = {}
>> + MockModel._mock_vms = defaultdict(list)
>> MockModel._mock_snapshots = {}
>> if hasattr(self, 'objstore'):
>> @@ -157,21 +157,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):
>> @@ -182,12 +176,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):
>> @@ -213,16 +202,18 @@ 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")
>> + node = ET.fromstring(xml)
>> + xml = ET.tostring(node, encoding="utf-8", pretty_print=True)
>> + 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",
>> pretty_print=True)
>> + 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/wok/plugins/kimchi/model/vms.py
>> b/src/wok/plugins/kimchi/model/vms.py
>> index 7e12b91..a67044d 100644
>> --- a/src/wok/plugins/kimchi/model/vms.py
>> +++ b/src/wok/plugins/kimchi/model/vms.py
>> @@ -56,6 +56,7 @@ from wok.plugins.kimchi.model.utils import
>> get_ascii_nonascii_name, get_vm_name
>> from wok.plugins.kimchi.model.utils import get_metadata_node
>> from wok.plugins.kimchi.model.utils import remove_metadata_node
>> from wok.plugins.kimchi.model.utils import set_metadata_node
>> +from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
>> from wok.plugins.kimchi.screenshot import VMScreenshot
>> from wok.plugins.kimchi.utils import template_name_from_uri
>> from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml
>> @@ -71,10 +72,15 @@ 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 = {}
>> +# update parameters which are updatable when the VM is online
>> +VM_ONLINE_UPDATE_PARAMS = ['graphics', 'groups', 'memory', 'users']
>> +# update parameters which are updatable when the VM is offline
>> +VM_OFFLINE_UPDATE_PARAMS = ['cpus', 'graphics', 'groups', 'memory',
>> 'name',
>> + 'users']
>> +
>> XPATH_DOMAIN_DISK =
>> "/domain/devices/disk[@device='disk']/source/@file"
>> XPATH_DOMAIN_DISK_BY_FILE =
>> "./devices/disk[@device='disk']/source[@file='%s']"
>> XPATH_DOMAIN_NAME = '/domain/name'
>> @@ -84,9 +90,12 @@ XPATH_DOMAIN_MAC_BY_ADDRESS =
>> "./devices/interface[@type='network']/"\
>> XPATH_DOMAIN_MEMORY = '/domain/memory'
>> XPATH_DOMAIN_MEMORY_UNIT = '/domain/memory/@unit'
>> XPATH_DOMAIN_UUID = '/domain/uuid'
>> +XPATH_DOMAIN_DEV_CPU_ID = '/domain/devices/spapr-cpu-socket/@id'
>> XPATH_NUMA_CELL = './cpu/numa/cell'
>> +XPATH_TOPOLOGY = './cpu/topology'
>> +
>> # key: VM name; value: lock object
>> vm_locks = {}
>> @@ -98,6 +107,17 @@ class VMsModel(object):
>> self.caps = CapabilitiesModel(**kargs)
>> self.task = TaskModel(**kargs)
>> + def _get_host_maxcpu(self):
>> + if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
>> + cpu_model = CPUInfoModel(conn=self.conn)
>> + max_vcpu_val = (cpu_model.cores_available *
>> + cpu_model.threads_per_core)
>> + if max_vcpu_val > 255:
>> + max_vcpu_val = 255
>> + else:
>> + max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
>> + return max_vcpu_val
>> +
>> def create(self, params):
>> t_name = template_name_from_uri(params['template'])
>> vm_list = self.get_list()
>> @@ -163,7 +183,8 @@ class VMsModel(object):
>> xml = t.to_vm_xml(name, vm_uuid,
>> libvirt_stream_protocols=stream_protocols,
>> graphics=graphics,
>> - volumes=vol_list)
>> + volumes=vol_list,
>> + max_vcpus=self._get_host_maxcpu())
>> cb('Defining new VM')
>> try:
>> @@ -231,6 +252,30 @@ class VMModel(object):
>> self.vmsnapshots = cls(**kargs)
>> self.stats = {}
>> + def has_topology(self, dom):
>> + xml = dom.XMLDesc(0)
>> + sockets = xpath_get_text(xml, XPATH_TOPOLOGY + '/@sockets')
>> + cores = xpath_get_text(xml, XPATH_TOPOLOGY + '/@cores')
>> + threads = xpath_get_text(xml, XPATH_TOPOLOGY + '/@threads')
>> + return sockets and cores and threads
>> +
>> + def get_vm_max_sockets(self, dom):
>> + return int(xpath_get_text(dom.XMLDesc(0),
>> + XPATH_TOPOLOGY + '/@sockets')[0])
>> +
>> + def get_vm_sockets(self, dom):
>> + current_vcpu =
>> dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_CURRENT)
>> + return (current_vcpu / self.get_vm_cores(dom) /
>> + self.get_vm_threads(dom))
>> +
>> + def get_vm_cores(self, dom):
>> + return int(xpath_get_text(dom.XMLDesc(0),
>> + XPATH_TOPOLOGY + '/@cores')[0])
>> +
>> + def get_vm_threads(self, dom):
>> + return int(xpath_get_text(dom.XMLDesc(0),
>> + XPATH_TOPOLOGY + '/@threads')[0])
>> +
>> def update(self, name, params):
>> lock = vm_locks.get(name)
>> if lock is None:
>> @@ -247,8 +292,20 @@ class VMModel(object):
>> 'alignment':
>> str(PPC_MEM_ALIGN)})
>> dom = self.get_vm(name, self.conn)
>> - vm_name, dom = self._static_vm_update(name, dom, params)
>> +
>> + if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
>> + ext_params = set(params.keys()) -
>> set(VM_OFFLINE_UPDATE_PARAMS)
>> + if len(ext_params) > 0:
>> + raise InvalidParameter('KCHVM0072E',
>> + {'params': ',
>> '.join(ext_params)})
>> + else:
>> + ext_params = set(params.keys()) -
>> set(VM_ONLINE_UPDATE_PARAMS)
>> + if len(ext_params) > 0:
>> + raise InvalidParameter('KCHVM0073E',
>> + {'params': ',
>> '.join(ext_params)})
>> +
>> self._live_vm_update(dom, params)
>> + vm_name, dom = self._static_vm_update(name, dom, params)
>> return vm_name
>> def clone(self, name):
>> @@ -713,23 +770,34 @@ class VMModel(object):
>> params['name'], nonascii_name =
>> get_ascii_nonascii_name(name)
>> for key, val in params.items():
>> + change_numa = True
>> if key in VM_STATIC_UPDATE_PARAMS:
>> if type(val) == int:
>> val = str(val)
>> xpath = VM_STATIC_UPDATE_PARAMS[key]
>> - new_xml = xml_item_update(new_xml, xpath, val)
>> + attrib = None
>> + if key == 'cpus':
>> + if self.has_topology(dom) or dom.isActive():
>> + change_numa = False
>> + continue
>> + # Update maxvcpu firstly
>> + new_xml = xml_item_update(new_xml, xpath,
>> + str(self._get_host_maxcpu()),
>> + attrib)
>> + # Update current vcpu
>> + attrib = 'current'
>> + new_xml = xml_item_update(new_xml, xpath, val, attrib)
>> # Updating memory and NUMA if necessary, if vm is offline
>> if not dom.isActive():
>> if 'memory' in params:
>> new_xml = self._update_memory_config(new_xml, params)
>> - elif 'cpus' in params and \
>> + elif 'cpus' in params and change_numa and \
>> (xpath_get_text(new_xml, XPATH_NUMA_CELL +
>> '/@memory') != []):
>> - vcpus = params['cpus']
>> new_xml = xml_item_update(
>> new_xml,
>> XPATH_NUMA_CELL,
>> - value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
>> + value='0',
>> attr='cpus')
>> if 'graphics' in params:
>> @@ -764,6 +832,8 @@ class VMModel(object):
>> raise OperationFailed("KCHVM0008E", {'name': vm_name,
>> 'err':
>> e.get_error_message()})
>> + if name is not None:
>> + vm_name = name
>> return (nonascii_name if nonascii_name is not None else
>> vm_name, dom)
>> def _update_memory_config(self, xml, params):
>> @@ -775,21 +845,20 @@ 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)
>> + cpu = get_cpu_xml(0, params['memory'] << 10)
>> root.insert(0, ET.fromstring(cpu))
>> else:
>> - numa_element = get_numa_xml(vcpus, params['memory']
>> << 10)
>> + numa_element = get_numa_xml(0, params['memory'] << 10)
>> cpu.insert(0, ET.fromstring(numa_element))
>> else:
>> if vcpus is not None:
>> xml = xml_item_update(
>> xml,
>> XPATH_NUMA_CELL,
>> - value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
>> + value='0',
>> attr='cpus')
>> root = ET.fromstring(xml_item_update(xml, XPATH_NUMA_CELL,
>> str(params['memory'] << 10),
>> @@ -853,11 +922,60 @@ class VMModel(object):
>> return new_xml
>> return ET.tostring(root, encoding="utf-8")
>> + def _get_host_maxcpu(self):
>> + if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
>> + cpu_model = CPUInfoModel(conn=self.conn)
>> + max_vcpu_val = (cpu_model.cores_available *
>> + cpu_model.threads_per_core)
>> + if max_vcpu_val > 255:
>> + max_vcpu_val = 255
>> + else:
>> + max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
>> + return max_vcpu_val
>> +
>> def _live_vm_update(self, dom, params):
>> self._vm_update_access_metadata(dom, params)
>> if 'memory' in params and dom.isActive():
>> self._update_memory_live(dom, params)
>> + if 'cpus' in params:
>> + cpus = params['cpus']
>> +
>> + if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
>> +
>> + # user cannot change vcpu if topology is defined. In
>> this case
>> + # vcpu must always be sockets * cores * threads.
>> + current_vcpu =
>> dom.vcpusFlags(libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
>> +
>> + if self.has_topology(dom):
>> + if current_vcpu != cpus:
>> + raise InvalidOperation('KCHVM0074E',
>> + {'vm': dom.name(),
>> + 'sockets':
>> + self.get_vm_max_sockets(),
>> + 'cores':
>> + self.get_vm_cores(),
>> + 'threads':
>> + self.get_vm_threads()})
>> +
>> + # do not need to update vcpu if the value edit
>> did not
>> + # change
>> + else:
>> + return
>> +
>> + try:
>> + # set maximum VCPU count
>> + max_vcpus = self._get_host_maxcpu()
>> + dom.setVcpusFlags(max_vcpus,
>> + 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:
>> diff --git a/src/wok/plugins/kimchi/tests/test_rest.py
>> b/src/wok/plugins/kimchi/tests/test_rest.py
>> index 544f2e6..9fa8c8d 100644
>> --- a/src/wok/plugins/kimchi/tests/test_rest.py
>> +++ b/src/wok/plugins/kimchi/tests/test_rest.py
>> @@ -144,6 +144,10 @@ class RestTests(unittest.TestCase):
>> vm =
>> json.loads(self.request('/plugins/kimchi/vms/vm-1').read())
>> self.assertEquals('vm-1', vm['name'])
>> + req = json.dumps({'cpus': 3})
>> + resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
>> + self.assertEquals(200, resp.status)
>> +
>> resp = self.request('/plugins/kimchi/vms/vm-1/start', '{}',
>> 'POST')
>> self.assertEquals(200, resp.status)
>> @@ -155,9 +159,10 @@ class RestTests(unittest.TestCase):
>> resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
>> self.assertEquals(400, resp.status)
>> - req = json.dumps({'cpus': 3})
>> + # Unable to do CPU hotplug
>> + req = json.dumps({'cpus': 5})
>> resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
>> - self.assertEquals(200, resp.status)
>> + self.assertEquals(400, resp.status)
>> # Check if there is support to memory hotplug, once vm is
>> running
>> resp =
>> self.request('/plugins/kimchi/config/capabilities').read()
>> @@ -171,6 +176,7 @@ class RestTests(unittest.TestCase):
>> req = json.dumps({"graphics": {'passwd': "abcdef"}})
>> resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
>> + self.assertEquals(200, resp.status)
>> info = json.loads(resp.read())
>> self.assertEquals('abcdef', info["graphics"]["passwd"])
>> self.assertEquals(None, info["graphics"]["passwdValidTo"])
>> diff --git a/src/wok/plugins/kimchi/vmtemplate.py
>> b/src/wok/plugins/kimchi/vmtemplate.py
>> index 3097b66..8d669db 100644
>> --- a/src/wok/plugins/kimchi/vmtemplate.py
>> +++ b/src/wok/plugins/kimchi/vmtemplate.py
>> @@ -298,7 +298,7 @@ class VMTemplate(object):
>> cpu_info = self.info.get('cpu_info')
>> if cpu_info is not None:
>> cpu_topo = cpu_info.get('topology')
>> - return get_cpu_xml(self.info.get('cpus'),
>> + return get_cpu_xml(0,
>> self.info.get('memory') << 10,
>> cpu_topo)
>> @@ -311,7 +311,6 @@ class VMTemplate(object):
>> params['qemu-namespace'] = ''
>> params['cdroms'] = ''
>> params['qemu-stream-cmdline'] = ''
>> - params['cpu_info'] = self._get_cpu_xml()
>> params['disks'] = self._get_disks_xml(vm_uuid)
>> params['serial'] = get_serial_xml(params)
>> @@ -322,6 +321,8 @@ class VMTemplate(object):
>> libvirt_stream_protocols =
>> kwargs.get('libvirt_stream_protocols', [])
>> cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols)
>> + max_vcpus = kwargs.get('max_vcpus', 1)
>> +
>> if not urlparse.urlparse(self.info.get('cdrom', "")).scheme
>> in \
>> libvirt_stream_protocols and \
>> params.get('iso_stream', False):
>> @@ -344,6 +345,25 @@ class VMTemplate(object):
>> if distro == "IBM_PowerKVM":
>> params['slots'] = 32
>> + cpu_topo = self.info.get('cpu_info').get('topology')
>> + if (cpu_topo is not None):
>> + sockets = int(max_vcpus / (cpu_topo['cores'] *
>> + cpu_topo['threads']))
>> + self.info['cpu_info']['topology']['sockets'] = sockets
>> +
>> + # Reduce maxvcpu to fit number of sockets if necessary
>> + total_max_vcpu = sockets * cpu_topo['cores'] *
>> cpu_topo['threads']
>> + if total_max_vcpu != max_vcpus:
>> + max_vcpus = total_max_vcpu
>> +
>> + params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
>> + (params['cpus'], max_vcpus)
>> + else:
>> + params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
>> + (params['cpus'], max_vcpus)
>> +
>> + params['cpu_info'] = self._get_cpu_xml()
>> +
>> xml = """
>> <domain type='%(domain)s'>
>> %(qemu-stream-cmdline)s
>> @@ -351,7 +371,7 @@ class VMTemplate(object):
>> <uuid>%(uuid)s</uuid>
>> <maxMemory slots='%(slots)s'
>> unit='KiB'>%(max_memory)s</maxMemory>
>> <memory unit='MiB'>%(memory)s</memory>
>> - <vcpu>%(cpus)s</vcpu>
>> + %(vcpus)s
>> %(cpu_info)s
>> <os>
>> <type arch='%(arch)s'>hvm</type>
>
> _______________________________________________
> 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