ia64/xen-unstable

view tools/python/xen/xend/XendCheckpoint.py @ 11614:fd13d7150dd6

[TOOLS] Wait for backend devices to set up before resuming execution
of a restored guest.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Mon Sep 25 09:11:52 2006 +0100 (2006-09-25)
parents 2d2f3b824fc3
children 6ffb8705f894
line source
1 # Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
2 # Copyright (C) 2005 XenSource Ltd
4 # This file is subject to the terms and conditions of the GNU General
5 # Public License. See the file "COPYING" in the main directory of
6 # this archive for more details.
8 import os
9 import re
10 import string
11 import sxp
12 import threading
13 from struct import pack, unpack, calcsize
15 from xen.util.xpopen import xPopen3
17 import xen.util.auxbin
19 import xen.lowlevel.xc
21 import balloon
22 from XendError import XendError
23 from XendLogging import log
24 from XendDomainInfo import DEV_MIGRATE_STEP1, DEV_MIGRATE_STEP2
25 from XendDomainInfo import DEV_MIGRATE_STEP3
27 SIGNATURE = "LinuxGuestRecord"
28 XC_SAVE = "xc_save"
29 XC_RESTORE = "xc_restore"
32 sizeof_int = calcsize("i")
33 sizeof_unsigned_long = calcsize("L")
36 xc = xen.lowlevel.xc.xc()
39 def write_exact(fd, buf, errmsg):
40 if os.write(fd, buf) != len(buf):
41 raise XendError(errmsg)
43 def read_exact(fd, size, errmsg):
44 buf = ''
45 while size != 0:
46 str = os.read(fd, size)
47 if not len(str):
48 log.error("read_exact: EOF trying to read %d (buf='%s')" % \
49 (size, buf))
50 raise XendError(errmsg)
51 size = size - len(str)
52 buf = buf + str
53 return buf
57 def save(fd, dominfo, network, live, dst):
58 write_exact(fd, SIGNATURE, "could not write guest state file: signature")
60 config = sxp.to_string(dominfo.sxpr())
62 domain_name = dominfo.getName()
63 # Rename the domain temporarily, so that we don't get a name clash if this
64 # domain is migrating (live or non-live) to the local host. Doing such a
65 # thing is useful for debugging.
66 dominfo.setName('migrating-' + domain_name)
68 try:
69 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name)
71 write_exact(fd, pack("!i", len(config)),
72 "could not write guest state file: config len")
73 write_exact(fd, config, "could not write guest state file: config")
75 # xc_save takes three customization parameters: maxit, max_f, and
76 # flags the last controls whether or not save is 'live', while the
77 # first two further customize behaviour when 'live' save is
78 # enabled. Passing "0" simply uses the defaults compiled into
79 # libxenguest; see the comments and/or code in xc_linux_save() for
80 # more information.
81 cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
82 str(dominfo.getDomid()), "0", "0", str(int(live)) ]
83 log.debug("[xc_save]: %s", string.join(cmd))
85 def saveInputHandler(line, tochild):
86 log.debug("In saveInputHandler %s", line)
87 if line == "suspend":
88 log.debug("Suspending %d ...", dominfo.getDomid())
89 dominfo.shutdown('suspend')
90 dominfo.waitForShutdown()
91 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
92 domain_name)
93 log.info("Domain %d suspended.", dominfo.getDomid())
94 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
95 domain_name)
96 tochild.write("done\n")
97 tochild.flush()
98 log.debug('Written done')
100 forkHelper(cmd, fd, saveInputHandler, False)
102 dominfo.destroyDomain()
104 except Exception, exn:
105 log.exception("Save failed on domain %s (%d).", domain_name,
106 dominfo.getDomid())
107 try:
108 dominfo.setName(domain_name)
109 except:
110 log.exception("Failed to reset the migrating domain's name")
111 raise Exception, exn
114 def restore(xd, fd):
115 signature = read_exact(fd, len(SIGNATURE),
116 "not a valid guest state file: signature read")
117 if signature != SIGNATURE:
118 raise XendError("not a valid guest state file: found '%s'" %
119 signature)
121 l = read_exact(fd, sizeof_int,
122 "not a valid guest state file: config size read")
123 vmconfig_size = unpack("!i", l)[0]
124 vmconfig_buf = read_exact(fd, vmconfig_size,
125 "not a valid guest state file: config read")
127 p = sxp.Parser()
128 p.input(vmconfig_buf)
129 if not p.ready:
130 raise XendError("not a valid guest state file: config parse")
132 vmconfig = p.get_val()
134 dominfo = xd.restore_(vmconfig)
136 store_port = dominfo.getStorePort()
137 console_port = dominfo.getConsolePort()
139 assert store_port
140 assert console_port
142 try:
143 l = read_exact(fd, sizeof_unsigned_long,
144 "not a valid guest state file: pfn count read")
145 nr_pfns = unpack("L", l)[0] # native sizeof long
146 if nr_pfns > 16*1024*1024: # XXX
147 raise XendError(
148 "not a valid guest state file: pfn count out of range")
150 balloon.free(xc.pages_to_kib(nr_pfns))
152 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
153 fd, dominfo.getDomid(), nr_pfns,
154 store_port, console_port])
155 log.debug("[xc_restore]: %s", string.join(cmd))
157 handler = RestoreInputHandler()
159 forkHelper(cmd, fd, handler.handler, True)
161 if handler.store_mfn is None or handler.console_mfn is None:
162 raise XendError('Could not read store/console MFN')
164 os.read(fd, 1) # Wait for source to close connection
165 dominfo.waitForDevices() # Wait for backends to set up
166 dominfo.unpause()
168 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
170 return dominfo
171 except:
172 dominfo.destroy()
173 raise
176 class RestoreInputHandler:
177 def __init__(self):
178 self.store_mfn = None
179 self.console_mfn = None
182 def handler(self, line, _):
183 m = re.match(r"^(store-mfn) (\d+)$", line)
184 if m:
185 self.store_mfn = int(m.group(2))
186 else:
187 m = re.match(r"^(console-mfn) (\d+)$", line)
188 if m:
189 self.console_mfn = int(m.group(2))
192 def forkHelper(cmd, fd, inputHandler, closeToChild):
193 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
195 if closeToChild:
196 child.tochild.close()
198 thread = threading.Thread(target = slurp, args = (child.childerr,))
199 thread.start()
201 try:
202 try:
203 while 1:
204 line = child.fromchild.readline()
205 if line == "":
206 break
207 else:
208 line = line.rstrip()
209 log.debug('%s', line)
210 inputHandler(line, child.tochild)
212 thread.join()
214 except IOError, exn:
215 raise XendError('Error reading from child process for %s: %s' %
216 (cmd, exn))
217 finally:
218 child.fromchild.close()
219 child.childerr.close()
220 if not closeToChild:
221 child.tochild.close()
223 status = child.wait()
224 if status >> 8 == 127:
225 raise XendError("%s failed: popen failed" % string.join(cmd))
226 elif status != 0:
227 raise XendError("%s failed" % string.join(cmd))
230 def slurp(infile):
231 while 1:
232 line = infile.readline()
233 if line == "":
234 break
235 else:
236 log.error('%s', line.strip())