[Kimchi-devel] [PATCH] [Kimchi 2/3] Add maxvcpus attribute to guests

Paulo Ricardo Paz Vital pvital at linux.vnet.ibm.com
Fri Jan 29 16:58:05 UTC 2016



On 01/27/2016 07:14 PM, Lucio Correia wrote:
> - Enable update of maxvcpus, vcpus and CPU topology
> - Enable vCPU cold plug when topology is defined
> - Add cpu_info validation to guest update
> - Simplify _get_cpu_xml() code
> - Remove change_numa verification
> - Update docs and APIs with maxvcpus attribute
> 
> Signed-off-by: Lucio Correia <luciojhc at linux.vnet.ibm.com>
> ---
>  API.json      |   7 ++++
>  docs/API.md   |  17 +++++++-
>  i18n.py       |   1 -
>  model/vms.py  | 128 +++++++++++++++++++++++++++++++++++++---------------------
>  vmtemplate.py |  30 ++++----------
>  5 files changed, 110 insertions(+), 73 deletions(-)
> 
> diff --git a/API.json b/API.json
> index 4876cc0..a3d99f8 100644
> --- a/API.json
> +++ b/API.json
> @@ -299,12 +299,19 @@
>                          }
>                      }
>                  },
> +                "cpu_info": { "$ref": "#/kimchitype/cpu_info" },
>                  "cpus": {
>                      "description": "The new number of virtual CPUs for the VM",
>                      "type": "integer",
>                      "minimum": 1,
>                      "error": "KCHTMPL0012E"
>                  },
> +                "maxvcpus": {
> +                    "description": "The maximum number of virtual CPUs that can be assigned to the VM",
> +                    "type": "integer",
> +                    "minimum": 1,
> +                    "error": "KCHTMPL0012E"
> +                },
>                  "memory": {
>                      "description": "The new amount (MB) of memory for the VM",
>                      "type": "integer",
> diff --git a/docs/API.md b/docs/API.md
> index 4790d08..8dbc27c 100644
> --- a/docs/API.md
> +++ b/docs/API.md
> @@ -118,6 +118,7 @@ server.
>      * uuid: UUID of the VM.
>      * memory: The amount of memory assigned to the VM (in MB)
>      * cpus: The number of CPUs assigned to the VM
> +    * maxvcpus: The maximum number of CPUs that can be assigned to the VM

Same as I asked on the first patch.

In API.json you said that maxvcpu is "The maximum number of virtual CPUs
that can be assigned to the VMs..." and here looks like you missed the
*virtual* word. Is it correct?

>      * screenshot: A link to a recent capture of the screen in PNG format
>      * icon: A link to an icon that represents the VM
>      * graphics: A dict to show detail of VM graphics.
> @@ -138,12 +139,13 @@ server.
>      * groups: A list of system groups whose users have permission to access
>        the VM. Default is: empty (i.e. no groups given access).
>  * **DELETE**: Remove the Virtual Machine
> -* **PUT**: update the parameters of existed VM
> +* **PUT**: update the parameters of existing VM
>      * name: New name for this VM (only applied for shutoff VM)
>      * users: New list of system users.
>      * groups: New list of system groups.
> -    * cpus: New number of virtual cpus for this VM (if VM is running, new value
> +    * cpus: New number of CPUs for this VM (if VM is running, new value
>              will take effect in next reboot)
> +    * maxvcpus: The maximum number of CPUs that can be assigned to the VM

Same here.

>      * memory: New amount of memory (MB) for this VM (if VM is running, new
>                value will take effect in next reboot)
>      * graphics: A dict to show detail of VM graphics.
> @@ -152,6 +154,17 @@ server.
>          * passwdValidTo *(optional)*: lifetime for the console password. When
>                                        omitted the password will be valid just
>                                        for 30 seconds.
> +    * cpu_info: CPU-specific information.
> +        * topology: Specify sockets, threads, and cores to run the virtual CPU
> +            threads on.
> +            All three are required in order to specify cpu topology.
> +            * sockets - The maximum number of sockets to use.
> +            * cores   - The number of cores per socket.
> +            * threads - The number of threads per core.
> +            If CPU topology is specified, *cpus* and *maxvcpus* also must be
> +            specified. Make sure *maxvcpus* is equal to the product of sockets,
> +            cores and threads, and *cpus* is a multiple of a product of cores
> +            and threads.
> 
>  * **POST**: *See Virtual Machine Actions*
> 
> diff --git a/i18n.py b/i18n.py
> index bb6f3d1..59870e8 100644
> --- a/i18n.py
> +++ b/i18n.py
> @@ -130,7 +130,6 @@ messages = {
>      "KCHVM0071E": _("Memory value %(mem)s must be aligned to %(alignment)sMiB."),
>      "KCHVM0073E": _("Unable to update the following parameters while the VM is offline: %(params)s"),
>      "KCHVM0074E": _("Unable to update the following parameters while the VM is online: %(params)s"),
> -    "KCHVM0075E": _("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/model/vms.py b/model/vms.py
> index 9aaf530..fa3bcbb 100644
> --- a/model/vms.py
> +++ b/model/vms.py
> @@ -41,7 +41,7 @@ from wok.model.tasks import TaskModel
>  from wok.rollbackcontext import RollbackContext
>  from wok.utils import add_task, convert_data_size, get_next_clone_name
>  from wok.utils import import_class, run_setfacl_set_attr, run_command, wok_log
> -from wok.xmlutils.utils import xpath_get_text, xml_item_update
> +from wok.xmlutils.utils import xpath_get_text, xml_item_remove, xml_item_update
>  from wok.xmlutils.utils import dictize
> 
>  from wok.plugins.kimchi import model
> @@ -72,15 +72,12 @@ DOM_STATE_MAP = {0: 'nostate',
>                   6: 'crashed',
>                   7: 'pmsuspended'}
> 
> -VM_STATIC_UPDATE_PARAMS = {'name': './name', 'cpus': './vcpu'}
> -
> -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']
> +VM_OFFLINE_UPDATE_PARAMS = ['cpus', 'cpu_info', 'graphics', 'groups',
> +                            'maxvcpus', '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']"
> @@ -93,8 +90,10 @@ XPATH_DOMAIN_MEMORY_UNIT = '/domain/memory/@unit'
>  XPATH_DOMAIN_UUID = '/domain/uuid'
>  XPATH_DOMAIN_DEV_CPU_ID = '/domain/devices/spapr-cpu-socket/@id'
> 
> +XPATH_NAME = './name'
>  XPATH_NUMA_CELL = './cpu/numa/cell'
>  XPATH_TOPOLOGY = './cpu/topology'
> +XPATH_VCPU = './vcpu'
> 
>  # key: VM name; value: lock object
>  vm_locks = {}
> @@ -285,17 +284,6 @@ class VMModel(object):
>                      raise InvalidParameter('KCHVM0074E',
>                                             {'params': ', '.join(ext_params)})
> 
> -            if 'cpus' in params and DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
> -                # user cannot change vcpu if topology is defined.
> -                curr_vcpu = dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_CURRENT)
> -                if self.has_topology(dom) and curr_vcpu != params['cpus']:
> -                    raise InvalidOperation(
> -                        'KCHVM0075E',
> -                        {'vm': dom.name(),
> -                         'sockets': self.get_vm_sockets(dom),
> -                         'cores': self.get_vm_cores(dom),
> -                         'threads': self.get_vm_threads(dom)})
> -
>              self._live_vm_update(dom, params)
>              vm_name, dom = self._static_vm_update(name, dom, params)
>              return vm_name
> @@ -751,6 +739,8 @@ class VMModel(object):
>          old_xml = new_xml = dom.XMLDesc(0)
> 
>          params = copy.deepcopy(params)
> +
> +        # Update name
>          name = params.get('name')
>          nonascii_name = None
>          if name is not None:
> @@ -760,37 +750,42 @@ class VMModel(object):
>                  raise InvalidParameter("KCHVM0003E", msg_args)
> 
>              params['name'], nonascii_name = get_ascii_nonascii_name(name)
> +            new_xml = xml_item_update(new_xml, XPATH_NAME, nonascii_name, None)
> +
> +        # Update vcpus
> +        cpus = params.get('cpus', None)
> +        if cpus:
> +            attrib = 'current'
> +            new_xml = xml_item_update(new_xml, XPATH_VCPU, str(cpus), attrib)
> +
> +        # Update maxvcpus
> +        maxvcpus = params.get('maxvcpus', None)
> +        if maxvcpus:
> +            new_xml = xml_item_update(new_xml, XPATH_VCPU, str(maxvcpus), None)
> +
> +        # Update topology
> +        if 'cpu_info' in params:
> +            topology = params.get('cpu_info', {}).get('topology', None)
> +
> +            if topology:
> +                sockets = str(topology['sockets'])
> +                cores = str(topology['cores'])
> +                threads = str(topology['threads'])
> +
> +                xpath = XPATH_TOPOLOGY
> +                new_xml = xml_item_update(new_xml, xpath, sockets, 'sockets')
> +                new_xml = xml_item_update(new_xml, xpath, cores, 'cores')
> +                new_xml = xml_item_update(new_xml, xpath, threads, 'threads')
> +            else:
> +                # topology may have been removed
> +                new_xml = xml_item_remove(new_xml, xpath)
> 
> -        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]
> -                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)
> +        # Revalidate cpu info - may raise CPUInfo exceptions
> +        self._validate_cpu_info(new_xml, dom)
> 
>          # 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 change_numa and \
> -                 (xpath_get_text(new_xml, XPATH_NUMA_CELL + '/@memory') != []):
> -                new_xml = xml_item_update(
> -                    new_xml,
> -                    XPATH_NUMA_CELL,
> -                    value='0',
> -                    attr='cpus')
> +        if 'memory' in params and not dom.isActive():
> +            new_xml = self._update_memory_config(new_xml, params)
> 
>          if 'graphics' in params:
>              new_xml = self._update_graphics(dom, new_xml, params)
> @@ -928,6 +923,27 @@ class VMModel(object):
> 
>          return ET.tostring(root, encoding="utf-8")
> 
> +    def _validate_cpu_info(self, new_xml, dom):
> +        topology = None
> +        if self.has_topology(dom):
> +            sockets = xpath_get_text(new_xml, XPATH_TOPOLOGY + '/@sockets')[0]
> +            cores = xpath_get_text(new_xml, XPATH_TOPOLOGY + '/@cores')[0]
> +            threads = xpath_get_text(new_xml, XPATH_TOPOLOGY + '/@threads')[0]
> +            topology = {
> +                'sockets': int(sockets),
> +                'cores': int(cores),
> +                'threads': int(threads),
> +            }
> +
> +        params = {
> +            'maxvcpus': int(xpath_get_text(new_xml, 'vcpu')[0]),
> +            'cpus': int(xpath_get_text(new_xml, './vcpu/@current')[0]),
> +            'cpu_info': {'topology': topology}
> +        }
> +
> +        cpu_model = CPUInfoModel(conn=self.conn)
> +        cpu_model.check_cpu_info(params)
> +
>      def _live_vm_update(self, dom, params):
>          self._vm_update_access_metadata(dom, params)
>          if 'memory' in params and dom.isActive():
> @@ -1140,8 +1156,24 @@ class VMModel(object):
>          res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
>          users, groups = self._get_access_info(dom)
> 
> +        xml = dom.XMLDesc(0)
> +        maxvcpus = int(xpath_get_text(xml, XPATH_VCPU)[0])
> +
> +        cpu_info = None
> +        if self.has_topology(dom):
> +            sockets = int(xpath_get_text(xml, XPATH_TOPOLOGY + '/@sockets')[0])
> +            cores = int(xpath_get_text(xml, XPATH_TOPOLOGY + '/@cores')[0])
> +            threads = int(xpath_get_text(xml, XPATH_TOPOLOGY + '/@threads')[0])
> +
> +            cpu_info = {
> +                'topology': {
> +                    'sockets': sockets,
> +                    'cores': cores,
> +                    'threads': threads,
> +                }
> +            }
> +
>          if state == 'shutoff':
> -            xml = dom.XMLDesc(0)
>              val = xpath_get_text(xml, XPATH_DOMAIN_MEMORY)[0]
>              unit_list = xpath_get_text(xml, XPATH_DOMAIN_MEMORY_UNIT)
>              if len(unit_list) > 0:
> @@ -1158,6 +1190,8 @@ class VMModel(object):
>                  'uuid': dom.UUIDString(),
>                  'memory': memory,
>                  'cpus': info[3],
> +                'cpu_info': cpu_info,
> +                'maxvcpus': maxvcpus,
>                  'screenshot': screenshot,
>                  'icon': icon,
>                  # (type, listen, port, passwd, passwdValidTo)
> diff --git a/vmtemplate.py b/vmtemplate.py
> index 2f524a7..e1571b0 100644
> --- a/vmtemplate.py
> +++ b/vmtemplate.py
> @@ -324,12 +324,9 @@ class VMTemplate(object):
> 
>      def _get_cpu_xml(self):
>          # Include CPU topology, if provided
> -        cpu_info = self.info.get('cpu_info')
> -        if cpu_info is not None:
> -            cpu_topo = cpu_info.get('topology')
> -        return get_cpu_xml(0,
> -                           self.info.get('memory') << 10,
> -                           cpu_topo)
> +        cpu_topo = self.info.get('cpu_info', {}).get('topology', None)
> +
> +        return get_cpu_xml(0, self.info.get('memory') << 10, cpu_topo)
> 
>      def _get_max_memory(self, guest_memory):
>          # Setting maxMemory of the VM, which will be lesser value between:
> @@ -371,8 +368,6 @@ 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):
> @@ -402,22 +397,11 @@ class VMTemplate(object):
>          # set a hard limit using max_memory + 1GiB
>          params['hard_limit'] = params['max_memory'] + (1024 << 10)
> 
> -        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
> +        # vcpu element
> +        params['vcpus'] = "<vcpu current='%d'>%d</vcpu>" % \
> +                          (params['cpus'], params['maxvcpus'])
> 
> -            # 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)
> +        # cpu_info element
>          params['cpu_info'] = self._get_cpu_xml()
> 
>          xml = """
> 




More information about the Kimchi-devel mailing list