WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 2/8] [xend] Domain Groups: xend classes for groups

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 2/8] [xend] Domain Groups: xend classes for groups
From: Chris <hap10@xxxxxxxxxxxxxx>
Date: Tue, 20 Feb 2007 14:55:57 -0500
Delivery-date: Tue, 20 Feb 2007 11:57:23 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Thunderbird 1.5.0.9 (Macintosh/20061207)
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
<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 2/8] [xend] Domain Groups: xend classes for groups, Chris <=