ia64/xen-unstable

view tools/python/xen/xend/XendCheckpoint.py @ 14433:487e37c22b28

Fix the python type dismatch exception when HVM restore

Signed-off-by: Zhai Edwin <edwin.zhai@intel.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Fri Mar 16 10:05:57 2007 +0000 (2007-03-16)
parents 35c9a1939ae4
children 121a97005d11
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 import fcntl
13 from struct import pack, unpack, calcsize
15 from xen.util.xpopen import xPopen3
16 import xen.util.auxbin
17 import xen.lowlevel.xc
19 from xen.xend import balloon, sxp
20 from xen.xend.XendError import XendError, VmError
21 from xen.xend.XendLogging import log
22 from xen.xend.XendConfig import XendConfig
23 from xen.xend.XendConstants import *
25 SIGNATURE = "LinuxGuestRecord"
26 QEMU_SIGNATURE = "QemuDeviceModelRecord"
27 dm_batch = 512
28 XC_SAVE = "xc_save"
29 XC_RESTORE = "xc_restore"
32 sizeof_int = calcsize("i")
33 sizeof_unsigned_int = calcsize("I")
34 sizeof_unsigned_long = calcsize("L")
37 xc = xen.lowlevel.xc.xc()
40 def write_exact(fd, buf, errmsg):
41 if os.write(fd, buf) != len(buf):
42 raise XendError(errmsg)
45 def read_exact(fd, size, errmsg):
46 buf = ''
47 while size != 0:
48 readstr = os.read(fd, size)
49 if not len(readstr):
50 log.error("read_exact: EOF trying to read %d (buf='%s')" % \
51 (size, buf))
52 raise XendError(errmsg)
53 size = size - len(readstr)
54 buf = buf + readstr
55 return buf
58 def save(fd, dominfo, network, live, dst, checkpoint=False):
59 write_exact(fd, SIGNATURE, "could not write guest state file: signature")
61 config = sxp.to_string(dominfo.sxpr())
63 domain_name = dominfo.getName()
64 # Rename the domain temporarily, so that we don't get a name clash if this
65 # domain is migrating (live or non-live) to the local host. Doing such a
66 # thing is useful for debugging.
67 dominfo.setName('migrating-' + domain_name)
69 try:
70 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name)
72 write_exact(fd, pack("!i", len(config)),
73 "could not write guest state file: config len")
74 write_exact(fd, config, "could not write guest state file: config")
76 image_cfg = dominfo.info.get('image', {})
77 hvm = dominfo.info.is_hvm()
78 stdvga = 0
80 if hvm:
81 log.info("save hvm domain")
82 if dominfo.info['platform'].has_key('stdvga'):
83 if dominfo.info['platform']['stdvga'] == 1:
84 stdvga = 1
86 # xc_save takes three customization parameters: maxit, max_f, and
87 # flags the last controls whether or not save is 'live', while the
88 # first two further customize behaviour when 'live' save is
89 # enabled. Passing "0" simply uses the defaults compiled into
90 # libxenguest; see the comments and/or code in xc_linux_save() for
91 # more information.
92 cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
93 str(dominfo.getDomid()), "0", "0",
94 str(int(live) | (int(hvm) << 2) | (int(stdvga) << 3)) ]
95 log.debug("[xc_save]: %s", string.join(cmd))
97 def saveInputHandler(line, tochild):
98 log.debug("In saveInputHandler %s", line)
99 if line == "suspend":
100 log.debug("Suspending %d ...", dominfo.getDomid())
101 dominfo.shutdown('suspend')
102 dominfo.waitForShutdown()
103 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
104 domain_name)
105 log.info("Domain %d suspended.", dominfo.getDomid())
106 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
107 domain_name)
108 #send signal to device model for save
109 if hvm:
110 log.info("release_devices for hvm domain")
111 dominfo._releaseDevices(True)
112 tochild.write("done\n")
113 tochild.flush()
114 log.debug('Written done')
116 forkHelper(cmd, fd, saveInputHandler, False)
118 # put qemu device model state
119 if hvm:
120 write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
121 qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(), os.O_RDONLY)
122 while True:
123 buf = os.read(qemu_fd, dm_batch)
124 if len(buf):
125 write_exact(fd, buf, "could not write device model state")
126 else:
127 break
128 os.close(qemu_fd)
129 os.remove("/tmp/xen.qemu-dm.%d" % dominfo.getDomid())
131 if checkpoint:
132 dominfo.resumeDomain()
133 else:
134 dominfo.destroyDomain()
135 dominfo.testDeviceComplete()
136 try:
137 dominfo.setName(domain_name)
138 except VmError:
139 # Ignore this. The name conflict (hopefully) arises because we
140 # are doing localhost migration; if we are doing a suspend of a
141 # persistent VM, we need the rename, and don't expect the
142 # conflict. This needs more thought.
143 pass
145 except Exception, exn:
146 log.exception("Save failed on domain %s (%s).", domain_name,
147 dominfo.getDomid())
149 dominfo.resumeDomain()
150 log.debug("XendCheckpoint.save: resumeDomain")
152 try:
153 dominfo.setName(domain_name)
154 except:
155 log.exception("Failed to reset the migrating domain's name")
158 def restore(xd, fd, dominfo = None, paused = False):
159 signature = read_exact(fd, len(SIGNATURE),
160 "not a valid guest state file: signature read")
161 if signature != SIGNATURE:
162 raise XendError("not a valid guest state file: found '%s'" %
163 signature)
165 l = read_exact(fd, sizeof_int,
166 "not a valid guest state file: config size read")
167 vmconfig_size = unpack("!i", l)[0]
168 vmconfig_buf = read_exact(fd, vmconfig_size,
169 "not a valid guest state file: config read")
171 p = sxp.Parser()
172 p.input(vmconfig_buf)
173 if not p.ready:
174 raise XendError("not a valid guest state file: config parse")
176 vmconfig = p.get_val()
178 if dominfo:
179 dominfo.resume()
180 else:
181 dominfo = xd.restore_(vmconfig)
183 store_port = dominfo.getStorePort()
184 console_port = dominfo.getConsolePort()
186 assert store_port
187 assert console_port
189 nr_pfns = (dominfo.getMemoryTarget() + 3) / 4
191 # if hvm, pass mem size to calculate the store_mfn
192 image_cfg = dominfo.info.get('image', {})
193 is_hvm = dominfo.info.is_hvm()
194 if is_hvm:
195 hvm = dominfo.info['memory_static_min']
196 apic = int(dominfo.info['platform'].get('apic', 0))
197 pae = int(dominfo.info['platform'].get('pae', 0))
198 log.info("restore hvm domain %d, mem=%d, apic=%d, pae=%d",
199 dominfo.domid, hvm, apic, pae)
200 else:
201 hvm = 0
202 apic = 0
203 pae = 0
205 try:
206 l = read_exact(fd, sizeof_unsigned_long,
207 "not a valid guest state file: pfn count read")
208 max_pfn = unpack("L", l)[0] # native sizeof long
210 if max_pfn > 16*1024*1024: # XXX
211 raise XendError(
212 "not a valid guest state file: pfn count out of range")
214 shadow = dominfo.info['shadow_memory']
215 log.debug("restore:shadow=0x%x, _static_max=0x%x, _static_min=0x%x, "
216 "nr_pfns=0x%x.", dominfo.info['shadow_memory'],
217 dominfo.info['memory_static_max'],
218 dominfo.info['memory_static_min'], nr_pfns)
220 balloon.free(xc.pages_to_kib(nr_pfns) + shadow * 1024)
222 shadow_cur = xc.shadow_mem_control(dominfo.getDomid(), shadow)
223 dominfo.info['shadow_memory'] = shadow_cur
225 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
226 fd, dominfo.getDomid(), max_pfn,
227 store_port, console_port, hvm, pae, apic])
228 log.debug("[xc_restore]: %s", string.join(cmd))
230 handler = RestoreInputHandler()
232 forkHelper(cmd, fd, handler.handler, True)
234 # We don't want to pass this fd to any other children -- we
235 # might need to recover ths disk space that backs it.
236 try:
237 flags = fcntl.fcntl(fd, fcntl.F_GETFD)
238 flags |= fcntl.FD_CLOEXEC
239 fcntl.fcntl(fd, fcntl.F_SETFD, flags)
240 except:
241 pass
243 if handler.store_mfn is None:
244 raise XendError('Could not read store MFN')
246 if not is_hvm and handler.console_mfn is None:
247 raise XendError('Could not read console MFN')
249 # get qemu state and create a tmp file for dm restore
250 if is_hvm:
251 qemu_signature = read_exact(fd, len(QEMU_SIGNATURE),
252 "invalid device model signature read")
253 if qemu_signature != QEMU_SIGNATURE:
254 raise XendError("not a valid device model state: found '%s'" %
255 qemu_signature)
256 qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(),
257 os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
258 while True:
259 buf = os.read(fd, dm_batch)
260 if len(buf):
261 write_exact(qemu_fd, buf,
262 "could not write dm state to tmp file")
263 else:
264 break
265 os.close(qemu_fd)
268 os.read(fd, 1) # Wait for source to close connection
270 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
272 dominfo.waitForDevices() # Wait for backends to set up
273 if not paused:
274 dominfo.unpause()
276 return dominfo
277 except:
278 dominfo.destroy()
279 raise
282 class RestoreInputHandler:
283 def __init__(self):
284 self.store_mfn = None
285 self.console_mfn = None
288 def handler(self, line, _):
289 m = re.match(r"^(store-mfn) (\d+)$", line)
290 if m:
291 self.store_mfn = int(m.group(2))
292 else:
293 m = re.match(r"^(console-mfn) (\d+)$", line)
294 if m:
295 self.console_mfn = int(m.group(2))
298 def forkHelper(cmd, fd, inputHandler, closeToChild):
299 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
301 if closeToChild:
302 child.tochild.close()
304 thread = threading.Thread(target = slurp, args = (child.childerr,))
305 thread.start()
307 try:
308 try:
309 while 1:
310 line = child.fromchild.readline()
311 if line == "":
312 break
313 else:
314 line = line.rstrip()
315 log.debug('%s', line)
316 inputHandler(line, child.tochild)
318 except IOError, exn:
319 raise XendError('Error reading from child process for %s: %s' %
320 (cmd, exn))
321 finally:
322 child.fromchild.close()
323 if not closeToChild:
324 child.tochild.close()
325 thread.join()
326 child.childerr.close()
327 status = child.wait()
329 if status >> 8 == 127:
330 raise XendError("%s failed: popen failed" % string.join(cmd))
331 elif status != 0:
332 raise XendError("%s failed" % string.join(cmd))
335 def slurp(infile):
336 while 1:
337 line = infile.readline()
338 if line == "":
339 break
340 else:
341 line = line.strip()
342 m = re.match(r"^ERROR: (.*)", line)
343 if m is None:
344 log.info('%s', line)
345 else:
346 log.error('%s', m.group(1))