[Kimchi-devel] [PATCH] Update VCPU by using libvirt function

Rodrigo Trujillo rodrigo.trujillo at linux.vnet.ibm.com
Mon Nov 30 12:55:34 UTC 2015


Sent the fix in  "[PATCH 0/5I -V4] Add multiple disks in Template,    
add max vcpu and topology support"

=)

Rodrigo

On 11/30/2015 09:28 AM, Aline Manera wrote:
>
> 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
>
> _______________________________________________
> 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