mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.1527.2.1 (429368a2F0SR4yrzuHsrRucuwriYqA)
Implement the parts of vm save which need interaction with xend
as part of xend, instead of using xfrd.
Execute xc_linux_save in a seperate process so that it can't
crash xend. Also handle errors passed from xc_linux_save.
xen_domain.c:
Disable save in xfrd.
xc_save.c:
new file
Makefile:
Add xc_save.
XendDomainInfo.py:
Add suspended state and threading Condition with notification, allowing
easy waiting for state changes.
XendDomain.py:
Implement the parts of vm save which need interaction with xend
as part of xend, instead of using xfrd. Set state to "suspended"
when detecting a suspended domain.
Fix reading output from subprocesses.
Fix ValueError in xen_domain().
xc.c:
Remove python binding for xc_linux_save.
xc_linux_save.c:
Implement the parts of vm save which need interaction with xend
as part of xend, instead of using xfrd. Also run xc_linux_save
in a seperate process.
xc_linux_restore.c:
Flush output so that xend picks it up timely.
Also disable debug output again.
xc.h:
Update xc_linux_save prototype and fix comments for xc_linux_{save,restore}.
ignore:
Add tools/xcutils/xc_save.
xpopen.py:
Exit with 127 if exec fails.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
This commit is contained in:
1 parent
e8968bea9c
commit
23999c8bc1
12 files changed
+307
-303
No files matched your search
@@ -254,93 +254,6 @@ static PyObject *pyxc_domain_getinfo(PyObject *self,
|
||||
return list;
|
||||
}
|
||||
|
||||
static int file_save(XcObject *xc, XcIOContext *ctxt, char *state_file)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd = -1;
|
||||
int open_flags = (O_CREAT | O_EXCL | O_WRONLY);
|
||||
int open_mode = 0644;
|
||||
|
||||
printf("%s>\n", __FUNCTION__);
|
||||
|
||||
if ( (fd = open(state_file, open_flags, open_mode)) < 0 )
|
||||
{
|
||||
xcio_perror(ctxt, "Could not open file for writing");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("%s>gzip_stream_fdopen... \n", __FUNCTION__);
|
||||
|
||||
/* Compression rate 1: we want speed over compression.
|
||||
* We're mainly going for those zero pages, after all.
|
||||
*/
|
||||
ctxt->io = gzip_stream_fdopen(fd, "wb1");
|
||||
if ( ctxt->io == NULL )
|
||||
{
|
||||
xcio_perror(ctxt, "Could not allocate compression state");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("%s> xc_linux_save...\n", __FUNCTION__);
|
||||
|
||||
rc = xc_linux_save(xc->xc_handle, ctxt);
|
||||
|
||||
exit:
|
||||
if ( ctxt->io != NULL )
|
||||
IOStream_close(ctxt->io);
|
||||
if ( fd >= 0 )
|
||||
close(fd);
|
||||
unlink(state_file);
|
||||
printf("%s> rc=%d\n", __FUNCTION__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_linux_save(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
char *state_file;
|
||||
int progress = 1, debug = 0;
|
||||
PyObject *val = NULL;
|
||||
int rc = -1;
|
||||
XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
|
||||
|
||||
static char *kwd_list[] = { "dom", "state_file", "vmconfig", "progress", "debug", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|sii", kwd_list,
|
||||
&ioctxt.domain,
|
||||
&state_file,
|
||||
&ioctxt.vmconfig,
|
||||
&progress,
|
||||
&debug) )
|
||||
goto exit;
|
||||
|
||||
ioctxt.vmconfig_n = (ioctxt.vmconfig ? strlen(ioctxt.vmconfig) : 0);
|
||||
|
||||
if ( progress )
|
||||
ioctxt.flags |= XCFLAGS_VERBOSE;
|
||||
if ( debug )
|
||||
ioctxt.flags |= XCFLAGS_DEBUG;
|
||||
|
||||
if ( (state_file == NULL) || (state_file[0] == '\0') )
|
||||
goto exit;
|
||||
|
||||
rc = file_save(xc, &ioctxt, state_file);
|
||||
if ( rc != 0 )
|
||||
{
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Py_INCREF(zero);
|
||||
val = zero;
|
||||
|
||||
exit:
|
||||
return val;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_linux_build(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
@@ -999,15 +912,6 @@ static PyMethodDef pyxc_methods[] = {
|
||||
"reason why it shut itself down.\n"
|
||||
" vcpu_to_cpu [[int]]: List that maps VCPUS to CPUS\n" },
|
||||
|
||||
{ "linux_save",
|
||||
(PyCFunction)pyxc_linux_save,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Save the CPU and memory state of a Linux guest OS.\n"
|
||||
" dom [int]: Identifier of domain to be saved.\n"
|
||||
" state_file [str]: Name of state file. Must not currently exist.\n"
|
||||
" progress [int, 1]: Bool - display a running progress indication?\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "linux_build",
|
||||
(PyCFunction)pyxc_linux_build,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
|
||||
@@ -129,7 +129,7 @@ class xPopen3:
|
||||
try:
|
||||
os.execvp(cmd[0], cmd)
|
||||
finally:
|
||||
os._exit(1)
|
||||
os._exit(127)
|
||||
|
||||
def poll(self):
|
||||
"""Return the exit status of the child process if it has finished,
|
||||
|
||||
@@ -90,7 +90,10 @@ class XendDomain:
|
||||
"""Get info about a single domain from xc.
|
||||
Returns None if not found.
|
||||
"""
|
||||
dom = int(dom)
|
||||
try:
|
||||
dom = int(dom)
|
||||
except ValueError:
|
||||
return None
|
||||
dominfo = xc.domain_getinfo(dom, 1)
|
||||
if dominfo == [] or dominfo[0]['dom'] != dom:
|
||||
dominfo = None
|
||||
@@ -198,6 +201,8 @@ class XendDomain:
|
||||
log.debug('XendDomain>reap> Suspended domain died id=%s', id)
|
||||
else:
|
||||
eserver.inject('xend.domain.suspended', [name, id])
|
||||
if dominfo:
|
||||
dominfo.state_set("suspended")
|
||||
continue
|
||||
if reason in ['poweroff', 'reboot']:
|
||||
eserver.inject('xend.domain.exit', [name, id, reason])
|
||||
@@ -349,7 +354,7 @@ class XendDomain:
|
||||
|
||||
l = fd.read_exact(sizeof_int,
|
||||
"not a valid guest state file: config size read")
|
||||
vmconfig_size = unpack("i", l)[0] # XXX endianess
|
||||
vmconfig_size = unpack("!i", l)[0]
|
||||
vmconfig_buf = fd.read_exact(vmconfig_size,
|
||||
"not a valid guest state file: config read")
|
||||
|
||||
@@ -384,17 +389,22 @@ class XendDomain:
|
||||
p.register(child.childerr.fileno())
|
||||
while True:
|
||||
r = p.poll()
|
||||
for l in child.childerr.readlines():
|
||||
log.error(l.rstrip())
|
||||
lasterr = l.rstrip()
|
||||
for l in child.fromchild.readlines():
|
||||
log.info(l.rstrip())
|
||||
for (fd, event) in r:
|
||||
if not event & select.POLLIN:
|
||||
continue
|
||||
if fd == child.childerr.fileno():
|
||||
l = child.childerr.readline()
|
||||
log.error(l.rstrip())
|
||||
lasterr = l.rstrip()
|
||||
if fd == child.fromchild.fileno():
|
||||
l = child.fromchild.readline()
|
||||
log.info(l.rstrip())
|
||||
if filter(lambda (fd, event): event & select.POLLHUP, r):
|
||||
break
|
||||
|
||||
if child.wait() != 0:
|
||||
raise XendError("xc_restore failed: %s" % lasterr)
|
||||
|
||||
|
||||
return dominfo
|
||||
|
||||
except IOError, ex:
|
||||
@@ -592,10 +602,66 @@ class XendDomain:
|
||||
@param dst: destination file
|
||||
@param progress: output progress if true
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
xmigrate = XendMigrate.instance()
|
||||
return xmigrate.save_begin(dominfo, dst)
|
||||
|
||||
|
||||
SIGNATURE = "LinuxGuestRecord"
|
||||
sizeof_int = calcsize("i")
|
||||
PATH_XC_SAVE = "/usr/libexec/xen/xc_save"
|
||||
|
||||
try:
|
||||
dominfo = self.domain_lookup(id)
|
||||
|
||||
fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
||||
|
||||
if os.write(fd, SIGNATURE) != len(SIGNATURE):
|
||||
raise XendError("could not write guest state file: signature")
|
||||
|
||||
config = sxp.to_string(dominfo.sxpr())
|
||||
if os.write(fd, pack("!i", len(config))) != sizeof_int:
|
||||
raise XendError("could not write guest state file: config len")
|
||||
if os.write(fd, config) != len(config):
|
||||
raise XendError("could not write guest state file: config")
|
||||
|
||||
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
|
||||
dominfo.id]
|
||||
log.info("[xc_save] " + join(cmd))
|
||||
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
|
||||
|
||||
lasterr = ""
|
||||
p = select.poll()
|
||||
p.register(child.fromchild.fileno())
|
||||
p.register(child.childerr.fileno())
|
||||
while True:
|
||||
r = p.poll()
|
||||
for (fd, event) in r:
|
||||
if not event & select.POLLIN:
|
||||
continue
|
||||
if fd == child.childerr.fileno():
|
||||
l = child.childerr.readline()
|
||||
log.error(l.rstrip())
|
||||
lasterr = l.rstrip()
|
||||
if fd == child.fromchild.fileno():
|
||||
l = child.fromchild.readline()
|
||||
if l.rstrip() == "suspend":
|
||||
log.info("suspending %s" % dominfo.id)
|
||||
self.domain_shutdown(dominfo.id, reason='suspend')
|
||||
dominfo.state_wait("suspended")
|
||||
log.info("suspend %s done" % dominfo.id)
|
||||
child.tochild.write("done\n")
|
||||
child.tochild.flush()
|
||||
if filter(lambda (fd, event): event & select.POLLHUP, r):
|
||||
break
|
||||
|
||||
if child.wait() >> 8 == 127:
|
||||
lasterr = "popen %s failed" % PATH_XC_SAVE
|
||||
if child.wait() != 0:
|
||||
raise XendError("xc_save failed: %s" % lasterr)
|
||||
|
||||
self.domain_destroy(dominfo.id)
|
||||
return None
|
||||
|
||||
except:
|
||||
raise
|
||||
|
||||
def domain_pincpu(self, id, vcpu, cpumap):
|
||||
"""Set which cpus vcpu can use
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Author: Mike Wray <mike.wray@hp.com>
|
||||
import string
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
import xen.util.ip
|
||||
@@ -74,6 +75,7 @@ STATE_RESTART_BOOTING = 'booting'
|
||||
|
||||
STATE_VM_OK = "ok"
|
||||
STATE_VM_TERMINATED = "terminated"
|
||||
STATE_VM_SUSPENDED = "suspended"
|
||||
|
||||
|
||||
def domain_exists(name):
|
||||
@@ -288,6 +290,7 @@ class XendDomainInfo:
|
||||
self.netif_backend = False
|
||||
#todo: state: running, suspended
|
||||
self.state = STATE_VM_OK
|
||||
self.state_updated = threading.Condition()
|
||||
self.shutdown_pending = None
|
||||
|
||||
#todo: set to migrate info if migrating
|
||||
@@ -328,6 +331,19 @@ class XendDomainInfo:
|
||||
self.info = info
|
||||
self.memory = self.info['mem_kb'] / 1024
|
||||
|
||||
def state_set(self, state):
|
||||
self.state_updated.acquire()
|
||||
if self.state != state:
|
||||
self.state = state
|
||||
self.state_updated.notifyAll()
|
||||
self.state_updated.release()
|
||||
|
||||
def state_wait(self, state):
|
||||
self.state_updated.acquire()
|
||||
while self.state != state:
|
||||
self.state_updated.wait()
|
||||
self.state_updated.release()
|
||||
|
||||
def __str__(self):
|
||||
s = "domain"
|
||||
s += " id=" + self.id
|
||||
|
||||