xen/snapshot-xend.patch
Charles Arnold 4b4fa7f68d - bnc#603583 - Fix migration of domUs using tapdisk devices
21317-xend-blkif-util-tap2.patch
  suse-disable-tap2-default.patch

- Match upstreams cpu pools switch from domctl to sysctl
- Upstream replacements for two of our custom patches (to ease
  applying further backports)
- Fixed dump-exec-state.patch (could previously hang the system, as
  could - with lower probability - the un-patched implementation)

- bnc#593536 - xen hypervisor takes very long to initialize Dom0 on
  128 CPUs and 256Gb
  21272-x86-dom0-alloc-performance.patch
  21266-vmx-disabled-check.patch
  21271-x86-cache-flush-global.patch

- bnc#558815 - using multiple npiv luns with same wwpn/wwnn broken
- bnc#601104 - Xen /etc/xen/scripts/block-npiv script fails when
  accessing multiple disks using NPIV
  block-npiv

- bnc#595124 - VT-d can not be enabled on 32PAE Xen on Nehalem-EX
  platform
  21234-x86-bad-srat-clear-pxm2node.patch
  bnc#585371 - kdump fails to load with xen: locate_hole failed
  21235-crashkernel-advanced.patch

- bnc#588918 - Attaching a U-disk to domain's failed by
  "xm usb-attach"
  init.xend

OBS-URL: https://build.opensuse.org/package/show/Virtualization/xen?expand=0&rev=44
2010-05-07 19:40:54 +00:00

661 lines
25 KiB
Diff

Index: xen-4.0.0-testing/tools/python/xen/xend/image.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/image.py
+++ xen-4.0.0-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.0.0-testing/tools/python/xen/xend/server/blkif.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/server/blkif.py
+++ xen-4.0.0-testing/tools/python/xen/xend/server/blkif.py
@@ -88,6 +88,9 @@ class BlkifController(DevController):
if bootable != None:
back['bootable'] = str(bootable)
+ if 'snapshotname' in self.vm.info:
+ back['snapshot'] = self.vm.info['snapshotname']
+
if security.on() == xsconstants.XS_POLICY_USE:
self.do_access_control(config, uname)
Index: xen-4.0.0-testing/tools/python/xen/xend/server/SrvDomain.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/server/SrvDomain.py
+++ xen-4.0.0-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)
@@ -245,7 +270,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.0.0-testing/tools/python/xen/xend/XendCheckpoint.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/XendCheckpoint.py
+++ xen-4.0.0-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):
+def save(fd, dominfo, network, live, dst, checkpoint=False, node=-1, name=None, diskonly=False):
from xen.xend import XendDomain
try:
@@ -112,52 +112,61 @@ 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.
- cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
- str(dominfo.getDomid()), "0", "0",
- str(int(live) | (int(hvm) << 2)) ]
- 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.
+ cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
+ str(dominfo.getDomid()), "0", "0",
+ str(int(live) | (int(hvm) << 2)) ]
+ 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()
@@ -221,6 +230,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:
@@ -329,24 +403,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.0.0-testing/tools/python/xen/xend/XendConfig.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/XendConfig.py
+++ xen-4.0.0-testing/tools/python/xen/xend/XendConfig.py
@@ -236,6 +236,7 @@ XENAPI_CFG_TYPES = {
'memory_sharing': int,
'Description': str,
'pool_name' : str,
+ 'snapshotname': str,
}
# List of legacy configuration keys that have no equivalent in the
Index: xen-4.0.0-testing/tools/python/xen/xend/XendDomain.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xend/XendDomain.py
+++ xen-4.0.0-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()
@@ -1564,6 +1565,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, 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.0.0-testing/tools/python/xen/xm/main.py
===================================================================
--- xen-4.0.0-testing.orig/tools/python/xen/xm/main.py
+++ xen-4.0.0-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.'),
@@ -335,6 +343,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'),
),
@@ -385,6 +396,10 @@ common_commands = [
"restore",
"resume",
"save",
+ "snapshot-create",
+ "snapshot-list",
+ "snapshot-apply",
+ "snapshot-delete",
"shell",
"shutdown",
"start",
@@ -418,6 +433,10 @@ domain_commands = [
"restore",
"resume",
"save",
+ "snapshot-create",
+ "snapshot-list",
+ "snapshot-apply",
+ "snapshot-delete",
"shutdown",
"start",
"suspend",
@@ -850,6 +869,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)
@@ -3696,6 +3771,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,