---
 tools/python/xen/xend/XendCheckpoint.py   |  221 +++++++++++++++++++-----------
 tools/python/xen/xend/XendConfig.py       |    1 
 tools/python/xen/xend/XendDomain.py       |  189 +++++++++++++++++++++++++
 tools/python/xen/xend/XendDomainInfo.py   |    8 -
 tools/python/xen/xend/image.py            |   19 ++
 tools/python/xen/xend/server/SrvDomain.py |   27 +++
 tools/python/xen/xend/server/blkif.py     |    4 
 tools/python/xen/xm/main.py               |   79 ++++++++++
 8 files changed, 456 insertions(+), 92 deletions(-)

Index: xen-4.1.6-testing/tools/python/xen/xend/XendCheckpoint.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/XendCheckpoint.py
+++ xen-4.1.6-testing/tools/python/xen/xend/XendCheckpoint.py
@@ -65,7 +65,7 @@ def insert_after(list, pred, value):
     return
 
 
-def save(fd, dominfo, network, live, dst, checkpoint=False, node=-1,sock=None):
+def save(fd, dominfo, network, live, dst, checkpoint=False, node=-1, sock=None, name=None, diskonly=False):
     from xen.xend import XendDomain
 
     try:
@@ -78,6 +78,8 @@ def save(fd, dominfo, network, live, dst
     write_exact(fd, SIGNATURE, "could not write guest state file: signature")
 
     sxprep = dominfo.sxpr()
+    if name:
+        sxprep.append(['snapshotname', name])
 
     if node > -1:
         insert_after(sxprep,'vcpus',['node', str(node)])
@@ -112,68 +114,77 @@ def save(fd, dominfo, network, live, dst
         image_cfg = dominfo.info.get('image', {})
         hvm = dominfo.info.is_hvm()
 
-        # xc_save takes three customization parameters: maxit, max_f, and
-        # flags the last controls whether or not save is 'live', while the
-        # first two further customize behaviour when 'live' save is
-        # enabled. Passing "0" simply uses the defaults compiled into
-        # libxenguest; see the comments and/or code in xc_linux_save() for
-        # more information.
-        max_iters = dominfo.info.get('max_iters', "0")
-        max_factor = dominfo.info.get('max_factor', "0")
-        min_remaining = dominfo.info.get('min_remaining', "0")
-        abort_if_busy = dominfo.info.get('abort_if_busy', "0")
-        log_save_progress = dominfo.info.get('log_save_progress', "0")
-        if max_iters == "None":
-            max_iters = "0"
-        if max_factor == "None":
-            max_factor = "0"
-        if min_remaining == "None":
-            min_remaining = "0"
-        if abort_if_busy == "None":
-            abort_if_busy = "0"
-        if log_save_progress == "None":
-            log_save_progress = "0"
-        cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
-               str(dominfo.getDomid()),
-               max_iters, max_factor, min_remaining,
-               str( int(live) | (int(hvm) << 2) | (int(abort_if_busy) << 5) | (int(log_save_progress) << 6) ) ]
-        log.debug("[xc_save]: %s", string.join(cmd))
-
-        def saveInputHandler(line, tochild):
-            log.debug("In saveInputHandler %s", line)
-            if line == "suspend":
-                log.debug("Suspending %d ...", dominfo.getDomid())
-                dominfo.shutdown('suspend')
-                dominfo.waitForSuspend()
-            if line in ('suspend', 'suspended'):
-                dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
-                                       domain_name)
-                log.info("Domain %d suspended.", dominfo.getDomid())
-                dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
-                                       domain_name)
-                if hvm:
-                    dominfo.image.saveDeviceModel()
-
-            if line == "suspend":
-                tochild.write("done\n")
-                tochild.flush()
-                log.debug('Written done')
-
-        forkHelper(cmd, fd, saveInputHandler, False)
-
-        # put qemu device model state
-        if os.path.exists("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()):
-            write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
-            qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(),
-                              os.O_RDONLY)
-            while True:
-                buf = os.read(qemu_fd, dm_batch)
-                if len(buf):
-                    write_exact(fd, buf, "could not write device model state")
-                else:
-                    break
-            os.close(qemu_fd)
-            os.remove("/var/lib/xen/qemu-save.%d" % dominfo.getDomid())
+        if not diskonly:
+            # xc_save takes three customization parameters: maxit, max_f, and
+            # flags the last controls whether or not save is 'live', while the
+            # first two further customize behaviour when 'live' save is
+            # enabled. Passing "0" simply uses the defaults compiled into
+            # libxenguest; see the comments and/or code in xc_linux_save() for
+            # more information.
+            max_iters = dominfo.info.get('max_iters', "0")
+            max_factor = dominfo.info.get('max_factor', "0")
+            min_remaining = dominfo.info.get('min_remaining', "0")
+            abort_if_busy = dominfo.info.get('abort_if_busy', "0")
+            log_save_progress = dominfo.info.get('log_save_progress', "0")
+            if max_iters == "None":
+                max_iters = "0"
+            if max_factor == "None":
+                max_factor = "0"
+            if min_remaining == "None":
+                min_remaining = "0"
+            if abort_if_busy == "None":
+                abort_if_busy = "0"
+            if log_save_progress == "None":
+                log_save_progress = "0"
+            cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
+                   str(dominfo.getDomid()),
+                   max_iters, max_factor, min_remaining,
+                   str( int(live) | (int(hvm) << 2) | (int(abort_if_busy) << 5) | (int(log_save_progress) << 6) ) ]
+            log.debug("[xc_save]: %s", string.join(cmd))
+
+            def saveInputHandler(line, tochild):
+                log.debug("In saveInputHandler %s", line)
+                if line == "suspend":
+                    log.debug("Suspending %d ...", dominfo.getDomid())
+                    dominfo.shutdown('suspend')
+                    dominfo.waitForSuspend()
+                if line in ('suspend', 'suspended'):
+                    dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
+                                           domain_name)
+                    log.info("Domain %d suspended.", dominfo.getDomid())
+                    dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
+                                           domain_name)
+                    if hvm:
+                        dominfo.image.saveDeviceModel()
+                        if name:
+                            dominfo.image.resumeDeviceModel()
+
+                if line == "suspend":
+                    tochild.write("done\n")
+                    tochild.flush()
+                    log.debug('Written done')
+
+            forkHelper(cmd, fd, saveInputHandler, False)
+
+            # put qemu device model state
+            if os.path.exists("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()):
+                write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
+                qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(),
+                                  os.O_RDONLY)
+                while True:
+                    buf = os.read(qemu_fd, dm_batch)
+                    if len(buf):
+                        write_exact(fd, buf, "could not write device model state")
+                    else:
+                        break
+                os.close(qemu_fd)
+                os.remove("/var/lib/xen/qemu-save.%d" % dominfo.getDomid())
+        else:
+            dominfo.shutdown('suspend')
+            dominfo.waitForShutdown()
+
+        if name:
+            dominfo.image.snapshotDeviceModel(name)
 
         if checkpoint:
             dominfo.resumeDomain()
@@ -247,6 +258,71 @@ def restore(xd, fd, dominfo = None, paus
         if othervm is not None and othervm.domid is not None: 
             raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid))
 
+    def contains_state(fd):
+        try:
+            cur = os.lseek(fd, 0, 1)
+            end = os.lseek(fd, 0, 2)
+
+            ret = False
+            if cur < end:
+                ret = True
+
+            os.lseek(fd, cur, 0)
+            return ret
+        except OSError, (errno, strerr):
+            # lseek failed <==> socket <==> state
+            return True
+
+   #
+   # We shouldn't hold the domains_lock over a waitForDevices
+   # As this function sometime gets called holding this lock,
+   # we must release it and re-acquire it appropriately
+   #
+    def wait_devs(dominfo):
+        from xen.xend import XendDomain
+
+        lock = True;
+        try:
+            XendDomain.instance().domains_lock.release()
+        except:
+            lock = False;
+
+        try:
+            dominfo.waitForDevices() # Wait for backends to set up
+        except Exception, exn:
+            log.exception(exn)
+            if lock:
+                XendDomain.instance().domains_lock.acquire()
+            raise
+
+        if lock:
+            XendDomain.instance().domains_lock.acquire()
+
+
+    if not contains_state(fd):
+        # Disk-only snapshot.  Just start the vm from config (which should
+        # contain snapshotname.
+        if dominfo:
+            log.debug("### starting domain directly through XendDomainInfo")
+            dominfo.start()
+        else:
+            # Warning! Do we need to call into XendDomain to get domain
+            # lock?  Similar to the xd.restore_() call below?
+            # We'll try XendDomain.domain_create()
+            log.debug("### starting domain through XendDomain.create()")
+            dominfo = xd.domain_create(vmconfig)
+
+        try:
+            wait_devs(dominfo)
+        except:
+            dominfo.destroy()
+            raise
+
+        dominfo.unpause()
+
+        # Done if disk only snapshot
+        return dominfo
+
     if dominfo:
         dominfo.resume()
     else:
@@ -348,24 +424,7 @@ def restore(xd, fd, dominfo = None, paus
         
         dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
 
-        #
-        # We shouldn't hold the domains_lock over a waitForDevices
-        # As this function sometime gets called holding this lock,
-        # we must release it and re-acquire it appropriately
-        #
-        from xen.xend import XendDomain
-
-        lock = True;
-        try:
-            XendDomain.instance().domains_lock.release()
-        except:
-            lock = False;
-
-        try:
-            dominfo.waitForDevices() # Wait for backends to set up
-        finally:
-            if lock:
-                XendDomain.instance().domains_lock.acquire()
+        wait_devs(dominfo)
 
         if not paused:
             dominfo.unpause()
Index: xen-4.1.6-testing/tools/python/xen/xend/XendConfig.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/XendConfig.py
+++ xen-4.1.6-testing/tools/python/xen/xend/XendConfig.py
@@ -244,6 +244,7 @@ XENAPI_CFG_TYPES = {
     'memory_sharing': int,
     'pool_name' : str,
     'Description': str,
+    'snapshotname': str,
 }
 
 # List of legacy configuration keys that have no equivalent in the
Index: xen-4.1.6-testing/tools/python/xen/xend/XendDomain.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/XendDomain.py
+++ xen-4.1.6-testing/tools/python/xen/xend/XendDomain.py
@@ -53,6 +53,7 @@ from xen.xend.xenstore.xstransact import
 from xen.xend.xenstore.xswatch import xswatch
 from xen.util import mkdir, rwlock
 from xen.xend import uuid
+from xen.xend import sxp
 
 xc = xen.lowlevel.xc.xc()
 xoptions = XendOptions.instance() 
@@ -319,11 +320,16 @@ class XendDomain:
                 fd, fn = tempfile.mkstemp()
                 f = os.fdopen(fd, 'w+b')
                 try:
+                    snapshotname = ''
+                    if dominfo.info.has_key('snapshotname'):
+                        snapshotname = dominfo.info.pop('snapshotname')
+
                     prettyprint(dominfo.sxpr(legacy_only = False), f,
                                 width = 78)
                 finally:
                     f.close()
-                    
+                    if snapshotname:
+                        dominfo.info['snapshotname'] = snapshotname
                 try:
                     shutil.move(fn, self._managed_config_path(dom_uuid))
                 except:
@@ -1585,6 +1591,187 @@ class XendDomain:
         else:
             log.debug("error: Domain is not running!")
 
+    def domain_snapshot_create(self, domid, name, diskonly=False):
+        """Snapshot a running domain.
+
+        @param domid: Domain ID or Name
+        @type domid: int or string.
+        @param name: Snapshot name
+        @type dst: string
+        @param diskonly: Snapshot disk only - exclude machine state
+        @type dst: bool
+        @rtype: None
+        @raise XendError: Failed to snapshot domain
+        @raise XendInvalidDomain: Domain is not valid
+        """
+        try:
+            dominfo = self.domain_lookup_nr(domid)
+            if not dominfo:
+                raise XendInvalidDomain(str(domid))
+
+            snap_file = os.path.join(xoptions.get_xend_domains_path(),
+                                dominfo.get_uuid(), "snapshots", name)
+
+            if os.access(snap_file, os.F_OK):
+                raise XendError("Snapshot:%s exist for domain %s\n" % (name, str(domid)))
+
+            if dominfo.getDomid() == DOM0_ID:
+                raise XendError("Cannot snapshot privileged domain %s" % str(domid))
+            if dominfo._stateGet() != DOM_STATE_RUNNING:
+                raise VMBadState("Domain is not running",
+                                 POWER_STATE_NAMES[DOM_STATE_RUNNING],
+                                 POWER_STATE_NAMES[dominfo._stateGet()])
+
+            if not os.path.exists(self._managed_config_path(dominfo.get_uuid())):
+                raise XendError("Domain is not managed by Xend lifecycle " +
+                                "support.")
+
+            # Check if all images support snapshots
+            for dev_type, dev_info in dominfo.info.all_devices_sxpr():
+                mode = sxp.child_value(dev_info, 'mode')
+                if mode == 'r':
+                    continue;
+                if dev_type == 'vbd':
+                    raise XendError("All writable images need to use the " +
+                        "tap:qcow2 protocol for snapshot support")
+                if dev_type == 'tap':
+                    # Fetch the protocol name from tap:xyz:filename
+                    type = sxp.child_value(dev_info, 'uname')
+                    type = type.split(':')[1]
+                    if type != 'qcow2':
+                        raise XendError("All writable images need to use the " +
+                            "tap:qcow2 protocol for snapshot support")
+
+            snap_path = os.path.join(xoptions.get_xend_domains_path(),
+                                    dominfo.get_uuid(), "snapshots")
+            mkdir.parents(snap_path, stat.S_IRWXU)
+            snap_file = os.path.join(snap_path, name)
+
+
+            oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
+            if hasattr(os, "O_LARGEFILE"):
+                oflags |= os.O_LARGEFILE
+            fd = os.open(snap_file, oflags)
+            try:
+                XendCheckpoint.save(fd, dominfo, False, False, snap_file,
+                                    True, sock=None, name=name, diskonly=diskonly)
+            except Exception, e:
+                os.close(fd)
+                os.unlink(snap_file)
+                raise e
+            os.close(fd)
+        except OSError, ex:
+            raise XendError("can't write guest state file %s: %s" %
+                            (snap_file, ex[1]))
+
+    def domain_snapshot_list(self, domid):
+        """List available snapshots for a domain.
+
+        @param domid: Domain ID or Name
+        @type domid: int or string.
+        @rtype: list of snapshot names
+        @raise XendInvalidDomain: Domain is not valid
+        """
+        try:
+            dominfo = self.domain_lookup_nr(domid)
+            if not dominfo:
+                raise XendInvalidDomain(str(domid))
+
+            snap_path = os.path.join(xoptions.get_xend_domains_path(),
+                                     dominfo.get_uuid(), "snapshots")
+
+            if not os.access(snap_path, os.R_OK):
+                return []
+
+            return os.listdir(snap_path)
+
+        except:
+            return []
+
+    def domain_snapshot_apply(self, domid, name):
+        """Start a domain from snapshot
+
+        @param domid: Domain ID or Name
+        @type domid: int or string.
+        @param name: Snapshot name
+        @type dst: string
+        @rtype: None
+        @raise XendError: Failed to apply snapshot
+        @raise XendInvalidDomain: Domain is not valid
+        """
+        try:
+            dominfo = self.domain_lookup_nr(domid)
+            if not dominfo:
+                log.debug("## no dominfo")
+                raise XendInvalidDomain(str(domid))
+
+            if dominfo.getDomid() == DOM0_ID:
+                raise XendError("Cannot apply snapshots to  privileged domain %s" % str(domid))
+            if dominfo._stateGet() != DOM_STATE_HALTED:
+                raise VMBadState("Domain is not halted",
+                                 POWER_STATE_NAMES[DOM_STATE_HALTED],
+                                 POWER_STATE_NAMES[dominfo._stateGet()])
+
+            snap_file = os.path.join(xoptions.get_xend_domains_path(),
+                                    dominfo.get_uuid(), "snapshots", name)
+            if not os.access(snap_file, os.R_OK):
+                raise XendError("Unable to access snapshot %s for domain %s" %
+                                (name, str(domid)))
+
+            oflags = os.O_RDONLY
+            if hasattr(os, "O_LARGEFILE"):
+                oflags |= os.O_LARGEFILE
+            fd = os.open(snap_file, oflags)
+            try:
+                self.domain_restore_fd(fd)
+            finally:
+                os.close(fd)
+        except OSError, ex:
+            raise XendError("Unable to read snapshot file file %s: %s" %
+                            (snap_file, ex[1]))
+
+    def domain_snapshot_delete(self, domid, name):
+        """Delete domain snapshot
+
+        @param domid: Domain ID or Name
+        @type domid: int or string.
+        @param name: Snapshot name
+        @type domid: string
+        @rtype: None
+        @raise XendInvalidDomain: Domain is not valid
+        """
+        dominfo = self.domain_lookup_nr(domid)
+        if not dominfo:
+            raise XendInvalidDomain(str(domid))
+
+        snap_file = os.path.join(xoptions.get_xend_domains_path(),
+                                 dominfo.get_uuid(), "snapshots", name)
+
+        if not os.access(snap_file, os.F_OK):
+            raise XendError("Snapshot %s does not exist for domain %s" %
+                            (name, str(domid)))
+
+        # Need to "remove" snapshot from qcow2 image file.
+        # For running domains, this is left to ioemu. For stopped domains
+        # we must invoke qemu-img for all devices ourselves
+        if dominfo._stateGet() != DOM_STATE_HALTED:
+            dominfo.image.signalDeviceModel("snapshot-delete",
+                "snapshot-deleted", name)
+        else:
+            for dev_type, dev_info in dominfo.info.all_devices_sxpr():
+                if dev_type != 'tap':
+                    continue
+
+                # Fetch the filename and strip off tap:xyz:
+                image_file = sxp.child_value(dev_info, 'uname')
+                image_file = image_file.split(':')[2]
+
+                os.system("qemu-img-xen snapshot -d %s %s" %
+                    (name, image_file));
+
+
+        os.unlink(snap_file)
+
     def domain_pincpu(self, domid, vcpu, cpumap):
         """Set which cpus vcpu can use
 
Index: xen-4.1.6-testing/tools/python/xen/xend/XendDomainInfo.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/XendDomainInfo.py
+++ xen-4.1.6-testing/tools/python/xen/xend/XendDomainInfo.py
@@ -508,8 +508,6 @@ class XendDomainInfo:
                 self._setSchedParams()
                 self._storeVmDetails()
                 self._createChannels()
-                self._createDevices()
-                self._storeDomDetails()
                 self._endRestore()
             except:
                 log.exception('VM resume failed')
@@ -2387,7 +2385,7 @@ class XendDomainInfo:
         return self.getDeviceController(deviceClass).reconfigureDevice(
             devid, devconfig)
 
-    def _createDevices(self):
+    def _createDevices(self, resume = False):
         """Create the devices for a vm.
 
         @raise: VmError for invalid devices
@@ -2436,7 +2434,7 @@ class XendDomainInfo:
 
 
         if self.image:
-            self.image.createDeviceModel()
+            self.image.createDeviceModel(resume)
 
         #if have pass-through devs, need the virtual pci slots info from qemu
         self.pci_device_configure_boot()
@@ -3066,7 +3064,7 @@ class XendDomainInfo:
         self._introduceDomain()
         self.image = image.create(self, self.info)
         if self.image:
-            self.image.createDeviceModel(True)
+            self._createDevices(True)
         self._storeDomDetails()
         self._registerWatches()
         self.refreshShutdown()
Index: xen-4.1.6-testing/tools/python/xen/xend/image.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/image.py
+++ xen-4.1.6-testing/tools/python/xen/xend/image.py
@@ -490,7 +490,7 @@ class ImageHandler:
 
             domains.domains_lock.acquire()
 
-    def signalDeviceModel(self, cmd, ret, par = None):
+    def signalDeviceModel(self, cmd, ret, par = None, timeout = True):
         if self.device_model is None:
             return
         # Signal the device model to for action
@@ -527,10 +527,17 @@ class ImageHandler:
         while state != ret:
             state = xstransact.Read("/local/domain/0/device-model/%i/state"
                                     % self.vm.getDomid())
+            if state == 'error':
+                msg = ("The device model returned an error: %s"
+                      % xstransact.Read("/local/domain/0/device-model/%i/error"
+                                        % self.vm.getDomid()))
+                raise VmError(msg)
+
             time.sleep(0.1)
-            count += 1
-            if count > 100:
-                raise VmError('Timed out waiting for device model action')
+            if timeout:
+                count += 1
+                if count > 100:
+                    raise VmError('Timed out waiting for device model action')
 
         #resotre orig state
         xstransact.Store("/local/domain/0/device-model/%i"
@@ -555,6 +562,10 @@ class ImageHandler:
         except:
             pass
 
+    def snapshotDeviceModel(self, name):
+        # Signal the device model to perform snapshot operation
+        self.signalDeviceModel('snapshot', 'paused', name, False)
+
     def recreate(self):
         if self.device_model is None:
             return
Index: xen-4.1.6-testing/tools/python/xen/xend/server/SrvDomain.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/server/SrvDomain.py
+++ xen-4.1.6-testing/tools/python/xen/xend/server/SrvDomain.py
@@ -95,6 +95,31 @@ class SrvDomain(SrvDir):
     def do_save(self, _, req):
         return self.xd.domain_save(self.dom.domid, req.args['file'][0])
 
+    def op_snapshot_create(self, op, req):
+        self.acceptCommand(req)
+        return req.threadRequest(self.do_snapshot_create, op, req)
+
+    def do_snapshot_create(self, _, req):
+        return self.xd.domain_snapshot_create(self.dom.domid, req.args['name'][0])
+
+    def op_snapshot_list(self, op, req):
+        self.acceptCommand(req)
+        return self.xd.domain_snapshot_list(self.dom.getName())
+
+    def op_snapshot_apply(self, op, req):
+        self.acceptCommand(req)
+        return req.threadRequest(self.do_snapshot_apply, op, req)
+
+    def do_snapshot_apply(self, _, req):
+        return self.xd.domain_snapshot_apply(self.dom.getName(), req.args['name'][0])
+
+    def op_snapshot_delete(self, op, req):
+        self.acceptCommand(req)
+        return req.threadRequest(self.do_snapshot_delete, op, req)
+
+    def do_snapshot_delete(self, _, req):
+        return self.xd.domain_snapshot_delete(self.dom.getName(), req.args['name'][0])
+
     def op_dump(self, op, req):
         self.acceptCommand(req)
         return req.threadRequest(self.do_dump, op, req)
@@ -273,7 +298,7 @@ class SrvDomain(SrvDir):
     def render_GET(self, req):
         op = req.args.get('op')
 
-        if op and op[0] in ['vcpuinfo']:
+        if op and op[0] in ['vcpuinfo', 'snapshot_list']:
             return self.perform(req)
 
         #
Index: xen-4.1.6-testing/tools/python/xen/xend/server/blkif.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xend/server/blkif.py
+++ xen-4.1.6-testing/tools/python/xen/xend/server/blkif.py
@@ -88,6 +88,10 @@ class BlkifController(DevController):
         if bootable != None:
             back['bootable'] = str(bootable)
 
+        if 'snapshotname' in self.vm.info:
+            back['snapshot'] = self.vm.info['snapshotname']
+            self.vm.info.pop('snapshotname')
+
         if security.on() == xsconstants.XS_POLICY_USE:
             self.do_access_control(config, uname)
 
Index: xen-4.1.6-testing/tools/python/xen/xm/main.py
===================================================================
--- xen-4.1.6-testing.orig/tools/python/xen/xm/main.py
+++ xen-4.1.6-testing/tools/python/xen/xm/main.py
@@ -123,6 +123,14 @@ SUBCOMMAND_HELP = {
                      'Restore a domain from a saved state.'),
     'save'        : ('[-c|-f] <Domain> <CheckpointFile>',
                      'Save a domain state to restore later.'),
+    'snapshot-create' : ('[-d] <Domain> <SnapshotName>',
+                         'Snapshot a running domain.'),
+    'snapshot-list'   : ('<Domain>',
+                         'List available snapshots for a domain.'),
+    'snapshot-apply'  : ('<Domain> <SnapshotName>',
+                         'Apply previous snapshot to domain.'),
+    'snapshot-delete' : ('<Domain> <SnapshotName>',
+                         'Delete snapshot of domain.'),
     'shutdown'    : ('<Domain> [-waRH]', 'Shutdown a domain.'),
     'top'         : ('', 'Monitor a host and the domains in real time.'),
     'unpause'     : ('<Domain>', 'Unpause a paused domain.'),
@@ -344,6 +352,9 @@ SUBCOMMAND_OPTIONS = {
        ('-c', '--checkpoint', 'Leave domain running after creating snapshot'),
        ('-f', '--force', 'Force to overwrite exist file'),
     ),
+    'snapshot-create': (
+       ('-d', '--diskonly', 'Perform disk only snapshot of domain'),
+    ),
     'restore': (
        ('-p', '--paused', 'Do not unpause domain after restoring it'),
     ),
@@ -395,6 +406,10 @@ common_commands = [
     "restore",
     "resume",
     "save",
+    "snapshot-create",
+    "snapshot-list",
+    "snapshot-apply",
+    "snapshot-delete",
     "shell",
     "shutdown",
     "start",
@@ -429,6 +444,10 @@ domain_commands = [
     "restore",
     "resume",
     "save",
+    "snapshot-create",
+    "snapshot-list",
+    "snapshot-apply",
+    "snapshot-delete",
     "shutdown",
     "start",
     "suspend",
@@ -863,6 +882,62 @@ def xm_event_monitor(args):
 #
 #########################################################################
 
+def xm_snapshot_create(args):
+
+    arg_check(args, "snapshot-create", 2, 3)
+
+    try:
+        (options, params) = getopt.gnu_getopt(args, 'd', ['diskonly'])
+    except getopt.GetoptError, opterr:
+        err(opterr)
+        sys.exit(1)
+
+    diskonly = False
+    for (k, v) in options:
+        if k in ['-d', '--diskonly']:
+            diskonly = True
+
+    if len(params) != 2:
+        err("Wrong number of parameters")
+        usage('snapshot-create')
+
+    if serverType == SERVER_XEN_API:
+        server.xenapi.VM.snapshot_create(get_single_vm(params[0]), params[1], diskonly)
+    else:
+        server.xend.domain.snapshot_create(params[0], params[1], diskonly)
+
+def xm_snapshot_list(args):
+    arg_check(args, "snapshot-list", 1, 2)
+
+    snapshots = None
+    if serverType == SERVER_XEN_API:
+        snapshots = server.xenapi.VM.snapshot_list(get_single_vm(args[0]))
+    else:
+        snapshots = server.xend.domain.snapshot_list(args[0])
+
+    if snapshots:
+        print "Available snapshots for domain %s" % args[0]
+        for snapshot in snapshots:
+            print "  %s" % snapshot
+    else:
+        print "No snapshot available for domain %s" % args[0]
+
+def xm_snapshot_apply(args):
+    arg_check(args, "snapshot-apply", 2, 3)
+
+    if serverType == SERVER_XEN_API:
+        server.xenapi.VM.snapshot_apply(get_single_vm(args[0]), args[1])
+    else:
+        server.xend.domain.snapshot_apply(args[0], args[1])
+
+def xm_snapshot_delete(args):
+    arg_check(args, "snapshot-delete", 2, 3)
+
+    if serverType == SERVER_XEN_API:
+        server.xenapi.VM.snapshot_delete(get_single_vm(args[0]), args[1])
+    else:
+        server.xend.domain.snapshot_delete(args[0], args[1])
+
 def xm_save(args):
 
     arg_check(args, "save", 2, 4)
@@ -3827,6 +3902,10 @@ commands = {
     "restore": xm_restore,
     "resume": xm_resume,
     "save": xm_save,
+    "snapshot-create": xm_snapshot_create,
+    "snapshot-list": xm_snapshot_list,
+    "snapshot-apply": xm_snapshot_apply,
+    "snapshot-delete": xm_snapshot_delete,
     "shutdown": xm_shutdown,
     "start": xm_start,
     "sysrq": xm_sysrq,
