ia64/xen-unstable

view tools/python/xen/xend/XendCheckpoint.py @ 12607:7a5246955bef

Don't bail if the VM renaming at the end of saving doesn't succeed -- we
expect a failure if this is a localhost migration.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Nov 28 11:35:10 2006 +0000 (2006-11-28)
parents 3b961863062d
children fa8b2059c5d8
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
20 from xen.xend.XendLogging import log
21 from xen.xend.XendConstants import *
22 from xen.xend.XendConfig import XendConfig
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.update(XendConfig(sxp = vmconfig), refresh = False)
141 dominfo.resume()
142 else:
143 dominfo = xd.restore_(vmconfig)
145 store_port = dominfo.getStorePort()
146 console_port = dominfo.getConsolePort()
148 assert store_port
149 assert console_port
151 try:
152 l = read_exact(fd, sizeof_unsigned_long,
153 "not a valid guest state file: pfn count read")
154 nr_pfns = unpack("L", l)[0] # native sizeof long
155 if nr_pfns > 16*1024*1024: # XXX
156 raise XendError(
157 "not a valid guest state file: pfn count out of range")
159 balloon.free(xc.pages_to_kib(nr_pfns))
161 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
162 fd, dominfo.getDomid(), nr_pfns,
163 store_port, console_port])
164 log.debug("[xc_restore]: %s", string.join(cmd))
166 handler = RestoreInputHandler()
168 forkHelper(cmd, fd, handler.handler, True)
170 if handler.store_mfn is None or handler.console_mfn is None:
171 raise XendError('Could not read store/console MFN')
173 os.read(fd, 1) # Wait for source to close connection
174 dominfo.waitForDevices() # Wait for backends to set up
175 if not paused:
176 dominfo.unpause()
178 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
180 return dominfo
181 except:
182 dominfo.destroy()
183 raise
186 class RestoreInputHandler:
187 def __init__(self):
188 self.store_mfn = None
189 self.console_mfn = None
192 def handler(self, line, _):
193 m = re.match(r"^(store-mfn) (\d+)$", line)
194 if m:
195 self.store_mfn = int(m.group(2))
196 else:
197 m = re.match(r"^(console-mfn) (\d+)$", line)
198 if m:
199 self.console_mfn = int(m.group(2))
202 def forkHelper(cmd, fd, inputHandler, closeToChild):
203 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
205 if closeToChild:
206 child.tochild.close()
208 thread = threading.Thread(target = slurp, args = (child.childerr,))
209 thread.start()
211 try:
212 try:
213 while 1:
214 line = child.fromchild.readline()
215 if line == "":
216 break
217 else:
218 line = line.rstrip()
219 log.debug('%s', line)
220 inputHandler(line, child.tochild)
222 thread.join()
224 except IOError, exn:
225 raise XendError('Error reading from child process for %s: %s' %
226 (cmd, exn))
227 finally:
228 child.fromchild.close()
229 child.childerr.close()
230 if not closeToChild:
231 child.tochild.close()
233 status = child.wait()
234 if status >> 8 == 127:
235 raise XendError("%s failed: popen failed" % string.join(cmd))
236 elif status != 0:
237 raise XendError("%s failed" % string.join(cmd))
240 def slurp(infile):
241 while 1:
242 line = infile.readline()
243 if line == "":
244 break
245 else:
246 line = line.strip()
247 m = re.match(r"^ERROR: (.*)", line)
248 if m is None:
249 log.info('%s', line)
250 else:
251 log.error('%s', m.group(1))