mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
Have xenstored initialise its connections, meaning that xend can be out of
that loop completely -- the xc_init_store, initDomainStore calls can all go. Have xenstored understand where the local domain information goes. Xend no longer has to generate a path and pass it to xenstored through xs_introduce_domain -- we just allow xenstored to generate the path, and then call GetDomainPath later. There is still some work required to tidy this up. Change the uuid module to generate uuids as lists of bytes, not in the stringified form. Added a unit test for that module. Change the semantics of Xend restart, relying on these changes to the xenstored semantics and earlier changes to add an opaque handle to the hypervisor's domain-specific data block. The semantics are now clearer, as Xend can validate whether the details in the store match the current live domain. Added a usage statement to xenstored. Some of this code is by Steven Hand. Signed-off-by: Ewan Mellor <ewan@xensource.com>
This commit is contained in:
1 parent
dcd2f1cf1f
commit
706fcdf4de
17 files changed
+312
-247
No files matched your search
@@ -862,23 +862,6 @@ static PyObject *pyxc_domain_memory_increase_reservation(PyObject *self,
|
||||
return zero;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_init_store(PyObject *self, PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
int remote_port;
|
||||
|
||||
static char *kwd_list[] = { "remote_port", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list,
|
||||
&remote_port) )
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(xc_init_store(xc->xc_handle, remote_port));
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef pyxc_methods[] = {
|
||||
{ "handle",
|
||||
(PyCFunction)pyxc_handle,
|
||||
@@ -1158,13 +1141,6 @@ static PyMethodDef pyxc_methods[] = {
|
||||
" mem_kb [long]: .\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "init_store",
|
||||
(PyCFunction)pyxc_init_store,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Initialize the store event channel and return the store page mfn.\n"
|
||||
" remote_port [int]: store event channel port number.\n"
|
||||
"Returns: [int] mfn on success; <0 on error.\n" },
|
||||
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -686,7 +686,6 @@ static PyObject *xspy_transaction_end(PyObject *self, PyObject *args,
|
||||
" dom [int] : domain id\n" \
|
||||
" page [long] : address of domain's xenstore page\n" \
|
||||
" port [int] : port the domain is using for xenstore\n" \
|
||||
" path [string]: path to the domain's data in xenstore\n" \
|
||||
"\n" \
|
||||
"Returns None on success.\n" \
|
||||
"Raises RuntimeError on error.\n" \
|
||||
@@ -695,12 +694,11 @@ static PyObject *xspy_transaction_end(PyObject *self, PyObject *args,
|
||||
static PyObject *xspy_introduce_domain(PyObject *self, PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
static char *kwd_spec[] = { "dom", "page", "port", "path", NULL };
|
||||
static char *arg_spec = "iiis|";
|
||||
static char *kwd_spec[] = { "dom", "page", "port", NULL };
|
||||
static char *arg_spec = "iii";
|
||||
domid_t dom = 0;
|
||||
unsigned long page = 0;
|
||||
unsigned int port = 0;
|
||||
char *path = NULL;
|
||||
|
||||
struct xs_handle *xh = xshandle(self);
|
||||
PyObject *val = NULL;
|
||||
@@ -709,10 +707,10 @@ static PyObject *xspy_introduce_domain(PyObject *self, PyObject *args,
|
||||
if (!xh)
|
||||
goto exit;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
|
||||
&dom, &page, &port, &path))
|
||||
&dom, &page, &port))
|
||||
goto exit;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
xsval = xs_introduce_domain(xh, dom, page, port, path);
|
||||
xsval = xs_introduce_domain(xh, dom, page, port);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (!xsval) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
|
||||
@@ -143,15 +143,13 @@ def restore(xd, fd):
|
||||
if m:
|
||||
store_mfn = int(m.group(2))
|
||||
dominfo.setStoreRef(store_mfn)
|
||||
log.debug("IntroduceDomain %d %d %d %s",
|
||||
log.debug("IntroduceDomain %d %d %d",
|
||||
dominfo.getDomid(),
|
||||
store_mfn,
|
||||
dominfo.store_channel,
|
||||
dominfo.getDomainPath())
|
||||
dominfo.store_channel)
|
||||
IntroduceDomain(dominfo.getDomid(),
|
||||
store_mfn,
|
||||
dominfo.store_channel,
|
||||
dominfo.getDomainPath())
|
||||
dominfo.store_channel)
|
||||
else:
|
||||
m = re.match(r"^(console-mfn) (\d+)$", line)
|
||||
if m:
|
||||
@@ -171,6 +169,7 @@ def forkHelper(cmd, fd, inputHandler, closeToChild):
|
||||
if closeToChild:
|
||||
child.tochild.close()
|
||||
|
||||
lasterr = "error unknown"
|
||||
try:
|
||||
fds = [child.fromchild.fileno(),
|
||||
child.childerr.fileno()]
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
Nothing here is persistent (across reboots).
|
||||
Needs to be persistent for one uptime.
|
||||
"""
|
||||
import os
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import xen.lowlevel.xc
|
||||
@@ -62,8 +64,11 @@ class XendDomain:
|
||||
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh(True)
|
||||
self._add_domain(
|
||||
XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN],
|
||||
True))
|
||||
self.dom0_setup()
|
||||
self.refresh(True)
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
@@ -178,25 +183,23 @@ class XendDomain:
|
||||
'Cannot recreate information for dying domain %d.'
|
||||
' Xend will ignore this domain from now on.',
|
||||
doms[d]['dom'])
|
||||
elif d == PRIV_DOMAIN:
|
||||
log.fatal(
|
||||
"No record of privileged domain %d! Terminating.", d)
|
||||
sys.exit(1)
|
||||
else:
|
||||
try:
|
||||
dominfo = XendDomainInfo.recreate(doms[d])
|
||||
self._add_domain(dominfo)
|
||||
self._add_domain(
|
||||
XendDomainInfo.recreate(doms[d], False))
|
||||
except:
|
||||
if d == PRIV_DOMAIN:
|
||||
log.exception(
|
||||
"Failed to recreate information for domain "
|
||||
"%d. Doing nothing except crossing my "
|
||||
"fingers.", d)
|
||||
else:
|
||||
log.exception(
|
||||
"Failed to recreate information for domain "
|
||||
"%d. Destroying it in the hope of "
|
||||
"recovery.", d)
|
||||
try:
|
||||
xc.domain_destroy(dom = d)
|
||||
except:
|
||||
log.exception('Destruction of %d failed.', d)
|
||||
log.exception(
|
||||
"Failed to recreate information for domain "
|
||||
"%d. Destroying it in the hope of "
|
||||
"recovery.", d)
|
||||
try:
|
||||
xc.domain_destroy(dom = d)
|
||||
except:
|
||||
log.exception('Destruction of %d failed.', d)
|
||||
|
||||
|
||||
## public:
|
||||
|
||||
@@ -28,7 +28,6 @@ import logging
|
||||
import string
|
||||
import time
|
||||
import threading
|
||||
import errno
|
||||
|
||||
import xen.lowlevel.xc
|
||||
from xen.util import asserts
|
||||
@@ -42,7 +41,7 @@ from xen.xend.XendBootloader import bootloader
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
from xen.xend.XendRoot import get_component
|
||||
|
||||
from uuid import getUuid
|
||||
import uuid
|
||||
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
|
||||
@@ -153,7 +152,7 @@ def create(config):
|
||||
|
||||
log.debug("XendDomainInfo.create(%s)", config)
|
||||
|
||||
vm = XendDomainInfo(getUuid(), parseConfig(config))
|
||||
vm = XendDomainInfo(uuid.create(), parseConfig(config))
|
||||
try:
|
||||
vm.construct()
|
||||
vm.initDomain()
|
||||
@@ -169,7 +168,7 @@ def create(config):
|
||||
raise
|
||||
|
||||
|
||||
def recreate(xeninfo):
|
||||
def recreate(xeninfo, priv):
|
||||
"""Create the VM object for an existing domain. The domain must not
|
||||
be dying, as the paths in the store should already have been removed,
|
||||
and asking us to recreate them causes problems."""
|
||||
@@ -179,39 +178,52 @@ def recreate(xeninfo):
|
||||
assert not xeninfo['dying']
|
||||
|
||||
domid = xeninfo['dom']
|
||||
dompath = GetDomainPath(domid)
|
||||
if not dompath:
|
||||
raise XendError(
|
||||
'No domain path in store for existing domain %d' % domid)
|
||||
try:
|
||||
dompath = GetDomainPath(domid)
|
||||
if not dompath:
|
||||
raise XendError(
|
||||
'No domain path in store for existing domain %d' % domid)
|
||||
vmpath = xstransact.Read(dompath, "vm")
|
||||
if not vmpath:
|
||||
raise XendError(
|
||||
'No vm path in store for existing domain %d' % domid)
|
||||
uuid = xstransact.Read(vmpath, "uuid")
|
||||
if not uuid:
|
||||
uuid1_str = xstransact.Read(vmpath, "uuid")
|
||||
if not uuid1_str:
|
||||
raise XendError(
|
||||
'No vm/uuid path in store for existing domain %d' % domid)
|
||||
|
||||
log.info("Recreating domain %d, UUID %s.", domid, uuid)
|
||||
uuid1 = uuid.fromString(uuid1_str)
|
||||
|
||||
vm = XendDomainInfo(uuid, xeninfo, domid, True)
|
||||
uuid2 = xeninfo['handle']
|
||||
|
||||
if uuid1 != uuid2:
|
||||
raise XendError(
|
||||
'Uuid in store does not match uuid for existing domain %d: '
|
||||
'%s != %s' % (domid, uuid1_str, uuid.toString(uuid2)))
|
||||
|
||||
log.info("Recreating domain %d, UUID %s.", domid, uuid1_str)
|
||||
|
||||
vm = XendDomainInfo(uuid2, xeninfo, domid, dompath, True)
|
||||
|
||||
except Exception, exn:
|
||||
log.warn(str(exn))
|
||||
|
||||
uuid = getUuid()
|
||||
if priv:
|
||||
new_uuid = [0 for i in range(0, 16)]
|
||||
else:
|
||||
new_uuid = uuid.create()
|
||||
|
||||
log.info("Recreating domain %d with new UUID %s.", domid, uuid)
|
||||
log.info("Recreating domain %d with new UUID %s.", domid,
|
||||
uuid.toString(new_uuid))
|
||||
|
||||
vm = XendDomainInfo(uuid, xeninfo, domid, True)
|
||||
vm = XendDomainInfo(new_uuid, xeninfo, domid, dompath, True)
|
||||
vm.removeDom()
|
||||
vm.storeVmDetails()
|
||||
vm.storeDomDetails()
|
||||
|
||||
vm.create_channel()
|
||||
if domid == 0:
|
||||
vm.initStoreConnection()
|
||||
if domid != 0:
|
||||
# Setup store and console channels
|
||||
vm.create_channels()
|
||||
|
||||
vm.refreshShutdown(xeninfo)
|
||||
return vm
|
||||
@@ -225,12 +237,12 @@ def restore(config):
|
||||
|
||||
log.debug("XendDomainInfo.restore(%s)", config)
|
||||
|
||||
uuid = sxp.child_value(config, 'uuid')
|
||||
vm = XendDomainInfo(uuid, parseConfig(config))
|
||||
vm = XendDomainInfo(uuid.fromString(sxp.child_value(config, 'uuid')),
|
||||
parseConfig(config))
|
||||
try:
|
||||
vm.construct()
|
||||
vm.configure()
|
||||
vm.create_channel()
|
||||
vm.create_channels()
|
||||
vm.storeVmDetails()
|
||||
vm.storeDomDetails()
|
||||
vm.refreshShutdown()
|
||||
@@ -355,9 +367,10 @@ class XendDomainInfo:
|
||||
MINIMUM_RESTART_TIME = 20
|
||||
|
||||
|
||||
def __init__(self, uuid, info, domid = None, augment = False):
|
||||
def __init__(self, uuidbytes, info, domid = None, dompath = None,
|
||||
augment = False):
|
||||
|
||||
self.uuid = uuid
|
||||
self.uuidbytes = uuidbytes
|
||||
self.info = info
|
||||
|
||||
if domid is not None:
|
||||
@@ -367,11 +380,8 @@ class XendDomainInfo:
|
||||
else:
|
||||
self.domid = None
|
||||
|
||||
self.vmpath = VMROOT + uuid
|
||||
if self.domid is None:
|
||||
self.dompath = None
|
||||
else:
|
||||
self.dompath = DOMROOT + str(self.domid)
|
||||
self.vmpath = VMROOT + uuid.toString(uuidbytes)
|
||||
self.dompath = dompath
|
||||
|
||||
if augment:
|
||||
self.augmentInfo()
|
||||
@@ -440,6 +450,9 @@ class XendDomainInfo:
|
||||
defaultInfo('cpu', lambda: None)
|
||||
defaultInfo('cpu_weight', lambda: 1.0)
|
||||
defaultInfo('vcpus', lambda: 1)
|
||||
|
||||
self.info['vcpus'] = int(self.info['vcpus'])
|
||||
|
||||
defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
|
||||
defaultInfo('bootloader', lambda: None)
|
||||
defaultInfo('backend', lambda: [])
|
||||
@@ -576,7 +589,7 @@ class XendDomainInfo:
|
||||
|
||||
def storeVmDetails(self):
|
||||
to_store = {
|
||||
'uuid': self.uuid,
|
||||
'uuid': uuid.toString(self.uuidbytes),
|
||||
|
||||
# XXX
|
||||
'memory/target': str(self.info['memory_KiB'])
|
||||
@@ -651,9 +664,6 @@ class XendDomainInfo:
|
||||
def getDomainPath(self):
|
||||
return self.dompath
|
||||
|
||||
def getUuid(self):
|
||||
return self.uuid
|
||||
|
||||
|
||||
def getVCpuCount(self):
|
||||
return self.info['vcpus']
|
||||
@@ -921,7 +931,7 @@ class XendDomainInfo:
|
||||
def sxpr(self):
|
||||
sxpr = ['domain',
|
||||
['domid', self.domid],
|
||||
['uuid', self.uuid],
|
||||
['uuid', uuid.toString(self.uuidbytes)],
|
||||
['memory', self.info['memory_KiB'] / 1024]]
|
||||
|
||||
for e in ROUNDTRIPPING_CONFIG_ENTRIES:
|
||||
@@ -1038,7 +1048,8 @@ class XendDomainInfo:
|
||||
self.domid,
|
||||
self.info['ssidref'])
|
||||
|
||||
self.domid = xc.domain_create(dom = 0, ssidref = self.info['ssidref'])
|
||||
self.domid = xc.domain_create(dom = 0, ssidref = self.info['ssidref'],
|
||||
handle = self.uuidbytes)
|
||||
|
||||
if self.domid < 0:
|
||||
raise VmError('Creating domain failed: name=%s' %
|
||||
@@ -1089,10 +1100,9 @@ class XendDomainInfo:
|
||||
def construct_image(self):
|
||||
"""Construct the boot image for the domain.
|
||||
"""
|
||||
self.create_channel()
|
||||
self.create_channels()
|
||||
self.image.createImage()
|
||||
IntroduceDomain(self.domid, self.store_mfn,
|
||||
self.store_channel, self.dompath)
|
||||
IntroduceDomain(self.domid, self.store_mfn, self.store_channel)
|
||||
|
||||
|
||||
## public:
|
||||
@@ -1199,7 +1209,7 @@ class XendDomainInfo:
|
||||
self.storeDom(path, port)
|
||||
return port
|
||||
|
||||
def create_channel(self):
|
||||
def create_channels(self):
|
||||
"""Create the channels to the domain.
|
||||
"""
|
||||
self.store_channel = self.eventChannel("store/port")
|
||||
@@ -1312,13 +1322,15 @@ class XendDomainInfo:
|
||||
"""
|
||||
|
||||
new_name = self.generateUniqueName()
|
||||
new_uuid = getUuid()
|
||||
new_uuid = uuid.create()
|
||||
new_uuid_str = uuid.toString(new_uuid)
|
||||
log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
|
||||
self.info['name'], self.domid, self.uuid, new_name, new_uuid)
|
||||
self.info['name'], self.domid, uuid.toString(self.uuidbytes),
|
||||
new_name, new_uuid_str)
|
||||
self.release_devices()
|
||||
self.info['name'] = new_name
|
||||
self.uuid = new_uuid
|
||||
self.vmpath = VMROOT + new_uuid
|
||||
self.uuidbytes = new_uuid
|
||||
self.vmpath = VMROOT + new_uuid_str
|
||||
self.storeVmDetails()
|
||||
self.preserve()
|
||||
|
||||
@@ -1384,20 +1396,6 @@ class XendDomainInfo:
|
||||
self.storeDom("control/sysrq", '%c' % key)
|
||||
|
||||
|
||||
def initStoreConnection(self):
|
||||
ref = xc.init_store(self.store_channel)
|
||||
if ref and ref >= 0:
|
||||
self.setStoreRef(ref)
|
||||
try:
|
||||
IntroduceDomain(self.domid, ref, self.store_channel,
|
||||
self.dompath)
|
||||
except RuntimeError, ex:
|
||||
if ex.args[0] == errno.EISCONN:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def infoIsSet(self, name):
|
||||
return name in self.info and self.info[name] is not None
|
||||
|
||||
|
||||
@@ -36,23 +36,27 @@ def getUuidUuidgen(random = True):
|
||||
cmd += " -r"
|
||||
else:
|
||||
cmd += " -t"
|
||||
return commands.getoutput(cmd)
|
||||
return fromString(commands.getoutput(cmd))
|
||||
|
||||
|
||||
def getUuidRandom():
|
||||
"""Generate a random UUID."""
|
||||
|
||||
bytes = [ random.randint(0, 255) for i in range(0, 16) ]
|
||||
# Encode the variant.
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||||
f = "%02x"
|
||||
return ( "-".join([f*4, f*2, f*2, f*2, f*6]) % tuple(bytes) )
|
||||
return [ random.randint(0, 255) for i in range(0, 16) ]
|
||||
|
||||
|
||||
#uuidFactory = getUuidUuidgen
|
||||
uuidFactory = getUuidRandom
|
||||
|
||||
|
||||
def getUuid():
|
||||
def create():
|
||||
return uuidFactory()
|
||||
|
||||
|
||||
def toString(u):
|
||||
f = "%02x"
|
||||
return ( "-".join([f*4, f*2, f*2, f*2, f*6]) % tuple(u) )
|
||||
|
||||
def fromString(s):
|
||||
s = s.replace('-', '')
|
||||
return [ int(s[i : i + 2], 16) for i in range(0, 32, 2) ]
|
||||
@@ -19,8 +19,8 @@ def xshandle():
|
||||
xs_lock.release()
|
||||
return xs_handle
|
||||
|
||||
def IntroduceDomain(domid, page, port, path):
|
||||
return xshandle().introduce_domain(domid, page, port, path)
|
||||
def IntroduceDomain(domid, page, port):
|
||||
return xshandle().introduce_domain(domid, page, port)
|
||||
|
||||
def GetDomainPath(domid):
|
||||
return xshandle().get_domain_path(domid)
|
||||