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] [RFC 2/2] Domain Groups - Tools Support

To: xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [RFC 2/2] Domain Groups - Tools Support
From: Chris <hap10@xxxxxxxxxxxxxx>
Date: Tue, 18 Dec 2007 11:03:13 -0500
Delivery-date: Tue, 18 Dec 2007 08:28:12 -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 2.0.0.9 (Macintosh/20071031)
diffstat domgrps-tools.patch

 examples/Makefile                      |    1
 examples/xmexample.grp                 |   21 +
 libxc/Makefile                         |    1
 libxc/xc_domain.c                      |    4
 libxc/xc_domain_group.c                |  100 +++++++++
 libxc/xc_private.h                     |   31 ++
 libxc/xenctrl.h                        |   31 ++
 python/xen/lowlevel/xc/xc.c            |  241 ++++++++++++++++++++--
 python/xen/xend/XendCheckpoint.py      |   54 ++++-
 python/xen/xend/XendClient.py          |    1
 python/xen/xend/XendConfig.py          |   27 ++
 python/xen/xend/XendConstants.py       |   11 +
 python/xen/xend/XendDomain.py          |   15 +
 python/xen/xend/XendDomainGroup.py     |  347 +++++++++++++++++++++++++++++++++
 python/xen/xend/XendDomainGroupInfo.py |  238 ++++++++++++++++++++++
 python/xen/xend/XendDomainInfo.py      |   37 ++-
 python/xen/xend/XendError.py           |    4
 python/xen/xend/server/XMLRPCServer.py |   34 ++-
 python/xen/xm/create.py                |    8
 python/xen/xm/group.py                 |  275 ++++++++++++++++++++++++++
 python/xen/xm/main.py                  |  166 +++++++++++++++

21 files changed, 1595 insertions(+), 52 deletions(-)
diff -urN xen-unstable/tools/examples/Makefile 
xen-unstable-trp-domgrps-rebase-tip/tools/examples/Makefile
--- xen-unstable/tools/examples/Makefile        2007-08-06 17:59:51.000000000 
-0400
+++ xen-unstable-trp-domgrps-rebase-tip/tools/examples/Makefile 2007-11-19 
18:42:00.000000000 -0500
@@ -16,6 +16,7 @@
 XEN_CONFIGS += xmexample2
 XEN_CONFIGS += xmexample.hvm
 XEN_CONFIGS += xmexample.vti
+XEN_CONFIGS += xmexample.grp
 XEN_CONFIGS += xend-pci-quirks.sxp
 XEN_CONFIGS += xend-pci-permissive.sxp
 
diff -urN xen-unstable/tools/examples/xmexample.grp 
xen-unstable-trp-domgrps-rebase-tip/tools/examples/xmexample.grp
--- xen-unstable/tools/examples/xmexample.grp   1969-12-31 19:00:00.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/examples/xmexample.grp    
2007-11-19 18:42:00.000000000 -0500
@@ -0,0 +1,21 @@
+################################################################################
+#
+# Defines a group of domains
+#
+################################################################################
+# grp_name must be unique (within the scope of a xend instance)
+#
+# member_list contains names and paths to configuration files for each
+# member of the domain group  
+# 
+# Note: The domain name in member_list must match the 'name' attribute in the 
+# corresponding VM configuration file.
+################################################################################
+
+(grp_name "TestGroup")
+
+(member_list
+       ('fc5-1:/etc/xen/vmconfig-1'
+        'fc5-2:/etc/xen/vmconfig-2'
+        'fc5-3:/etc/xen/vmconfig-3')
+)
diff -urN xen-unstable/tools/libxc/Makefile 
xen-unstable-trp-domgrps-rebase-tip/tools/libxc/Makefile
--- xen-unstable/tools/libxc/Makefile   2007-12-17 16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/libxc/Makefile    2007-12-17 
16:09:54.000000000 -0500
@@ -10,6 +10,7 @@
 CTRL_SRCS-$(CONFIG_IA64) += xc_core_ia64.c
 CTRL_SRCS-$(CONFIG_POWERPC) += xc_core_powerpc.c
 CTRL_SRCS-y       += xc_domain.c
+CTRL_SRCS-y       += xc_domain_group.c
 CTRL_SRCS-y       += xc_evtchn.c
 CTRL_SRCS-y       += xc_misc.c
 CTRL_SRCS-y       += xc_acm.c
diff -urN xen-unstable/tools/libxc/xc_domain.c 
xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_domain.c
--- xen-unstable/tools/libxc/xc_domain.c        2007-12-17 16:45:04.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_domain.c 2007-12-17 
16:09:54.000000000 -0500
@@ -197,6 +197,7 @@
             info->crashed  = 1;
         }
 
+        info->grpid = domctl.u.getdomaininfo.group;
         info->ssidref  = domctl.u.getdomaininfo.ssidref;
         info->nr_pages = domctl.u.getdomaininfo.tot_pages;
         info->max_memkb = domctl.u.getdomaininfo.max_pages << (PAGE_SHIFT-10);
@@ -208,6 +209,9 @@
         memcpy(info->handle, domctl.u.getdomaininfo.handle,
                sizeof(xen_domain_handle_t));
 
+        memcpy(info->dg_handle, domctl.u.getdomaininfo.dg_handle,
+               sizeof(xen_domain_group_handle_t));
+
         next_domid = (uint16_t)domctl.domain + 1;
         info++;
     }
diff -urN xen-unstable/tools/libxc/xc_domain_group.c 
xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_domain_group.c
--- xen-unstable/tools/libxc/xc_domain_group.c  1969-12-31 19:00:00.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_domain_group.c   
2007-11-19 18:42:00.000000000 -0500
@@ -0,0 +1,100 @@
+/******************************************************************************
+ * xc_domain_group.c
+ * 
+ * API for manipulating and obtaining information on domain groups.
+ * 
+ * Chris Bookholt (hap10@xxxxxxxxxxxxxx)
+ */
+
+#include "xc_private.h"
+#include <xen/memory.h>
+
+int xc_domain_group_create(int xc_handle, 
+                          xen_domain_group_handle_t handle,
+                          uint32_t *pdgid)
+{
+       int err;
+       DECLARE_DOMGRPCTL;
+       domgrpctl.cmd = XEN_DOMGRPCTL_creategrp;
+       memcpy(domgrpctl.u.create_grp.handle, handle,
+              sizeof(xen_domain_group_handle_t));
+       
+       err = do_domgrpctl(xc_handle, &domgrpctl);
+       if (err) 
+               return err;
+
+       *pdgid = (uint16_t)domgrpctl.u.get_grp_info.dgid;
+       return 0;
+}
+
+int xc_domain_group_pause(int xc_handle, uint32_t dgid)
+{
+       DECLARE_DOMGRPCTL;
+       domgrpctl.cmd = XEN_DOMGRPCTL_pausegrp;
+       domgrpctl.u.pause_grp.dgid = (dgid_t) dgid;
+       return do_domgrpctl(xc_handle, &domgrpctl);
+}
+
+int xc_domain_group_unpause(int xc_handle, uint32_t dgid)
+{
+       DECLARE_DOMGRPCTL;
+       domgrpctl.cmd = XEN_DOMGRPCTL_unpausegrp;
+       domgrpctl.u.unpause_grp.dgid = (dgid_t) dgid;
+       return do_domgrpctl(xc_handle, &domgrpctl);
+}
+
+int xc_domain_group_destroy(int xc_handle, uint32_t dgid)
+{
+       DECLARE_DOMGRPCTL;
+       domgrpctl.cmd = XEN_DOMGRPCTL_destroygrp;
+       domgrpctl.u.destroy_grp.dgid = (dgid_t) dgid;
+       return do_domgrpctl(xc_handle, &domgrpctl);
+}
+
+int xc_domain_group_join(int xc_handle, uint32_t domid, uint32_t dgid)
+{
+       DECLARE_DOMGRPCTL;
+       domgrpctl.cmd = XEN_DOMGRPCTL_joingrp;
+       domgrpctl.u.join_grp.domid = (domid_t) domid;
+       domgrpctl.u.join_grp.dgid = (dgid_t) dgid;
+       return do_domgrpctl(xc_handle, &domgrpctl);
+}
+
+#define TRANSFER_LIST_TO_INFO(list_name)                               \
+       memcpy(info->list_name, domgrpctl.u.get_grp_info.list_name,     \
+               MAX_GROUP_SIZE*sizeof(domid_t));
+
+int xc_domain_group_getinfo(int xc_handle, uint32_t first_dgid,
+                           unsigned int max_grps, xc_grpinfo_t * info)
+{
+       unsigned int nr_grps;
+       uint32_t next_dgid = first_dgid;
+       DECLARE_DOMGRPCTL;
+       int rc = 0;
+
+       memset(info, 0, max_grps * sizeof(xc_grpinfo_t));
+
+       for (nr_grps = 0; nr_grps < max_grps; nr_grps++) {
+               domgrpctl.cmd = XEN_DOMGRPCTL_getgrpinfo;
+               domgrpctl.u.get_grp_info.dgid = (dgid_t) next_dgid;
+
+               rc = do_domgrpctl(xc_handle, &domgrpctl);
+               if (rc < 0)
+                       break;
+
+               info->dgid = (uint16_t) domgrpctl.u.get_grp_info.dgid;
+               info->size = (uint16_t) domgrpctl.u.get_grp_info.size;
+
+               TRANSFER_LIST_TO_INFO(member_list);
+               memcpy(info->handle, domgrpctl.u.get_grp_info.handle,
+                      sizeof(xen_domain_group_handle_t));
+
+               next_dgid = (uint16_t) domgrpctl.u.get_grp_info.dgid + 1;
+               info++;
+       }
+
+       if (!nr_grps)
+               return rc;
+
+       return nr_grps;
+}
diff -urN xen-unstable/tools/libxc/xc_private.h 
xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_private.h
--- xen-unstable/tools/libxc/xc_private.h       2007-11-19 10:38:07.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xc_private.h        
2007-11-19 18:42:00.000000000 -0500
@@ -23,10 +23,12 @@
 #ifdef VALGRIND
 #define DECLARE_HYPERCALL privcmd_hypercall_t hypercall = { 0 }
 #define DECLARE_DOMCTL struct xen_domctl domctl = { 0 }
+#define DECLARE_DOMGRPCTL struct xen_domgrpctl domgrpctl = { 0 }
 #define DECLARE_SYSCTL struct xen_sysctl sysctl = { 0 }
 #else
 #define DECLARE_HYPERCALL privcmd_hypercall_t hypercall
 #define DECLARE_DOMCTL struct xen_domctl domctl
+#define DECLARE_DOMGRPCTL struct xen_domgrpctl domgrpctl
 #define DECLARE_SYSCTL struct xen_sysctl sysctl
 #endif
 
@@ -125,6 +127,35 @@
     return ret;
 }
 
+static inline int do_domgrpctl(int xc_handle, struct xen_domgrpctl *domgrpctl)
+{
+    int ret = -1;
+    DECLARE_HYPERCALL;
+
+    domgrpctl->interface_version = XEN_DOMGRPCTL_INTERFACE_VERSION;
+
+    hypercall.op     = __HYPERVISOR_domgrpctl;
+    hypercall.arg[0] = (unsigned long)domgrpctl;
+
+    if ( mlock(domgrpctl, sizeof(*domgrpctl)) != 0 )
+    {
+        PERROR("Could not lock memory for Xen hypercall");
+        goto out1;
+    }
+
+    if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 )
+    {
+        if ( errno == EACCES )
+            DPRINTF("domgrpctl operation failed -- need to"
+                    " rebuild the user-space tool set?\n");
+    }
+
+    safe_munlock(domgrpctl, sizeof(*domgrpctl));
+
+ out1:
+    return ret;
+}
+
 static inline int do_sysctl(int xc_handle, struct xen_sysctl *sysctl)
 {
     int ret = -1;
diff -urN xen-unstable/tools/libxc/xenctrl.h 
xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xenctrl.h
--- xen-unstable/tools/libxc/xenctrl.h  2007-12-17 16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/libxc/xenctrl.h   2007-12-17 
16:09:54.000000000 -0500
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <xen/xen.h>
 #include <xen/domctl.h>
+#include <xen/domgrpctl.h>
 #include <xen/sysctl.h>
 #include <xen/version.h>
 #include <xen/event_channel.h>
@@ -151,6 +152,7 @@
 
 typedef struct xc_dominfo {
     uint32_t      domid;
+    uint32_t      grpid;
     uint32_t      ssidref;
     unsigned int  dying:1, crashed:1, shutdown:1,
                   paused:1, blocked:1, running:1,
@@ -163,8 +165,16 @@
     unsigned int  nr_online_vcpus;
     unsigned int  max_vcpu_id;
     xen_domain_handle_t handle;
+    xen_domain_group_handle_t dg_handle;
 } xc_dominfo_t;
 
+typedef struct{
+    dgid_t        dgid;
+    uint16_t      size;
+    domid_t       member_list[MAX_GROUP_SIZE];
+    xen_domain_group_handle_t handle;
+} xc_grpinfo_t;
+
 typedef xen_domctl_getdomaininfo_t xc_domaininfo_t;
 int xc_domain_create(int xc_handle,
                      uint32_t ssidref,
@@ -298,6 +308,27 @@
                       unsigned int max_doms,
                       xc_dominfo_t *info);
 
+int xc_domain_group_getinfo(int xc_handle,
+                            uint32_t first_dgid,
+                           unsigned int max_grps,
+                           xc_grpinfo_t *info);
+
+int xc_domain_group_create(int xc_handle,
+                           xen_domain_group_handle_t handle,
+                           uint32_t *pdgid);
+
+int xc_domain_group_pause(int xc_handle,
+                          uint32_t dgid);
+
+int xc_domain_group_unpause(int xc_handle,
+                            uint32_t dgid);
+
+int xc_domain_group_destroy(int xc_handle,
+                            uint32_t dgid);
+
+int xc_domain_group_join(int xc_handle,
+                         uint32_t domid,
+                         uint32_t dgid);
 
 /**
  * This function will set the execution context for the specified vcpu.
diff -urN xen-unstable/tools/python/xen/lowlevel/xc/xc.c 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/lowlevel/xc/xc.c
--- xen-unstable/tools/python/xen/lowlevel/xc/xc.c      2007-12-17 
16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/lowlevel/xc/xc.c       
2007-12-17 16:09:54.000000000 -0500
@@ -40,10 +40,6 @@
     int xc_handle;
 } XcObject;
 
-
-static PyObject *dom_op(XcObject *self, PyObject *args,
-                        int (*fn)(int, uint32_t));
-
 static PyObject *pyxc_error_to_exception(void)
 {
     PyObject *pyerr;
@@ -69,6 +65,21 @@
     return NULL;
 }
 
+static PyObject *xc_op(XcObject *self, PyObject *args,
+                    int (*fn)(int, uint32_t))
+{
+    uint32_t id; /* used for both domid and grpid */
+
+    if (!PyArg_ParseTuple(args, "i", &id))
+        return NULL;
+
+    if (fn(self->xc_handle, id) != 0)
+        return pyxc_error_to_exception();
+
+    Py_INCREF(zero);
+    return zero;
+}
+
 static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args)
 {
     uint32_t dom;
@@ -155,12 +166,12 @@
 
 static PyObject *pyxc_domain_pause(XcObject *self, PyObject *args)
 {
-    return dom_op(self, args, xc_domain_pause);
+    return xc_op(self, args, xc_domain_pause);
 }
 
 static PyObject *pyxc_domain_unpause(XcObject *self, PyObject *args)
 {
-    return dom_op(self, args, xc_domain_unpause);
+    return xc_op(self, args, xc_domain_unpause);
 }
 
 static PyObject *pyxc_domain_destroy_hook(XcObject *self, PyObject *args)
@@ -175,7 +186,7 @@
 
 static PyObject *pyxc_domain_destroy(XcObject *self, PyObject *args)
 {
-    return dom_op(self, args, xc_domain_destroy);
+    return xc_op(self, args, xc_domain_destroy);
 }
 
 static PyObject *pyxc_domain_shutdown(XcObject *self, PyObject *args)
@@ -296,7 +307,7 @@
                                      PyObject *args,
                                      PyObject *kwds)
 {
-    PyObject *list, *info_dict, *pyhandle;
+    PyObject *list, *info_dict, *pyhandle, *pydg_handle;
 
     uint32_t first_dom = 0;
     int max_doms = 1024, nr_doms, i, j;
@@ -324,8 +335,9 @@
     {
         info_dict = Py_BuildValue(
             "{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i"
-            ",s:L,s:L,s:L,s:i,s:i}",
+            ",s:i,s:L,s:L,s:L,s:i,s:i}",
             "domid",           (int)info[i].domid,
+            "dgid",            (int)info[i].grpid,
             "online_vcpus",    info[i].nr_online_vcpus,
             "max_vcpu_id",     info[i].max_vcpu_id,
             "hvm",             info[i].hvm,
@@ -341,6 +353,7 @@
             "ssidref",         (int)info[i].ssidref,
             "shutdown_reason", info[i].shutdown_reason);
         pyhandle = PyList_New(sizeof(xen_domain_handle_t));
+        pydg_handle = PyList_New(sizeof(xen_domain_group_handle_t));
         if ( (pyhandle == NULL) || (info_dict == NULL) )
         {
             Py_DECREF(list);
@@ -351,7 +364,10 @@
         }
         for ( j = 0; j < sizeof(xen_domain_handle_t); j++ )
             PyList_SetItem(pyhandle, j, PyInt_FromLong(info[i].handle[j]));
+        for ( j = 0; j < sizeof(xen_domain_group_handle_t); j++ )
+            PyList_SetItem(pydg_handle, j, 
PyInt_FromLong(info[i].dg_handle[j]));
         PyDict_SetItemString(info_dict, "handle", pyhandle);
+        PyDict_SetItemString(info_dict, "dg_handle", pydg_handle);
         Py_DECREF(pyhandle);
         PyList_SetItem(list, i, info_dict);
     }
@@ -1214,21 +1230,6 @@
     return zero;
 }
 
-static PyObject *dom_op(XcObject *self, PyObject *args,
-                        int (*fn)(int, uint32_t))
-{
-    uint32_t dom;
-
-    if (!PyArg_ParseTuple(args, "i", &dom))
-        return NULL;
-
-    if (fn(self->xc_handle, dom) != 0)
-        return pyxc_error_to_exception();
-
-    Py_INCREF(zero);
-    return zero;
-}
-
 #ifdef __powerpc__
 static PyObject *pyxc_alloc_real_mode_area(XcObject *self,
                                            PyObject *args,
@@ -1251,6 +1252,148 @@
 }
 #endif /* powerpc */
 
+#define EXTRACT_DOM_LIST(list_name, dict)                              \
+        dom_list = PyList_New(0);                                      \
+        for ( j = 0; j < info[i].size; j++ )                           \
+            PyList_Append(dom_list, PyInt_FromLong(info[i].list_name[j]));\
+        PyDict_SetItemString(dict, #list_name, dom_list);              \
+        Py_DECREF(dom_list);
+
+static PyObject *pyxc_domain_group_getinfo(XcObject *self,
+                                     PyObject *args,
+                                     PyObject *kwds)
+{
+    PyObject *list, *info_dict, *dom_list, *pyhandle;
+
+    uint32_t first_grp = 0;
+    /* max_grps is unrealistically large and causes a large heap allocation 
+       for the duration of this function that, in the vast majority of cases, 
+       will be very sparsely populated with information about real groups.  
+
+       Leaving this alone for now to keep an equal limit on max number of 
+       groups in both the VMM and the control stack. 
+
+       Could add a new case to the domain group control hypercall to return 
+       the current number of groups instead of assuming the worst case...
+    */
+    int max_grps = NULL_GROUP_ID+1, nr_grps, i, j;
+    xc_grpinfo_t *info;
+
+    static char *kwd_list[] = { "first_grp", "max_grps", NULL };
+
+    /* pull values from python args */
+    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list,
+                                      &first_grp, &max_grps) )
+        return NULL;
+
+    /* alloc space for the group info and ask Xen (via libxc) for the info */
+    if ( (info = malloc(max_grps * sizeof(xc_grpinfo_t))) == NULL )
+        return PyErr_NoMemory();
+    nr_grps = xc_domain_group_getinfo(self->xc_handle, first_grp, max_grps,
+                                      info);
+
+    if (nr_grps < 0) {
+        free(info);
+        return PyErr_SetFromErrno(xc_error_obj);
+    }
+
+    /* iterate over the returned groups and 
+       put the returned values into python objects */
+    list = PyList_New(nr_grps);
+    for ( i = 0 ; i < nr_grps; i++ ) {
+       /* extract group ID and size */
+        info_dict = Py_BuildValue(
+                       "{s:i,s:i}", 
+                       "dgid", info[i].dgid, 
+                       "size", info[i].size);
+
+       EXTRACT_DOM_LIST(member_list, info_dict);
+
+       /* extract the group's handle */        
+        pyhandle = PyList_New(sizeof(xen_domain_group_handle_t));
+        for ( j = 0; j < sizeof(xen_domain_group_handle_t); j++ )
+            PyList_SetItem(pyhandle, j, PyInt_FromLong(info[i].handle[j]));
+        PyDict_SetItemString(info_dict, "dg_handle", pyhandle);
+        Py_DECREF(pyhandle);
+
+        PyList_SetItem(list, i, info_dict);
+    }
+
+    free(info);
+
+    return list;
+}
+
+static PyObject *pyxc_domain_group_create(XcObject *self,
+                                    PyObject *args,
+                                    PyObject *kwds)
+{
+    uint32_t dgid = 0;
+    int i;
+    PyObject *pyhandle = NULL;
+    xen_domain_group_handle_t handle = {
+        0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+        0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef };
+
+    static char *kwd_list[] = { "handle", NULL };
+
+    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwd_list,
+                                      &pyhandle))
+        return NULL;
+
+    if ( pyhandle != NULL )
+    {
+        if ( !PyList_Check(pyhandle) ||
+             (PyList_Size(pyhandle) != sizeof(xen_domain_group_handle_t)) )
+            goto out_exception;
+
+        for ( i = 0; i < sizeof(xen_domain_group_handle_t); i++ )
+        {
+            PyObject *p = PyList_GetItem(pyhandle, i);
+            if ( !PyInt_Check(p) )
+                goto out_exception;
+            handle[i] = (uint8_t)PyInt_AsLong(p);
+        }
+    } else
+       goto out_exception;
+
+    if ( (xc_domain_group_create(self->xc_handle, handle, &dgid)) < 0 )
+        return PyErr_SetFromErrno(xc_error_obj);
+
+    return PyInt_FromLong(dgid);
+
+out_exception:
+    errno = EINVAL;
+    PyErr_SetFromErrno(xc_error_obj);
+    return NULL;
+}
+
+static PyObject *pyxc_domain_group_pause(XcObject *self, PyObject *args)
+{
+    return xc_op(self, args, xc_domain_group_pause);
+}
+
+static PyObject *pyxc_domain_group_unpause(XcObject *self, PyObject *args)
+{
+    return xc_op(self, args, xc_domain_group_unpause);
+}
+
+static PyObject *pyxc_domain_group_destroy(XcObject *self, PyObject *args)
+{
+    return xc_op(self, args, xc_domain_group_destroy);
+}
+
+static PyObject *pyxc_domain_group_join(XcObject *self, PyObject *args)
+{
+    uint32_t dgid, domid;
+    if (!PyArg_ParseTuple(args, "ii", &domid, &dgid))
+        return NULL;
+    if (xc_domain_group_join(self->xc_handle, domid, dgid) != 0)
+        return PyErr_SetFromErrno(xc_error_obj);
+    Py_INCREF(zero);
+    return zero;
+}
+
 static PyMethodDef pyxc_methods[] = {
     { "handle",
       (PyCFunction)pyxc_handle,
@@ -1265,6 +1408,13 @@
       " dom    [int, 0]:        Domain identifier to use (allocated if 
zero).\n"
       "Returns: [int] new domain identifier; -1 on error.\n" },
 
+    { "domain_group_create", 
+      (PyCFunction)pyxc_domain_group_create, 
+      METH_VARARGS | METH_KEYWORDS, "\n"
+      "Create a new domain group.\n"
+      " grp    [int, 0]:        Domain group identifier to use (allocated if 
zero).\n"
+      "Returns: [int] new domain group identifier; -1 on error.\n" },
+
     { "domain_max_vcpus", 
       (PyCFunction)pyxc_domain_max_vcpus,
       METH_VARARGS, "\n"
@@ -1288,6 +1438,13 @@
       " dom [int]: Identifier of domain to be paused.\n\n"
       "Returns: [int] 0 on success; -1 on error.\n" },
 
+    { "domain_group_pause", 
+      (PyCFunction)pyxc_domain_group_pause, 
+      METH_VARARGS, "\n"
+      "Temporarily pause execution of all domains in a group.\n"
+      " grp [int]: Identifier of domain group to be paused.\n\n"
+      "Returns: [int] 0 on success; -1 on error.\n" },
+
     { "domain_unpause", 
       (PyCFunction)pyxc_domain_unpause, 
       METH_VARARGS, "\n"
@@ -1295,6 +1452,13 @@
       " dom [int]: Identifier of domain to be unpaused.\n\n"
       "Returns: [int] 0 on success; -1 on error.\n" },
 
+    { "domain_group_unpause", 
+      (PyCFunction)pyxc_domain_group_unpause, 
+      METH_VARARGS, "\n"
+      "(Re)start execution of all domains in a group.\n"
+      " grp [int]: Identifier of domain group to be unpaused.\n\n"
+      "Returns: [int] 0 on success; -1 on error.\n" },
+
     { "domain_destroy", 
       (PyCFunction)pyxc_domain_destroy, 
       METH_VARARGS, "\n"
@@ -1302,6 +1466,13 @@
       " dom [int]:    Identifier of domain to be destroyed.\n\n"
       "Returns: [int] 0 on success; -1 on error.\n" },
 
+    { "domain_group_destroy", 
+      (PyCFunction)pyxc_domain_group_destroy, 
+      METH_VARARGS, "\n"
+      "Destroy an empty domain group.\n"
+      " grp [int]:    Identifier of domain group to be destroyed.\n\n"
+      "Returns: [int] 0 on success; -1 on error.\n" },
+
     { "domain_destroy_hook", 
       (PyCFunction)pyxc_domain_destroy_hook, 
       METH_VARARGS, "\n"
@@ -1375,6 +1546,20 @@
       " shutdown_reason [int]: Numeric code from guest OS, explaining "
       "reason why it shut itself down.\n" },
 
+    { "domain_group_getinfo", 
+      (PyCFunction)pyxc_domain_group_getinfo, 
+      METH_VARARGS | METH_KEYWORDS, "\n"
+      "Get information regarding a set of domain groups.\n"
+      " first_grp [int, 0]:    First domain to retrieve info about.\n"
+      " max_grps  [int, 1024]: Maximum number of domains to retrieve info"
+      " about.\n\n"
+      "Returns:  [list of dicts] if list length is less than 'max_grps'\n"
+      "          parameter then there was an error, or the end of the\n"
+      "          group-id space was reached.\n"
+      " grp               [int]: Id of group to which this info pertains\n"
+      " size              [int]: Number of domains in this group\n"
+      " member_list       [int array]: Unordered list of member Ids\n"},
+
     { "vcpu_getinfo", 
       (PyCFunction)pyxc_vcpu_getinfo, 
       METH_VARARGS | METH_KEYWORDS, "\n"
@@ -1621,6 +1806,14 @@
       " dom        [int]: Domain whose time offset is being set.\n"
       "Returns: [int] 0 on success; -1 on error.\n" },
 
+    { "domain_group_join",
+      (PyCFunction)pyxc_domain_group_join,
+      METH_VARARGS, "\n"
+      "Request that the given domain join the supplied group.\n"
+      " dom [int]: Identifier of domain joining group.\n"
+      " grp [int]: Identifier of group the given domain is joining.\n"
+      "Returns: [int] 0 on success; -1 on error.\n" },
+
     { "domain_send_trigger",
       (PyCFunction)pyxc_domain_send_trigger,
       METH_VARARGS | METH_KEYWORDS, "\n"
diff -urN xen-unstable/tools/python/xen/xend/server/XMLRPCServer.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/server/XMLRPCServer.py
--- xen-unstable/tools/python/xen/xend/server/XMLRPCServer.py   2007-12-17 
16:45:04.000000000 -0500
+++ 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/server/XMLRPCServer.py
    2007-12-17 16:09:54.000000000 -0500
@@ -29,10 +29,11 @@
 
 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.
@@ -46,16 +47,16 @@
             ret.append(k)
     return ret
 
-def lookup(domid):
+def lookup_dom(domid):
     info = XendDomain.instance().domain_lookup(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):
@@ -76,6 +77,19 @@
     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:
@@ -91,6 +105,10 @@
 
 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,
                  ssl_key_file = None, ssl_cert_file = None,
@@ -190,6 +208,12 @@
                 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', 'send_debug_keys'], 'node'),
                              (XendDmesg, ['info', 'clear'], 'node.dmesg')]:
@@ -206,6 +230,8 @@
         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')
 
         # A couple of the security functions
         from xen.util.xsm import xsm as security
diff -urN xen-unstable/tools/python/xen/xend/XendCheckpoint.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendCheckpoint.py
--- xen-unstable/tools/python/xen/xend/XendCheckpoint.py        2007-11-19 
10:38:07.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendCheckpoint.py 
2007-12-03 14:26:13.000000000 -0500
@@ -70,10 +70,17 @@
     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 = dominfo.info.is_hvm()
 
@@ -158,17 +165,16 @@
         raise XendError("not a valid guest state file: found '%s'" %
                         signature)
 
-    l = read_exact(fd, sizeof_int,
+    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")
 
     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()
 
     if dominfo:
@@ -194,6 +200,42 @@
         apic = 0
         pae  = 0
 
+    # re-create (if necessary) and re-join group
+    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.reset()
+    p.input(grpconfig_buf) #FIXME: gracefully handle domains with no group data
+    if not p.ready:
+        raise XendError("not a valid group state file: config parse")
+    grpconfig = eval(p.get_val())
+
+    src_dguuid = sxp.child_value(vmconfig, 'dguuid')
+    src_grp_name = grpconfig['grp_name'] 
+
+    xdg = xen.xend.XendDomainGroup.instance()
+    xdg.domain_groups_lock.acquire()
+    log.debug("%s acquire grplock", xdg)
+    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()
+        log.debug("%s release grplock", xdg)
+    except:
+        xdg.domain_groups_lock.release()
+        log.debug("%s release grplock", xdg)
+        dominfo.destroy()
+        raise XendError("problem joining/creating old group")
+
     try:
         restore_image = image.create(dominfo, dominfo.info)
         memory = restore_image.getRequiredAvailableMemory(
diff -urN xen-unstable/tools/python/xen/xend/XendClient.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendClient.py
--- xen-unstable/tools/python/xen/xend/XendClient.py    2007-08-06 
17:59:53.000000000 -0400
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendClient.py     
2007-11-19 18:42:00.000000000 -0500
@@ -27,6 +27,7 @@
 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 -urN xen-unstable/tools/python/xen/xend/XendConfig.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendConfig.py
--- xen-unstable/tools/python/xen/xend/XendConfig.py    2007-12-17 
16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendConfig.py     
2007-12-17 16:09:54.000000000 -0500
@@ -27,7 +27,7 @@
 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
 from xen.xend.xenstore.xstransact import xstransact
 from xen.xend.server.BlktapController import blktap_disk_types
 from xen.xend.server.netif import randomMAC
@@ -111,6 +111,8 @@
 
 XENAPI_CFG_TO_LEGACY_CFG = {
     'uuid': 'uuid',
+    'dgid': 'dgid',
+    'dguuid': 'dguuid',
     'VCPUs_max': 'vcpus',
     'cpus': 'cpus',
     'name_label': 'name',
@@ -142,6 +144,8 @@
 
 XENAPI_CFG_TYPES = {
     'uuid': str,
+    'dguid': int,
+    'dguuid': str,
     'name_label': str,
     'name_description': str,
     'user_version': str,
@@ -197,6 +201,9 @@
 
 LEGACY_CFG_TYPES = {
     'uuid':          str,
+    'dgid':          int,
+    'dguuid':        str,
+    'grp_name':      str,
     'name':          str,
     'vcpus':         int,
     'vcpu_avail':    long,
@@ -222,6 +229,8 @@
 # xenstore.
 LEGACY_XENSTORE_VM_PARAMS = [
     'uuid',
+    'dgid',
+    'dguuid',
     'name',
     'vcpus',
     'vcpu_avail',
@@ -336,7 +345,9 @@
             'vbd_refs': [],
             'vtpm_refs': [],
             'other_config': {},
-            'platform': {}
+            'platform': {},
+            'dgid': NULL_GROUP_ID,
+            'dguuid': NULL_GROUP_UUID,
         }
         
         return defaults
@@ -388,6 +399,10 @@
             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:
@@ -417,6 +432,7 @@
 
     def _dominfo_to_xapi(self, dominfo, update_mem = False):
         self['domid'] = dominfo['domid']
+        self['dgid'] = dominfo['dgid']
         self['online_vcpus'] = dominfo['online_vcpus']
         self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
 
@@ -448,6 +464,9 @@
 
         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.
@@ -924,6 +943,8 @@
                     sxpr.append([legacy, int(self[xenapi])])
                 else:
                     sxpr.append([legacy, self[xenapi]])
+           else:
+                    log.debug("Unconverted key: " + xenapi)
 
         MiB = 1024*1024
 
@@ -931,7 +952,7 @@
         sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
 
         for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
-            if legacy in ('domid', 'uuid', 'cpus'): # skip these
+            if legacy in ('domid', 'uuid', 'cpus', 'dgid', 'dguuid'): # skip 
these
                 continue
             if self.has_key(legacy) and self[legacy] not in (None, []):
                 sxpr.append([legacy, self[legacy]])
diff -urN xen-unstable/tools/python/xen/xend/XendConstants.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendConstants.py
--- xen-unstable/tools/python/xen/xend/XendConstants.py 2007-11-19 
10:38:07.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendConstants.py  
2007-11-19 18:42:00.000000000 -0500
@@ -123,4 +123,15 @@
 #
 
 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 -urN xen-unstable/tools/python/xen/xend/XendDomainGroupInfo.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainGroupInfo.py
--- xen-unstable/tools/python/xen/xend/XendDomainGroupInfo.py   1969-12-31 
19:00:00.000000000 -0500
+++ 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainGroupInfo.py
    2007-12-03 12:32:56.000000000 -0500
@@ -0,0 +1,238 @@
+#============================================================================
+# 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:
+            info = grp_get(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)
diff -urN xen-unstable/tools/python/xen/xend/XendDomainGroup.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainGroup.py
--- xen-unstable/tools/python/xen/xend/XendDomainGroup.py       1969-12-31 
19:00:00.000000000 -0500
+++ 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainGroup.py    
    2007-12-03 14:37:28.000000000 -0500
@@ -0,0 +1,347 @@
+#============================================================================
+# 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()
+
+    """ expects domain_groups_lock """
+    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)
+
+
+    """ expects domain_groups_lock """
+    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
+
+
+    """ expects domain_groups_lock """
+    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()
+
+
+    """ expects domain_groups_lock """
+    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)
+            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)
+
+            self.refresh()
+
+            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))
+
+
+def instance():
+    """Singleton constructor. Use this instead of the class constructor.
+    """
+    global inst
+    try:
+        inst
+    except:
+        inst = XendDomainGroup()
+        inst.init()
+    return inst
diff -urN xen-unstable/tools/python/xen/xend/XendDomainInfo.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainInfo.py
--- xen-unstable/tools/python/xen/xend/XendDomainInfo.py        2007-12-17 
16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomainInfo.py 
2007-12-17 16:09:54.000000000 -0500
@@ -952,6 +952,8 @@
     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),
@@ -1037,7 +1039,7 @@
         # changed in Xenstore.
         
         cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
-                  'rtc/timeoffset']
+                  'rtc/timeoffset', 'dgid', 'dguuid', 'grp_name']
         
         vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
                                            for k in cfg_vm])
@@ -1086,6 +1088,9 @@
         
         reason = self.readDom('control/shutdown')
 
+        # stash current dgid for restart/resume/restore
+        self.old_dgid = self.info.get('dgid')
+
         if reason and reason != 'suspend':
             sst = self.readDom('xend/shutdown_start_time')
             now = time.time()
@@ -1113,6 +1118,12 @@
     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)
         self.info['name_label'] = name
@@ -1393,8 +1404,21 @@
 
             new_dom = None
             try:
-                new_dom = XendDomain.instance().domain_create_from_dict(
-                    self.info)
+                xd = XendDomain.instance()
+                new_dom = xd.domain_create_from_dict(self.info)
+
+                # rejoin former domain group
+                xdg = xen.xend.XendDomainGroup.instance()
+
+                # wait until both needed locks are available
+                rc = False
+                while rc == False:
+                    rc = xd.domains_lock.acquire( blocking = False )
+                    if rc == False:
+                        time.sleep(1)
+                xdg.grp_join(new_dom.domid, self.old_dgid)
+                xd.domains_lock.release()
+
                 new_dom.waitForDevices()
                 new_dom.unpause()
                 rst_cnt = self._readVm('xend/restart_count')
@@ -2845,9 +2869,10 @@
         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'],
-                str(self.info['memory_dynamic_max']), 
DOM_STATES[self._stateGet()])
+        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_dynamic_max']),
+                DOM_STATES[self._stateGet()])
 
     __repr__ = __str__
 
diff -urN xen-unstable/tools/python/xen/xend/XendDomain.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomain.py
--- xen-unstable/tools/python/xen/xend/XendDomain.py    2007-12-17 
16:45:04.000000000 -0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendDomain.py     
2007-12-17 16:09:54.000000000 -0500
@@ -432,6 +432,14 @@
             if domid not in running_domids and domid != DOM0_ID:
                 self._remove_domain(dom, domid)
 
+        # update group information
+        xdg = xen.xend.XendDomainGroup.instance()
+        xdg.domain_groups_lock.acquire()
+        try:
+            xdg.refresh()
+        finally:
+            xdg.domain_groups_lock.release()
+
 
     def add_domain(self, info):
         """Add a domain to the list of running domains
@@ -925,10 +933,15 @@
                     if hasattr(os, "O_LARGEFILE"):
                         oflags |= os.O_LARGEFILE
                     fd = os.open(chkpath, oflags)
-                    XendCheckpoint.restore(self,
+                    pdated_dominfo = XendCheckpoint.restore(self,
                                            fd,
                                            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 -urN xen-unstable/tools/python/xen/xend/XendError.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendError.py
--- xen-unstable/tools/python/xen/xend/XendError.py     2007-08-06 
17:59:53.000000000 -0400
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xend/XendError.py      
2007-11-19 18:42:00.000000000 -0500
@@ -24,6 +24,10 @@
     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):
     
     def __init__(self, value):
diff -urN xen-unstable/tools/python/xen/xm/create.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/create.py
--- xen-unstable/tools/python/xen/xm/create.py  2007-12-17 16:45:04.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/create.py   
2007-12-17 16:09:54.000000000 -0500
@@ -521,6 +521,11 @@
           - suspend:        Domain is suspended;
           """)
 
+gopts.var('dgid', val='NUM',
+          fn=set_int, default=32767,
+          use='Default domain group to join.')
+
+
 def err(msg):
     """Print an error to stderr and exit.
     """
@@ -768,6 +773,8 @@
         config.append(['backend', ['tpmif']])
     if vals.localtime:
         config.append(['localtime', vals.localtime])
+    if vals.dgid is not None:
+        config.append(['dgid', vals.dgid])
 
     config_image = configure_image(vals)
     if vals.bootloader:
@@ -1188,6 +1195,7 @@
         map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs)
     elif not opts.is_xml:
         dom = make_domain(opts, config)
+       return dom
         
 def do_console(domain_name):
     cpid = os.fork() 
diff -urN xen-unstable/tools/python/xen/xm/group.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/group.py
--- xen-unstable/tools/python/xen/xm/group.py   1969-12-31 19:00:00.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/group.py    
2007-11-27 09:40:06.000000000 -0500
@@ -0,0 +1,275 @@
+#============================================================================
+# 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 os.path
+import sys
+import xmlrpclib
+import time
+from xen.xm.opts import Opts
+from xen.xend import sxp
+from xen.xend.XendClient import server
+from sets import Set
+
+opts = Opts()
+
+# print an error to stderr and exit.
+def err(msg):
+    print >>sys.stderr, "Error:", msg
+    sys.exit(1)
+
+
+def get_grp(dgid):
+    grpinfo = server.xend.group(dgid)
+    if not grpinfo:
+        err("group %s does not exist" % dgid)
+    return grpinfo
+
+
+def idCheck(dgid, op):
+    dgid = int(dgid)
+    if (dgid == 0 or dgid == 32767):
+        err("cannot %s group %d" % (op,dgid))
+
+
+# wait @timeout seconds for @domname to be created
+def wait_create(domname, timeout):
+    counter = 0
+    while counter < timeout:
+        domains = server.xend.domains(0)
+        if domname in domains:
+            return
+        counter += 1
+        time.sleep(1)
+    err("timeout reached; problem starting group member dom: %s" % domname)
+
+
+# read and parse sxp config from file
+def make_config(config_file_path):
+    try:
+        fin = file(config_file_path, 'rb')
+        try:
+            config = sxp.parse(fin)
+        finally:
+            fin.close()
+        if config is None:
+            config = ['grp_config']
+        else:
+            config.insert(0, 'grp_config')
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+    return config
+
+
+# create a domain group
+def make_domain_group(config):
+    try:
+        grpinfo = server.xend.group.create(config)
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+    return grpinfo
+
+
+# start the group members
+def populate_group(grpinfo, managed):
+    if managed:
+        from xen.xm import new
+    else:
+        from xen.xm import create
+
+    for domdata in grpinfo['member_list']:
+        domdata_list = domdata.split(":")
+        domname = domdata_list[0]
+        dom_config_path = domdata_list[1]
+        if managed:
+            #print "creating managed domain %s from file: %s" % 
(domname,dom_config_path)
+            new.main([None,dom_config_path])
+            #print "starting managed domain %s" % domname
+            server.xend.domain.start(domname, False)
+            domid = sxp.child_value(server.xend.domain(domname), 'domid')
+        else:
+            domid = create.main([None,dom_config_path])
+        wait_create(domname, 10)
+        dgid = grpinfo['dgid']
+        grp_join(domid, dgid)
+    #grp_unpause(grp.dgid)
+
+
+def grp_create(config_file_path, managed = False):
+    config = make_config(config_file_path)
+    grpinfo = make_domain_group(config)
+    populate_group(grpinfo, managed)
+
+
+# shutdown or reboot a group of domains
+def grp_shutdown(dgid, reason, keep):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"shutdown or reboot")
+    try:
+        server.xend.group.shutdown(dgid, reason)
+        if reason == "poweroff" and not keep:
+            print "waiting for group members to shutdown..."
+            from xen.xm.shutdown import wait_shutdown
+            wait_shutdown(opts, grpinfo['members'])
+            grp_destroy(dgid, force = False)
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# destroy a domain group
+def grp_destroy(dgid, force = False):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"destroy")
+    try:
+        if force:
+            grp_pause(dgid)
+            for domid in grpinfo['members']:
+                server.xend.domain.destroy(domid)
+            print "waiting for group members to be destroyed..."
+            from xen.xm.shutdown import wait_shutdown
+            wait_shutdown(opts, grpinfo['members'])
+        server.xend.group.destroy(grpinfo['dgid'])
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+    return
+
+
+# save a group of running domains
+def grp_save(dgid, prefix):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"save")
+    try:
+        server.xend.group.save(dgid, prefix)
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# retore a group of running domains
+def grp_restore(srcs):
+    try:
+        server.xend.group.restore(srcs)
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# suspend a group of running, managed domains
+#def grp_suspend(dgid):
+#    grpinfo = get_grp(dgid)
+#    idCheck(grpinfo['dgid'],"suspend")
+#    try:
+#        server.xend.group.suspend(dgid)
+#    except xmlrpclib.Fault, ex:
+#        err(str(ex))
+#    except Exception, ex:
+#        print("Error: %s" % str(ex))
+#        raise Exception
+    
+
+# resume a suspended domain group
+#def grp_resume(dgid):
+#    try:
+#        server.xend.group.resume(dgid)
+#    except xmlrpclib.Fault, ex:
+#        err(str(ex))
+#    except Exception, ex:
+#        print("Error: %s" % str(ex))
+#        raise Exception
+    
+
+# pause a domain group
+def grp_pause(dgid):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"pause")
+    try:
+        server.xend.group.pause(grpinfo['dgid'])
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# unpause a domain group
+def grp_unpause(dgid):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"unpause")
+    try:
+        server.xend.group.unpause(grpinfo['dgid'])
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# migrate a domain group
+def grp_migrate(dgid, dst, live, resource, port):
+    grpinfo = get_grp(dgid)
+    idCheck(grpinfo['dgid'],"unpause")
+    try:
+        server.xend.group.migrate(grpinfo['dgid'], dst, live, resource, port)
+    except xmlrpclib.Fault, ex:
+       err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# cause a domain to join a group
+def grp_join( dom, dgid ):
+    grpinfo = get_grp(dgid)
+    domid = sxp.child_value(server.xend.domain(dom), 'domid')
+    try:
+        server.xend.group.join(domid, grpinfo['dgid'])
+    except xmlrpclib.Fault, ex:
+       err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
+
+
+# print list of domain group stats
+def grp_list():
+    try:
+        domgrps = server.xend.group.list()
+        format = "%-20s %-6s %-5s %-46s"
+        print format % ('Name', 'ID', 'Size', 'Members')
+        for grpinfo in domgrps:
+            name = grpinfo['grp_name']
+            dgid = grpinfo['dgid']
+            size = grpinfo['size']
+            members = ", ".join(grpinfo['members'])
+            print format % (name, dgid, size, members)
+    except xmlrpclib.Fault, ex:
+        err(str(ex))
+    except Exception, ex:
+        print("Error: %s" % str(ex))
+        raise Exception
diff -urN xen-unstable/tools/python/xen/xm/main.py 
xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/main.py
--- xen-unstable/tools/python/xen/xm/main.py    2007-12-17 16:45:04.000000000 
-0500
+++ xen-unstable-trp-domgrps-rebase-tip/tools/python/xen/xm/main.py     
2007-12-17 16:44:21.000000000 -0500
@@ -49,6 +49,7 @@
 from xen.xm.opts import OptionError, Opts, wrap, set_true
 from xen.xm import console
 from xen.util.xmlrpcclient import ServerProxy
+from xen.xm import group
 import xen.util.xsm.xsm as security
 from xen.util.xsm.xsm import XSMError
 from xen.util.acmpolicy import ACM_LABEL_UNLABELED_DISPLAY
@@ -197,6 +198,27 @@
     'labels'        :  ('[policy] [type=dom|res|any]',
                         'List <type> labels for (active) policy.'),
     'serve'         :  ('', 'Proxy Xend XMLRPC over stdio.'),
+
+    # domain groups
+
+    'grp-create'    :  ('<config> [--managed | -m]','Create a domain group'),
+    'grp-shutdown'  :  ('<grp> [--keep| -k]','Shutdown all domains in group'),
+    'grp-destroy'   :  ('<grp> [--force | -f]','Destroy group'),
+    'grp-reboot'    :  ('<grp>','Reboot all domains in group'),
+    'grp-pause'     :  ('<grp>','Pause all domains in group'),
+    'grp-unpause'   :  ('<grp>','Unpause all domains in group'),
+    'grp-save'      :  ('<grp> <prefix>','Save all domains in group to ' 
+                        'files with the specified prefix'),
+    'grp-restore'   :  ('<CheckpointFile> [CheckpointFile ...]',
+                        'Restore all domains in group from file(s)'),
+    'grp-suspend'   :  ('<grp>','Suspend all domains in group'
+                       ' ** NOT YET IMPLEMENTED **'),
+    'grp-resume'    :  ('<grp>','Resume all domains in group'
+                       ' ** NOT YET IMPLEMENTED **'),
+    'grp-migrate'   :  ('<grp> <host> [--live | -l]',
+                        'Migrate all domains in group to host'),
+    'grp-join'      :  ('<dom> <grp>','Add domain to group'),
+    'grp-list'      :  ('','List groups and their members'),
 }
 
 SUBCOMMAND_OPTIONS = {
@@ -313,6 +335,22 @@
     "vcpu-set",
     ]
 
+group_commands = [
+    "grp-create",
+    "grp-shutdown",
+    "grp-destroy",
+    "grp-reboot",
+    "grp-pause",
+    "grp-unpause",
+    "grp-save",
+    "grp-restore",
+    "grp-suspend",
+    "grp-resume",
+    "grp-migrate",
+    "grp-join",
+    "grp-list",
+]
+
 host_commands = [
     "debug-keys",
     "dmesg",
@@ -358,7 +396,7 @@
 
 all_commands = (domain_commands + host_commands + scheduler_commands +
                 device_commands + vnet_commands + acm_commands +
-                ['shell', 'event-monitor'])
+                ['shell', 'event-monitor'] + group_commands)
 
 
 ##
@@ -529,14 +567,19 @@
 def map2sxp(m):
     return [[k, m[k]] for k in m.keys()]
 
-def arg_check(args, name, lo, hi = -1):
+def arg_check(args, name, lo, hi = None):
     n = len([i for i in args if i != '--'])
     
-    if hi == -1:
+    if hi == None:
         if n != lo:
             err("'xm %s' requires %d argument%s.\n" % (name, lo,
                                                        lo == 1 and '' or 's'))
             usage(name)
+    elif hi == -1:
+        if n < lo:
+            err("'xm %s' requires at least %d argument%s.\n" % (name, lo,
+                                                       lo == 1 and '' or 's'))
+            usage(name)
     else:
         if n < lo or n > hi:
             err("'xm %s' requires between %d and %d arguments.\n" %
@@ -863,6 +906,7 @@
 
     parsed_info = {
         'domid'    : get_info('domid',              str,   ''),
+        'dgid'     : get_info('dgid',               int,   -1),
         'name'     : get_info('name',               str,   '??'),
         'state'    : get_info('state',              str,   ''),
 
@@ -932,12 +976,11 @@
         print format % d
 
 def xm_label_list(doms):
-    print '%-40s %5s %5s %5s %10s %9s %-10s' % \
-          ('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)', 'Label')
-
+    print '%-40s %5s %5s %5s %10s %9s %-10s %7s' % \
+          ('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)', 'Label', 'Dgid')
     output = []
     format = '%(name)-40s %(domid)5s %(mem)5d %(vcpus)5d %(state)10s ' \
-             '%(cpu_time)8.1f %(seclabel)10s'
+             '%(cpu_time)8.1f %(seclabel)10s %(dgid)7d'
 
     for dom in doms:
         d = parse_doms_info(dom)
@@ -2391,7 +2434,100 @@
 
             print format2 % r
 
-            
+def xm_grp_create(args):
+    arg_check(args, "grp-create", 1)
+
+    CONFIG_ROOT = '/etc/xen/'
+    config_path = args[0]
+
+    if ( os.path.exists( config_path ) ):
+        verified_config_path = config_path
+    elif ( os.path.exists(CONFIG_ROOT + config_path ) ):
+        verified_config_path = CONFIG_ROOT + config_path
+    else:
+        err( "configuration file [%s] not found" % config_path )
+        return 1
+
+    return group.grp_create( verified_config_path )
+
+def xm_grp_destroy(args):
+    force = False
+    arg_check(args, "grp-destroy", 1, 2)
+    dgid = args[0]
+    for clflag in ['--force', '-f']:
+        if clflag in args:
+            force = True
+    return group.grp_destroy(dgid, force)
+
+def xm_grp_shutdown(args):
+    keep = False
+    arg_check(args, "grp-shutdown", 1, 2)
+    dgid = args[0]
+    for clflag in ['--keep', '-k']:
+        if clflag in args:
+            keep = True
+    return group.grp_shutdown(dgid, "poweroff", keep)
+
+def xm_grp_reboot(args):
+    arg_check(args, "grp-reboot", 1)
+    dgid = args[0]
+    return group.grp_shutdown(dgid, "reboot", True)
+
+def xm_grp_pause(args):
+    arg_check(args, "grp-pause", 1)
+    dgid = args[0]
+    return group.grp_pause(dgid)
+
+def xm_grp_unpause(args):
+    arg_check(args, "grp-unpause", 1)
+    dgid = args[0]
+    return group.grp_unpause(dgid)
+
+def xm_grp_save(args):
+    arg_check(args, "grp-save", 2)
+    dgid = args[0]
+    prefix = args[1]
+    return group.grp_save(dgid, prefix)
+
+def xm_grp_restore(args):
+    arg_check(args, "grp-restore", 1, -1)
+    return group.grp_restore(args)
+
+def xm_grp_suspend(args):
+    print "Not yet implemented"
+    #arg_check(args, "grp-suspend", 1)
+    #dgid = args[0]
+    #return group.grp_suspend(dgid)
+
+def xm_grp_resume(args):
+    print "Not yet implemented"
+    #arg_check(args, "grp-resume", 1)
+    #dgid = args[0]
+    #return group.grp_resume(dgid)
+
+def xm_grp_migrate(args):
+    live = False
+    arg_check(args, "grp-migrate", 2, 3)
+    dgid = args[0]
+    dst = args[1]
+    for clflag in ['--live', '-l']:
+        if clflag in args:
+            live = True
+    # hardcode these options for now
+    resource = 0
+    port = 0
+    return group.grp_migrate(dgid,dst,live,resource,port)
+
+def xm_grp_join(args):
+    arg_check(args, "grp-join", 2)
+    domid = args[0]
+    dgid = args[1]
+    return group.grp_join(domid, dgid)
+
+def xm_grp_list(args):
+    arg_check(args, "grp-list", 0)
+    return group.grp_list()
+
 commands = {
     "shell": xm_shell,
     "event-monitor": xm_event_monitor,
@@ -2455,6 +2591,20 @@
     "vnet-delete": xm_vnet_delete,
     # vtpm
     "vtpm-list": xm_vtpm_list,
+    # group
+    "grp-create": xm_grp_create,
+    "grp-shutdown": xm_grp_shutdown,
+    "grp-destroy": xm_grp_destroy,
+    "grp-reboot": xm_grp_reboot,
+    "grp-pause": xm_grp_pause,
+    "grp-unpause": xm_grp_unpause,
+    "grp-save": xm_grp_save,
+    "grp-restore": xm_grp_restore,
+    "grp-suspend": xm_grp_suspend,
+    "grp-resume": xm_grp_resume,
+    "grp-migrate": xm_grp_migrate,
+    "grp-join": xm_grp_join,
+    "grp-list": xm_grp_list,
     }
 
 ## The commands supported by a separate argument parser in xend.xm.
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [RFC 2/2] Domain Groups - Tools Support, Chris <=