[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC 2/2] Domain Groups - Tools Support



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

 


Rackspace

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