mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
cpupools, xend: Add missing file XendCPUPool.py
Signed-off-by: Juergen Gross <juergen.gross@ts.fujitsu.com>
This commit is contained in:
1 parent
6cf942ffa2
commit
8149e93337
1 file changed
+905
@@ -0,0 +1,905 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (c) 2009 Fujitsu Technology Solutions.
|
||||
#============================================================================
|
||||
|
||||
""" CPU Pool support including XEN-API and Legacy API.
|
||||
"""
|
||||
|
||||
import types
|
||||
import threading
|
||||
import re
|
||||
import xen.lowlevel.xc
|
||||
import XendNode
|
||||
import XendDomain
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendBase import XendBase
|
||||
from xen.xend import XendAPIStore
|
||||
from xen.xend.XendConstants import XS_POOLROOT
|
||||
from xen.xend import uuid as genuuid
|
||||
from xen.xend.XendError import VmError, XendAPIError, PoolError
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
from xen.util.sxputils import sxp2map, map2sxp
|
||||
|
||||
|
||||
XEND_ERROR_INTERNAL = 'INTERNAL_ERROR'
|
||||
XEND_ERROR_UNKOWN_SCHED_POLICY = 'UNKOWN_SCHED_POLICY'
|
||||
XEND_ERROR_BAD_POOL_STATE = 'POOL_BAD_STATE'
|
||||
XEND_ERROR_POOL_PARAM = 'PARAMETER_ERROR'
|
||||
XEND_ERROR_INSUFFICIENT_CPUS = 'INSUFFICIENT_CPUS'
|
||||
XEND_ERROR_POOL_RECONF = 'POOL_RECONF'
|
||||
XEND_ERROR_INVALID_CPU = 'INVAILD_CPU'
|
||||
XEND_ERROR_LAST_CPU_NOT_REM = 'LAST_CPU_NOT_REMOVEABLE'
|
||||
|
||||
|
||||
XEN_SCHEDULER_TO_ID = {
|
||||
'credit2': xen.lowlevel.xc.XEN_SCHEDULER_CREDIT2,
|
||||
'credit' : xen.lowlevel.xc.XEN_SCHEDULER_CREDIT,
|
||||
'sedf' : xen.lowlevel.xc.XEN_SCHEDULER_SEDF,
|
||||
}
|
||||
|
||||
xc = xen.lowlevel.xc.xc()
|
||||
|
||||
class XendCPUPool(XendBase):
|
||||
""" CPU Pool management.
|
||||
@ivar pool_lock: Lock to secure modification of pool data
|
||||
@type pool_lock: Rlock
|
||||
"""
|
||||
|
||||
pool_lock = threading.RLock()
|
||||
|
||||
def getClass(cls):
|
||||
return "cpu_pool"
|
||||
|
||||
def getAttrRO(cls):
|
||||
attrRO = ['resident_on',
|
||||
'started_VMs',
|
||||
'host_CPUs',
|
||||
'activated',
|
||||
]
|
||||
return XendBase.getAttrRO() + attrRO
|
||||
|
||||
def getAttrRW(cls):
|
||||
attrRW = ['name_label',
|
||||
'name_description',
|
||||
'auto_power_on',
|
||||
'ncpu',
|
||||
'sched_policy',
|
||||
'proposed_CPUs',
|
||||
'other_config',
|
||||
]
|
||||
return XendBase.getAttrRW() + attrRW
|
||||
|
||||
def getMethods(cls):
|
||||
methods = ['destroy',
|
||||
'activate',
|
||||
'deactivate',
|
||||
'add_host_CPU_live',
|
||||
'remove_host_CPU_live',
|
||||
'add_to_proposed_CPUs',
|
||||
'remove_from_proposed_CPUs',
|
||||
'add_to_other_config',
|
||||
'remove_from_other_config',
|
||||
]
|
||||
return XendBase.getMethods() + methods
|
||||
|
||||
def getFuncs(cls):
|
||||
funcs = ['create',
|
||||
'get_by_name_label',
|
||||
]
|
||||
return XendBase.getFuncs() + funcs
|
||||
|
||||
getClass = classmethod(getClass)
|
||||
getAttrRO = classmethod(getAttrRO)
|
||||
getAttrRW = classmethod(getAttrRW)
|
||||
getMethods = classmethod(getMethods)
|
||||
getFuncs = classmethod(getFuncs)
|
||||
|
||||
|
||||
#
|
||||
# XenAPI function calls
|
||||
#
|
||||
|
||||
def create(cls, record):
|
||||
""" Create a new managed pool instance.
|
||||
@param record: attributes of pool
|
||||
@type record: dict
|
||||
@return: uuid of created pool
|
||||
@rtype: str
|
||||
"""
|
||||
new_uuid = genuuid.createString()
|
||||
XendCPUPool(record, new_uuid)
|
||||
XendNode.instance().save_cpu_pools()
|
||||
return new_uuid
|
||||
|
||||
create = classmethod(create)
|
||||
|
||||
|
||||
def get_by_name_label(cls, name_label):
|
||||
""" Query a Pool(ref) by its name.
|
||||
@return: ref of pool
|
||||
@rtype: str
|
||||
"""
|
||||
cls.pool_lock.acquire()
|
||||
try:
|
||||
return [ inst.get_uuid()
|
||||
for inst in XendAPIStore.get_all(cls.getClass())
|
||||
if inst.name_label == name_label
|
||||
]
|
||||
finally:
|
||||
cls.pool_lock.release()
|
||||
|
||||
get_by_name_label = classmethod(get_by_name_label)
|
||||
|
||||
|
||||
def get_cpu_pool_by_cpu_ref(cls, host_cpu):
|
||||
""" Query cpu_pool ref the given cpu belongs to.
|
||||
@param host_cpu: ref of host_cpu to lookup
|
||||
@type host_cpu: str
|
||||
@return: list cpu_pool refs (list contains not more than one element)
|
||||
@rtype: list of str
|
||||
"""
|
||||
node = XendNode.instance()
|
||||
cpu_nr = node.get_host_cpu_field(host_cpu, 'number')
|
||||
for pool_rec in xc.cpupool_getinfo():
|
||||
if cpu_nr in pool_rec['cpulist']:
|
||||
# pool found; return the ref
|
||||
return cls.query_pool_ref(pool_rec['cpupool'])
|
||||
return []
|
||||
|
||||
get_cpu_pool_by_cpu_ref = classmethod(get_cpu_pool_by_cpu_ref)
|
||||
|
||||
|
||||
def get_all_managed(cls):
|
||||
""" Query all managed pools.
|
||||
@return: uuids of all managed pools
|
||||
@rtype: list of str
|
||||
"""
|
||||
cls.pool_lock.acquire()
|
||||
try:
|
||||
managed_pools = [ inst.get_uuid()
|
||||
for inst in XendAPIStore.get_all(cls.getClass())
|
||||
if inst.is_managed() ]
|
||||
finally:
|
||||
cls.pool_lock.release()
|
||||
return managed_pools
|
||||
|
||||
get_all_managed = classmethod(get_all_managed)
|
||||
|
||||
|
||||
#
|
||||
# XenAPI methods calls
|
||||
#
|
||||
|
||||
def __init__(self, record, new_uuid, managed_pool=True):
|
||||
XendBase.__init__(self, new_uuid, record)
|
||||
try:
|
||||
self._managed = managed_pool
|
||||
self.name_label = None
|
||||
|
||||
name = record.get('name_label', 'Pool-Unnamed')
|
||||
self._checkName(name)
|
||||
self.name_label = name
|
||||
self.name_description = record.get('name_description',
|
||||
self.name_label)
|
||||
self.proposed_cpus = [ int(cpu)
|
||||
for cpu in record.get('proposed_CPUs', []) ]
|
||||
self.auto_power_on = bool(record.get('auto_power_on', False))
|
||||
self.ncpu = int(record.get('ncpu', 1))
|
||||
self.sched_policy = record.get('sched_policy', '')
|
||||
self.other_config = record.get('other_config', {})
|
||||
except Exception, ex:
|
||||
XendBase.destroy(self)
|
||||
raise ex
|
||||
|
||||
|
||||
def get_resident_on(self):
|
||||
""" Always return uuid of own node.
|
||||
@return: uuid of this node
|
||||
@rytpe: str
|
||||
"""
|
||||
return XendNode.instance().uuid
|
||||
|
||||
def get_started_VMs(self):
|
||||
""" Query all VMs currently assigned to pool.
|
||||
@return: ref of all VMs assigned to pool; if pool is not active,
|
||||
an empty list will be returned
|
||||
@rtype: list of str
|
||||
"""
|
||||
if self.get_activated():
|
||||
# search VMs related to this pool
|
||||
pool_id = self.query_pool_id()
|
||||
started_VMs = [ vm.get_uuid()
|
||||
for vm in XendDomain.instance().list('all')
|
||||
if vm.get_cpu_pool() == pool_id ]
|
||||
else:
|
||||
# pool not active, so it couldn't have any started VMs
|
||||
started_VMs = []
|
||||
|
||||
return started_VMs
|
||||
|
||||
def get_host_CPUs(self):
|
||||
""" Query all cpu refs of this pool currently asisgned .
|
||||
- Read pool id of this pool from xenstore
|
||||
- Read cpu configuration from hypervisor
|
||||
- lookup cpu number -> cpu ref
|
||||
@return: host_cpu refs
|
||||
@rtype: list of str
|
||||
"""
|
||||
if self.get_activated():
|
||||
node = XendNode.instance()
|
||||
pool_id = self.query_pool_id()
|
||||
if pool_id == None:
|
||||
raise PoolError(XEND_ERROR_INTERNAL,
|
||||
[self.getClass(), 'get_host_CPUs'])
|
||||
cpus = []
|
||||
for pool_rec in xc.cpupool_getinfo():
|
||||
if pool_rec['cpupool'] == pool_id:
|
||||
cpus = pool_rec['cpulist']
|
||||
|
||||
# query host_cpu ref for any cpu of the pool
|
||||
host_CPUs = [ cpu_ref
|
||||
for cpu_ref in node.get_host_cpu_refs()
|
||||
if node.get_host_cpu_field(cpu_ref, 'number')
|
||||
in cpus ]
|
||||
else:
|
||||
# pool not active, so it couldn't have any assigned cpus
|
||||
host_CPUs = []
|
||||
|
||||
return host_CPUs
|
||||
|
||||
def get_activated(self):
|
||||
""" Query if the pool is registered in XendStore.
|
||||
If pool uuid is not in XenStore, the pool is not activated.
|
||||
@return: True, if activated
|
||||
@rtype: bool
|
||||
"""
|
||||
return self.query_pool_id() != None
|
||||
|
||||
def get_name_label(self):
|
||||
return self.name_label
|
||||
|
||||
def get_name_description(self):
|
||||
return self.name_description
|
||||
|
||||
def get_auto_power_on(self):
|
||||
return self.auto_power_on
|
||||
|
||||
def get_ncpu(self):
|
||||
return self.ncpu
|
||||
|
||||
def get_sched_policy(self):
|
||||
if len(self.sched_policy) == 0:
|
||||
# default scheduler selected
|
||||
return XendNode.instance().get_vcpus_policy()
|
||||
else:
|
||||
return self.sched_policy
|
||||
|
||||
def get_proposed_CPUs(self):
|
||||
return [ str(cpu) for cpu in self.proposed_cpus ]
|
||||
|
||||
def get_other_config(self):
|
||||
return self.other_config
|
||||
|
||||
def set_name_label(self, name_label):
|
||||
self._checkName(name_label)
|
||||
self.name_label = name_label
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_name_description(self, name_descr):
|
||||
self.name_description = name_descr
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_auto_power_on(self, auto_power_on):
|
||||
self.auto_power_on = bool(int(auto_power_on))
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_ncpu(self, ncpu):
|
||||
_ncpu = int(ncpu)
|
||||
if _ncpu < 1:
|
||||
raise PoolError(XEND_ERROR_POOL_PARAM, 'ncpu')
|
||||
self.ncpu = _ncpu
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_sched_policy(self, sched_policy):
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
self.sched_policy = sched_policy
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_proposed_CPUs(self, proposed_cpus):
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
self.proposed_cpus = [ int(cpu) for cpu in proposed_cpus ]
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def set_other_config(self, other_config):
|
||||
self.other_config = other_config
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def destroy(self):
|
||||
""" In order to destroy a cpu pool, it must be deactivated """
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
XendBase.destroy(self)
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def activate(self):
|
||||
""" Create pool in hypervisor and add cpus.
|
||||
Preconditions:
|
||||
- pool not already active
|
||||
- enough unbound cpus available
|
||||
Actions:
|
||||
- create pool in hypervisor
|
||||
- select free cpus (preferred from proposed_CPUs list) and bind it to
|
||||
the pool
|
||||
- create entries in Xenstore
|
||||
"""
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
sched_policy = self.get_sched_policy()
|
||||
if sched_policy not in XEN_SCHEDULER_TO_ID.keys():
|
||||
raise PoolError(XEND_ERROR_UNKOWN_SCHED_POLICY)
|
||||
unbound_cpus = set(self.unbound_cpus())
|
||||
if len(unbound_cpus) < self.ncpu:
|
||||
raise PoolError(XEND_ERROR_INSUFFICIENT_CPUS,
|
||||
[str(self.ncpu), str(len(unbound_cpus))])
|
||||
|
||||
# build list of cpu numbers to bind to pool
|
||||
cpu_set = set(self.proposed_cpus).intersection(unbound_cpus)
|
||||
if len(cpu_set) < self.ncpu:
|
||||
pool_cpus = (list(cpu_set) +
|
||||
list(unbound_cpus.difference(cpu_set)))
|
||||
else:
|
||||
pool_cpus = list(cpu_set)
|
||||
pool_cpus = pool_cpus[0:self.ncpu]
|
||||
|
||||
# create pool in hypervisor
|
||||
pool_id = xc.cpupool_create(
|
||||
sched = XEN_SCHEDULER_TO_ID.get(sched_policy, 0))
|
||||
|
||||
self.update_XS(pool_id)
|
||||
# add cpus
|
||||
for cpu in pool_cpus:
|
||||
xc.cpupool_addcpu(pool_id, cpu)
|
||||
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
def deactivate(self):
|
||||
""" Delete pool in hypervisor
|
||||
Preconditions:
|
||||
- pool is activated
|
||||
- no running VMs in pool
|
||||
Actions:
|
||||
- call hypervisor for deletion
|
||||
- remove path of pool in xenstore
|
||||
"""
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
if not self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
|
||||
if len(self.get_started_VMs()) != 0:
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'in use')
|
||||
|
||||
pool_id = self.query_pool_id()
|
||||
# remove cpus from pool
|
||||
cpus = []
|
||||
for pool_rec in xc.cpupool_getinfo():
|
||||
if pool_rec['cpupool'] == pool_id:
|
||||
cpus = pool_rec['cpulist']
|
||||
for cpu_number in cpus:
|
||||
xc.cpupool_removecpu(pool_id, cpu_number)
|
||||
xc.cpupool_destroy(pool_id)
|
||||
|
||||
# update XenStore
|
||||
xs_path = XS_POOLROOT + "%s/" % pool_id
|
||||
xstransact.Remove(xs_path)
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
def add_host_CPU_live(self, cpu_ref):
|
||||
""" Add cpu to pool, if it is currently not assigned to a pool.
|
||||
@param cpu_ref: reference of host_cpu instance to add
|
||||
@type cpu_ref: str
|
||||
"""
|
||||
if not self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
|
||||
node = XendNode.instance()
|
||||
number = node.get_host_cpu_field(cpu_ref, 'number')
|
||||
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
pool_id = self.query_pool_id()
|
||||
other_pool_ref = self.get_cpu_pool_by_cpu_ref(cpu_ref)
|
||||
if len(other_pool_ref) != 0:
|
||||
raise PoolError(XEND_ERROR_INVALID_CPU,
|
||||
'cpu already assigned to pool "%s"' % other_pool_ref[0])
|
||||
xc.cpupool_addcpu(pool_id, number)
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
if number not in self.proposed_cpus:
|
||||
self.proposed_cpus.append(number)
|
||||
self._update_ncpu(pool_id)
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def remove_host_CPU_live(self, cpu_ref):
|
||||
""" Remove cpu from pool.
|
||||
After successfull call, the cpu is free.
|
||||
Remove of the last cpu of the pool is rejected.
|
||||
@param cpu_ref: reference of host_cpu instance to remove
|
||||
@type cpu_ref: str
|
||||
"""
|
||||
if not self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'deactivated')
|
||||
node = XendNode.instance()
|
||||
number = node.get_host_cpu_field(cpu_ref, 'number')
|
||||
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
pool_id = self.query_pool_id()
|
||||
pool_rec = {}
|
||||
for pool in xc.cpupool_getinfo():
|
||||
if pool['cpupool'] == pool_id:
|
||||
pool_rec = pool
|
||||
break
|
||||
|
||||
if number in pool_rec['cpulist']:
|
||||
if len(pool_rec['cpulist']) < 2 and pool_rec['n_dom'] > 0:
|
||||
raise PoolError(XEND_ERROR_LAST_CPU_NOT_REM,
|
||||
'could not remove last cpu')
|
||||
xc.cpupool_removecpu(pool_id, number)
|
||||
else:
|
||||
raise PoolError(XEND_ERROR_INVALID_CPU,
|
||||
'CPU not assigned to pool')
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
if number in self.proposed_cpus:
|
||||
self.proposed_cpus.remove(number)
|
||||
self._update_ncpu(pool_id)
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def add_to_proposed_CPUs(self, cpu):
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
|
||||
_cpu = int(cpu)
|
||||
if _cpu not in self.proposed_cpus:
|
||||
self.proposed_cpus.append(_cpu)
|
||||
self.proposed_cpus.sort()
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def remove_from_proposed_CPUs(self, cpu):
|
||||
if self.get_activated():
|
||||
raise PoolError(XEND_ERROR_BAD_POOL_STATE, 'activated')
|
||||
_cpu = int(cpu)
|
||||
if _cpu in self.proposed_cpus:
|
||||
self.proposed_cpus.remove(_cpu)
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def add_to_other_config(self, key, value):
|
||||
self.other_config[key] = value
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
def remove_from_other_config(self, key):
|
||||
if key in self.other_config:
|
||||
del self.other_config[key]
|
||||
if self._managed:
|
||||
XendNode.instance().save_cpu_pools()
|
||||
|
||||
|
||||
#
|
||||
# Legacy RPC calls
|
||||
#
|
||||
def pool_new(cls, config):
|
||||
try:
|
||||
record = sxp2map(config)
|
||||
if record.has_key('proposed_CPUs') and \
|
||||
not isinstance(record['proposed_CPUs'], types.ListType):
|
||||
record['proposed_CPUs'] = [record['proposed_CPUs']]
|
||||
new_uuid = cls.create(record)
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
return new_uuid
|
||||
|
||||
def pool_create(cls, config):
|
||||
try:
|
||||
record = sxp2map(config)
|
||||
if record.has_key('proposed_CPUs') and \
|
||||
not isinstance(record['proposed_CPUs'], types.ListType):
|
||||
record['proposed_CPUs'] = [record['proposed_CPUs']]
|
||||
new_uuid = genuuid.createString()
|
||||
pool = XendCPUPool(record, new_uuid, False)
|
||||
pool.activate()
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_start(cls, poolname):
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
try:
|
||||
pool.activate()
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_list(cls, names):
|
||||
sxprs = []
|
||||
try:
|
||||
node = XendNode.instance()
|
||||
xd = XendDomain.instance()
|
||||
pools = cls.get_all_records()
|
||||
for (pool_uuid, pool_vals) in pools.items():
|
||||
if pool_vals['name_label'] in names or len(names) == 0:
|
||||
# conv host_cpu refs to cpu number
|
||||
cpus = [ node.get_host_cpu_field(cpu_ref, 'number')
|
||||
for cpu_ref in pool_vals['host_CPUs'] ]
|
||||
cpus.sort()
|
||||
pool_vals['host_CPU_numbers'] = cpus
|
||||
# query VMs names. Take in account, that a VM
|
||||
# returned by get_all_records could be destroy, now
|
||||
vm_names = [ vm.getName()
|
||||
for vm in map(xd.get_vm_by_uuid,
|
||||
pool_vals['started_VMs'])
|
||||
if vm ]
|
||||
pool_vals['started_VM_names'] = vm_names
|
||||
pool_vals['auto_power_on'] = int(pool_vals['auto_power_on'])
|
||||
sxprs += [[pool_uuid] + map2sxp(pool_vals)]
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
return sxprs
|
||||
|
||||
def pool_destroy(cls, poolname):
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
try:
|
||||
pool.deactivate()
|
||||
if not pool.is_managed():
|
||||
pool.destroy()
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_delete(cls, poolname):
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
try:
|
||||
pool.destroy()
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_cpu_add(cls, poolname, cpu):
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
try:
|
||||
cpu_ref = cls._cpu_number_to_ref(int(cpu))
|
||||
if cpu_ref:
|
||||
pool.add_host_CPU_live(cpu_ref)
|
||||
else:
|
||||
raise PoolError(XEND_ERROR_INVALID_CPU,
|
||||
'CPU unknown')
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_cpu_remove(cls, poolname, cpu):
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
try:
|
||||
cpu_ref = cls._cpu_number_to_ref(int(cpu))
|
||||
if cpu_ref:
|
||||
pool.remove_host_CPU_live(cpu_ref)
|
||||
else:
|
||||
raise PoolError(XEND_ERROR_INVALID_CPU,
|
||||
'CPU unknown')
|
||||
except XendAPIError, ex:
|
||||
raise VmError(ex.get_api_error())
|
||||
|
||||
def pool_migrate(cls, domname, poolname):
|
||||
dom = XendDomain.instance()
|
||||
pool = cls.lookup_pool(poolname)
|
||||
if not pool:
|
||||
raise VmError('unknown pool %s' % poolname)
|
||||
dominfo = dom.domain_lookup_nr(domname)
|
||||
if not dominfo:
|
||||
raise VmError('unknown domain %s' % domname)
|
||||
domid = dominfo.getDomid()
|
||||
if domid is not None:
|
||||
if domid == 0:
|
||||
raise VmError('could not move Domain-0')
|
||||
try:
|
||||
cls.move_domain(pool.get_uuid(), domid)
|
||||
except Exception, ex:
|
||||
raise VmError('could not move domain')
|
||||
dominfo.info['pool_name'] = poolname
|
||||
dom.managed_config_save(dominfo)
|
||||
|
||||
pool_new = classmethod(pool_new)
|
||||
pool_create = classmethod(pool_create)
|
||||
pool_start = classmethod(pool_start)
|
||||
pool_list = classmethod(pool_list)
|
||||
pool_destroy = classmethod(pool_destroy)
|
||||
pool_delete = classmethod(pool_delete)
|
||||
pool_cpu_add = classmethod(pool_cpu_add)
|
||||
pool_cpu_remove = classmethod(pool_cpu_remove)
|
||||
pool_migrate = classmethod(pool_migrate)
|
||||
|
||||
|
||||
#
|
||||
# methods
|
||||
#
|
||||
|
||||
def is_managed(self):
|
||||
""" Check, if pool is managed.
|
||||
@return: True, if managed
|
||||
@rtype: bool
|
||||
"""
|
||||
return self._managed
|
||||
|
||||
def query_pool_id(self):
|
||||
""" Get corresponding pool-id of pool instance from XenStore.
|
||||
@return: pool id or None
|
||||
@rytpe: int
|
||||
"""
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
for pool_id in xstransact.List(XS_POOLROOT):
|
||||
uuid = xstransact.Read(XS_POOLROOT + "%s/" % pool_id, 'uuid')
|
||||
if uuid == self.get_uuid():
|
||||
return int(pool_id)
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
return None
|
||||
|
||||
def update_XS(self, pool_id):
|
||||
""" Write (or update) data in xenstore taken from instance.
|
||||
@param pool_id: Pool id to build path to pool data in xenstore
|
||||
@type pool_id: int
|
||||
"""
|
||||
self.pool_lock.acquire()
|
||||
try:
|
||||
xs_path = XS_POOLROOT + "%s/" % pool_id
|
||||
xs_entries = { 'uuid' : self.get_uuid(),
|
||||
'name' : self.name_label,
|
||||
'description' : self.name_description
|
||||
}
|
||||
xstransact.Mkdir(xs_path)
|
||||
xstransact.Mkdir(xs_path, 'other_config')
|
||||
xstransact.Write(xs_path, xs_entries)
|
||||
xstransact.Write('%s%s' % (xs_path, 'other_config'),
|
||||
self.other_config)
|
||||
finally:
|
||||
self.pool_lock.release()
|
||||
|
||||
def _update_ncpu(self, pool_id):
|
||||
for pool_rec in xc.cpupool_getinfo():
|
||||
if pool_rec['cpupool'] == pool_id:
|
||||
self.ncpu = len(pool_rec['cpulist'])
|
||||
|
||||
def _checkName(self, name):
|
||||
""" Check if a pool name is valid. Valid names contain alphabetic
|
||||
characters, digits, or characters in '_-.:/+'.
|
||||
The same name cannot be used for more than one pool at the same
|
||||
time.
|
||||
@param name: name
|
||||
@type name: str
|
||||
@raise: PoolError if invalid
|
||||
"""
|
||||
if name is None or name == '':
|
||||
raise PoolError(XEND_ERROR_POOL_PARAM, 'Missing Pool Name')
|
||||
if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
|
||||
raise PoolError(XEND_ERROR_POOL_PARAM, 'Invalid Pool Name')
|
||||
|
||||
pool = self.lookup_pool(name)
|
||||
if pool and pool.get_uuid() != self.get_uuid():
|
||||
raise PoolError(XEND_ERROR_POOL_PARAM,
|
||||
'Pool name "%s" already exists' % name)
|
||||
|
||||
|
||||
#
|
||||
# class methods
|
||||
#
|
||||
|
||||
def recreate_active_pools(cls):
|
||||
""" Read active pool config from hypervisor and create pool instances.
|
||||
- Query pool ids and assigned CPUs from hypervisor.
|
||||
- Query additional information for any pool from xenstore.
|
||||
If an entry for a pool id is missing in xenstore, it will be
|
||||
recreated with a new uuid and generic name (this is an error case)
|
||||
- Create an XendCPUPool instance for any pool id
|
||||
Function have to be called after recreation of managed pools.
|
||||
"""
|
||||
log.debug('recreate_active_pools')
|
||||
|
||||
for pool_rec in xc.cpupool_getinfo():
|
||||
pool = pool_rec['cpupool']
|
||||
|
||||
# read pool data from xenstore
|
||||
path = XS_POOLROOT + "%s/" % pool
|
||||
uuid = xstransact.Read(path, 'uuid')
|
||||
if not uuid:
|
||||
# xenstore entry missing / invaild; create entry with new uuid
|
||||
uuid = genuuid.createString()
|
||||
name = "Pool-%s" % pool
|
||||
try:
|
||||
inst = XendCPUPool( { 'name_label' : name }, uuid, False )
|
||||
inst.update_XS(pool)
|
||||
except PoolError, ex:
|
||||
# log error and skip domain
|
||||
log.error('cannot recreate pool %s; skipping (reason: %s)' \
|
||||
% (name, ex))
|
||||
else:
|
||||
(name, descr) = xstransact.Read(path, 'name', 'description')
|
||||
other_config = {}
|
||||
for key in xstransact.List(path + 'other_config'):
|
||||
other_config[key] = xstransact.Read(
|
||||
path + 'other_config/%s' % key)
|
||||
|
||||
# check existance of pool instance
|
||||
inst = XendAPIStore.get(uuid, cls.getClass())
|
||||
if inst:
|
||||
# update attributes of existing instance
|
||||
inst.name_label = name
|
||||
inst.name_description = descr
|
||||
inst.other_config = other_config
|
||||
else:
|
||||
# recreate instance
|
||||
try:
|
||||
inst = XendCPUPool(
|
||||
{ 'name_label' : name,
|
||||
'name_description' : descr,
|
||||
'other_config' : other_config,
|
||||
'proposed_CPUs' : pool_rec['cpulist'],
|
||||
'ncpu' : len(pool_rec['cpulist']),
|
||||
},
|
||||
uuid, False )
|
||||
except PoolError, ex:
|
||||
# log error and skip domain
|
||||
log.error(
|
||||
'cannot recreate pool %s; skipping (reason: %s)' \
|
||||
% (name, ex))
|
||||
|
||||
recreate_active_pools = classmethod(recreate_active_pools)
|
||||
|
||||
|
||||
def recreate(cls, record, current_uuid):
|
||||
""" Recreate a pool instance while xend restart.
|
||||
@param record: attributes of pool
|
||||
@type record: dict
|
||||
@param current_uuid: uuid of pool to create
|
||||
@type current_uuid: str
|
||||
"""
|
||||
XendCPUPool(record, current_uuid)
|
||||
|
||||
recreate = classmethod(recreate)
|
||||
|
||||
|
||||
def autostart_pools(cls):
|
||||
""" Start managed pools that are marked as autostart pools.
|
||||
Function is called after recreation of managed domains while
|
||||
xend restart.
|
||||
"""
|
||||
cls.pool_lock.acquire()
|
||||
try:
|
||||
for inst in XendAPIStore.get_all(cls.getClass()):
|
||||
if inst.is_managed() and inst.auto_power_on and \
|
||||
inst.query_pool_id() == None:
|
||||
inst.activate()
|
||||
finally:
|
||||
cls.pool_lock.release()
|
||||
|
||||
autostart_pools = classmethod(autostart_pools)
|
||||
|
||||
|
||||
def move_domain(cls, pool_ref, domid):
|
||||
cls.pool_lock.acquire()
|
||||
try:
|
||||
pool = XendAPIStore.get(pool_ref, cls.getClass())
|
||||
pool_id = pool.query_pool_id()
|
||||
|
||||
xc.cpupool_movedomain(pool_id, domid)
|
||||
finally:
|
||||
cls.pool_lock.release()
|
||||
|
||||
move_domain = classmethod(move_domain)
|
||||
|
||||
|
||||
def query_pool_ref(cls, pool_id):
|
||||
""" Get pool ref by pool id.
|
||||
Take the ref from xenstore.
|
||||
@param pool_id:
|
||||
@type pool_id: int
|
||||
@return: ref
|
||||
@rtype: str
|
||||
"""
|
||||
uuid = xstransact.Read(XS_POOLROOT + "%s/" % pool_id, 'uuid')
|
||||
if uuid:
|
||||
return [uuid]
|
||||
else:
|
||||
return []
|
||||
|
||||
query_pool_ref = classmethod(query_pool_ref)
|
||||
|
||||
|
||||
def lookup_pool(cls, id_or_name):
|
||||
""" Search XendCPUPool instance with given id_or_name.
|
||||
@param id_or_name: pool id or pool nameto search
|
||||
@type id_or_name: [int, str]
|
||||
@return: instane or None if not found
|
||||
@rtype: XendCPUPool
|
||||
"""
|
||||
pool_uuid = None
|
||||
try:
|
||||
pool_id = int(id_or_name)
|
||||
# pool id given ?
|
||||
pool_uuid = cls.query_pool_ref(pool_id)
|
||||
if not pool_uuid:
|
||||
# not found -> search name
|
||||
pool_uuid = cls.get_by_name_label(id_or_name)
|
||||
except ValueError:
|
||||
# pool name given
|
||||
pool_uuid = cls.get_by_name_label(id_or_name)
|
||||
|
||||
if len(pool_uuid) > 0:
|
||||
return XendAPIStore.get(pool_uuid[0], cls.getClass())
|
||||
else:
|
||||
return None
|
||||
|
||||
lookup_pool = classmethod(lookup_pool)
|
||||
|
||||
|
||||
def _cpu_number_to_ref(cls, number):
|
||||
node = XendNode.instance()
|
||||
for cpu_ref in node.get_host_cpu_refs():
|
||||
if node.get_host_cpu_field(cpu_ref, 'number') == number:
|
||||
return cpu_ref
|
||||
return None
|
||||
|
||||
_cpu_number_to_ref = classmethod(_cpu_number_to_ref)
|
||||
|
||||
|
||||
def unbound_cpus(cls):
|
||||
""" Build list containing the numbers of all cpus not bound to a pool.
|
||||
Info is taken from Hypervisor.
|
||||
@return: list of cpu numbers
|
||||
@rytpe: list of int
|
||||
"""
|
||||
return xc.cpupool_freeinfo()
|
||||
|
||||
unbound_cpus = classmethod(unbound_cpus)
|
||||
|
||||