[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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.