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
|