[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 2/8] [xend] Domain Groups: xend classes for groups
xend: Augment Xend with classes and framework to manage group instances and persistent data. diff -r ecb6cd61a9cf tools/python/xen/xend/XendCheckpoint.py --- a/tools/python/xen/xend/XendCheckpoint.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendCheckpoint.py Tue Feb 20 12:59:11 2007 -0500 @@ -68,9 +68,16 @@ def save(fd, dominfo, network, live, dst try: dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name) - write_exact(fd, pack("!i", len(config)), - "could not write guest state file: config len") + dgid = dominfo.info['dgid'] + xdg = xen.xend.XendDomainGroup.instance() + grpinfo = xdg.grp_lookup_nr(dgid) + grpconfig = sxp.to_string(grpinfo.sxpr()) + + write_exact(fd, pack("!i", len(config)), "could not write guest state file: config len") write_exact(fd, config, "could not write guest state file: config") + + write_exact(fd, pack("!i", len(grpconfig)), "could not write group state file: grpconfig len") + write_exact(fd, grpconfig, "could not write group state file: grpconfig") image_cfg = dominfo.info.get('image', {}) hvm = image_cfg.has_key('hvm') @@ -170,23 +177,50 @@ def restore(xd, fd, dominfo = None, paus raise XendError("not a valid guest state file: found '%s'" % signature) - l = read_exact(fd, sizeof_int, - "not a valid guest state file: config size read") + l = read_exact(fd, sizeof_int, "not a valid guest state file: config size read") vmconfig_size = unpack("!i", l)[0] - vmconfig_buf = read_exact(fd, vmconfig_size, - "not a valid guest state file: config read") + vmconfig_buf = read_exact(fd, vmconfig_size, "not a valid guest state file: config read") + + l = read_exact(fd, sizeof_int, "not a valid group state file: grpconfig size read") + grpconfig_size = unpack("!i", l)[0] + grpconfig_buf = read_exact(fd, grpconfig_size, "not a valid group state file: config read") p = sxp.Parser() p.input(vmconfig_buf) if not p.ready: raise XendError("not a valid guest state file: config parse") - vmconfig = p.get_val() + + p.reset() + p.input(grpconfig_buf) + if not p.ready: + raise XendError("not a valid group state file: config parse") + grpconfig = eval(p.get_val()) if dominfo: dominfo.resume() else: dominfo = xd.restore_(vmconfig) + + src_dguuid = sxp.child_value(vmconfig, 'dguuid') + src_grp_name = grpconfig['grp_name'] + + xdg = xen.xend.XendDomainGroup.instance() + xdg.domain_groups_lock.acquire() + try: + grpinfo = xdg.grp_lookup(src_dguuid) + if not grpinfo: + grpcfg = {} + grpcfg['dguuid'] = src_dguuid + grpcfg['grp_name'] = src_grp_name + grpinfo = xen.xend.XendDomainGroupInfo.XendDomainGroupInfo(grpcfg) + grpinfo.construct(src_dguuid) + xdg.grp_join(dominfo.getDomid(), grpinfo.info['dgid']) + xdg.domain_groups_lock.release() + except: + xdg.domain_groups_lock.release() + dominfo.destroy() + raise store_port = dominfo.getStorePort() console_port = dominfo.getConsolePort() @@ -246,6 +280,7 @@ def restore(xd, fd, dominfo = None, paus raise XendError('Could not read console MFN') dominfo.waitForDevices() # Wait for backends to set up + if not paused: dominfo.unpause() diff -r ecb6cd61a9cf tools/python/xen/xend/XendClient.py --- a/tools/python/xen/xend/XendClient.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendClient.py Tue Feb 20 12:59:11 2007 -0500 @@ -27,6 +27,7 @@ ERROR_INTERNAL = 1 ERROR_INTERNAL = 1 ERROR_GENERIC = 2 ERROR_INVALID_DOMAIN = 3 +ERROR_INVALID_DOMAIN_GROUP = 4 uri = 'httpu:///var/run/xend/xmlrpc.sock' if os.environ.has_key('XM_SERVER'): diff -r ecb6cd61a9cf tools/python/xen/xend/XendConfig.py --- a/tools/python/xen/xend/XendConfig.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendConfig.py Tue Feb 20 12:59:11 2007 -0500 @@ -25,7 +25,7 @@ from xen.xend.XendError import VmError from xen.xend.XendError import VmError from xen.xend.XendDevices import XendDevices from xen.xend.PrettyPrint import prettyprintstring -from xen.xend.XendConstants import DOM_STATE_HALTED +from xen.xend.XendConstants import DOM_STATE_HALTED,NULL_GROUP_ID,NULL_GROUP_UUID log = logging.getLogger("xend.XendConfig") log.setLevel(logging.DEBUG) @@ -103,6 +103,8 @@ def scrub_password(data): XENAPI_CFG_TO_LEGACY_CFG = { 'uuid': 'uuid', + 'dgid': 'dgid', + 'dguuid': 'dguuid', 'vcpus_number': 'vcpus', 'cpus': 'cpus', 'memory_static_min': 'memory', @@ -133,6 +135,8 @@ XENAPI_HVM_CFG = { XENAPI_CFG_TYPES = { 'uuid': str, + 'dgid': int, + 'dguuid': str, 'power_state': str, 'name_label': str, 'name_description': str, @@ -196,6 +200,9 @@ LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [ LEGACY_CFG_TYPES = { 'uuid': str, + 'dgid': int, + 'dguuid': str, + 'grp_name': str, 'name': str, 'vcpus': int, 'vcpu_avail': long, @@ -222,6 +229,8 @@ LEGACY_CFG_TYPES = { # xenstore. LEGACY_XENSTORE_VM_PARAMS = [ 'uuid', + 'dgid', + 'dguuid', 'name', 'vcpus', 'vcpu_avail', @@ -377,6 +386,8 @@ class XendConfig(dict): 'vbd_refs': [], 'vtpm_refs': [], 'other_config': {}, + 'dgid': NULL_GROUP_ID, + 'dguuid': NULL_GROUP_UUID, } return defaults @@ -412,6 +423,10 @@ class XendConfig(dict): self['uuid'] = uuid.createString() else: self['uuid'] = uuid.toString(uuid.fromString(self['uuid'])) + if 'dguuid' not in self or not self['dguuid']: + self['dguuid'] = uuid.createString() + else: + self['dguuid'] = uuid.toString(uuid.fromString(self['dguuid'])) def _name_sanity_check(self): if 'name_label' not in self: @@ -426,6 +441,7 @@ class XendConfig(dict): def _dominfo_to_xapi(self, dominfo): self['domid'] = dominfo['domid'] + self['dgid'] = dominfo['dgid'] self['online_vcpus'] = dominfo['online_vcpus'] self['vcpus_number'] = dominfo['max_vcpu_id'] + 1 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024 @@ -449,6 +465,9 @@ class XendConfig(dict): if 'handle' in dominfo: self['uuid'] = uuid.toString(dominfo['handle']) + + if 'dg_handle' in dominfo: + self['dguuid'] = uuid.toString(dominfo['dg_handle']) def _parse_sxp(self, sxp_cfg): """ Populate this XendConfig using the parsed SXP. @@ -879,9 +898,11 @@ class XendConfig(dict): sxpr.append([legacy, int(self[xenapi])]) else: sxpr.append([legacy, self[xenapi]]) + else: + log.debug("Unconverted key: " + xenapi) for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG: - if legacy in ('domid', 'uuid'): # skip these + if legacy in ('domid', 'uuid', 'dgid', 'dguuid'): # skip these continue if self.has_key(legacy) and self[legacy] not in (None, []): sxpr.append([legacy, self[legacy]]) diff -r ecb6cd61a9cf tools/python/xen/xend/XendConstants.py --- a/tools/python/xen/xend/XendConstants.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendConstants.py Tue Feb 20 12:59:11 2007 -0500 @@ -102,4 +102,15 @@ VTPM_DELETE_SCRIPT = '/etc/xen/scripts/v # XS_VMROOT = "/vm/" +XS_GRPROOT = "/group/" +# +# Domain Group Constants +# + +GROUP0_ID = 0 +GROUP0_UUID = "00000000-0000-0000-0000-000000000000" +GROUP0_NAME = "Group-0" +NULL_GROUP_ID = 32767 +NULL_GROUP_UUID = "ffffffff-ffff-ffff-ffff-ffffffffffff" +NULL_GROUP_NAME = "Null-Group" diff -r ecb6cd61a9cf tools/python/xen/xend/XendDomain.py --- a/tools/python/xen/xend/XendDomain.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendDomain.py Tue Feb 20 12:59:11 2007 -0500 @@ -415,6 +415,9 @@ class XendDomain: if domid not in running_domids and domid != DOM0_ID: self.remove_domain(dom, domid) + # update group information + xen.xend.XendDomainGroup.instance().refresh() + def add_domain(self, info): """Add a domain to the list of running domains @@ -846,10 +849,15 @@ class XendDomain: oflags = os.O_RDONLY if hasattr(os, "O_LARGEFILE"): oflags |= os.O_LARGEFILE - XendCheckpoint.restore(self, + updated_dominfo = XendCheckpoint.restore(self, os.open(chkpath, oflags), dominfo, paused = start_paused) + domid = dominfo.getDomid() + dgid = dominfo.getOldDgid() + xdg = xen.xend.XendDomainGroup.instance() + xdg.grp_join(domid, dgid) + self._add_domain(updated_dominfo) os.unlink(chkpath) except OSError, ex: raise XendError("Failed to read stored checkpoint file") diff -r ecb6cd61a9cf tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendDomainInfo.py Tue Feb 20 12:59:11 2007 -0500 @@ -341,7 +341,7 @@ class XendDomainInfo: self.domid = self.info.get('domid') else: self.domid = domid - + #REMOVE: uuid is now generated in XendConfig #if not self._infoIsSet('uuid'): # self.info['uuid'] = uuid.toString(uuid.create()) @@ -768,6 +768,8 @@ class XendDomainInfo: def _storeDomDetails(self): to_store = { 'domid': str(self.domid), + 'dgid': str(self.info['dgid']), + 'dguuid': self.info['dguuid'], 'vm': self.vmpath, 'name': self.info['name_label'], 'console/limit': str(xoptions.get_console_limit() * 1024), @@ -823,7 +825,8 @@ class XendDomainInfo: # Check whether values in the configuration have # changed in Xenstore. - cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash'] + cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash', 'dgid', + 'dguuid', 'grp_name'] vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k]) for k in cfg_vm]) @@ -855,6 +858,8 @@ class XendDomainInfo: log.debug('XendDomainInfo.handleShutdownWatch') reason = self.readDom('control/shutdown') + # stash current dgid so it can be used during domain restart + self.old_dgid = self.info.get('dgid') if reason and reason != 'suspend': sst = self.readDom('xend/shutdown_start_time') @@ -882,6 +887,12 @@ class XendDomainInfo: def getDomid(self): return self.domid + + def getDgid(self): + return self.info.get('dgid') + + def getOldDgid(self): + return self.old_dgid def setName(self, name): self._checkName(name) @@ -1115,7 +1126,7 @@ class XendDomainInfo: False if it is to be destroyed. """ from xen.xend import XendDomain - + if self._readVm(RESTART_IN_PROGRESS): log.error('Xend failed during restart of domain %s. ' 'Refusing to restart to avoid loops.', @@ -1158,6 +1169,11 @@ class XendDomainInfo: try: new_dom = XendDomain.instance().domain_create_from_dict( self.info) + + # rejoin former domain group + xdg = xen.xend.XendDomainGroup.instance() + xdg.grp_join(new_dom.domid, self.old_dgid) + new_dom.unpause() rst_cnt = self._readVm('xend/restart_count') rst_cnt = int(rst_cnt) + 1 @@ -2345,8 +2361,8 @@ class XendDomainInfo: return (dev_uuid in self.info['%s_refs' % dev_class.lower()]) def __str__(self): - return '<domain id=%s name=%s memory=%s state=%s>' % \ - (str(self.domid), self.info['name_label'], + return '<domain id=%s dgid=%s name=%s memory=%s state=%s>' % \ + (str(self.domid), str(self.info.get('dgid')), self.info['name_label'], str(self.info['memory_static_min']), DOM_STATES[self.state]) __repr__ = __str__ diff -r ecb6cd61a9cf tools/python/xen/xend/XendError.py --- a/tools/python/xen/xend/XendError.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/XendError.py Tue Feb 20 12:59:11 2007 -0500 @@ -22,6 +22,10 @@ class XendInvalidDomain(Fault): class XendInvalidDomain(Fault): def __init__(self, value): Fault.__init__(self, XendClient.ERROR_INVALID_DOMAIN, value) + +class XendInvalidDomainGroup(Fault): + def __init__(self, value): + Fault.__init__(self, XendClient.ERROR_INVALID_DOMAIN_GROUP, value) class XendError(Fault): diff -r ecb6cd61a9cf tools/python/xen/xend/server/XMLRPCServer.py --- a/tools/python/xen/xend/server/XMLRPCServer.py Tue Feb 20 12:27:03 2007 +0000 +++ b/tools/python/xen/xend/server/XMLRPCServer.py Tue Feb 20 12:59:11 2007 -0500 @@ -24,10 +24,11 @@ from xen.util.xmlrpclib2 import UnixXMLR from xen.xend import XendAPI, XendDomain, XendDomainInfo, XendNode from xen.xend import XendLogging, XendDmesg +from xen.xend import XendDomainGroup from xen.xend.XendClient import XML_RPC_SOCKET from xen.xend.XendConstants import DOM_STATE_RUNNING from xen.xend.XendLogging import log -from xen.xend.XendError import XendInvalidDomain +from xen.xend.XendError import XendInvalidDomain, XendInvalidDomainGroup # vcpu_avail is a long and is not needed by the clients. It's far easier # to just remove it then to try and marshal the long. @@ -41,18 +42,18 @@ def fixup_sxpr(sexpr): ret.append(k) return ret -def lookup(domid): +def lookup_dom(domid): info = XendDomain.instance().domain_lookup(domid) if not info: raise XendInvalidDomain(str(domid)) return info def dispatch(domid, fn, args): - info = lookup(domid) + info = lookup_dom(domid) return getattr(info, fn)(*args) def domain(domid, full = 0): - info = lookup(domid) + info = lookup_dom(domid) return fixup_sxpr(info.sxpr(not full)) def domains(detail = True, full = False): @@ -73,6 +74,19 @@ def domain_restore(src, paused=False): info = XendDomain.instance().domain_restore(src, paused) return fixup_sxpr(info.sxpr()) +def lookup_grp(dgid): + grpinfo = XendDomainGroup.instance().grp_lookup(dgid) + if not grpinfo: + raise XendInvalidDomainGroup("Invalid group: %s" % str(dgid)) + return grpinfo + +def group(dgid): + return lookup_grp(dgid) + +def group_create(config): + info = XendDomainGroup.instance().grp_create(config) + return info.sxpr() + def get_log(): f = open(XendLogging.getLogFilename(), 'r') try: @@ -87,6 +101,10 @@ methods = ['device_create', 'device_conf 'getRestartCount'] exclude = ['domain_create', 'domain_restore'] + +grp_methods = ['grp_destroy', 'grp_pause', 'grp_unpause', 'grp_members', + 'grp_join', 'grp_migrate', 'grp_list', 'grp_suspend', + 'grp_resume', 'grp_save', 'grp_restore', 'grp_shutdown'] class XMLRPCServer: def __init__(self, auth, use_xenapi, use_tcp=False, host = "localhost", @@ -158,6 +176,12 @@ class XMLRPCServer: if name not in exclude: self.server.register_function(fn, "xend.domain.%s" % name[7:]) + # Domain Group Operations + xdg_inst = XendDomainGroup.instance() + for name in grp_methods: + fn = getattr(xdg_inst, name) + self.server.register_function(fn, "xend.group.%s" % name[4:]) + # Functions in XendNode and XendDmesg for type, lst, n in [(XendNode, ['info'], 'node'), (XendDmesg, ['info', 'clear'], 'node.dmesg')]: @@ -174,6 +198,8 @@ class XMLRPCServer: self.server.register_function(get_log, 'xend.node.log') self.server.register_function(domain_create, 'xend.domain.create') self.server.register_function(domain_restore, 'xend.domain.restore') + self.server.register_function(group, 'xend.group') + self.server.register_function(group_create, 'xend.group.create') self.server.register_introspection_functions() self.ready = True diff -r ecb6cd61a9cf tools/python/xen/xend/XendDomainGroup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/XendDomainGroup.py Tue Feb 20 12:59:11 2007 -0500 @@ -0,0 +1,345 @@ +#============================================================================ +# 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 +#============================================================================ +# Author: Chris Bookholt <hap10@xxxxxxxxxxxxxx> +#============================================================================ +import logging +import os +import socket +import sys +import threading +import uuid + +import xen.lowlevel.xc +from xen.xend import XendDomain +from xen.xend import XendDomainGroupInfo +from xen.xend.XendError import XendError +from XendLogging import log +from xen.xend.XendConstants import XS_GRPROOT, GROUP0_ID, \ + GROUP0_NAME, NULL_GROUP_ID, NULL_GROUP_NAME + + +xc = xen.lowlevel.xc.xc() + + +class XendDomainGroup: + + def __init__(self): + self.domain_groups = {} + self.domain_groups_lock = threading.RLock() + self.xd = XendDomain.instance() + self.xst = xen.xend.xenstore.xstransact.xstransact + + + def init(self): + # not sure how xenstore permissions work + #xstransact.Mkdir(XS_GRPROOT) + #xstransact.SetPermissions(XS_GRPROOT, {'dom': DOM0_ID}) + + self.domain_groups_lock.acquire() + try: + grps = self.xen_domain_groups() + for dgid,grpdata in grps.items(): + log.debug("init'ing grp%s: %s", dgid, grpdata) + if dgid == GROUP0_ID: + grpdata['grp_name'] = GROUP0_NAME + elif dgid == NULL_GROUP_ID: + grpdata['grp_name'] = NULL_GROUP_NAME + else: + path = "%s%s/grp_name" % (XS_GRPROOT,grpdata['dguuid']) + grpdata['grp_name'] = self.xst.Read(path) + if not grpdata['grp_name']: + grpdata['grp_name'] = "Group-%s" % grpdata['dguuid'] + grpinfo = XendDomainGroupInfo.recreate(grpdata) + self._add_domain_group(grpinfo) + grpinfo.storeGrpDetails() + finally: + self.domain_groups_lock.release() + + + def _add_domain_group(self, info): + dgid = info.dgid + self.domain_groups[dgid] = info + log.debug("Added grp%s to domain_groups: %s", dgid, info) + + + def _delete_domain_group(self, dgid): + info = self.domain_groups.get(dgid) + if info: + del self.domain_groups[dgid] + log.debug("Deleted grp%s from domain_groups", dgid) + + + def _prependGrpPath(self, dguuid, string): + grppath = "%s%s" % (XS_GRPROOT,dguuid) + return "%s%s" % (grppath,string) + + + def _getGrpName(self, dguuid): + name = "" + namepath = "" + try: + namepath = self._prependGrpPath(dguuid, "/grp_name") + name = self.xst.Read(namepath) + except: + log.exception("Error reading %s from xenstore", namepath) + if (name == "") or (name == None): + grpinfo = self.grp_lookup_nr(dguuid) + if grpinfo: + name = grpinfo.grp_name + else: + name = "Group-%s" % dguuid + return name + + + def _rebuild_config(self, grpdata): + domlist = [] + for domid in grpdata['member_list']: + dominfo = self.xd.domain_lookup_nr(domid) + if dominfo: + domname = dominfo.getName() + # TODO: could store/retrieve member config paths to/from xs, + # but at the moment there is no need for accurate values once + # the members are started + domlist.append(domname+":nullconfig") + + sxpr = {} + sxpr['dgid'] = grpdata['dgid'] + sxpr['dg_handle'] = grpdata['dg_handle'] + sxpr['dguuid'] = uuid.toString(grpdata['dg_handle']) + sxpr['grp_name'] = self._getGrpName(sxpr['dguuid']) + sxpr['member_list'] = domlist + sxpr['size'] = len(domlist) + + return sxpr + + + def xen_domain_groups(self): + grps = {} + grplist = xc.domain_group_getinfo() + for grp in grplist: + dgid = grp['dgid'] + grpdata = self._rebuild_config(grp) + grps[dgid] = grpdata + return grps + + + def refresh(self): + grps = self.xen_domain_groups() + + for grp in self.domain_groups.values(): + info = grps.get(grp.dgid) + if info: + grp.update(info) + else: + self._delete_domain_group(grp.dgid) + + for grp in grps: + if grp not in self.domain_groups: + try: + grpinfo = XendDomainGroupInfo.recreate(grps[grp]) + self._add_domain_group(grpinfo) + except: + log.exception( + "Failed to recreate information for domain " + "group %d.", grp) + + self.push_grp_data_to_xenstore() + + def push_grp_data_to_xenstore(self): + for grpinfo in self.domain_groups.values(): + grpinfo.storeGrpDetails() + + + def grp_lookup_nr(self, grp): + self.domain_groups_lock.acquire() + try: + # match by name + for grpinfo in self.domain_groups.values(): + if grpinfo.getName() == grp: + return grpinfo + # match by id + try: + if int(grp) in self.domain_groups: + return self.domain_groups[int(grp)] + except ValueError: + pass + # match by dguuid + for grpinfo in self.domain_groups.values(): + if grpinfo.getDguuid() == grp: + return grpinfo + # group not found + return None + finally: + self.domain_groups_lock.release() + + + def grp_lookup(self, grp): + self.domain_groups_lock.acquire() + try: + self.refresh() + return self.grp_lookup_nr(grp) + finally: + self.domain_groups_lock.release() + + + def grp_members(self, dgid): + grpinfo = self.grp_lookup(dgid) + return grpinfo.members + + + def grp_list(self): + self.domain_groups_lock.acquire() + try: + self.refresh() + return self.domain_groups.values() + finally: + self.domain_groups_lock.release() + + + def grp_create(self, config): + self.domain_groups_lock.acquire() + try: + grpinfo = XendDomainGroupInfo.create(config) + self._add_domain_group(grpinfo) + return grpinfo + finally: + self.domain_groups_lock.release() + + + def grp_shutdown(self, dgid, reason): + members = self.grp_members(dgid) + for domname in members: + dominfo = self.xd.domain_lookup(domname) + dominfo.shutdown(reason) + + + def grp_destroy(self, dgid, rmxs = True): + ret = -1 + self.domain_groups_lock.acquire() + try: + grpinfo = self.grp_lookup(dgid) + ret = grpinfo.destroy(rmxs) + if ret == 0: + self._delete_domain_group(dgid) + finally: + self.domain_groups_lock.release() + return ret + + + def grp_save(self, dgid, prefix): + members = self.grp_members(dgid) + for dom in members: + self.xd.domain_save(dom, prefix + "." + dom) + self.grp_destroy(dgid) + + + def grp_restore(self, srcs): + for dompath in srcs: + self.xd.domain_restore(dompath, paused=False) + + + def grp_suspend(self, dgid): + log.debug("grp_suspend not yet implemented") + # members = self.grp_members(dgid) + # for dom in members: + # self.xd.domain_suspend(dom) + # self.grp_destroy(dgid, rmxs=False) + + + def grp_resume(self, dgid): + log.debug("grp_resume not yet implemented") + # member_list_str = self.xst.Read(XS_GRPROOT, "%s/members" % dguuid) + # member_list = member_list_str.split(", ") + # for dom in member_list: + # self.xd.domain_resume(dom) + + + def grp_pause(self, dgid): + self.domain_groups_lock.acquire() + try: + grpinfo = self.grp_lookup(dgid) + return grpinfo.pause() + finally: + self.domain_groups_lock.release() + + + def grp_unpause(self, dgid): + self.domain_groups_lock.acquire() + try: + grpinfo = self.grp_lookup(dgid) + return grpinfo.unpause() + finally: + self.domain_groups_lock.release() + + + def grp_join(self, domid, dgid): + self.domain_groups_lock.acquire() + try: + dominfo = self.xd.domain_lookup(domid) + old_dgid = dominfo.getDgid() + rc = xc.domain_group_join(domid, dgid) + if rc != 0: + raise XendError("group_join failed with error: %s" % rc) + dominfo = self.xd.domain_lookup(domid) + dominfo._storeVmDetails() + dominfo._storeDomDetails() + self.xd.managed_config_save(dominfo) + grpinfo = self.grp_lookup(dgid) + grpinfo.storeGrpDetails() + old_grpinfo = self.grp_lookup(old_dgid) + old_grpinfo.storeGrpDetails() + log.debug("dom%s joining grp%s", domid, dgid) + return rc + finally: + self.domain_groups_lock.release() + + + def grp_migrate(self, dgid, dst, live, resource, port): + + def threadHelper(dom): + return threading.Thread(target = self.xd.domain_migrate, + args = (dom,dst,live,resource,port)) + + try: + member_names = self.grp_members(dgid) + migration_threads = {} + # spawn and start a threaded migration request + # for each group member + for domname in member_names: + migration_threads[domname] = threadHelper(domname) + migration_threads[domname].start() + log.debug("Migration began for domain %s to %s", + domname, dst) + # block until all group members finish migration + for domname in member_names: + migration_threads[domname].join() + log.debug("Migration complete for domain %s to %s", + domname, dst) + self.grp_destroy(dgid) + except e: + log.exception("error during grp_migrate: %s", str(e)) + self.domain_groups_lock.release() + + +def instance(): + """Singleton constructor. Use this instead of the class constructor. + """ + global inst + try: + inst + except: + inst = XendDomainGroup() + inst.init() + return inst diff -r ecb6cd61a9cf tools/python/xen/xend/XendDomainGroupInfo.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/XendDomainGroupInfo.py Tue Feb 20 13:14:46 2007 -0500 @@ -0,0 +1,239 @@ +#============================================================================ +# 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 +#============================================================================ +# Author: Chris Bookholt <hap10@xxxxxxxxxxxxxx> +#============================================================================ +import logging +import string +import sxp +import uuid +import xen.lowlevel.xc +import XendDomain +import XendDomainInfo +from xen.xend.XendError import VmError +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.XendConstants import XS_GRPROOT + + +xc = xen.lowlevel.xc.xc() +log = logging.getLogger("xend.XendDomainGroupInfo") +default_ops = ['create','shutdown','pause','unpause','save','restore', + 'migrate_up','migrate_down'] + + +def create(config): + log.debug("XendDomainGroupInfo.create(%s)", config) + + grp = XendDomainGroupInfo(parseConfig(config)) + + try: + grp.construct() + return grp + except: + raise VmError('Domain group construction failed') + + +def recreate(xeninfo): + log.debug("XendDomainGroupInfo.recreate(%s)", xeninfo) + + dgid = xeninfo['dgid'] + dg_handle = xeninfo['dg_handle'] + xeninfo['dguuid'] = uuid.toString(dg_handle) + + log.info("Recreating domain group %d, UUID %s.", dgid, xeninfo['dguuid']) + + return XendDomainGroupInfo(xeninfo) + + +def parseConfig(config): + result = {} + + result['grp_name'] = sxp.child_value(config,'grp_name') + result['member_list'] = sxp.child_value(config,'member_list') + + log.info("parseConfig result is %s" % result) + return result + + +def grp_get(dgid): + try: + grplist = xc.domain_group_getinfo(dgid, 1) + if grplist and grplist[0]['dgid'] == dgid: + return grplist[0] + except Exception, err: + # ignore missing domain group + log.debug("grp_getinfo(%d) failed, ignoring: %s", dgid, str(err)) + return None + + +class XendDomainGroupInfo: + def __init__(self, info): + + self.info = info + + if self.infoIsSet('dgid'): + self.dgid = self.info['dgid'] + + if not self.infoIsSet('dguuid'): + self.info['dguuid'] = uuid.toString(uuid.create()) + self.dguuid = self.info['dguuid'] + + if not self.infoIsSet('grp_name'): + self.info['grp_name'] = ("Group-%s" % self.dguuid) + self.grp_name = self.info['grp_name'] + + if not self.infoIsSet('grp_path'): + self.info['grp_path'] = "%s%s" % (XS_GRPROOT,self.dguuid) + self.grppath = self.info['grp_path'] + + self.parse_member_list() + self.validateInfo() + + + def parse_member_list(self): + # set up member info dict to pair members and their manifests + # TODO: add checks to ensure neither component is empty + self.members = [] + self.member_info = {} + + if self.infoIsSet('member_list'): + for str in self.info['member_list']: + if (':' not in str): + raise VmError('invalid grpinfo format; member_list missing \':\'') + smember = str.split(':') + mbr_name = smember[0] + self.members.append(mbr_name) + mbr_manifest_path = smember[1] + self.member_info[mbr_name] = mbr_manifest_path + + self.size = len(self.member_info) + self.info['size'] = self.size + + + def getName(self): + return self.info['grp_name'] + + + def getDgid(self): + return self.info['dgid'] + + + def getDguuid(self): + return self.info['dguuid'] + + + def update(self, info = None): + log.trace("XendDomainGroupInfo.update(%s) on grp %d", self.dgid) + + if not info: + xdg = xen.xend.XendDomainGroup.instance() + info = xdg.grp_lookup(self.dgid) + if not info: + return + + self.info.update(info) + self.dgid = self.info['dgid'] + self.dguuid = self.info['dguuid'] + self.grp_name = self.info['grp_name'] + self.parse_member_list() + self.validateInfo() + + log.trace("XendDomainGroupInfo.update done on grp %d: %s", self.dgid, + self.info) + + + def sxpr(self): + return self.info + + + def validateInfo(self): + def defaultInfo(name, val): + if not self.infoIsSet(name): + self.info[name] = val() + try: + defaultInfo('grp_name', lambda: "Group-%s" % self.dguuid) + self.check_name(self.info['grp_name']) + except KeyError, exn: + log.exception(exn) + raise VmError('Unspecified domain group detail: %s' % exn) + + def _readGrp(self, *args): + return xstransact.Read(self.grppath, *args) + + + def _writeGrp(self, *args): + return xstransact.Write(self.grppath, *args) + + + def _removeGrp(self, *args): + return xstransact.Remove(self.grppath, *args) + + + def storeGrpDetails(self): + to_store = { + 'dgid': str(self.dgid), + 'dguuid': self.dguuid, + 'grp_name': self.grp_name, + 'members': ", ".join(self.members) + } + self._writeGrp(to_store) + + + # create an empty group + def construct(self, dguuid = None): + if dguuid: + dg_handle = uuid.fromString(dguuid) + else: + dg_handle = uuid.fromString(self.info['dguuid']) + self.dgid = xc.domain_group_create(dg_handle) + if (self.dgid < 0) or (self.dgid == None): + raise VmError('Creating domain group %s failed' % self.info['grp_name']) + self.info['dgid'] = self.dgid + self.storeGrpDetails() + + + def infoIsSet(self, name): + return name in self.info and self.info[name] is not None + + + def check_name(self, name): + # check for lack of name + if name is None or name == '': + raise VmError('missing grp name') + # check name for invalid characters + for c in name: + if c in string.digits: continue + if c in '_-.:/+': continue + if c in string.ascii_letters: continue + raise VmError("check_name: invalid grp name caused by [%s]" % c) + # check for duplicate names + xdg = xen.xend.XendDomainGroup.instance() + grp = xdg.grp_lookup_nr(name) + if grp and grp.info['dguuid'] != self.info['dguuid']: + raise VmError("Group name %s already exists" % name) + + + def destroy(self, rmxs): + ret = xc.domain_group_destroy(self.dgid) + if ret == 0 and rmxs: + self._removeGrp() + return ret + + + def pause(self): + xc.domain_group_pause(self.dgid) + + + def unpause(self): + xc.domain_group_unpause(self.dgid) _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |