ia64/xen-unstable

view tools/python/xen/xend/XendCheckpoint.py @ 12852:27294bab3d62

[XEND] Fix resume for managed domains.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Dec 07 13:47:13 2006 +0000 (2006-12-07)
parents 5bed7bc05c8a
children c38370be1e0e
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 threading
12 from struct import pack, unpack, calcsize
14 from xen.util.xpopen import xPopen3
15 import xen.util.auxbin
16 import xen.lowlevel.xc
18 from xen.xend import balloon, sxp
19 from xen.xend.XendError import XendError, VmError
20 from xen.xend.XendLogging import log
21 from xen.xend.XendConfig import XendConfig
22 from xen.xend.XendConstants import *
24 SIGNATURE = "LinuxGuestRecord"
25 XC_SAVE = "xc_save"
26 XC_RESTORE = "xc_restore"
29 sizeof_int = calcsize("i")
30 sizeof_unsigned_long = calcsize("L")
33 xc = xen.lowlevel.xc.xc()
36 def write_exact(fd, buf, errmsg):
37 if os.write(fd, buf) != len(buf):
38 raise XendError(errmsg)
41 def read_exact(fd, size, errmsg):
42 buf = ''
43 while size != 0:
44 readstr = os.read(fd, size)
45 if not len(readstr):
46 log.error("read_exact: EOF trying to read %d (buf='%s')" % \
47 (size, buf))
48 raise XendError(errmsg)
49 size = size - len(readstr)
50 buf = buf + readstr
51 return buf
54 def save(fd, dominfo, network, live, dst):
55 write_exact(fd, SIGNATURE, "could not write guest state file: signature")
57 config = sxp.to_string(dominfo.sxpr())
59 domain_name = dominfo.getName()
60 # Rename the domain temporarily, so that we don't get a name clash if this
61 # domain is migrating (live or non-live) to the local host. Doing such a
62 # thing is useful for debugging.
63 dominfo.setName('migrating-' + domain_name)
65 try:
66 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name)
68 write_exact(fd, pack("!i", len(config)),
69 "could not write guest state file: config len")
70 write_exact(fd, config, "could not write guest state file: config")
72 # xc_save takes three customization parameters: maxit, max_f, and
73 # flags the last controls whether or not save is 'live', while the
74 # first two further customize behaviour when 'live' save is
75 # enabled. Passing "0" simply uses the defaults compiled into
76 # libxenguest; see the comments and/or code in xc_linux_save() for
77 # more information.
78 cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
79 str(dominfo.getDomid()), "0", "0", str(int(live)) ]
80 log.debug("[xc_save]: %s", string.join(cmd))
82 def saveInputHandler(line, tochild):
83 log.debug("In saveInputHandler %s", line)
84 if line == "suspend":
85 log.debug("Suspending %d ...", dominfo.getDomid())
86 dominfo.shutdown('suspend')
87 dominfo.waitForShutdown()
88 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
89 domain_name)
90 log.info("Domain %d suspended.", dominfo.getDomid())
91 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
92 domain_name)
93 tochild.write("done\n")
94 tochild.flush()
95 log.debug('Written done')
97 forkHelper(cmd, fd, saveInputHandler, False)
99 dominfo.destroyDomain()
100 try:
101 dominfo.setName(domain_name)
102 except VmError:
103 # Ignore this. The name conflict (hopefully) arises because we
104 # are doing localhost migration; if we are doing a suspend of a
105 # persistent VM, we need the rename, and don't expect the
106 # conflict. This needs more thought.
107 pass
109 except Exception, exn:
110 log.exception("Save failed on domain %s (%s).", domain_name,
111 dominfo.getDomid())
112 try:
113 dominfo.setName(domain_name)
114 except:
115 log.exception("Failed to reset the migrating domain's name")
116 raise Exception, exn
119 def restore(xd, fd, dominfo = None, paused = False):
120 signature = read_exact(fd, len(SIGNATURE),
121 "not a valid guest state file: signature read")
122 if signature != SIGNATURE:
123 raise XendError("not a valid guest state file: found '%s'" %
124 signature)
126 l = read_exact(fd, sizeof_int,
127 "not a valid guest state file: config size read")
128 vmconfig_size = unpack("!i", l)[0]
129 vmconfig_buf = read_exact(fd, vmconfig_size,
130 "not a valid guest state file: config read")
132 p = sxp.Parser()
133 p.input(vmconfig_buf)
134 if not p.ready:
135 raise XendError("not a valid guest state file: config parse")
137 vmconfig = p.get_val()
139 if dominfo:
140 dominfo.resume()
141 else:
142 dominfo = xd.restore_(vmconfig)
144 store_port = dominfo.getStorePort()
145 console_port = dominfo.getConsolePort()
147 assert store_port
148 assert console_port
150 try:
151 l = read_exact(fd, sizeof_unsigned_long,
152 "not a valid guest state file: pfn count read")
153 nr_pfns = unpack("L", l)[0] # native sizeof long
154 if nr_pfns > 16*1024*1024: # XXX
155 raise XendError(
156 "not a valid guest state file: pfn count out of range")
158 balloon.free(xc.pages_to_kib(nr_pfns))
160 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
161 fd, dominfo.getDomid(), nr_pfns,
162 store_port, console_port])
163 log.debug("[xc_restore]: %s", string.join(cmd))
165 handler = RestoreInputHandler()
167 forkHelper(cmd, fd, handler.handler, True)
169 if handler.store_mfn is None or handler.console_mfn is None:
170 raise XendError('Could not read store/console MFN')
172 os.read(fd, 1) # Wait for source to close connection
173 dominfo.waitForDevices() # Wait for backends to set up
174 if not paused:
175 dominfo.unpause()
177 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
179 return dominfo
180 except:
181 dominfo.destroy()
182 raise
185 class RestoreInputHandler:
186 def __init__(self):
187 self.store_mfn = None
188 self.console_mfn = None
191 def handler(self, line, _):
192 m = re.match(r"^(store-mfn) (\d+)$", line)
193 if m:
194 self.store_mfn = int(m.group(2))
195 else:
196 m = re.match(r"^(console-mfn) (\d+)$", line)
197 if m:
198 self.console_mfn = int(m.group(2))
201 def forkHelper(cmd, fd, inputHandler, closeToChild):
202 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
204 if closeToChild:
205 child.tochild.close()
207 thread = threading.Thread(target = slurp, args = (child.childerr,))
208 thread.start()
210 try:
211 try:
212 while 1:
213 line = child.fromchild.readline()
214 if line == "":
215 break
216 else:
217 line = line.rstrip()
218 log.debug('%s', line)
219 inputHandler(line, child.tochild)
221 thread.join()
223 except IOError, exn:
224 raise XendError('Error reading from child process for %s: %s' %
225 (cmd, exn))
226 finally:
227 child.fromchild.close()
228 child.childerr.close()
229 if not closeToChild:
230 child.tochild.close()
232 status = child.wait()
233 if status >> 8 == 127:
234 raise XendError("%s failed: popen failed" % string.join(cmd))
235 elif status != 0:
236 raise XendError("%s failed" % string.join(cmd))
239 def slurp(infile):
240 while 1:
241 line = infile.readline()
242 if line == "":
243 break
244 else:
245 line = line.strip()
246 m = re.match(r"^ERROR: (.*)", line)
247 if m is None:
248 log.info('%s', line)
249 else:
250 log.error('%s', m.group(1))