debuggers.hg

changeset 21276:c87ec146229a

cpupools, xend: Add missing file XendCPUPool.py

Signed-off-by: Juergen Gross <juergen.gross@ts.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Apr 23 15:04:26 2010 +0100 (2010-04-23)
parents ffcc927a19a4
children 7ccf597281d8
files tools/python/xen/xend/XendCPUPool.py
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/python/xen/xend/XendCPUPool.py	Fri Apr 23 15:04:26 2010 +0100
     1.3 @@ -0,0 +1,905 @@
     1.4 +#============================================================================
     1.5 +# This library is free software; you can redistribute it and/or
     1.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     1.7 +# License as published by the Free Software Foundation.
     1.8 +#
     1.9 +# This library is distributed in the hope that it will be useful,
    1.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.12 +# Lesser General Public License for more details.
    1.13 +#
    1.14 +# You should have received a copy of the GNU Lesser General Public
    1.15 +# License along with this library; if not, write to the Free Software
    1.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.17 +#============================================================================
    1.18 +# Copyright (c) 2009 Fujitsu Technology Solutions.
    1.19 +#============================================================================
    1.20 +
    1.21 +""" CPU Pool support including XEN-API and Legacy API.
    1.22 +"""
    1.23 +
    1.24 +import types
    1.25 +import threading
    1.26 +import re
    1.27 +import xen.lowlevel.xc
    1.28 +import XendNode
    1.29 +import XendDomain
    1.30 +from xen.xend.XendLogging import log
    1.31 +from xen.xend.XendBase import XendBase
    1.32 +from xen.xend import XendAPIStore
    1.33 +from xen.xend.XendConstants import XS_POOLROOT
    1.34 +from xen.xend import uuid as genuuid
    1.35 +from xen.xend.XendError import VmError, XendAPIError, PoolError
    1.36 +from xen.xend.xenstore.xstransact import xstransact
    1.37 +from xen.util.sxputils import sxp2map, map2sxp
    1.38 +
    1.39 +
    1.40 +XEND_ERROR_INTERNAL             = 'INTERNAL_ERROR'
    1.41 +XEND_ERROR_UNKOWN_SCHED_POLICY  = 'UNKOWN_SCHED_POLICY'
    1.42 +XEND_ERROR_BAD_POOL_STATE       = 'POOL_BAD_STATE'
    1.43 +XEND_ERROR_POOL_PARAM           = 'PARAMETER_ERROR'
    1.44 +XEND_ERROR_INSUFFICIENT_CPUS    = 'INSUFFICIENT_CPUS'
    1.45 +XEND_ERROR_POOL_RECONF          = 'POOL_RECONF'
    1.46 +XEND_ERROR_INVALID_CPU          = 'INVAILD_CPU'
    1.47 +XEND_ERROR_LAST_CPU_NOT_REM     = 'LAST_CPU_NOT_REMOVEABLE'
    1.48 +
    1.49 +
    1.50 +XEN_SCHEDULER_TO_ID = {
    1.51 +    'credit2': xen.lowlevel.xc.XEN_SCHEDULER_CREDIT2,
    1.52 +    'credit' : xen.lowlevel.xc.XEN_SCHEDULER_CREDIT,
    1.53 +    'sedf'   : xen.lowlevel.xc.XEN_SCHEDULER_SEDF,
    1.54 +    }
    1.55 +
    1.56 +xc = xen.lowlevel.xc.xc()
    1.57 +
    1.58 +class XendCPUPool(XendBase):
    1.59 +    """ CPU Pool management.
    1.60 +        @ivar pool_lock: Lock to secure modification of pool data
    1.61 +        @type pool_lock: Rlock
    1.62 +    """
    1.63 +
    1.64 +    pool_lock = threading.RLock()
    1.65 +
    1.66 +    def getClass(cls):
    1.67 +        return "cpu_pool"
    1.68 +
    1.69 +    def getAttrRO(cls):
    1.70 +        attrRO = ['resident_on',
    1.71 +                  'started_VMs',
    1.72 +                  'host_CPUs',
    1.73 +                  'activated',
    1.74 +                 ]
    1.75 +        return XendBase.getAttrRO() + attrRO
    1.76 +
    1.77 +    def getAttrRW(cls):
    1.78 +        attrRW = ['name_label',
    1.79 +                  'name_description',
    1.80 +                  'auto_power_on',
    1.81 +                  'ncpu',
    1.82 +                  'sched_policy',
    1.83 +                  'proposed_CPUs',
    1.84 +                  'other_config',
    1.85 +                 ]
    1.86 +        return XendBase.getAttrRW() + attrRW
    1.87 +
    1.88 +    def getMethods(cls):
    1.89 +        methods = ['destroy',
    1.90 +                   'activate',
    1.91 +                   'deactivate',
    1.92 +                   'add_host_CPU_live',
    1.93 +                   'remove_host_CPU_live',
    1.94 +                   'add_to_proposed_CPUs',
    1.95 +                   'remove_from_proposed_CPUs',
    1.96 +                   'add_to_other_config',
    1.97 +                   'remove_from_other_config',
    1.98 +                  ]
    1.99 +        return XendBase.getMethods() + methods
   1.100 +
   1.101 +    def getFuncs(cls):
   1.102 +        funcs = ['create',
   1.103 +                 'get_by_name_label',
   1.104 +                ]
   1.105 +        return XendBase.getFuncs() + funcs
   1.106 +
   1.107 +    getClass    = classmethod(getClass)
   1.108 +    getAttrRO   = classmethod(getAttrRO)
   1.109 +    getAttrRW   = classmethod(getAttrRW)
   1.110 +    getMethods  = classmethod(getMethods)
   1.111 +    getFuncs    = classmethod(getFuncs)
   1.112 +
   1.113 +
   1.114 +    #
   1.115 +    # XenAPI function calls
   1.116 +    #
   1.117 +
   1.118 +    def create(cls, record):
   1.119 +        """ Create a new managed pool instance.
   1.120 +            @param record: attributes of pool
   1.121 +            @type record:  dict
   1.122 +            @return: uuid of created pool
   1.123 +            @rtype:  str
   1.124 +        """
   1.125 +        new_uuid = genuuid.createString()
   1.126 +        XendCPUPool(record, new_uuid)
   1.127 +        XendNode.instance().save_cpu_pools()
   1.128 +        return new_uuid
   1.129 +
   1.130 +    create = classmethod(create)
   1.131 +
   1.132 +
   1.133 +    def get_by_name_label(cls, name_label):
   1.134 +        """ Query a Pool(ref) by its name.
   1.135 +            @return: ref of pool
   1.136 +            @rtype:  str
   1.137 +        """
   1.138 +        cls.pool_lock.acquire()
   1.139 +        try:
   1.140 +            return [ inst.get_uuid()
   1.141 +                     for inst in XendAPIStore.get_all(cls.getClass())
   1.142 +                     if inst.name_label == name_label
   1.143 +                   ]
   1.144 +        finally:
   1.145 +            cls.pool_lock.release()
   1.146 +
   1.147 +    get_by_name_label = classmethod(get_by_name_label)
   1.148 +
   1.149 +
   1.150 +    def get_cpu_pool_by_cpu_ref(cls, host_cpu):
   1.151 +        """ Query cpu_pool ref the given cpu belongs to.
   1.152 +            @param host_cpu: ref of host_cpu to lookup
   1.153 +            @type host_cpu:  str
   1.154 +            @return: list cpu_pool refs (list contains not more than one element)
   1.155 +            @rtype:  list of str
   1.156 +        """
   1.157 +        node = XendNode.instance()
   1.158 +        cpu_nr = node.get_host_cpu_field(host_cpu, 'number')
   1.159 +        for pool_rec in xc.cpupool_getinfo():
   1.160 +            if cpu_nr in pool_rec['cpulist']:
   1.161 +                # pool found; return the ref
   1.162 +                return cls.query_pool_ref(pool_rec['cpupool'])
   1.163 +        return []
   1.164 +
   1.165 +    get_cpu_pool_by_cpu_ref = classmethod(get_cpu_pool_by_cpu_ref)
   1.166 +
   1.167 +
   1.168 +    def get_all_managed(cls):
   1.169 +        """ Query all managed pools.
   1.170 +            @return: uuids of all managed pools
   1.171 +            @rtype:  list of str
   1.172 +        """
   1.173 +        cls.pool_lock.acquire()
   1.174 +        try:
   1.175 +            managed_pools = [ inst.get_uuid()
   1.176 +                              for inst in XendAPIStore.get_all(cls.getClass())
   1.177 +                              if inst.is_managed() ]
   1.178 +        finally:
   1.179 +            cls.pool_lock.release()
   1.180 +        return managed_pools
   1.181 +
   1.182 +    get_all_managed = classmethod(get_all_managed)
   1.183 +
   1.184 +
   1.185 +    #
   1.186 +    # XenAPI methods calls
   1.187 +    #
   1.188 +
   1.189 +    def __init__(self, record, new_uuid, managed_pool=True):
   1.190 +        XendBase.__init__(self, new_uuid, record)
   1.191 +        try:
   1.192 +            self._managed = managed_pool
   1.193 +            self.name_label = None
   1.194 +
   1.195 +            name = record.get('name_label', 'Pool-Unnamed')
   1.196 +            self._checkName(name)
   1.197 +            self.name_label = name
   1.198 +            self.name_description = record.get('name_description',
   1.199 +                                               self.name_label)
   1.200 +            self.proposed_cpus = [ int(cpu)
   1.201 +                                   for cpu in record.get('proposed_CPUs', []) ]
   1.202 +            self.auto_power_on = bool(record.get('auto_power_on', False))
   1.203 +            self.ncpu = int(record.get('ncpu', 1))
   1.204 +            self.sched_policy = record.get('sched_policy', '')
   1.205 +            self.other_config = record.get('other_config', {})
   1.206 +        except Exception, ex:
   1.207 +            XendBase.destroy(self)
   1.208 +            raise ex
   1.209 +
   1.210 +
   1.211 +    def get_resident_on(self):
   1.212 +        """ Always return uuid of own node.
   1.213 +            @return: uuid of this node
   1.214 +            @rytpe:  str
   1.215 +        """
   1.216 +        return XendNode.instance().uuid
   1.217 +
   1.218 +    def get_started_VMs(self):
   1.219 +        """ Query all VMs currently assigned to pool.
   1.220 +            @return: ref of all VMs assigned to pool; if pool is not active,
   1.221 +                     an empty list will be returned
   1.222 +            @rtype:  list of str
   1.223 +        """
   1.224 +        if self.get_activated():
   1.225 +            # search VMs related to this pool
   1.226 +            pool_id = self.query_pool_id()
   1.227 +            started_VMs = [ vm.get_uuid()
   1.228 +                            for vm in XendDomain.instance().list('all')
   1.229 +                            if vm.get_cpu_pool() == pool_id ]
   1.230 +        else:
   1.231 +            # pool not active, so it couldn't have any started VMs
   1.232 +            started_VMs = []
   1.233 +
   1.234 +        return started_VMs
   1.235 +
   1.236 +    def get_host_CPUs(self):
   1.237 +        """ Query all cpu refs of this pool currently asisgned .
   1.238 +            - Read pool id of this pool from xenstore
   1.239 +            - Read cpu configuration from hypervisor
   1.240 +            - lookup cpu number -> cpu ref
   1.241 +            @return: host_cpu refs
   1.242 +            @rtype:  list of str
   1.243 +        """
   1.244 +        if self.get_activated():
   1.245 +            node = XendNode.instance()
   1.246 +            pool_id = self.query_pool_id()
   1.247 +            if pool_id == None:
   1.248 +                raise PoolError(XEND_ERROR_INTERNAL,
   1.249 +                                [self.getClass(), 'get_host_CPUs'])
   1.250 +            cpus = []
   1.251 +            for pool_rec in xc.cpupool_getinfo():
   1.252 +                if pool_rec['cpupool'] == pool_id:
   1.253 +                    cpus = pool_rec['cpulist']
   1.254 +
   1.255 +            # query host_cpu ref for any cpu of the pool
   1.256 +            host_CPUs = [ cpu_ref
   1.257 +                          for cpu_ref in node.get_host_cpu_refs()
   1.258 +                          if node.get_host_cpu_field(cpu_ref, 'number')
   1.259 +                              in cpus ]
   1.260 +        else:
   1.261 +            # pool not active, so it couldn't have any assigned cpus
   1.262 +            host_CPUs = []
   1.263 +
   1.264 +        return host_CPUs
   1.265 +
   1.266 +    def get_activated(self):
   1.267 +        """ Query if the pool is registered in XendStore.
   1.268 +            If pool uuid is not in XenStore, the pool is not activated.
   1.269 +            @return: True, if activated
   1.270 +            @rtype:  bool
   1.271 +        """
   1.272 +        return self.query_pool_id() != None
   1.273 +
   1.274 +    def get_name_label(self):
   1.275 +        return self.name_label
   1.276 +
   1.277 +    def get_name_description(self):
   1.278 +        return self.name_description
   1.279 +
   1.280 +    def get_auto_power_on(self):
   1.281 +        return self.auto_power_on
   1.282 +
   1.283 +    def get_ncpu(self):
   1.284 +        return self.ncpu
   1.285 +
   1.286 +    def get_sched_policy(self):
   1.287 +        if len(self.sched_policy) == 0:
   1.288 +            # default scheduler selected
   1.289 +            return XendNode.instance().get_vcpus_policy()
   1.290 +        else:
   1.291 +            return self.sched_policy
   1.292 +
   1.293 +    def get_proposed_CPUs(self):
   1.294 +        return [ str(cpu) for cpu in self.proposed_cpus ]
   1.295 +
   1.296 +    def get_other_config(self):
   1.297 +        return self.other_config
   1.298 +
   1.299 +    def set_name_label(self, name_label):
   1.300 +        self._checkName(name_label)
   1.301 +        self.name_label = name_label
   1.302 +        if self._managed:
   1.303 +            XendNode.instance().save_cpu_pools()
   1.304 +
   1.305 +    def set_name_description(self, name_descr):
   1.306 +        self.name_description = name_descr
   1.307 +        if self._managed:
   1.308 +            XendNode.instance().save_cpu_pools()
   1.309 +
   1.310 +    def set_auto_power_on(self, auto_power_on):
   1.311 +        self.auto_power_on = bool(int(auto_power_on))
   1.312 +        if self._managed:
   1.313 +            XendNode.instance().save_cpu_pools()
   1.314 +
   1.315 +    def set_ncpu(self, ncpu):
   1.316 +        _ncpu = int(ncpu)
   1.317 +        if _ncpu < 1:
   1.318 +            raise PoolError(XEND_ERROR_POOL_PARAM, 'ncpu')
   1.319 +        self.ncpu = _ncpu
   1.320 +        if self._managed:
   1.321 +            XendNode.instance().save_cpu_pools()
   1.322 +
   1.323 +    def set_sched_policy(self, sched_policy):
   1.324 +        if self.get_activated():
   1.325 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.326 +        self.sched_policy = sched_policy
   1.327 +        if self._managed:
   1.328 +            XendNode.instance().save_cpu_pools()
   1.329 +
   1.330 +    def set_proposed_CPUs(self, proposed_cpus):
   1.331 +        if self.get_activated():
   1.332 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.333 +        self.proposed_cpus = [ int(cpu) for cpu in proposed_cpus ]
   1.334 +        if self._managed:
   1.335 +            XendNode.instance().save_cpu_pools()
   1.336 +
   1.337 +    def set_other_config(self, other_config):
   1.338 +        self.other_config = other_config
   1.339 +        if self._managed:
   1.340 +            XendNode.instance().save_cpu_pools()
   1.341 +
   1.342 +    def destroy(self):
   1.343 +        """ In order to destroy a cpu pool, it must be deactivated """
   1.344 +        self.pool_lock.acquire()
   1.345 +        try:
   1.346 +            if self.get_activated():
   1.347 +                raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.348 +            XendBase.destroy(self)
   1.349 +        finally:
   1.350 +            self.pool_lock.release()
   1.351 +        XendNode.instance().save_cpu_pools()
   1.352 +
   1.353 +    def activate(self):
   1.354 +        """ Create pool in hypervisor and add cpus.
   1.355 +            Preconditions:
   1.356 +            - pool not already active
   1.357 +            - enough unbound cpus available
   1.358 +            Actions:
   1.359 +            - create pool in hypervisor
   1.360 +            - select free cpus (preferred from proposed_CPUs list) and bind it to
   1.361 +              the pool
   1.362 +            - create entries in Xenstore
   1.363 +        """
   1.364 +        self.pool_lock.acquire()
   1.365 +        try:
   1.366 +            if self.get_activated():
   1.367 +                raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.368 +            sched_policy = self.get_sched_policy()
   1.369 +            if sched_policy not in XEN_SCHEDULER_TO_ID.keys():
   1.370 +                raise PoolError(XEND_ERROR_UNKOWN_SCHED_POLICY)
   1.371 +            unbound_cpus = set(self.unbound_cpus())
   1.372 +            if len(unbound_cpus) < self.ncpu:
   1.373 +                raise PoolError(XEND_ERROR_INSUFFICIENT_CPUS,
   1.374 +                                [str(self.ncpu), str(len(unbound_cpus))])
   1.375 +
   1.376 +            # build list of cpu numbers to bind to pool
   1.377 +            cpu_set = set(self.proposed_cpus).intersection(unbound_cpus)
   1.378 +            if len(cpu_set) < self.ncpu:
   1.379 +                pool_cpus = (list(cpu_set) +
   1.380 +                             list(unbound_cpus.difference(cpu_set)))
   1.381 +            else:
   1.382 +                pool_cpus = list(cpu_set)
   1.383 +            pool_cpus = pool_cpus[0:self.ncpu]
   1.384 +
   1.385 +            # create pool in hypervisor
   1.386 +            pool_id = xc.cpupool_create(
   1.387 +                sched = XEN_SCHEDULER_TO_ID.get(sched_policy, 0))
   1.388 +
   1.389 +            self.update_XS(pool_id)
   1.390 +            # add cpus
   1.391 +            for cpu in pool_cpus:
   1.392 +                xc.cpupool_addcpu(pool_id, cpu)
   1.393 +
   1.394 +        finally:
   1.395 +            self.pool_lock.release()
   1.396 +
   1.397 +    def deactivate(self):
   1.398 +        """ Delete pool in hypervisor
   1.399 +            Preconditions:
   1.400 +            - pool is activated
   1.401 +            - no running VMs in pool
   1.402 +            Actions:
   1.403 +            - call hypervisor for deletion
   1.404 +            - remove path of pool in xenstore
   1.405 +        """
   1.406 +        self.pool_lock.acquire()
   1.407 +        try:
   1.408 +            if not self.get_activated():
   1.409 +                raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
   1.410 +            if len(self.get_started_VMs()) != 0:
   1.411 +                raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'in use')
   1.412 +
   1.413 +            pool_id = self.query_pool_id()
   1.414 +            # remove cpus from pool
   1.415 +            cpus = []
   1.416 +            for pool_rec in xc.cpupool_getinfo():
   1.417 +                if pool_rec['cpupool'] == pool_id:
   1.418 +                    cpus = pool_rec['cpulist']
   1.419 +            for cpu_number in cpus:
   1.420 +                xc.cpupool_removecpu(pool_id, cpu_number)
   1.421 +            xc.cpupool_destroy(pool_id)
   1.422 +
   1.423 +            # update XenStore
   1.424 +            xs_path = XS_POOLROOT + "%s/" % pool_id
   1.425 +            xstransact.Remove(xs_path)
   1.426 +        finally:
   1.427 +            self.pool_lock.release()
   1.428 +
   1.429 +    def add_host_CPU_live(self, cpu_ref):
   1.430 +        """ Add cpu to pool, if it is currently not assigned to a pool.
   1.431 +            @param cpu_ref: reference of host_cpu instance to add
   1.432 +            @type  cpu_ref: str
   1.433 +        """
   1.434 +        if not self.get_activated():
   1.435 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
   1.436 +        node = XendNode.instance()
   1.437 +        number = node.get_host_cpu_field(cpu_ref, 'number')
   1.438 +
   1.439 +        self.pool_lock.acquire()
   1.440 +        try:
   1.441 +            pool_id = self.query_pool_id()
   1.442 +            other_pool_ref = self.get_cpu_pool_by_cpu_ref(cpu_ref)
   1.443 +            if len(other_pool_ref) != 0:
   1.444 +                raise PoolError(XEND_ERROR_INVALID_CPU,
   1.445 +                            'cpu already assigned to pool "%s"' % other_pool_ref[0])
   1.446 +            xc.cpupool_addcpu(pool_id, number)
   1.447 +        finally:
   1.448 +            self.pool_lock.release()
   1.449 +
   1.450 +        if number not in self.proposed_cpus:
   1.451 +            self.proposed_cpus.append(number)
   1.452 +        self._update_ncpu(pool_id)
   1.453 +        if self._managed:
   1.454 +            XendNode.instance().save_cpu_pools()
   1.455 +
   1.456 +    def remove_host_CPU_live(self, cpu_ref):
   1.457 +        """ Remove cpu from pool.
   1.458 +            After successfull call, the cpu is free.
   1.459 +            Remove of the last cpu of the pool is rejected.
   1.460 +            @param cpu_ref: reference of host_cpu instance to remove
   1.461 +            @type  cpu_ref: str
   1.462 +        """
   1.463 +        if not self.get_activated():
   1.464 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
   1.465 +        node = XendNode.instance()
   1.466 +        number = node.get_host_cpu_field(cpu_ref, 'number')
   1.467 +
   1.468 +        self.pool_lock.acquire()
   1.469 +        try:
   1.470 +            pool_id = self.query_pool_id()
   1.471 +            pool_rec = {}
   1.472 +            for pool in xc.cpupool_getinfo():
   1.473 +                if pool['cpupool'] == pool_id:
   1.474 +                    pool_rec = pool
   1.475 +                    break
   1.476 +
   1.477 +            if number in pool_rec['cpulist']:
   1.478 +                if len(pool_rec['cpulist']) < 2 and pool_rec['n_dom'] > 0:
   1.479 +                    raise PoolError(XEND_ERROR_LAST_CPU_NOT_REM,
   1.480 +                                    'could not remove last cpu')
   1.481 +                xc.cpupool_removecpu(pool_id, number)
   1.482 +            else:
   1.483 +                raise PoolError(XEND_ERROR_INVALID_CPU,
   1.484 +                                'CPU not assigned to pool')
   1.485 +        finally:
   1.486 +            self.pool_lock.release()
   1.487 +
   1.488 +        if number in self.proposed_cpus:
   1.489 +            self.proposed_cpus.remove(number)
   1.490 +        self._update_ncpu(pool_id)
   1.491 +        if self._managed:
   1.492 +            XendNode.instance().save_cpu_pools()
   1.493 +
   1.494 +    def add_to_proposed_CPUs(self, cpu):
   1.495 +        if self.get_activated():
   1.496 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.497 +
   1.498 +        _cpu = int(cpu)
   1.499 +        if _cpu not in self.proposed_cpus:
   1.500 +            self.proposed_cpus.append(_cpu)
   1.501 +            self.proposed_cpus.sort()
   1.502 +            if self._managed:
   1.503 +                XendNode.instance().save_cpu_pools()
   1.504 +
   1.505 +    def remove_from_proposed_CPUs(self, cpu):
   1.506 +        if self.get_activated():
   1.507 +            raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
   1.508 +        _cpu = int(cpu)
   1.509 +        if _cpu in self.proposed_cpus:
   1.510 +            self.proposed_cpus.remove(_cpu)
   1.511 +            if self._managed:
   1.512 +                XendNode.instance().save_cpu_pools()
   1.513 +
   1.514 +    def add_to_other_config(self, key, value):
   1.515 +        self.other_config[key] = value
   1.516 +        if self._managed:
   1.517 +            XendNode.instance().save_cpu_pools()
   1.518 +
   1.519 +    def remove_from_other_config(self, key):
   1.520 +        if key in self.other_config:
   1.521 +            del self.other_config[key]
   1.522 +        if self._managed:
   1.523 +            XendNode.instance().save_cpu_pools()
   1.524 +
   1.525 +
   1.526 +    #
   1.527 +    # Legacy RPC calls
   1.528 +    #
   1.529 +    def pool_new(cls, config):
   1.530 +        try:
   1.531 +            record = sxp2map(config)
   1.532 +            if record.has_key('proposed_CPUs') and \
   1.533 +               not isinstance(record['proposed_CPUs'], types.ListType):
   1.534 +                record['proposed_CPUs'] = [record['proposed_CPUs']]
   1.535 +            new_uuid = cls.create(record)
   1.536 +        except XendAPIError, ex:
   1.537 +            raise VmError(ex.get_api_error())
   1.538 +        return new_uuid
   1.539 +
   1.540 +    def pool_create(cls, config):
   1.541 +        try:
   1.542 +            record = sxp2map(config)
   1.543 +            if record.has_key('proposed_CPUs') and \
   1.544 +               not isinstance(record['proposed_CPUs'], types.ListType):
   1.545 +                record['proposed_CPUs'] = [record['proposed_CPUs']]
   1.546 +            new_uuid = genuuid.createString()
   1.547 +            pool = XendCPUPool(record, new_uuid, False)
   1.548 +            pool.activate()
   1.549 +        except XendAPIError, ex:
   1.550 +            raise VmError(ex.get_api_error())
   1.551 +
   1.552 +    def pool_start(cls, poolname):
   1.553 +        pool = cls.lookup_pool(poolname)
   1.554 +        if not pool:
   1.555 +            raise VmError('unknown pool %s' % poolname)
   1.556 +        try:
   1.557 +            pool.activate()
   1.558 +        except XendAPIError, ex:
   1.559 +            raise VmError(ex.get_api_error())
   1.560 +
   1.561 +    def pool_list(cls, names):
   1.562 +        sxprs = []
   1.563 +        try:
   1.564 +            node = XendNode.instance()
   1.565 +            xd = XendDomain.instance()
   1.566 +            pools = cls.get_all_records()
   1.567 +            for (pool_uuid, pool_vals) in pools.items():
   1.568 +                if pool_vals['name_label'] in names or len(names) == 0:
   1.569 +                    # conv host_cpu refs to cpu number
   1.570 +                    cpus = [ node.get_host_cpu_field(cpu_ref, 'number')
   1.571 +                             for cpu_ref in pool_vals['host_CPUs'] ]
   1.572 +                    cpus.sort()
   1.573 +                    pool_vals['host_CPU_numbers'] = cpus
   1.574 +                    # query VMs names. Take in account, that a VM
   1.575 +                    # returned by get_all_records could be destroy, now
   1.576 +                    vm_names = [ vm.getName()
   1.577 +                                 for vm in map(xd.get_vm_by_uuid,
   1.578 +                                               pool_vals['started_VMs'])
   1.579 +                                 if vm ]
   1.580 +                    pool_vals['started_VM_names'] = vm_names
   1.581 +                    pool_vals['auto_power_on'] = int(pool_vals['auto_power_on'])
   1.582 +                    sxprs += [[pool_uuid] + map2sxp(pool_vals)]
   1.583 +        except XendAPIError, ex:
   1.584 +            raise VmError(ex.get_api_error())
   1.585 +        return sxprs
   1.586 +
   1.587 +    def pool_destroy(cls, poolname):
   1.588 +        pool = cls.lookup_pool(poolname)
   1.589 +        if not pool:
   1.590 +            raise VmError('unknown pool %s' % poolname)
   1.591 +        try:
   1.592 +            pool.deactivate()
   1.593 +            if not pool.is_managed():
   1.594 +                pool.destroy()
   1.595 +        except XendAPIError, ex:
   1.596 +            raise VmError(ex.get_api_error())
   1.597 +
   1.598 +    def pool_delete(cls, poolname):
   1.599 +        pool = cls.lookup_pool(poolname)
   1.600 +        if not pool:
   1.601 +            raise VmError('unknown pool %s' % poolname)
   1.602 +        try:
   1.603 +            pool.destroy()
   1.604 +        except XendAPIError, ex:
   1.605 +            raise VmError(ex.get_api_error())
   1.606 +
   1.607 +    def pool_cpu_add(cls, poolname, cpu):
   1.608 +        pool = cls.lookup_pool(poolname)
   1.609 +        if not pool:
   1.610 +            raise VmError('unknown pool %s' % poolname)
   1.611 +        try:
   1.612 +            cpu_ref = cls._cpu_number_to_ref(int(cpu))
   1.613 +            if cpu_ref:
   1.614 +                pool.add_host_CPU_live(cpu_ref)
   1.615 +            else:
   1.616 +                raise PoolError(XEND_ERROR_INVALID_CPU,
   1.617 +                                'CPU unknown')
   1.618 +        except XendAPIError, ex:
   1.619 +            raise VmError(ex.get_api_error())
   1.620 +
   1.621 +    def pool_cpu_remove(cls, poolname, cpu):
   1.622 +        pool = cls.lookup_pool(poolname)
   1.623 +        if not pool:
   1.624 +            raise VmError('unknown pool %s' % poolname)
   1.625 +        try:
   1.626 +            cpu_ref = cls._cpu_number_to_ref(int(cpu))
   1.627 +            if cpu_ref:
   1.628 +                pool.remove_host_CPU_live(cpu_ref)
   1.629 +            else:
   1.630 +                raise PoolError(XEND_ERROR_INVALID_CPU,
   1.631 +                                'CPU unknown')
   1.632 +        except XendAPIError, ex:
   1.633 +            raise VmError(ex.get_api_error())
   1.634 +
   1.635 +    def pool_migrate(cls, domname, poolname):
   1.636 +        dom = XendDomain.instance()
   1.637 +        pool = cls.lookup_pool(poolname)
   1.638 +        if not pool:
   1.639 +            raise VmError('unknown pool %s' % poolname)
   1.640 +        dominfo = dom.domain_lookup_nr(domname)
   1.641 +        if not dominfo:
   1.642 +            raise VmError('unknown domain %s' % domname)
   1.643 +        domid = dominfo.getDomid()
   1.644 +        if domid is not None:
   1.645 +            if domid == 0:
   1.646 +                raise VmError('could not move Domain-0')
   1.647 +            try:
   1.648 +                cls.move_domain(pool.get_uuid(), domid)
   1.649 +            except Exception, ex:
   1.650 +                raise VmError('could not move domain')
   1.651 +        dominfo.info['pool_name'] = poolname
   1.652 +        dom.managed_config_save(dominfo)
   1.653 +
   1.654 +    pool_new        = classmethod(pool_new)
   1.655 +    pool_create     = classmethod(pool_create)
   1.656 +    pool_start      = classmethod(pool_start)
   1.657 +    pool_list       = classmethod(pool_list)
   1.658 +    pool_destroy    = classmethod(pool_destroy)
   1.659 +    pool_delete     = classmethod(pool_delete)
   1.660 +    pool_cpu_add    = classmethod(pool_cpu_add)
   1.661 +    pool_cpu_remove = classmethod(pool_cpu_remove)
   1.662 +    pool_migrate    = classmethod(pool_migrate)
   1.663 +
   1.664 +
   1.665 +    #
   1.666 +    # methods
   1.667 +    #
   1.668 +
   1.669 +    def is_managed(self):
   1.670 +        """ Check, if pool is managed.
   1.671 +            @return: True, if managed
   1.672 +            @rtype: bool
   1.673 +        """
   1.674 +        return self._managed
   1.675 +
   1.676 +    def query_pool_id(self):
   1.677 +        """ Get corresponding pool-id of pool instance from XenStore.
   1.678 +            @return: pool id or None
   1.679 +            @rytpe:  int
   1.680 +        """
   1.681 +        self.pool_lock.acquire()
   1.682 +        try:
   1.683 +            for pool_id in xstransact.List(XS_POOLROOT):
   1.684 +                uuid = xstransact.Read(XS_POOLROOT + "%s/" % pool_id, 'uuid')
   1.685 +                if uuid == self.get_uuid():
   1.686 +                    return int(pool_id)
   1.687 +        finally:
   1.688 +            self.pool_lock.release()
   1.689 +
   1.690 +        return None
   1.691 +
   1.692 +    def update_XS(self, pool_id):
   1.693 +        """ Write (or update) data in xenstore taken from instance.
   1.694 +            @param pool_id: Pool id to build path to pool data in xenstore
   1.695 +            @type  pool_id: int
   1.696 +        """
   1.697 +        self.pool_lock.acquire()
   1.698 +        try:
   1.699 +            xs_path = XS_POOLROOT + "%s/" % pool_id
   1.700 +            xs_entries = { 'uuid' : self.get_uuid(),
   1.701 +                           'name' : self.name_label,
   1.702 +                           'description' : self.name_description
   1.703 +                         }
   1.704 +            xstransact.Mkdir(xs_path)
   1.705 +            xstransact.Mkdir(xs_path, 'other_config')
   1.706 +            xstransact.Write(xs_path, xs_entries)
   1.707 +            xstransact.Write('%s%s' % (xs_path, 'other_config'),
   1.708 +                             self.other_config)
   1.709 +        finally:
   1.710 +            self.pool_lock.release()
   1.711 +
   1.712 +    def _update_ncpu(self, pool_id):
   1.713 +        for pool_rec in xc.cpupool_getinfo():
   1.714 +            if pool_rec['cpupool'] == pool_id:
   1.715 +                self.ncpu = len(pool_rec['cpulist'])
   1.716 +
   1.717 +    def _checkName(self, name):
   1.718 +        """ Check if a pool name is valid. Valid names contain alphabetic
   1.719 +            characters, digits, or characters in '_-.:/+'.
   1.720 +            The same name cannot be used for more than one pool at the same
   1.721 +            time.
   1.722 +            @param name: name
   1.723 +            @type name:  str
   1.724 +            @raise: PoolError if invalid
   1.725 +        """
   1.726 +        if name is None or name == '':
   1.727 +            raise PoolError(XEND_ERROR_POOL_PARAM, 'Missing Pool Name')
   1.728 +        if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
   1.729 +            raise PoolError(XEND_ERROR_POOL_PARAM, 'Invalid Pool Name')
   1.730 +
   1.731 +        pool = self.lookup_pool(name)
   1.732 +        if pool and pool.get_uuid() != self.get_uuid():
   1.733 +            raise PoolError(XEND_ERROR_POOL_PARAM,
   1.734 +                'Pool name "%s" already exists' % name)
   1.735 +
   1.736 +
   1.737 +    #
   1.738 +    # class methods
   1.739 +    #
   1.740 +
   1.741 +    def recreate_active_pools(cls):
   1.742 +        """ Read active pool config from hypervisor and create pool instances.
   1.743 +            - Query pool ids and assigned CPUs from hypervisor.
   1.744 +            - Query additional information for any pool from xenstore.
   1.745 +              If an entry for a pool id is missing in xenstore, it will be
   1.746 +              recreated with a new uuid and generic name (this is an error case)
   1.747 +            - Create an XendCPUPool instance for any pool id
   1.748 +            Function have to be called after recreation of managed pools.
   1.749 +        """
   1.750 +        log.debug('recreate_active_pools')
   1.751 +
   1.752 +        for pool_rec in xc.cpupool_getinfo():
   1.753 +            pool = pool_rec['cpupool']
   1.754 +
   1.755 +            # read pool data from xenstore
   1.756 +            path = XS_POOLROOT + "%s/" % pool
   1.757 +            uuid = xstransact.Read(path, 'uuid')
   1.758 +            if not uuid:
   1.759 +                # xenstore entry missing / invaild; create entry with new uuid
   1.760 +                uuid = genuuid.createString()
   1.761 +                name = "Pool-%s" % pool
   1.762 +                try:
   1.763 +                    inst = XendCPUPool( { 'name_label' : name }, uuid, False )
   1.764 +                    inst.update_XS(pool)
   1.765 +                except PoolError, ex:
   1.766 +                    # log error and skip domain
   1.767 +                    log.error('cannot recreate pool %s; skipping (reason: %s)' \
   1.768 +                        % (name, ex))
   1.769 +            else:
   1.770 +                (name, descr) = xstransact.Read(path, 'name', 'description')
   1.771 +                other_config = {}
   1.772 +                for key in xstransact.List(path + 'other_config'):
   1.773 +                    other_config[key] = xstransact.Read(
   1.774 +                        path + 'other_config/%s' % key)
   1.775 +
   1.776 +                # check existance of pool instance
   1.777 +                inst = XendAPIStore.get(uuid, cls.getClass())
   1.778 +                if inst:
   1.779 +                    # update attributes of existing instance
   1.780 +                    inst.name_label = name
   1.781 +                    inst.name_description = descr
   1.782 +                    inst.other_config = other_config
   1.783 +                else:
   1.784 +                    # recreate instance
   1.785 +                    try:
   1.786 +                        inst = XendCPUPool(
   1.787 +                            { 'name_label' : name,
   1.788 +                              'name_description' : descr,
   1.789 +                              'other_config' : other_config,
   1.790 +                              'proposed_CPUs' : pool_rec['cpulist'],
   1.791 +                              'ncpu' : len(pool_rec['cpulist']),
   1.792 +                            },
   1.793 +                            uuid, False )
   1.794 +                    except PoolError, ex:
   1.795 +                        # log error and skip domain
   1.796 +                        log.error(
   1.797 +                            'cannot recreate pool %s; skipping (reason: %s)' \
   1.798 +                            % (name, ex))
   1.799 +
   1.800 +    recreate_active_pools = classmethod(recreate_active_pools)
   1.801 +
   1.802 +
   1.803 +    def recreate(cls, record, current_uuid):
   1.804 +        """ Recreate a pool instance while xend restart.
   1.805 +            @param record: attributes of pool
   1.806 +            @type record:  dict
   1.807 +            @param current_uuid: uuid of pool to create
   1.808 +            @type current_uuid:  str
   1.809 +        """
   1.810 +        XendCPUPool(record, current_uuid)
   1.811 +
   1.812 +    recreate = classmethod(recreate)
   1.813 +
   1.814 +
   1.815 +    def autostart_pools(cls):
   1.816 +        """ Start managed pools that are marked as autostart pools.
   1.817 +            Function is called after recreation of managed domains while
   1.818 +            xend restart.
   1.819 +        """
   1.820 +        cls.pool_lock.acquire()
   1.821 +        try:
   1.822 +            for inst in XendAPIStore.get_all(cls.getClass()):
   1.823 +                if inst.is_managed() and inst.auto_power_on and \
   1.824 +                   inst.query_pool_id() == None:
   1.825 +                    inst.activate()
   1.826 +        finally:
   1.827 +            cls.pool_lock.release()
   1.828 +
   1.829 +    autostart_pools = classmethod(autostart_pools)
   1.830 +
   1.831 +
   1.832 +    def move_domain(cls, pool_ref, domid):
   1.833 +        cls.pool_lock.acquire()
   1.834 +        try:
   1.835 +            pool = XendAPIStore.get(pool_ref, cls.getClass())
   1.836 +            pool_id = pool.query_pool_id()
   1.837 +
   1.838 +            xc.cpupool_movedomain(pool_id, domid)
   1.839 +        finally:
   1.840 +            cls.pool_lock.release()
   1.841 +
   1.842 +    move_domain = classmethod(move_domain)
   1.843 +
   1.844 +
   1.845 +    def query_pool_ref(cls, pool_id):
   1.846 +        """ Get pool ref by pool id.
   1.847 +            Take the ref from xenstore.
   1.848 +            @param pool_id:
   1.849 +            @type  pool_id: int
   1.850 +            @return: ref
   1.851 +            @rtype:  str
   1.852 +        """
   1.853 +        uuid = xstransact.Read(XS_POOLROOT + "%s/" % pool_id, 'uuid')
   1.854 +        if uuid:
   1.855 +            return [uuid]
   1.856 +        else:
   1.857 +            return []
   1.858 +
   1.859 +    query_pool_ref = classmethod(query_pool_ref)
   1.860 +
   1.861 +
   1.862 +    def lookup_pool(cls, id_or_name):
   1.863 +        """ Search XendCPUPool instance with given id_or_name.
   1.864 +            @param id_or_name: pool id or pool nameto search
   1.865 +            @type id_or_name:  [int, str]
   1.866 +            @return: instane or None if not found
   1.867 +            @rtype:  XendCPUPool
   1.868 +        """
   1.869 +        pool_uuid = None
   1.870 +        try:
   1.871 +            pool_id = int(id_or_name)
   1.872 +            # pool id given ?
   1.873 +            pool_uuid = cls.query_pool_ref(pool_id)
   1.874 +            if not pool_uuid:
   1.875 +                # not found -> search name
   1.876 +                pool_uuid = cls.get_by_name_label(id_or_name)
   1.877 +        except ValueError:
   1.878 +            # pool name given
   1.879 +            pool_uuid = cls.get_by_name_label(id_or_name)
   1.880 +
   1.881 +        if len(pool_uuid) > 0:
   1.882 +            return XendAPIStore.get(pool_uuid[0], cls.getClass())
   1.883 +        else:
   1.884 +            return None
   1.885 +
   1.886 +    lookup_pool = classmethod(lookup_pool)
   1.887 +
   1.888 +
   1.889 +    def _cpu_number_to_ref(cls, number):
   1.890 +        node = XendNode.instance()
   1.891 +        for cpu_ref in node.get_host_cpu_refs():
   1.892 +            if node.get_host_cpu_field(cpu_ref, 'number') == number:
   1.893 +                return cpu_ref
   1.894 +        return None
   1.895 +
   1.896 +    _cpu_number_to_ref = classmethod(_cpu_number_to_ref)
   1.897 +
   1.898 +
   1.899 +    def unbound_cpus(cls):
   1.900 +        """ Build list containing the numbers of all cpus not bound to a pool.
   1.901 +            Info is taken from Hypervisor.
   1.902 +            @return: list of cpu numbers
   1.903 +            @rytpe:  list of int
   1.904 +        """
   1.905 +        return xc.cpupool_freeinfo()
   1.906 +
   1.907 +    unbound_cpus = classmethod(unbound_cpus)
   1.908 +