debuggers.hg

view tools/python/xen/xend/XendCheckpoint.py @ 0:7d21f7218375

Exact replica of unstable on 051908 + README-this
author Mukesh Rathor
date Mon May 19 15:34:57 2008 -0700 (2008-05-19)
parents
children 5c0bf00e371d
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 os.path
10 import re
11 import string
12 import threading
13 import fcntl
14 from struct import pack, unpack, calcsize
16 from xen.util.xpopen import xPopen3
17 import xen.util.auxbin
18 import xen.lowlevel.xc
20 from xen.xend import balloon, sxp, image
21 from xen.xend.XendError import XendError, VmError
22 from xen.xend.XendLogging import log
23 from xen.xend.XendConfig import XendConfig
24 from xen.xend.XendConstants import *
25 from xen.xend import XendNode
27 SIGNATURE = "LinuxGuestRecord"
28 QEMU_SIGNATURE = "QemuDeviceModelRecord"
29 dm_batch = 512
30 XC_SAVE = "xc_save"
31 XC_RESTORE = "xc_restore"
34 sizeof_int = calcsize("i")
35 sizeof_unsigned_int = calcsize("I")
36 sizeof_unsigned_long = calcsize("L")
39 xc = xen.lowlevel.xc.xc()
42 def write_exact(fd, buf, errmsg):
43 if os.write(fd, buf) != len(buf):
44 raise XendError(errmsg)
47 def read_exact(fd, size, errmsg):
48 buf = ''
49 while size != 0:
50 readstr = os.read(fd, size)
51 if not len(readstr):
52 log.error("read_exact: EOF trying to read %d (buf='%s')" % \
53 (size, buf))
54 raise XendError(errmsg)
55 size = size - len(readstr)
56 buf = buf + readstr
57 return buf
60 def insert_after(list, pred, value):
61 for i,k in enumerate(list):
62 if type(k) == type([]):
63 if k[0] == pred:
64 list.insert (i+1, value)
65 return
68 def save(fd, dominfo, network, live, dst, checkpoint=False, node=-1):
69 write_exact(fd, SIGNATURE, "could not write guest state file: signature")
71 sxprep = dominfo.sxpr()
73 if node > -1:
74 insert_after(sxprep,'vcpus',['node', str(node)])
76 config = sxp.to_string(sxprep)
78 domain_name = dominfo.getName()
79 # Rename the domain temporarily, so that we don't get a name clash if this
80 # domain is migrating (live or non-live) to the local host. Doing such a
81 # thing is useful for debugging.
82 dominfo.setName('migrating-' + domain_name)
84 try:
85 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name)
87 write_exact(fd, pack("!i", len(config)),
88 "could not write guest state file: config len")
89 write_exact(fd, config, "could not write guest state file: config")
91 image_cfg = dominfo.info.get('image', {})
92 hvm = dominfo.info.is_hvm()
94 # xc_save takes three customization parameters: maxit, max_f, and
95 # flags the last controls whether or not save is 'live', while the
96 # first two further customize behaviour when 'live' save is
97 # enabled. Passing "0" simply uses the defaults compiled into
98 # libxenguest; see the comments and/or code in xc_linux_save() for
99 # more information.
100 cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
101 str(dominfo.getDomid()), "0", "0",
102 str(int(live) | (int(hvm) << 2)) ]
103 log.debug("[xc_save]: %s", string.join(cmd))
105 def saveInputHandler(line, tochild):
106 log.debug("In saveInputHandler %s", line)
107 if line == "suspend":
108 log.debug("Suspending %d ...", dominfo.getDomid())
109 dominfo.shutdown('suspend')
110 dominfo.waitForShutdown()
111 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
112 domain_name)
113 log.info("Domain %d suspended.", dominfo.getDomid())
114 dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
115 domain_name)
116 if hvm:
117 dominfo.image.saveDeviceModel()
119 tochild.write("done\n")
120 tochild.flush()
121 log.debug('Written done')
123 forkHelper(cmd, fd, saveInputHandler, False)
125 # put qemu device model state
126 if os.path.exists("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()):
127 write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
128 qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(),
129 os.O_RDONLY)
130 while True:
131 buf = os.read(qemu_fd, dm_batch)
132 if len(buf):
133 write_exact(fd, buf, "could not write device model state")
134 else:
135 break
136 os.close(qemu_fd)
137 os.remove("/var/lib/xen/qemu-save.%d" % dominfo.getDomid())
139 if checkpoint:
140 dominfo.resumeDomain()
141 else:
142 dominfo.destroy()
143 dominfo.testDeviceComplete()
144 try:
145 dominfo.setName(domain_name, False)
146 except VmError:
147 # Ignore this. The name conflict (hopefully) arises because we
148 # are doing localhost migration; if we are doing a suspend of a
149 # persistent VM, we need the rename, and don't expect the
150 # conflict. This needs more thought.
151 pass
153 except Exception, exn:
154 log.exception("Save failed on domain %s (%s) - resuming.", domain_name,
155 dominfo.getDomid())
156 dominfo.resumeDomain()
158 try:
159 dominfo.setName(domain_name)
160 except:
161 log.exception("Failed to reset the migrating domain's name")
163 raise exn
166 def restore(xd, fd, dominfo = None, paused = False, relocating = False):
167 signature = read_exact(fd, len(SIGNATURE),
168 "not a valid guest state file: signature read")
169 if signature != SIGNATURE:
170 raise XendError("not a valid guest state file: found '%s'" %
171 signature)
173 l = read_exact(fd, sizeof_int,
174 "not a valid guest state file: config size read")
175 vmconfig_size = unpack("!i", l)[0]
176 vmconfig_buf = read_exact(fd, vmconfig_size,
177 "not a valid guest state file: config read")
179 p = sxp.Parser()
180 p.input(vmconfig_buf)
181 if not p.ready:
182 raise XendError("not a valid guest state file: config parse")
184 vmconfig = p.get_val()
186 if not relocating:
187 domconfig = XendConfig(sxp_obj = vmconfig)
188 othervm = xd.domain_lookup_nr(domconfig["name_label"])
189 if othervm is None or othervm.domid is None:
190 othervm = xd.domain_lookup_nr(domconfig["uuid"])
191 if othervm is not None and othervm.domid is not None:
192 raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid))
194 if dominfo:
195 dominfo.resume()
196 else:
197 dominfo = xd.restore_(vmconfig)
199 # repin domain vcpus if a target node number was specified
200 # this is done prior to memory allocation to aide in memory
201 # distribution for NUMA systems.
202 nodenr = -1
203 for i,l in enumerate(vmconfig):
204 if type(l) == type([]):
205 if l[0] == 'node':
206 nodenr = int(l[1])
208 if nodenr >= 0:
209 node_to_cpu = XendNode.instance().xc.physinfo()['node_to_cpu']
210 if nodenr < len(node_to_cpu):
211 for v in range(0, dominfo.info['VCPUs_max']):
212 xc.vcpu_setaffinity(dominfo.domid, v, node_to_cpu[nodenr])
214 store_port = dominfo.getStorePort()
215 console_port = dominfo.getConsolePort()
217 assert store_port
218 assert console_port
220 # if hvm, pass mem size to calculate the store_mfn
221 image_cfg = dominfo.info.get('image', {})
222 is_hvm = dominfo.info.is_hvm()
223 if is_hvm:
224 apic = int(dominfo.info['platform'].get('apic', 0))
225 pae = int(dominfo.info['platform'].get('pae', 0))
226 log.info("restore hvm domain %d, apic=%d, pae=%d",
227 dominfo.domid, apic, pae)
228 else:
229 apic = 0
230 pae = 0
232 try:
233 restore_image = image.create(dominfo, dominfo.info)
234 memory = restore_image.getRequiredAvailableMemory(
235 dominfo.info['memory_dynamic_max'] / 1024)
236 maxmem = restore_image.getRequiredAvailableMemory(
237 dominfo.info['memory_static_max'] / 1024)
238 shadow = restore_image.getRequiredShadowMemory(
239 dominfo.info['shadow_memory'] * 1024,
240 dominfo.info['memory_static_max'] / 1024)
242 log.debug("restore:shadow=0x%x, _static_max=0x%x, _static_min=0x%x, ",
243 dominfo.info['shadow_memory'],
244 dominfo.info['memory_static_max'],
245 dominfo.info['memory_static_min'])
247 # Round shadow up to a multiple of a MiB, as shadow_mem_control
248 # takes MiB and we must not round down and end up under-providing.
249 shadow = ((shadow + 1023) / 1024) * 1024
251 # set memory limit
252 xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
254 balloon.free(memory + shadow)
256 shadow_cur = xc.shadow_mem_control(dominfo.getDomid(), shadow / 1024)
257 dominfo.info['shadow_memory'] = shadow_cur
259 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
260 fd, dominfo.getDomid(),
261 store_port, console_port, int(is_hvm), pae, apic])
262 log.debug("[xc_restore]: %s", string.join(cmd))
264 handler = RestoreInputHandler()
266 forkHelper(cmd, fd, handler.handler, True)
268 # We don't want to pass this fd to any other children -- we
269 # might need to recover the disk space that backs it.
270 try:
271 flags = fcntl.fcntl(fd, fcntl.F_GETFD)
272 flags |= fcntl.FD_CLOEXEC
273 fcntl.fcntl(fd, fcntl.F_SETFD, flags)
274 except:
275 pass
277 if handler.store_mfn is None:
278 raise XendError('Could not read store MFN')
280 if not is_hvm and handler.console_mfn is None:
281 raise XendError('Could not read console MFN')
283 # get qemu state and create a tmp file for dm restore
284 # Even PV guests may have QEMU stat, but its not currently
285 # used so only bother with HVM currently.
286 if is_hvm:
287 qemu_signature = read_exact(fd, len(QEMU_SIGNATURE),
288 "invalid device model signature read")
289 if qemu_signature != QEMU_SIGNATURE:
290 raise XendError("not a valid device model state: found '%s'" %
291 qemu_signature)
292 qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(),
293 os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
294 while True:
295 buf = os.read(fd, dm_batch)
296 if len(buf):
297 write_exact(qemu_fd, buf,
298 "could not write dm state to tmp file")
299 else:
300 break
301 os.close(qemu_fd)
302 restore_image.setCpuid()
305 os.read(fd, 1) # Wait for source to close connection
307 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
309 #
310 # We shouldn't hold the domains_lock over a waitForDevices
311 # As this function sometime gets called holding this lock,
312 # we must release it and re-acquire it appropriately
313 #
314 from xen.xend import XendDomain
316 lock = True;
317 try:
318 XendDomain.instance().domains_lock.release()
319 except:
320 lock = False;
322 try:
323 dominfo.waitForDevices() # Wait for backends to set up
324 except Exception, exn:
325 log.exception(exn)
327 if lock:
328 XendDomain.instance().domains_lock.acquire()
330 if not paused:
331 dominfo.unpause()
333 return dominfo
334 except:
335 dominfo.destroy()
336 raise
339 class RestoreInputHandler:
340 def __init__(self):
341 self.store_mfn = None
342 self.console_mfn = None
345 def handler(self, line, _):
346 m = re.match(r"^(store-mfn) (\d+)$", line)
347 if m:
348 self.store_mfn = int(m.group(2))
349 else:
350 m = re.match(r"^(console-mfn) (\d+)$", line)
351 if m:
352 self.console_mfn = int(m.group(2))
355 def forkHelper(cmd, fd, inputHandler, closeToChild):
356 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
358 if closeToChild:
359 child.tochild.close()
361 thread = threading.Thread(target = slurp, args = (child.childerr,))
362 thread.start()
364 try:
365 try:
366 while 1:
367 line = child.fromchild.readline()
368 if line == "":
369 break
370 else:
371 line = line.rstrip()
372 log.debug('%s', line)
373 inputHandler(line, child.tochild)
375 except IOError, exn:
376 raise XendError('Error reading from child process for %s: %s' %
377 (cmd, exn))
378 finally:
379 child.fromchild.close()
380 if not closeToChild:
381 child.tochild.close()
382 thread.join()
383 child.childerr.close()
384 status = child.wait()
386 if status >> 8 == 127:
387 raise XendError("%s failed: popen failed" % string.join(cmd))
388 elif status != 0:
389 raise XendError("%s failed" % string.join(cmd))
392 def slurp(infile):
393 while 1:
394 line = infile.readline()
395 if line == "":
396 break
397 else:
398 line = line.strip()
399 m = re.match(r"^ERROR: (.*)", line)
400 if m is None:
401 log.info('%s', line)
402 else:
403 log.error('%s', m.group(1))