direct-io.hg

view tools/python/xen/xend/XendCheckpoint.py @ 7480:a90d670c98b9

Change the semantics of GetDomainPath so that it always succeeds, regardless of
whether a domain has been introduced to the store. Added a separate message
XS_IS_DOMAIN_INTRODUCED and API for that (xs_is_domain_introduced) to determine
whether the domain has really been introduced. This change means that the
tools can determine the correct domain path earlier in the domain creation
process, which is particularly a factor with live migration, as it allows us
to create the devices earlier in the process, and unpause the new domain before
performing the introduce. Until recently we already had these features, but
the simplification of the interface between xend and xenstored caused breakage.

No longer clear out the domain path when a domain is introduced -- this was a
hack to work around the recent problematic semantics of GetDomainPath.

Do not write the contents of the info block to the store. All the configuration
info is written to the /vm path, and anything else in the info block is either
dealt with explicitly or is ephemeral and has no place in the store.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Sun Oct 23 22:45:15 2005 +0100 (2005-10-23)
parents 8dbf531776e1
children 7f8db234e9db
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 select
11 import string
12 import sxp
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 from xen.xend.xenstore.xsutil import IntroduceDomain
23 from XendError import XendError
24 from XendLogging import log
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.new()
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 = os.read(fd, size)
45 if len(buf) != size:
46 raise XendError(errmsg)
47 return buf
49 def save(fd, dominfo, live):
50 write_exact(fd, SIGNATURE, "could not write guest state file: signature")
52 config = sxp.to_string(dominfo.sxpr())
54 domain_name = dominfo.getName()
55 # Rename the domain temporarily, so that we don't get a name clash if this
56 # domain is migrating (live or non-live) to the local host. Doing such a
57 # thing is useful for debugging.
58 dominfo.setName('migrating-' + domain_name)
60 try:
61 write_exact(fd, pack("!i", len(config)),
62 "could not write guest state file: config len")
63 write_exact(fd, config, "could not write guest state file: config")
65 # xc_save takes three customization parameters: maxit, max_f, and
66 # flags the last controls whether or not save is 'live', while the
67 # first two further customize behaviour when 'live' save is
68 # enabled. Passing "0" simply uses the defaults compiled into
69 # libxenguest; see the comments and/or code in xc_linux_save() for
70 # more information.
71 cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(xc.handle()), str(fd),
72 str(dominfo.getDomid()), "0", "0", str(int(live)) ]
73 log.debug("[xc_save]: %s", string.join(cmd))
75 def saveInputHandler(line, tochild):
76 log.debug("In saveInputHandler %s", line)
77 if line == "suspend":
78 log.debug("Suspending %d ...", dominfo.getDomid())
79 dominfo.shutdown('suspend')
80 dominfo.waitForShutdown()
81 log.info("Domain %d suspended.", dominfo.getDomid())
82 tochild.write("done\n")
83 tochild.flush()
85 forkHelper(cmd, fd, saveInputHandler, False)
87 dominfo.destroyDomain()
89 except Exception, exn:
90 log.exception("Save failed on domain %s (%d).", domain_name,
91 dominfo.getDomid())
92 try:
93 dominfo.setName(domain_name)
94 except:
95 log.exception("Failed to reset the migrating domain's name")
96 raise Exception, exn
99 def restore(xd, fd):
100 signature = read_exact(fd, len(SIGNATURE),
101 "not a valid guest state file: signature read")
102 if signature != SIGNATURE:
103 raise XendError("not a valid guest state file: found '%s'" %
104 signature)
106 l = read_exact(fd, sizeof_int,
107 "not a valid guest state file: config size read")
108 vmconfig_size = unpack("!i", l)[0]
109 vmconfig_buf = read_exact(fd, vmconfig_size,
110 "not a valid guest state file: config read")
112 p = sxp.Parser()
113 p.input(vmconfig_buf)
114 if not p.ready:
115 raise XendError("not a valid guest state file: config parse")
117 vmconfig = p.get_val()
119 dominfo = xd.restore_(vmconfig)
121 store_port = dominfo.getStorePort()
122 console_port = dominfo.getConsolePort()
124 assert store_port
125 assert console_port
127 try:
128 l = read_exact(fd, sizeof_unsigned_long,
129 "not a valid guest state file: pfn count read")
130 nr_pfns = unpack("=L", l)[0] # XXX endianess
131 if nr_pfns > 1024*1024: # XXX
132 raise XendError(
133 "not a valid guest state file: pfn count out of range")
135 cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
136 xc.handle(), fd, dominfo.getDomid(), nr_pfns,
137 store_port, console_port])
138 log.debug("[xc_restore]: %s", string.join(cmd))
140 handler = RestoreInputHandler()
142 forkHelper(cmd, fd, handler.handler, True)
144 if handler.store_mfn is None or handler.console_mfn is None:
145 raise XendError('Could not read store/console MFN')
147 dominfo.unpause()
149 dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
151 return dominfo
152 except:
153 dominfo.destroy()
154 raise
157 class RestoreInputHandler:
158 def __init__(self):
159 self.store_mfn = None
160 self.console_mfn = None
163 def handler(self, line, _):
164 m = re.match(r"^(store-mfn) (\d+)$", line)
165 if m:
166 self.store_mfn = int(m.group(2))
167 else:
168 m = re.match(r"^(console-mfn) (\d+)$", line)
169 if m:
170 self.console_mfn = int(m.group(2))
173 def forkHelper(cmd, fd, inputHandler, closeToChild):
174 child = xPopen3(cmd, True, -1, [fd, xc.handle()])
176 if closeToChild:
177 child.tochild.close()
179 lasterr = "error unknown"
180 try:
181 fds = [child.fromchild.fileno(),
182 child.childerr.fileno()]
183 p = select.poll()
184 map(p.register, fds)
185 while len(fds) > 0:
186 r = p.poll()
187 for (fd, event) in r:
188 if event & select.POLLIN:
189 if fd == child.childerr.fileno():
190 lasterr = child.childerr.readline().rstrip()
191 log.error('%s', lasterr)
192 else:
193 l = child.fromchild.readline().rstrip()
194 while l:
195 log.debug('%s', l)
196 inputHandler(l, child.tochild)
197 try:
198 l = child.fromchild.readline().rstrip()
199 except:
200 l = None
202 if event & select.POLLERR:
203 raise XendError('Error reading from child process for %s',
204 cmd)
206 if event & select.POLLHUP:
207 fds.remove(fd)
208 p.unregister(fd)
209 finally:
210 child.fromchild.close()
211 child.childerr.close()
212 if not closeToChild:
213 child.tochild.close()
215 if child.wait() >> 8 == 127:
216 lasterr = "popen failed"
217 if child.wait() != 0:
218 raise XendError("%s failed: %s" % (string.join(cmd), lasterr))