2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/image.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/image.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/image.py
|
|
|
|
@@ -445,7 +445,7 @@ class ImageHandler:
|
2009-05-04 18:38:09 +02:00
|
|
|
# have a callback but sadly we don't have Twisted in xend
|
|
|
|
self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
|
|
|
|
|
|
|
|
- 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
|
2009-08-01 11:53:46 +02:00
|
|
|
@@ -468,10 +468,17 @@ class ImageHandler:
|
2008-11-21 15:57:01 +01:00
|
|
|
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)
|
2009-05-04 18:38:09 +02:00
|
|
|
- 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"
|
2009-08-01 11:53:46 +02:00
|
|
|
@@ -496,6 +503,10 @@ class ImageHandler:
|
2009-05-04 18:38:09 +02:00
|
|
|
except:
|
|
|
|
pass
|
2008-07-19 01:04:37 +02:00
|
|
|
|
|
|
|
+ def snapshotDeviceModel(self, name):
|
|
|
|
+ # Signal the device model to perform snapshot operation
|
2009-05-04 18:38:09 +02:00
|
|
|
+ self.signalDeviceModel('snapshot', 'paused', name, False)
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
def recreate(self):
|
|
|
|
if self.device_model is None:
|
|
|
|
return
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/server/blkif.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/server/blkif.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/server/blkif.py
|
2009-05-04 18:38:09 +02:00
|
|
|
@@ -88,6 +88,9 @@ class BlkifController(DevController):
|
|
|
|
if bootable != None:
|
|
|
|
back['bootable'] = str(bootable)
|
2008-07-19 01:04:37 +02:00
|
|
|
|
|
|
|
+ if 'snapshotname' in self.vm.info:
|
|
|
|
+ back['snapshot'] = self.vm.info['snapshotname']
|
|
|
|
+
|
2009-05-04 18:38:09 +02:00
|
|
|
if security.on() == xsconstants.XS_POLICY_USE:
|
2008-07-19 01:04:37 +02:00
|
|
|
self.do_access_control(config, uname)
|
|
|
|
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/server/SrvDomain.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/server/SrvDomain.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/server/SrvDomain.py
|
2008-09-12 17:57:53 +02:00
|
|
|
@@ -95,6 +95,31 @@ class SrvDomain(SrvDir):
|
2008-07-19 01:04:37 +02:00
|
|
|
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):
|
2008-09-12 17:57:53 +02:00
|
|
|
+ return self.xd.domain_snapshot_create(self.dom.domid, req.args['name'][0])
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ def op_snapshot_list(self, op, req):
|
|
|
|
+ self.acceptCommand(req)
|
2008-09-12 17:57:53 +02:00
|
|
|
+ return self.xd.domain_snapshot_list(self.dom.getName())
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ 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):
|
2008-09-12 17:57:53 +02:00
|
|
|
+ return self.xd.domain_snapshot_apply(self.dom.getName(), req.args['name'][0])
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ 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):
|
2008-09-12 17:57:53 +02:00
|
|
|
+ return self.xd.domain_snapshot_delete(self.dom.getName(), req.args['name'][0])
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
def op_dump(self, op, req):
|
|
|
|
self.acceptCommand(req)
|
|
|
|
return req.threadRequest(self.do_dump, op, req)
|
2009-05-04 18:38:09 +02:00
|
|
|
@@ -231,7 +256,7 @@ class SrvDomain(SrvDir):
|
2008-09-12 17:57:53 +02:00
|
|
|
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)
|
|
|
|
|
|
|
|
#
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/XendCheckpoint.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/XendCheckpoint.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/XendCheckpoint.py
|
2009-05-04 18:38:09 +02:00
|
|
|
@@ -65,7 +65,7 @@ def insert_after(list, pred, value):
|
2008-07-19 01:04:37 +02:00
|
|
|
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):
|
2009-05-04 18:38:09 +02:00
|
|
|
try:
|
|
|
|
if not os.path.isdir("/var/lib/xen"):
|
|
|
|
os.makedirs("/var/lib/xen")
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -98,52 +98,61 @@ def save(fd, dominfo, network, live, dst
|
2008-07-19 01:04:37 +02:00
|
|
|
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')
|
2009-05-04 18:38:09 +02:00
|
|
|
- dominfo.waitForSuspend()
|
2008-07-19 01:04:37 +02:00
|
|
|
- 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),
|
2009-05-04 18:38:09 +02:00
|
|
|
+ str(dominfo.getDomid()), "0", "0",
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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')
|
2009-05-04 18:38:09 +02:00
|
|
|
+ dominfo.waitForSuspend()
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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()
|
2009-06-12 20:43:24 +02:00
|
|
|
+ if name:
|
2009-08-01 11:53:46 +02:00
|
|
|
+ dominfo.image.resumeDeviceModel()
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ 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()
|
2008-11-21 15:57:01 +01:00
|
|
|
+
|
|
|
|
+ if name:
|
2008-07-19 01:04:37 +02:00
|
|
|
+ dominfo.image.snapshotDeviceModel(name)
|
|
|
|
|
|
|
|
if checkpoint:
|
|
|
|
dominfo.resumeDomain()
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -207,6 +216,71 @@ def restore(xd, fd, dominfo = None, paus
|
2008-07-19 01:04:37 +02:00
|
|
|
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):
|
2008-11-08 20:32:12 +01:00
|
|
|
+ try:
|
|
|
|
+ cur = os.lseek(fd, 0, 1)
|
|
|
|
+ end = os.lseek(fd, 0, 2)
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
2008-11-08 20:32:12 +01:00
|
|
|
+ ret = False
|
|
|
|
+ if cur < end:
|
|
|
|
+ ret = True
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-11-08 20:32:12 +01:00
|
|
|
+ os.lseek(fd, cur, 0)
|
|
|
|
+ return ret
|
|
|
|
+ except OSError, (errno, strerr):
|
|
|
|
+ # lseek failed <==> socket <==> state
|
|
|
|
+ return True
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ #
|
|
|
|
+ # 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)
|
2009-05-04 18:38:09 +02:00
|
|
|
+ if lock:
|
|
|
|
+ XendDomain.instance().domains_lock.acquire()
|
|
|
|
+ raise
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ if lock:
|
|
|
|
+ XendDomain.instance().domains_lock.acquire()
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
+ 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)
|
|
|
|
+
|
2009-05-04 18:38:09 +02:00
|
|
|
+ try:
|
|
|
|
+ wait_devs(dominfo)
|
|
|
|
+ except:
|
|
|
|
+ dominfo.destroy()
|
|
|
|
+ raise
|
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ dominfo.unpause()
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ # Done if disk only snapshot
|
|
|
|
+ return dominfo
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
if dominfo:
|
|
|
|
dominfo.resume()
|
|
|
|
else:
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -322,26 +396,7 @@ def restore(xd, fd, dominfo = None, paus
|
2008-07-19 01:04:37 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
- except Exception, exn:
|
|
|
|
- log.exception(exn)
|
|
|
|
-
|
|
|
|
- if lock:
|
|
|
|
- XendDomain.instance().domains_lock.acquire()
|
|
|
|
+ wait_devs(dominfo)
|
2009-05-04 18:38:09 +02:00
|
|
|
|
2008-07-19 01:04:37 +02:00
|
|
|
if not paused:
|
|
|
|
dominfo.unpause()
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/XendConfig.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/XendConfig.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/XendConfig.py
|
2009-05-04 18:38:09 +02:00
|
|
|
@@ -220,6 +220,7 @@ XENAPI_CFG_TYPES = {
|
2008-08-18 00:24:29 +02:00
|
|
|
'machine_address_size': int,
|
2008-10-26 01:33:59 +02:00
|
|
|
'suppress_spurious_page_faults': bool0,
|
2009-05-04 18:38:09 +02:00
|
|
|
's3_integrity' : int,
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 'snapshotname': str,
|
|
|
|
}
|
|
|
|
|
|
|
|
# List of legacy configuration keys that have no equivalent in the
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xend/XendDomain.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xend/XendDomain.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xend/XendDomain.py
|
2008-09-01 02:39:12 +02:00
|
|
|
@@ -52,6 +52,7 @@ from xen.xend.xenstore.xstransact import
|
|
|
|
from xen.xend.xenstore.xswatch import xswatch
|
2009-05-04 18:38:09 +02:00
|
|
|
from xen.util import mkdir, rwlock
|
2008-09-01 02:39:12 +02:00
|
|
|
from xen.xend import uuid
|
|
|
|
+from xen.xend import sxp
|
|
|
|
|
|
|
|
xc = xen.lowlevel.xc.xc()
|
|
|
|
xoptions = XendOptions.instance()
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -1421,6 +1422,187 @@ class XendDomain:
|
2008-07-19 01:04:37 +02:00
|
|
|
raise XendError("can't write guest state file %s: %s" %
|
|
|
|
(dst, ex[1]))
|
|
|
|
|
|
|
|
+ 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
|
2009-05-04 18:38:09 +02:00
|
|
|
+ @raise XendInvalidDomain: Domain is not valid
|
2008-07-19 01:04:37 +02:00
|
|
|
+ """
|
|
|
|
+ try:
|
|
|
|
+ dominfo = self.domain_lookup_nr(domid)
|
|
|
|
+ if not dominfo:
|
|
|
|
+ raise XendInvalidDomain(str(domid))
|
|
|
|
+
|
2009-06-12 20:43:24 +02:00
|
|
|
+ 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)))
|
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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()])
|
|
|
|
+
|
2008-09-01 02:39:12 +02:00
|
|
|
+ if not os.path.exists(self._managed_config_path(dominfo.get_uuid())):
|
|
|
|
+ raise XendError("Domain is not managed by Xend lifecycle " +
|
|
|
|
+ "support.")
|
|
|
|
+
|
2008-11-21 15:57:01 +01:00
|
|
|
+ # 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")
|
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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)
|
2008-11-21 15:57:01 +01:00
|
|
|
+ os.unlink(snap_file)
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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
|
2009-05-04 18:38:09 +02:00
|
|
|
+ @raise XendInvalidDomain: Domain is not valid
|
2008-07-19 01:04:37 +02:00
|
|
|
+ """
|
|
|
|
+ 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 []
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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
|
2009-05-04 18:38:09 +02:00
|
|
|
+ @raise XendInvalidDomain: Domain is not valid
|
2008-07-19 01:04:37 +02:00
|
|
|
+ """
|
|
|
|
+ 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
|
2009-05-04 18:38:09 +02:00
|
|
|
+ @raise XendInvalidDomain: Domain is not valid
|
2008-07-19 01:04:37 +02:00
|
|
|
+ """
|
2008-09-01 02:39:12 +02:00
|
|
|
+ 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
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
2008-09-01 02:39:12 +02:00
|
|
|
+ # Fetch the filename and strip off tap:xyz:
|
|
|
|
+ image_file = sxp.child_value(dev_info, 'uname')
|
|
|
|
+ image_file = image_file.split(':')[2]
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
2008-09-01 02:39:12 +02:00
|
|
|
+ os.system("qemu-img-xen snapshot -d %s %s" %
|
|
|
|
+ (name, image_file));
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
2008-09-01 02:39:12 +02:00
|
|
|
+
|
|
|
|
+ os.unlink(snap_file)
|
2008-07-19 01:04:37 +02:00
|
|
|
+
|
|
|
|
def domain_pincpu(self, domid, vcpu, cpumap):
|
|
|
|
"""Set which cpus vcpu can use
|
|
|
|
|
2009-08-01 11:53:46 +02:00
|
|
|
Index: xen-3.4.1-testing/tools/python/xen/xm/main.py
|
2008-07-19 01:04:37 +02:00
|
|
|
===================================================================
|
2009-08-01 11:53:46 +02:00
|
|
|
--- xen-3.4.1-testing.orig/tools/python/xen/xm/main.py
|
|
|
|
+++ xen-3.4.1-testing/tools/python/xen/xm/main.py
|
2009-05-04 18:38:09 +02:00
|
|
|
@@ -120,6 +120,14 @@ SUBCOMMAND_HELP = {
|
2008-07-19 01:04:37 +02:00
|
|
|
'Restore a domain from a saved state.'),
|
2009-06-12 20:43:24 +02:00
|
|
|
'save' : ('[-c|-f] <Domain> <CheckpointFile>',
|
2008-07-19 01:04:37 +02:00
|
|
|
'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.'),
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -277,6 +285,9 @@ SUBCOMMAND_OPTIONS = {
|
2008-07-19 01:04:37 +02:00
|
|
|
('-c', '--checkpoint', 'Leave domain running after creating snapshot'),
|
2009-06-12 20:43:24 +02:00
|
|
|
('-f', '--force', 'Force to overwrite exist file'),
|
2008-07-19 01:04:37 +02:00
|
|
|
),
|
|
|
|
+ 'snapshot-create': (
|
|
|
|
+ ('-d', '--diskonly', 'Perform disk only snapshot of domain'),
|
|
|
|
+ ),
|
|
|
|
'restore': (
|
|
|
|
('-p', '--paused', 'Do not unpause domain after restoring it'),
|
|
|
|
),
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -303,6 +314,10 @@ common_commands = [
|
2008-07-19 01:04:37 +02:00
|
|
|
"restore",
|
|
|
|
"resume",
|
|
|
|
"save",
|
|
|
|
+ "snapshot-create",
|
|
|
|
+ "snapshot-list",
|
|
|
|
+ "snapshot-apply",
|
|
|
|
+ "snapshot-delete",
|
|
|
|
"shell",
|
|
|
|
"shutdown",
|
|
|
|
"start",
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -334,6 +349,10 @@ domain_commands = [
|
2008-07-19 01:04:37 +02:00
|
|
|
"restore",
|
|
|
|
"resume",
|
|
|
|
"save",
|
|
|
|
+ "snapshot-create",
|
|
|
|
+ "snapshot-list",
|
|
|
|
+ "snapshot-apply",
|
|
|
|
+ "snapshot-delete",
|
|
|
|
"shutdown",
|
|
|
|
"start",
|
|
|
|
"suspend",
|
2009-06-12 20:43:24 +02:00
|
|
|
@@ -725,6 +744,62 @@ def xm_event_monitor(args):
|
2008-07-19 01:04:37 +02:00
|
|
|
#
|
|
|
|
#########################################################################
|
|
|
|
|
|
|
|
+def xm_snapshot_create(args):
|
|
|
|
+
|
|
|
|
+ arg_check(args, "snapshot-create", 2, 3)
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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')
|
|
|
|
+
|
2009-05-04 18:38:09 +02:00
|
|
|
+ if serverType == SERVER_XEN_API:
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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
|
2009-05-04 18:38:09 +02:00
|
|
|
+ if serverType == SERVER_XEN_API:
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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)
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
|
|
|
+ if serverType == SERVER_XEN_API:
|
2008-07-19 01:04:37 +02:00
|
|
|
+ server.xenapi.VM.snapshot_apply(get_single_vm(args[0]), args[1])
|
|
|
|
+ else:
|
|
|
|
+ server.xend.domain.snapshot_apply(args[0], args[1])
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
2008-07-19 01:04:37 +02:00
|
|
|
+def xm_snapshot_delete(args):
|
|
|
|
+ arg_check(args, "snapshot-delete", 2, 3)
|
2009-05-04 18:38:09 +02:00
|
|
|
+
|
|
|
|
+ if serverType == SERVER_XEN_API:
|
2008-07-19 01:04:37 +02:00
|
|
|
+ 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):
|
|
|
|
|
2009-06-12 20:43:24 +02:00
|
|
|
arg_check(args, "save", 2, 4)
|
|
|
|
@@ -2857,6 +2932,10 @@ commands = {
|
2008-07-19 01:04:37 +02:00
|
|
|
"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,
|