aboutsummaryrefslogtreecommitdiffstats
path: root/tools/python
diff options
context:
space:
mode:
Diffstat (limited to 'tools/python')
-rw-r--r--tools/python/setup.py16
-rw-r--r--tools/python/xen/lowlevel/xc/xc.c31
-rw-r--r--tools/python/xen/lowlevel/xs/xs.c617
-rw-r--r--tools/python/xen/lowlevel/xu/xu.c3
-rw-r--r--tools/python/xen/util/mac.py11
-rw-r--r--tools/python/xen/web/SrvDir.py23
-rw-r--r--tools/python/xen/xend/PrettyPrint.py21
-rw-r--r--tools/python/xen/xend/XendCheckpoint.py8
-rw-r--r--tools/python/xen/xend/XendDomain.py284
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py704
-rw-r--r--tools/python/xen/xend/XendRoot.py21
-rw-r--r--tools/python/xen/xend/XendVnet.py22
-rw-r--r--tools/python/xen/xend/image.py339
-rw-r--r--tools/python/xen/xend/server/SrvConsole.py2
-rw-r--r--tools/python/xen/xend/server/SrvDaemon.py44
-rw-r--r--tools/python/xen/xend/server/SrvDomain.py38
-rw-r--r--tools/python/xen/xend/server/SrvDomainDir.py2
-rwxr-xr-xtools/python/xen/xend/server/blkif.py39
-rwxr-xr-xtools/python/xen/xend/server/channel.py181
-rwxr-xr-xtools/python/xen/xend/server/console.py14
-rwxr-xr-xtools/python/xen/xend/server/controller.py125
-rwxr-xr-xtools/python/xen/xend/server/netif.py85
-rw-r--r--tools/python/xen/xend/server/params.py36
-rw-r--r--tools/python/xen/xend/server/usbif.py13
-rw-r--r--tools/python/xen/xend/uuid.py65
-rw-r--r--tools/python/xen/xend/xenstore/__init__.py2
-rw-r--r--tools/python/xen/xend/xenstore/xsnode.py382
-rw-r--r--tools/python/xen/xend/xenstore/xsobj.py522
-rw-r--r--tools/python/xen/xend/xenstore/xsresource.py136
29 files changed, 2994 insertions, 792 deletions
diff --git a/tools/python/setup.py b/tools/python/setup.py
index e6b04f8708..fabe80bd8b 100644
--- a/tools/python/setup.py
+++ b/tools/python/setup.py
@@ -9,13 +9,15 @@ extra_compile_args = [ "-fno-strict-aliasing", "-Wall", "-Werror" ]
include_dirs = [ XEN_ROOT + "/tools/python/xen/lowlevel/xu",
XEN_ROOT + "/tools/libxc",
+ XEN_ROOT + "/tools/xenstore",
XEN_ROOT + "/tools/xcs",
]
library_dirs = [ XEN_ROOT + "/tools/libxc",
+ XEN_ROOT + "/tools/xenstore",
]
-libraries = [ "xc" ]
+libraries = [ "xc", "xenstore" ]
xc = Extension("xc",
extra_compile_args = extra_compile_args,
@@ -30,7 +32,14 @@ xu = Extension("xu",
library_dirs = library_dirs,
libraries = libraries,
sources = [ "xen/lowlevel/xu/xu.c" ])
-
+
+xs = Extension("xs",
+ extra_compile_args = extra_compile_args,
+ include_dirs = include_dirs + [ "xen/lowlevel/xs" ],
+ library_dirs = library_dirs,
+ libraries = libraries,
+ sources = [ "xen/lowlevel/xs/xs.c" ])
+
setup(name = 'xen',
version = '2.0',
description = 'Xen',
@@ -39,11 +48,12 @@ setup(name = 'xen',
'xen.util',
'xen.xend',
'xen.xend.server',
+ 'xen.xend.xenstore',
'xen.xm',
'xen.web',
],
ext_package = "xen.lowlevel",
- ext_modules = [ xc, xu ]
+ ext_modules = [ xc, xu, xs ]
)
os.chdir('logging')
diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c
index 013fbe1fcc..13d60be08e 100644
--- a/tools/python/xen/lowlevel/xc/xc.c
+++ b/tools/python/xen/lowlevel/xc/xc.c
@@ -14,6 +14,7 @@
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
+
#include "xc_private.h"
#include "linux_boot_params.h"
@@ -259,25 +260,28 @@ static PyObject *pyxc_linux_build(PyObject *self,
{
XcObject *xc = (XcObject *)self;
- u32 dom;
+ u32 dom;
char *image, *ramdisk = NULL, *cmdline = "";
- int control_evtchn, flags = 0, vcpus = 1;
+ int flags = 0, vcpus = 1;
+ int control_evtchn, store_evtchn;
+ unsigned long store_mfn = 0;
- static char *kwd_list[] = { "dom", "control_evtchn",
- "image", "ramdisk", "cmdline", "flags", "vcpus",
- NULL };
+ static char *kwd_list[] = { "dom", "control_evtchn", "store_evtchn",
+ "image", "ramdisk", "cmdline", "flags",
+ "vcpus", NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssii", kwd_list,
- &dom, &control_evtchn,
- &image, &ramdisk, &cmdline, &flags, &vcpus) )
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiis|ssii", kwd_list,
+ &dom, &control_evtchn, &store_evtchn,
+ &image, &ramdisk, &cmdline, &flags,
+ &vcpus) )
return NULL;
if ( xc_linux_build(xc->xc_handle, dom, image,
- ramdisk, cmdline, control_evtchn, flags, vcpus) != 0 )
+ ramdisk, cmdline, control_evtchn, flags, vcpus,
+ store_evtchn, &store_mfn) != 0 )
return PyErr_SetFromErrno(xc_error);
- Py_INCREF(zero);
- return zero;
+ return Py_BuildValue("{s:i}", "store_mfn", store_mfn);
}
static PyObject *pyxc_plan9_build(PyObject *self,
@@ -834,6 +838,7 @@ static PyMethodDef pyxc_methods[] = {
0, "\n"
"Query the xc control interface file descriptor.\n\n"
"Returns: [int] file descriptor\n" },
+
{ "domain_create",
(PyCFunction)pyxc_domain_create,
METH_VARARGS | METH_KEYWORDS, "\n"
@@ -844,8 +849,8 @@ static PyMethodDef pyxc_methods[] = {
{ "domain_dumpcore",
(PyCFunction)pyxc_domain_dumpcore,
METH_VARARGS | METH_KEYWORDS, "\n"
- "dump core of a domain.\n"
- " dom [int]: Identifier of domain to be paused.\n\n"
+ "Dump core of a domain.\n"
+ " dom [int]: Identifier of domain to dump core of.\n"
" corefile [string]: Name of corefile to be created.\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
diff --git a/tools/python/xen/lowlevel/xs/xs.c b/tools/python/xen/lowlevel/xs/xs.c
new file mode 100644
index 0000000000..98d7826809
--- /dev/null
+++ b/tools/python/xen/lowlevel/xs/xs.c
@@ -0,0 +1,617 @@
+#include <Python.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "xs.h"
+
+/** @file
+ * Python interface to the Xen Store Daemon (xs).
+ */
+
+/* Needed for Python versions earlier than 2.3. */
+//#ifndef PyMODINIT_FUNC
+//#define PyMODINIT_FUNC DL_EXPORT(void)
+//#endif
+
+#define PYPKG "xen.lowlevel.xs"
+
+/** Python wrapper round an xs handle.
+ */
+typedef struct XsHandle {
+ PyObject_HEAD;
+ struct xs_handle *xh;
+} XsHandle;
+
+static inline struct xs_handle *xshandle(PyObject *self)
+{
+ struct xs_handle *xh = ((XsHandle*)self)->xh;
+ if (!xh)
+ PyErr_SetString(PyExc_RuntimeError, "invalid xenstore daemon handle");
+ return xh;
+}
+
+static inline PyObject *pyvalue_int(int val) {
+ return (val
+ ? PyInt_FromLong(val)
+ : PyErr_SetFromErrno(PyExc_RuntimeError));
+}
+
+static inline PyObject *pyvalue_str(char *val) {
+ return (val
+ ? PyString_FromString(val)
+ : PyErr_SetFromErrno(PyExc_RuntimeError));
+}
+
+static PyObject *xspy_write(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", "data", "create", "excl", NULL };
+ static char *arg_spec = "ss#|ii";
+ char *path = NULL;
+ char *data = NULL;
+ int data_n = 0;
+ int create = 0;
+ int excl = 0;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int flags = 0;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &path, &data, &data_n, &create, &excl))
+ goto exit;
+ if (create)
+ flags |= O_CREAT;
+ if (excl)
+ flags |= O_EXCL;
+ xsval = xs_write(xh, path, data, data_n, flags);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_read(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ char *xsval = NULL;
+ int xsval_n = 0;
+ PyObject *val = NULL;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &path))
+ goto exit;
+ xsval = xs_read(xh, path, &xsval_n);
+ if (!xsval) {
+ val = pyvalue_int(0);
+ goto exit;
+ }
+ val = PyString_FromStringAndSize(xsval, xsval_n);
+ exit:
+ if (xsval)
+ free(xsval);
+ return val;
+}
+
+static PyObject *xspy_mkdir(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ xsval = xs_mkdir(xh, path);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_ls(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ char **xsval = NULL;
+ int xsval_n = 0;
+ int i;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ xsval = xs_directory(xh, path, &xsval_n);
+ if (!xsval) {
+ val = pyvalue_int(0);
+ goto exit;
+ }
+ val = PyList_New(xsval_n);
+ for (i = 0; i < xsval_n; i++)
+ PyList_SetItem(val, i, PyString_FromString(xsval[i]));
+ exit:
+ return val;
+}
+
+static PyObject *xspy_rm(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ xsval = xs_rm(xh, path);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_get_permissions(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ struct xs_permissions *perms;
+ int perms_n = 0;
+ int i;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ perms = xs_get_permissions(xh, path, &perms_n);
+ if (!perms) {
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ goto exit;
+ }
+ val = PyList_New(perms_n);
+ for (i = 0; i < perms_n; i++, perms++) {
+ PyObject *p = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i}",
+ "dom", perms->id,
+ "read", (perms->perms & XS_PERM_READ),
+ "write", (perms->perms & XS_PERM_WRITE),
+ "create", (perms->perms & XS_PERM_CREATE),
+ "owner", (perms->perms & XS_PERM_OWNER));
+ PyList_SetItem(val, i, p);
+ }
+ exit:
+ return val;
+}
+
+static PyObject *xspy_set_permissions(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", "perms", NULL };
+ static char *arg_spec = "sO";
+ char *path = NULL;
+ PyObject *perms = NULL;
+ static char *perm_names[] = { "dom", "read", "write", "create", "owner",
+ NULL };
+ static char *perm_spec = "i|iiii";
+
+ struct xs_handle *xh = xshandle(self);
+ int i, xsval;
+ struct xs_permissions *xsperms = NULL;
+ int xsperms_n = 0;
+ PyObject *tuple0 = NULL;
+ PyObject *val = NULL;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &path, &perms))
+ goto exit;
+ if (!PyList_Check(perms)) {
+ PyErr_SetString(PyExc_RuntimeError, "perms must be a list");
+ goto exit;
+ }
+ xsperms_n = PyList_Size(perms);
+ xsperms = calloc(xsperms_n, sizeof(struct xs_permissions));
+ if (!xsperms) {
+ PyErr_SetString(PyExc_RuntimeError, "out of memory");
+ goto exit;
+ }
+ tuple0 = PyTuple_New(0);
+ if (!tuple0)
+ goto exit;
+ for (i = 0; i < xsperms_n; i++) {
+ /* Domain the permissions apply to. */
+ int dom = 0;
+ /* Read/write perms. Set these. */
+ int p_read = 0, p_write = 0;
+ /* Create/owner perms. Ignore them.
+ * This is so the output from get_permissions() can be used
+ * as input to set_permissions().
+ */
+ int p_create = 0, p_owner = 0;
+ PyObject *p = PyList_GetItem(perms, i);
+ if (!PyArg_ParseTupleAndKeywords(tuple0, p, perm_spec, perm_names,
+ &dom, &p_read, &p_write, &p_create,
+ &p_owner))
+ goto exit;
+ xsperms[i].id = dom;
+ if (p_read)
+ xsperms[i].perms |= XS_PERM_READ;
+ if (p_write)
+ xsperms[i].perms |= XS_PERM_WRITE;
+ }
+ xsval = xs_set_permissions(xh, path, xsperms, xsperms_n);
+ val = pyvalue_int(xsval);
+ exit:
+ Py_XDECREF(tuple0);
+ if (xsperms)
+ free(xsperms);
+ return val;
+}
+
+static PyObject *xspy_watch(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", "priority", NULL };
+ static char *arg_spec = "s|i";
+ char *path = NULL;
+ int priority = 0;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &path, &priority))
+ goto exit;
+ xsval = xs_watch(xh, path, priority);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_read_watch(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { NULL };
+ static char *arg_spec = "";
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ char *xsval = NULL;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
+ goto exit;
+ xsval = xs_read_watch(xh);
+ val = pyvalue_str(xsval);
+ exit:
+ if (xsval)
+ free(xsval);
+ return val;
+}
+
+static PyObject *xspy_acknowledge_watch(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { NULL };
+ static char *arg_spec = "";
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
+ goto exit;
+ xsval = xs_acknowledge_watch(xh);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_unwatch(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ xsval = xs_unwatch(xh, path);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_transaction_start(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { "path", NULL };
+ static char *arg_spec = "s|";
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
+ goto exit;
+ xsval = xs_transaction_start(xh, path);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_transaction_end(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { "abort", NULL };
+ static char *arg_spec = "|i";
+ int abort = 0;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &abort))
+ goto exit;
+ xsval = xs_transaction_end(xh, abort);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+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|";
+ domid_t dom = 0;
+ unsigned long page = 0;
+ unsigned int port = 0;
+ char *path = NULL;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &dom, &page, &port, &path))
+ goto exit;
+ printf("%s> dom=%u page=0x%08lx port=%u path=%s\n", __FUNCTION__, dom,
+ page, port, path);
+ xsval = xs_introduce_domain(xh, dom, page, port, path);
+ printf("%s> xsval=%d\n", __FUNCTION__, xsval);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_release_domain(PyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *kwd_spec[] = { "dom", NULL };
+ static char *arg_spec = "i|";
+ domid_t dom;
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &dom))
+ goto exit;
+ printf("%s> dom=%u\n", __FUNCTION__, dom);
+ xsval = xs_release_domain(xh, dom);
+ printf("%s> xsval=%d\n", __FUNCTION__, xsval);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_close(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { NULL };
+ static char *arg_spec = "";
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 1;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
+ goto exit;
+ xs_daemon_close(xh);
+ ((XsHandle*)self)->xh = NULL;
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+static PyObject *xspy_shutdown(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { NULL };
+ static char *arg_spec = "";
+
+ struct xs_handle *xh = xshandle(self);
+ PyObject *val = NULL;
+ int xsval = 0;
+
+ if (!xh)
+ goto exit;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
+ goto exit;
+ xsval = xs_shutdown(xh);
+ val = pyvalue_int(xsval);
+ exit:
+ return val;
+}
+
+#define XSPY_METH(_name) \
+ #_name, \
+ (PyCFunction) xspy_ ## _name, \
+ (METH_VARARGS | METH_KEYWORDS)
+// mtime
+// ctime
+
+static PyMethodDef xshandle_methods[] = {
+ { XSPY_METH(read),
+ "read(path) : read data\n" },
+ { XSPY_METH(write),
+ "write(path, data, [creat], [excl]): write data\n" },
+ { XSPY_METH(ls),
+ "ls(path): list directory.\n" },
+ { XSPY_METH(mkdir),
+ "mkdir(path): make a directory.\n" },
+ { XSPY_METH(rm),
+ "rm(path): remove a path (dir must be empty).\n" },
+ { XSPY_METH(get_permissions),
+ "get_permissions(path)\n" },
+ { XSPY_METH(set_permissions),
+ "set_permissions(path)\n" },
+ { XSPY_METH(watch),
+ "watch(path)\n" },
+ { XSPY_METH(read_watch),
+ "read_watch()\n" },
+ { XSPY_METH(acknowledge_watch),
+ "acknowledge_watch()\n" },
+ { XSPY_METH(unwatch),
+ "unwatch()\n" },
+ { XSPY_METH(transaction_start),
+ "transaction_start()\n" },
+ { XSPY_METH(transaction_end),
+ "transaction_end([abort])\n" },
+ { XSPY_METH(introduce_domain),
+ "introduce_domain(dom, page, port)\n" },
+ { XSPY_METH(release_domain),
+ "release_domain(dom)\n" },
+ { XSPY_METH(close),
+ "close()\n" },
+ { XSPY_METH(shutdown),
+ "shutdown()\n" },
+ { NULL, NULL, 0, NULL }
+};
+
+static PyObject *xshandle_getattr(PyObject *self, char *name)
+{
+ PyObject *val = NULL;
+ if (strcmp(name, "fileno") == 0) {
+ struct xs_handle *xh = xshandle(self);
+ val = PyInt_FromLong((xh ? xs_fileno(xh) : -1));
+ } else
+ val = Py_FindMethod(xshandle_methods, self, name);
+ return val;
+}
+
+static void xshandle_dealloc(PyObject *self)
+{
+ XsHandle *xh = (XsHandle*)self;
+ if (xh->xh) {
+ xs_daemon_close(xh->xh);
+ xh->xh = NULL;
+ }
+ PyObject_Del(self);
+}
+
+static PyTypeObject xshandle_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "xshandle",
+ sizeof(XsHandle),
+ 0,
+ xshandle_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xshandle_getattr, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL /* tp_hash */
+};
+
+static PyObject *xshandle_open(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_spec[] = { "readonly", NULL };
+ static char *arg_spec = "|i";
+ int readonly = 0;
+
+ XsHandle *xsh = NULL;
+ PyObject *val = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
+ &readonly))
+ goto exit;
+
+ xsh = PyObject_New(XsHandle, &xshandle_type);
+ if (!xsh)
+ goto exit;
+ xsh->xh = (readonly ? xs_daemon_open_readonly() : xs_daemon_open());
+ if (!xsh->xh) {
+ PyObject_Del(xsh);
+ val = pyvalue_int(0);
+ goto exit;
+ }
+ val = (PyObject *)xsh;
+ exit:
+ return val;
+}
+
+static PyMethodDef xs_methods[] = {
+ { "open", (PyCFunction)xshandle_open, (METH_VARARGS | METH_KEYWORDS),
+ "Open a connection to the xenstore daemon.\n" },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC initxs (void)
+{
+ PyObject *module;
+
+ module = Py_InitModule(PYPKG, xs_methods);
+}
diff --git a/tools/python/xen/lowlevel/xu/xu.c b/tools/python/xen/lowlevel/xu/xu.c
index c9c5b3873a..359cb71a2d 100644
--- a/tools/python/xen/lowlevel/xu/xu.c
+++ b/tools/python/xen/lowlevel/xu/xu.c
@@ -1370,7 +1370,8 @@ static PyObject *xu_port_new(PyObject *self, PyObject *args, PyObject *kwds)
fail1:
PyObject_Del((PyObject *)xup);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, "cannot create port");
+ return NULL;
}
static PyObject *xu_port_getattr(PyObject *obj, char *name)
diff --git a/tools/python/xen/util/mac.py b/tools/python/xen/util/mac.py
new file mode 100644
index 0000000000..47dffd80d5
--- /dev/null
+++ b/tools/python/xen/util/mac.py
@@ -0,0 +1,11 @@
+
+from string import join, split
+
+def macToString(mac):
+ return ':'.join(map(lambda x: "%02x" % x, mac))
+
+def macFromString(str):
+ mac = [ int(x, 16) for x in str.split(':') ]
+ if len(mac) != 6:
+ raise ValueError("invalid mac: %s" % str)
+ return mac
diff --git a/tools/python/xen/web/SrvDir.py b/tools/python/xen/web/SrvDir.py
index fb9eb14b3c..b168a8ef48 100644
--- a/tools/python/xen/web/SrvDir.py
+++ b/tools/python/xen/web/SrvDir.py
@@ -77,19 +77,16 @@ class SrvDir(SrvBase):
return v
def render_GET(self, req):
- try:
- if self.use_sxp(req):
- req.setHeader("Content-type", sxp.mime_type)
- self.ls(req, 1)
- else:
- req.write('<html><head></head><body>')
- self.print_path(req)
- self.ls(req)
- self.form(req)
- req.write('</body></html>')
- return ''
- except Exception, ex:
- self._perform_err(ex, "GET", req)
+ if self.use_sxp(req):
+ req.setHeader("Content-type", sxp.mime_type)
+ self.ls(req, 1)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ self.ls(req)
+ self.form(req)
+ req.write('</body></html>')
+ return ''
def ls(self, req, use_sxp=0):
url = req.prePathURL()
diff --git a/tools/python/xen/xend/PrettyPrint.py b/tools/python/xen/xend/PrettyPrint.py
index 5fcc6e6d08..a57a3c6b52 100644
--- a/tools/python/xen/xend/PrettyPrint.py
+++ b/tools/python/xen/xend/PrettyPrint.py
@@ -285,15 +285,18 @@ def prettyprint(sxpr, out=sys.stdout, width=80):
sxp.show(sxpr, out=out)
print >> out
-def prettyprintstring(sxp):
- class tmpstr:
- def __init__(self):
- self.str = ""
- def write(self, str):
- self.str = self.str + str
- tmp = tmpstr()
- prettyprint(sxp, out=tmp)
- return tmp.str
+def prettyprintstring(sxpr, width=80):
+ """Prettyprint an SXP form to a string.
+
+ sxpr s-expression
+ width maximum output width
+ """
+ io = StringIO.StringIO()
+ prettyprint(sxpr, out=io, width=width)
+ io.seek(0)
+ val = io.getvalue()
+ io.close()
+ return val
def main():
pin = sxp.Parser()
diff --git a/tools/python/xen/xend/XendCheckpoint.py b/tools/python/xen/xend/XendCheckpoint.py
index e3908df885..654fb022c5 100644
--- a/tools/python/xen/xend/XendCheckpoint.py
+++ b/tools/python/xen/xend/XendCheckpoint.py
@@ -43,7 +43,7 @@ def save(xd, fd, dominfo):
write_exact(fd, config, "could not write guest state file: config")
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
- dominfo.id]
+ str(dominfo.id)]
log.info("[xc_save] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
@@ -63,10 +63,10 @@ def save(xd, fd, dominfo):
if fd == child.fromchild.fileno():
l = child.fromchild.readline()
if l.rstrip() == "suspend":
- log.info("suspending %s" % dominfo.id)
+ log.info("suspending %d" % dominfo.id)
xd.domain_shutdown(dominfo.id, reason='suspend')
dominfo.state_wait("suspended")
- log.info("suspend %s done" % dominfo.id)
+ log.info("suspend %d done" % dominfo.id)
child.tochild.write("done\n")
child.tochild.flush()
if filter(lambda (fd, event): event & select.POLLHUP, r):
@@ -109,7 +109,7 @@ def restore(xd, fd):
"not a valid guest state file: pfn count out of range")
cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
- dominfo.id, str(nr_pfns)]
+ str(dominfo.id), str(nr_pfns)]
log.info("[xc_restore] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
child.tochild.close()
diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py
index 3fb066327f..ff688f6df1 100644
--- a/tools/python/xen/xend/XendDomain.py
+++ b/tools/python/xen/xend/XendDomain.py
@@ -7,46 +7,42 @@
"""
import errno
import os
-import scheduler
-import string
import sys
-import traceback
import time
+import traceback
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
-from xen.xend.server import relocate
-import sxp
-import XendRoot; xroot = XendRoot.instance()
-import XendCheckpoint
-import XendDB
-import XendDomainInfo
-import EventServer; eserver = EventServer.instance()
-from XendError import XendError
-from XendLogging import log
-
+from xen.xend import sxp
+from xen.xend import XendRoot; xroot = XendRoot.instance()
+from xen.xend import XendCheckpoint
+from xen.xend.XendDomainInfo import XendDomainInfo, shutdown_reason
+from xen.xend import EventServer; eserver = EventServer.instance()
+from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
+from xen.xend import scheduler
from xen.xend.server import channel
+from xen.xend.server import relocate
+from xen.xend.uuid import getUuid
+from xen.xend.xenstore import XenNode, DBMap
__all__ = [ "XendDomain" ]
SHUTDOWN_TIMEOUT = 30
+class XendDomainDict(dict):
+ def get_by_name(self, name):
+ try:
+ return filter(lambda d: d.name == name, self.values())[0]
+ except IndexError, err:
+ return None
+
class XendDomain:
"""Index of all domains. Singleton.
"""
- """Path to domain database."""
- dbpath = "domain"
-
- class XendDomainDict(dict):
- def get_by_name(self, name):
- try:
- return filter(lambda d: d.name == name, self.values())[0]
- except IndexError, err:
- return None
-
"""Dict of domain info indexed by domain id."""
- domains = XendDomainDict()
+ domains = None
def __init__(self):
# Hack alert. Python does not support mutual imports, but XendDomainInfo
@@ -54,8 +50,8 @@ class XendDomain:
# to import XendDomain from XendDomainInfo causes unbounded recursion.
# So we stuff the XendDomain instance (self) into xroot's components.
xroot.add_component("xen.xend.XendDomain", self)
- # Table of domain info indexed by domain id.
- self.db = XendDB.XendDB(self.dbpath)
+ self.domains = XendDomainDict()
+ self.dbmap = DBMap(db=XenNode("/domain"))
eserver.subscribe('xend.virq', self.onVirq)
self.initial_refresh()
@@ -77,18 +73,16 @@ class XendDomain:
domlist = xc.domain_getinfo()
doms = {}
for d in domlist:
- domid = str(d['dom'])
+ domid = d['dom']
doms[domid] = d
return doms
def xen_domain(self, dom):
"""Get info about a single domain from xc.
Returns None if not found.
+
+ @param dom domain id (int)
"""
- try:
- dom = int(dom)
- except ValueError:
- return None
dominfo = xc.domain_getinfo(dom, 1)
if dominfo == [] or dominfo[0]['dom'] != dom:
dominfo = None
@@ -100,37 +94,36 @@ class XendDomain:
"""Refresh initial domain info from db.
"""
doms = self.xen_domains()
- for config in self.db.fetchall("").values():
- domid = str(sxp.child_value(config, 'id'))
- if domid in doms:
+ self.dbmap.readDB()
+ for domdb in self.dbmap.values():
+ try:
+ domid = int(domdb.id)
+ except:
+ domid = None
+ # XXX if domid in self.domains, then something went wrong
+ if (domid is None) or (domid in self.domains):
+ domdb.delete()
+ elif domid in doms:
try:
- self._new_domain(config, doms[domid])
- self.update_domain(domid)
+ self._new_domain(domdb, doms[domid])
except Exception, ex:
- log.exception("Error recreating domain info: id=%s", domid)
+ log.exception("Error recreating domain info: id=%d", domid)
self._delete_domain(domid)
else:
self._delete_domain(domid)
self.refresh(cleanup=True)
- def sync_domain(self, info):
- """Sync info for a domain to disk.
-
- info domain info
- """
- self.db.save(info.id, info.sxpr())
-
def close(self):
pass
- def _new_domain(self, savedinfo, info):
+ def _new_domain(self, db, info):
"""Create a domain entry from saved info.
- @param savedinfo: saved info from the db
- @param info: domain info from xen
+ @param db: saved info from the db
+ @param info: domain info from xen
@return: domain
"""
- dominfo = XendDomainInfo.vm_recreate(savedinfo, info)
+ dominfo = XendDomainInfo.recreate(db, info)
self.domains[dominfo.id] = dominfo
return dominfo
@@ -144,11 +137,11 @@ class XendDomain:
for i, d in self.domains.items():
if i != d.id:
del self.domains[i]
- self.db.delete(i)
+ self.dbmap.delete(d.uuid)
if info.id in self.domains:
notify = False
self.domains[info.id] = info
- self.sync_domain(info)
+ info.exportToDB(save=True)
if notify:
eserver.inject('xend.domain.create', [info.name, info.id])
@@ -158,12 +151,26 @@ class XendDomain:
@param id: domain id
@param notify: send a domain died event if true
"""
+ try:
+ if self.xen_domain(id):
+ return
+ except:
+ pass
info = self.domains.get(id)
if info:
del self.domains[id]
+ info.cleanup()
+ info.delete()
if notify:
eserver.inject('xend.domain.died', [info.name, info.id])
- self.db.delete(id)
+ # XXX this should not be needed
+ for domdb in self.dbmap.values():
+ try:
+ domid = int(domdb.id)
+ except:
+ domid = None
+ if (domid is None) or (domid == id):
+ domdb.delete()
def reap(self):
"""Look for domains that have crashed or stopped.
@@ -178,22 +185,19 @@ class XendDomain:
not(d['running'] or d['paused'] or d['blocked']))
if dead:
casualties.append(d)
- destroyed = 0
for d in casualties:
- id = str(d['dom'])
- #print 'reap>', id
+ id = d['dom']
dominfo = self.domains.get(id)
name = (dominfo and dominfo.name) or '??'
if dominfo and dominfo.is_terminated():
- #print 'reap> already terminated:', id
continue
- log.debug('XendDomain>reap> domain died name=%s id=%s', name, id)
+ log.debug('XendDomain>reap> domain died name=%s id=%d', name, id)
if d['shutdown']:
- reason = XendDomainInfo.shutdown_reason(d['shutdown_reason'])
- log.debug('XendDomain>reap> shutdown name=%s id=%s reason=%s', name, id, reason)
+ reason = shutdown_reason(d['shutdown_reason'])
+ log.debug('XendDomain>reap> shutdown name=%s id=%d reason=%s', name, id, reason)
if reason in ['suspend']:
if dominfo and dominfo.is_terminated():
- log.debug('XendDomain>reap> Suspended domain died id=%s', id)
+ log.debug('XendDomain>reap> Suspended domain died id=%d', id)
else:
eserver.inject('xend.domain.suspended', [name, id])
if dominfo:
@@ -203,10 +207,9 @@ class XendDomain:
eserver.inject('xend.domain.exit', [name, id, reason])
self.domain_restart_schedule(id, reason)
else:
- if xroot.get_enable_dump() == 'true':
- xc.domain_dumpcore(dom = int(id), corefile = "/var/xen/dump/%s.%s.core"%(name,id))
+ if xroot.get_enable_dump():
+ self.domain_dumpcore(id)
eserver.inject('xend.domain.exit', [name, id, 'crash'])
- destroyed += 1
self.final_domain_destroy(id)
def refresh(self, cleanup=False):
@@ -216,7 +219,7 @@ class XendDomain:
self.reap()
doms = self.xen_domains()
# Add entries for any domains we don't know about.
- for (id, d) in doms.items():
+ for id in doms.keys():
if id not in self.domains:
self.domain_lookup(id)
# Remove entries for domains that no longer exist.
@@ -234,16 +237,7 @@ class XendDomain:
scheduler.now(self.domain_restarts)
def update_domain(self, id):
- """Update the saved info for a domain.
-
- @param id: domain id
- """
- dominfo = self.domains.get(id)
- if dominfo:
- self.sync_domain(dominfo)
-
- def refresh_domain(self, id):
- """Refresh information for a single domain.
+ """Update information for a single domain.
@param id: domain id
"""
@@ -279,8 +273,7 @@ class XendDomain:
@param config: configuration
@return: domain
"""
- dominfo = XendDomainInfo.vm_create(config)
- self._add_domain(dominfo)
+ dominfo = XendDomainInfo.create(self.dbmap, config)
return dominfo
def domain_restart(self, dominfo):
@@ -293,7 +286,6 @@ class XendDomain:
[dominfo.name, dominfo.id, "begin"])
try:
dominfo.restart()
- self._add_domain(dominfo)
log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
eserver.inject("xend.domain.restart",
[dominfo.name, dominfo.id, "success"])
@@ -309,14 +301,13 @@ class XendDomain:
"""Configure an existing domain. This is intended for internal
use by domain restore and migrate.
- @param id: domain id
@param vmconfig: vm configuration
"""
config = sxp.child_value(vmconfig, 'config')
- dominfo = XendDomainInfo.vm_restore(config)
- self._add_domain(dominfo)
+ uuid = sxp.child_value(vmconfig, 'uuid')
+ dominfo = XendDomainInfo.restore(self.dbmap, config, uuid=uuid)
return dominfo
-
+
def domain_restore(self, src, progress=False):
"""Restore a domain from file.
@@ -326,9 +317,7 @@ class XendDomain:
try:
fd = os.open(src, os.O_RDONLY)
-
return XendCheckpoint.restore(self, fd)
-
except OSError, ex:
raise XendError("can't read guest state file %s: %s" %
(src, ex[1]))
@@ -339,24 +328,35 @@ class XendDomain:
@param id: domain id
@return: domain object (or None)
"""
- id = str(id)
- self.refresh_domain(id)
+ self.update_domain(id)
return self.domains.get(id)
- def domain_lookup(self, name):
- name = str(name)
- dominfo = self.domains.get_by_name(name) or self.domains.get(name)
- if dominfo:
- return dominfo
- try:
- d = self.xen_domain(name)
- if d:
- log.info("Creating entry for unknown domain: id=%s", name)
- dominfo = XendDomainInfo.vm_recreate(None, d)
- self._add_domain(dominfo)
- return dominfo
- except Exception, ex:
- log.exception("Error creating domain info: id=%s", name)
+ def domain_lookup(self, id):
+ dominfo = self.domains.get(id)
+ if not dominfo:
+ try:
+ info = self.xen_domain(id)
+ if info:
+ uuid = getUuid()
+ log.info(
+ "Creating entry for unknown domain: id=%d uuid=%s",
+ id, uuid)
+ db = self.dbmap.addChild(uuid)
+ dominfo = XendDomainInfo.recreate(db, info)
+ self._add_domain(dominfo)
+ except Exception, ex:
+ log.exception("Error creating domain info: id=%d", id)
+ return dominfo
+
+ def domain_lookup_by_name(self, name):
+ dominfo = self.domains.get_by_name(name)
+ if not dominfo:
+ try:
+ id = int(name)
+ dominfo = self.domain_lookup(id)
+ except ValueError:
+ pass
+ return dominfo
def domain_unpause(self, id):
"""Unpause domain execution.
@@ -366,7 +366,7 @@ class XendDomain:
dominfo = self.domain_lookup(id)
eserver.inject('xend.domain.unpause', [dominfo.name, dominfo.id])
try:
- return xc.domain_unpause(dom=dominfo.dom)
+ return xc.domain_unpause(dom=dominfo.id)
except Exception, ex:
raise XendError(str(ex))
@@ -378,7 +378,7 @@ class XendDomain:
dominfo = self.domain_lookup(id)
eserver.inject('xend.domain.pause', [dominfo.name, dominfo.id])
try:
- return xc.domain_pause(dom=dominfo.dom)
+ return xc.domain_pause(dom=dominfo.id)
except Exception, ex:
raise XendError(str(ex))
@@ -436,7 +436,7 @@ class XendDomain:
@param id: domain id
@param reason: shutdown reason
"""
- log.debug('domain_restart_schedule> %s %s %d', id, reason, force)
+ log.debug('domain_restart_schedule> %d %s %d', id, reason, force)
dominfo = self.domain_lookup(id)
if not dominfo:
return
@@ -484,7 +484,7 @@ class XendDomain:
except:
#todo
try:
- val = xc.domain_destroy(dom=int(id))
+ val = xc.domain_destroy(dom=id)
except Exception, ex:
raise XendError(str(ex))
return val
@@ -553,7 +553,7 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
try:
- return xc.domain_pincpu(int(dominfo.id), vcpu, cpumap)
+ return xc.domain_pincpu(dominfo.id, vcpu, cpumap)
except Exception, ex:
raise XendError(str(ex))
@@ -562,7 +562,7 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
try:
- return xc.bvtsched_domain_set(dom=dominfo.dom, mcuadv=mcuadv,
+ return xc.bvtsched_domain_set(dom=dominfo.id, mcuadv=mcuadv,
warpback=warpback, warpvalue=warpvalue,
warpl=warpl, warpu=warpu)
except Exception, ex:
@@ -573,7 +573,7 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
try:
- return xc.bvtsched_domain_get(dominfo.dom)
+ return xc.bvtsched_domain_get(dominfo.id)
except Exception, ex:
raise XendError(str(ex))
@@ -581,20 +581,21 @@ class XendDomain:
def domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight):
"""Set Simple EDF scheduler parameters for a domain.
"""
- dominfo = self.domain_lookup(id)
+ dominfo = self.domain_lookup(id)
try:
- return xc.sedf_domain_set(dominfo.dom, period, slice, latency, extratime, weight)
+ return xc.sedf_domain_set(dominfo.id, period, slice, latency, extratime, weight)
except Exception, ex:
raise XendError(str(ex))
def domain_cpu_sedf_get(self, id):
- """Get Atropos scheduler parameters for a domain.
+ """Get Simple EDF scheduler parameters for a domain.
"""
dominfo = self.domain_lookup(id)
try:
- return xc.sedf_domain_get(dominfo.dom)
+ return xc.sedf_domain_get(dominfo.id)
except Exception, ex:
raise XendError(str(ex))
+
def domain_device_create(self, id, devconfig):
"""Create a new device for a domain.
@@ -603,44 +604,44 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
val = dominfo.device_create(devconfig)
- self.update_domain(dominfo.id)
+ dominfo.exportToDB()
return val
- def domain_device_configure(self, id, devconfig, idx):
+ def domain_device_configure(self, id, devconfig, devid):
"""Configure an existing device for a domain.
@param id: domain id
@param devconfig: device configuration
- @param idx: device index
+ @param devid: device id
@return: updated device configuration
"""
dominfo = self.domain_lookup(id)
- val = dominfo.device_configure(devconfig, idx)
- self.update_domain(dominfo.id)
+ val = dominfo.device_configure(devconfig, devid)
+ dominfo.exportToDB()
return val
- def domain_device_refresh(self, id, type, idx):
+ def domain_device_refresh(self, id, type, devid):
"""Refresh a device.
@param id: domain id
- @param idx: device index
+ @param devid: device id
@param type: device type
"""
dominfo = self.domain_lookup(id)
- val = dominfo.device_refresh(type, idx)
- self.update_domain(dominfo.id)
+ val = dominfo.device_refresh(type, devid)
+ dominfo.exportToDB()
return val
- def domain_device_destroy(self, id, type, idx):
+ def domain_device_destroy(self, id, type, devid):
"""Destroy a device.
@param id: domain id
- @param idx: device index
+ @param devid: device id
@param type: device type
"""
dominfo = self.domain_lookup(id)
- val = dominfo.device_destroy(type, idx)
- self.update_domain(dominfo.id)
+ val = dominfo.device_destroy(type, devid)
+ dominfo.exportToDB()
return val
def domain_devtype_ls(self, id, type):
@@ -653,22 +654,22 @@ class XendDomain:
dominfo = self.domain_lookup(id)
return dominfo.getDeviceSxprs(type)
- def domain_devtype_get(self, id, type, idx):
+ def domain_devtype_get(self, id, type, devid):
"""Get a device from a domain.
-
+
@param id: domain
@param type: device type
- @param idx: device index
+ @param devid: device id
@return: device object (or None)
"""
dominfo = self.domain_lookup(id)
- return dominfo.getDeviceByIndex(type, idx)
+ return dominfo.getDevice(type, devid)
def domain_vif_limit_set(self, id, vif, credit, period):
"""Limit the vif's transmission rate
"""
dominfo = self.domain_lookup(id)
- dev = dominfo.getDeviceById('vif', vif)
+ dev = dominfo.getDevice('vif', vif)
if not dev:
raise XendError("invalid vif")
return dev.setCreditLimit(credit, period)
@@ -681,30 +682,47 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
try:
- return xc.shadow_control(dominfo.dom, op)
+ return xc.shadow_control(dominfo.id, op)
except Exception, ex:
raise XendError(str(ex))
def domain_maxmem_set(self, id, mem):
"""Set the memory limit for a domain.
- @param dom: domain
+ @param id: domain
@param mem: memory limit (in MB)
@return: 0 on success, -1 on error
"""
dominfo = self.domain_lookup(id)
maxmem = int(mem) * 1024
try:
- return xc.domain_setmaxmem(dominfo.dom, maxmem_kb = maxmem)
+ return xc.domain_setmaxmem(dominfo.id, maxmem_kb = maxmem)
except Exception, ex:
raise XendError(str(ex))
- def domain_mem_target_set(self, id, target):
+ def domain_mem_target_set(self, id, mem):
+ """Set the memory target for a domain.
+
+ @param id: domain
+ @param mem: memory target (in MB)
+ @return: 0 on success, -1 on error
+ """
dominfo = self.domain_lookup(id)
- return dominfo.mem_target_set(target)
-
+ return dominfo.mem_target_set(mem)
+ def domain_dumpcore(self, id):
+ """Save a core dump for a crashed domain.
+ @param id: domain
+ """
+ dominfo = self.domain_lookup(id)
+ corefile = "/var/xen/dump/%s.%s.core"% (dominfo.name, dominfo.id)
+ try:
+ xc.domain_dumpcore(dom=dominfo.id, corefile=corefile)
+ except Exception, ex:
+ log.warning("Dumpcore failed, id=%s name=%s: %s",
+ dominfo.id, dominfo.name, ex)
+
def instance():
"""Singleton constructor. Use this instead of the class constructor.
"""
diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
index 0dc1aae79c..16415d78a7 100644
--- a/tools/python/xen/xend/XendDomainInfo.py
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -14,21 +14,23 @@ import time
import threading
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
-import xen.util.ip
-from xen.xend.server import channel, controller
+from xen.util.ip import check_subnet, get_current_ipgw
from xen.util.blkif import blkdev_uname_to_file
-from server.channel import channelFactory
-import server.SrvDaemon; xend = server.SrvDaemon.instance()
-from server import messages
+from xen.xend.server import controller
+from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
+from xen.xend.server import messages
+from xen.xend.server.channel import EventChannel, channelFactory
+from xen.xend import sxp
+from xen.xend.PrettyPrint import prettyprintstring
from xen.xend.XendBootloader import bootloader
-import sxp
-from XendLogging import log
+from xen.xend.XendLogging import log
from XendError import XendError, VmError
-from XendRoot import get_component
+from xen.xend.XendRoot import get_component
-from PrettyPrint import prettyprintstring
+from xen.xend.uuid import getUuid
+from xen.xend.xenstore import DBVar
"""Flag for a block device backend domain."""
SIF_BLK_BE_DOMAIN = (1<<4)
@@ -45,11 +47,16 @@ DOMAIN_REBOOT = 1
"""Shutdown code for suspend."""
DOMAIN_SUSPEND = 2
+"""Shutdown code for crash."""
+DOMAIN_CRASH = 3
+
"""Map shutdown codes to strings."""
shutdown_reasons = {
DOMAIN_POWEROFF: "poweroff",
DOMAIN_REBOOT : "reboot",
- DOMAIN_SUSPEND : "suspend" }
+ DOMAIN_SUSPEND : "suspend",
+ DOMAIN_CRASH : "crash",
+ }
"""Map shutdown reasons to the message type to use.
"""
@@ -81,7 +88,7 @@ STATE_VM_SUSPENDED = "suspended"
def domain_exists(name):
# See comment in XendDomain constructor.
xd = get_component('xen.xend.XendDomain')
- return xd.domain_lookup(name)
+ return xd.domain_lookup_by_name(name)
def shutdown_reason(code):
"""Get a shutdown reason from a code.
@@ -110,25 +117,6 @@ def get_config_handler(name):
"""
return config_handlers.get(name)
-"""Table of handlers for virtual machine images.
-Indexed by image type.
-"""
-image_handlers = {}
-
-def add_image_handler(name, h):
- """Add a handler for an image type
- @param name: image type
- @param h: handler: fn(config, name, memory, image)
- """
- image_handlers[name] = h
-
-def get_image_handler(name):
- """Get the handler for an image type.
- @param name: image type
- @return: handler or None
- """
- return image_handlers.get(name)
-
"""Table of handlers for devices.
Indexed by device type.
"""
@@ -139,61 +127,6 @@ def add_device_handler(name, type):
def get_device_handler(name):
return device_handlers[name]
-
-
-def vm_create(config):
- """Create a VM from a configuration.
- If a vm has been partially created and there is an error it
- is destroyed.
-
- @param config configuration
- @raise: VmError for invalid configuration
- """
- vm = XendDomainInfo()
- vm.construct(config)
- return vm
-
-def vm_restore(config):
- """Create a domain and a VM object to do a restore.
-
- @param config: domain configuration
- """
- vm = XendDomainInfo()
- dom = xc.domain_create()
- vm.dom_construct(dom, config)
- return vm
-
-def vm_recreate(savedinfo, info):
- """Create the VM object for an existing domain.
-
- @param savedinfo: saved info from the domain DB
- @type savedinfo: sxpr
- @param info: domain info from xc
- @type info: xc domain dict
- """
- log.debug('savedinfo=' + prettyprintstring(savedinfo))
- log.debug('info=' + str(info))
- vm = XendDomainInfo()
- vm.recreate = True
- vm.savedinfo = savedinfo
- vm.setdom(info['dom'])
- vm.memory = info['mem_kb']/1024
- start_time = sxp.child_value(savedinfo, 'start_time')
- if start_time is not None:
- vm.start_time = float(start_time)
- vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
- vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0))
- restart_time = sxp.child_value(savedinfo, 'restart_time')
- if restart_time is not None:
- vm.restart_time = float(restart_time)
- config = sxp.child_value(savedinfo, 'config')
- if config:
- vm.construct(config)
- else:
- vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
- vm.recreate = False
- vm.savedinfo = None
- return vm
def dom_get(dom):
"""Get info from xen for an existing domain.
@@ -213,25 +146,104 @@ class XendDomainInfo:
"""
MINIMUM_RESTART_TIME = 20
- def __init__(self):
+ def create(cls, parentdb, config):
+ """Create a VM from a configuration.
+
+ @param parentdb: parent db
+ @param config configuration
+ @raise: VmError for invalid configuration
+ """
+ uuid = getUuid()
+ db = parentdb.addChild(uuid)
+ vm = cls(db)
+ vm.construct(config)
+ vm.saveDB(sync=True)
+ return vm
+
+ create = classmethod(create)
+
+ def recreate(cls, db, info):
+ """Create the VM object for an existing domain.
+
+ @param db: domain db
+ @param info: domain info from xc
+ """
+ dom = info['dom']
+ vm = cls(db)
+ db.readDB()
+ vm.importFromDB()
+ config = vm.config
+ log.debug('info=' + str(info))
+ log.debug('config=' + prettyprintstring(config))
+
+ vm.setdom(dom)
+ vm.memory = info['mem_kb']/1024
+
+ if config:
+ try:
+ vm.recreate = True
+ vm.construct(config)
+ finally:
+ vm.recreate = False
+ else:
+ vm.setName("Domain-%d" % dom)
+
+ vm.exportToDB(save=True)
+ return vm
+
+ recreate = classmethod(recreate)
+
+ def restore(cls, parentdb, config, uuid=None):
+ """Create a domain and a VM object to do a restore.
+
+ @param parentdb: parent db
+ @param config: domain configuration
+ @param uuid: uuid to use
+ """
+ db = parentdb.addChild(uuid)
+ vm = cls(db)
+ dom = xc.domain_create()
+ vm.setdom(dom)
+ vm.dom_construct(vm.id, config)
+ vm.saveDB(sync=True)
+ return vm
+
+ restore = classmethod(restore)
+
+ __exports__ = [
+ DBVar('id', ty='str'),
+ DBVar('name', ty='str'),
+ DBVar('uuid', ty='str'),
+ DBVar('config', ty='sxpr'),
+ DBVar('start_time', ty='float'),
+ DBVar('state', ty='str'),
+ DBVar('store_mfn', ty='long'),
+ DBVar('restart_mode', ty='str'),
+ DBVar('restart_state', ty='str'),
+ DBVar('restart_time', ty='float'),
+ DBVar('restart_count', ty='int'),
+ ]
+
+ def __init__(self, db):
+ self.db = db
+ self.uuid = db.getName()
+
self.recreate = 0
self.restore = 0
+
self.config = None
self.id = None
- self.dom = None
self.cpu_weight = 1
self.start_time = None
self.name = None
self.memory = None
self.image = None
- self.ramdisk = None
- self.cmdline = None
self.channel = None
+ self.store_channel = None
+ self.store_mfn = None
self.controllers = {}
- self.configs = []
-
self.info = None
self.blkif_backend = False
self.netif_backend = False
@@ -249,22 +261,39 @@ class XendDomainInfo:
self.restart_count = 0
self.console_port = None
- self.savedinfo = None
- self.image_handler = None
- self.is_vmx = False
self.vcpus = 1
self.bootloader = None
+ def setDB(self, db):
+ self.db = db
+
+ def saveDB(self, save=False, sync=False):
+ self.db.saveDB(save=save, sync=sync)
+
+ def exportToDB(self, save=False, sync=False):
+ if self.channel:
+ self.channel.saveToDB(self.db.addChild("channel"))
+ if self.store_channel:
+ self.store_channel.saveToDB(self.db.addChild("store_channel"))
+ self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
+
+ def importFromDB(self):
+ self.db.importFromDB(self, fields=self.__exports__)
+
def setdom(self, dom):
"""Set the domain id.
@param dom: domain id
"""
- self.dom = int(dom)
- self.id = str(dom)
+ self.id = int(dom)
+ #self.db.id = self.id
def getDomain(self):
- return self.dom
+ return self.id
+
+ def setName(self, name):
+ self.name = name
+ self.db.name = self.name
def getName(self):
return self.name
@@ -272,6 +301,9 @@ class XendDomainInfo:
def getChannel(self):
return self.channel
+ def getStoreChannel(self):
+ return self.store_channel
+
def update(self, info):
"""Update with info from xc.domain_getinfo().
"""
@@ -284,6 +316,7 @@ class XendDomainInfo:
self.state = state
self.state_updated.notifyAll()
self.state_updated.release()
+ self.saveDB()
def state_wait(self, state):
self.state_updated.acquire()
@@ -293,14 +326,12 @@ class XendDomainInfo:
def __str__(self):
s = "domain"
- s += " id=" + self.id
+ s += " id=" + str(self.id)
s += " name=" + self.name
s += " memory=" + str(self.memory)
console = self.getConsole()
if console:
s += " console=" + str(console.console_port)
- if self.image:
- s += " image=" + self.image
s += ""
return s
@@ -327,9 +358,10 @@ class XendDomainInfo:
self.controllers[type] = ctrl
return ctrl
- def createDevice(self, type, devconfig, recreate=False):
+ def createDevice(self, type, devconfig, change=False):
ctrl = self.findDeviceController(type)
- return ctrl.createDevice(devconfig, recreate=self.recreate)
+ return ctrl.createDevice(devconfig, recreate=self.recreate,
+ change=change)
def configureDevice(self, type, id, devconfig):
ctrl = self.getDeviceController(type)
@@ -343,30 +375,14 @@ class XendDomainInfo:
ctrl = self.getDeviceController(type)
return ctrl.deleteDevice(id)
- def getDevice(self, type, id):
+ def getDevice(self, type, id, error=True):
ctrl = self.getDeviceController(type)
- return ctrl.getDevice(id)
+ return ctrl.getDevice(id, error=error)
- def getDeviceByIndex(self, type, idx):
- ctrl = self.getDeviceController(type)
- return ctrl.getDeviceByIndex(idx)
-
- def getDeviceConfig(self, type, id):
- ctrl = self.getDeviceController(type)
- return ctrl.getDeviceConfig(id)
-
def getDeviceIds(self, type):
ctrl = self.getDeviceController(type)
return ctrl.getDeviceIds()
- def getDeviceIndexes(self, type):
- ctrl = self.getDeviceController(type)
- return ctrl.getDeviceIndexes()
-
- def getDeviceConfigs(self, type):
- ctrl = self.getDeviceController(type)
- return ctrl.getDeviceConfigs()
-
def getDeviceSxprs(self, type):
ctrl = self.getDeviceController(type)
return ctrl.getDeviceSxprs()
@@ -376,7 +392,8 @@ class XendDomainInfo:
['id', self.id],
['name', self.name],
['memory', self.memory] ]
-
+ if self.uuid:
+ sxpr.append(['uuid', self.uuid])
if self.info:
sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
run = (self.info['running'] and 'r') or '-'
@@ -403,6 +420,8 @@ class XendDomainInfo:
if self.channel:
sxpr.append(self.channel.sxpr())
+ if self.store_channel:
+ sxpr.append(self.store_channel.sxpr())
console = self.getConsole()
if console:
sxpr.append(console.sxpr())
@@ -454,7 +473,7 @@ class XendDomainInfo:
return
if dominfo.is_terminated():
return
- if not self.dom or (dominfo.dom != self.dom):
+ if not self.id or (dominfo.id != self.id):
raise VmError('vm name clash: ' + name)
def construct(self, config):
@@ -467,10 +486,10 @@ class XendDomainInfo:
self.config = config
try:
# Initial domain create.
- self.name = sxp.child_value(config, 'name')
+ self.setName(sxp.child_value(config, 'name'))
self.check_name(self.name)
+ self.init_image()
self.configure_cpus(config)
- self.find_image_handler()
self.init_domain()
self.register_domain()
self.configure_bootloader()
@@ -481,6 +500,7 @@ class XendDomainInfo:
self.configure_restart()
self.construct_image()
self.configure()
+ self.exportToDB()
except Exception, ex:
# Catch errors, cleanup and re-raise.
print 'Domain construction error:', ex
@@ -492,6 +512,7 @@ class XendDomainInfo:
def register_domain(self):
xd = get_component('xen.xend.XendDomain')
xd._add_domain(self)
+ self.exportToDB()
def configure_cpus(self, config):
try:
@@ -502,8 +523,8 @@ class XendDomainInfo:
if self.memory is None:
raise VmError('missing memory size')
cpu = sxp.child_value(config, 'cpu')
- if self.recreate and self.dom and cpu is not None and int(cpu) >= 0:
- xc.domain_pincpu(self.dom, 0, 1<<int(cpu))
+ if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
+ xc.domain_pincpu(self.id, 0, 1<<int(cpu))
try:
image = sxp.child_value(self.config, 'image')
vcpus = sxp.child_value(image, 'vcpus')
@@ -512,89 +533,50 @@ class XendDomainInfo:
except:
raise VmError('invalid vcpus value')
- def find_image_handler(self):
- """Construct the boot image for the domain.
-
- @return vm
+ def init_image(self):
+ """Create boot image handler for the domain.
"""
image = sxp.child_value(self.config, 'image')
if image is None:
raise VmError('missing image')
- image_name = sxp.name(image)
- if image_name is None:
- raise VmError('missing image name')
- if image_name == "vmx":
- self.is_vmx = True
- image_handler = get_image_handler(image_name)
- if image_handler is None:
- raise VmError('unknown image type: ' + image_name)
- self.image_handler = image_handler
- return self
+ self.image = ImageHandler.create(self, image)
def construct_image(self):
- image = sxp.child_value(self.config, 'image')
- self.image_handler(self, image)
- return self
-
- def config_devices(self, name):
- """Get a list of the 'device' nodes of a given type from the config.
-
- @param name: device type
- @type name: string
- @return: device configs
- @rtype: list
- """
- devices = []
- for d in sxp.children(self.config, 'device'):
- dev = sxp.child0(d)
- if dev is None: continue
- if name == sxp.name(dev):
- devices.append(dev)
- return devices
-
- def get_device_savedinfo(self, type, index):
- val = None
- if self.savedinfo is None:
- return val
- devices = sxp.child(self.savedinfo, 'devices')
- if devices is None:
- return val
- index = str(index)
- for d in sxp.children(devices, type):
- dindex = sxp.child_value(d, 'index')
- if dindex is None: continue
- if str(dindex) == index:
- val = d
- break
- return val
-
- def get_device_recreate(self, type, index):
- return self.get_device_savedinfo(type, index) or self.recreate
-
- def add_config(self, val):
- """Add configuration data to a virtual machine.
-
- @param val: data to add
+ """Construct the boot image for the domain.
"""
- self.configs.append(val)
-
- def destroy(self):
- """Completely destroy the vm.
+ self.create_channel()
+ self.image.createImage()
+ self.image.exportToDB()
+ #if self.store_channel:
+ # self.db.introduceDomain(self.id,
+ # self.store_mfn,
+ # self.store_channel)
+
+ def delete(self):
+ """Delete the vm's db.
"""
- self.cleanup()
- return self.destroy_domain()
+ if self.dom_get(self.id):
+ return
+ self.id = None
+ self.saveDB(sync=True)
+ try:
+ # Todo: eventually will have to wait for devices to signal
+ # destruction before can delete the db.
+ if self.db:
+ self.db.delete()
+ except Exception, ex:
+ log.warning("error in domain db delete: %s", ex)
+ pass
def destroy_domain(self):
"""Destroy the vm's domain.
The domain will not finally go away unless all vm
devices have been released.
"""
- if self.channel:
- self.channel.close()
- self.channel = None
- if self.dom is None: return 0
+ if self.id is None:
+ return
try:
- return xc.domain_destroy(dom=self.dom)
+ xc.domain_destroy(dom=self.id)
except Exception, err:
log.exception("Domain destroy failed: %s", self.name)
@@ -603,6 +585,37 @@ class XendDomainInfo:
"""
self.state = STATE_VM_TERMINATED
self.release_devices()
+ if self.channel:
+ try:
+ self.channel.close()
+ self.channel = None
+ except:
+ pass
+ if self.store_channel:
+ try:
+ self.store_channel.close()
+ self.store_channel = None
+ except:
+ pass
+ #try:
+ # self.db.releaseDomain(self.id)
+ #except Exception, ex:
+ # log.warning("error in domain release on xenstore: %s", ex)
+ # pass
+ if self.image:
+ try:
+ self.image.destroy()
+ self.image = None
+ except:
+ pass
+
+ def destroy(self):
+ """Clenup vm and destroy domain.
+ """
+ self.cleanup()
+ self.destroy_domain()
+ self.saveDB()
+ return 0
def is_terminated(self):
"""Check if a domain has been terminated.
@@ -616,20 +629,13 @@ class XendDomainInfo:
for ctrl in self.getDeviceControllers():
if ctrl.isDestroyed(): continue
ctrl.destroyController(reboot=reboot)
- if not reboot:
- self.configs = []
def show(self):
"""Print virtual machine info.
"""
- print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
+ print "[VM dom=%d name=%s memory=%d" % (self.id, self.name, self.memory)
print "image:"
sxp.show(self.image)
- print
- for val in self.configs:
- print "config:"
- sxp.show(val)
- print
print "]"
def init_domain(self):
@@ -639,107 +645,42 @@ class XendDomainInfo:
return
if self.start_time is None:
self.start_time = time.time()
- if self.restore:
- return
- dom = self.dom or 0
- memory = self.memory
try:
cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
except:
raise VmError('invalid cpu')
- cpu_weight = self.cpu_weight
- memory = memory * 1024 + self.pgtable_size(memory)
- dom = xc.domain_create(dom= dom)
- if self.bootloader:
- try:
- if kernel: os.unlink(kernel)
- if ramdisk: os.unlink(ramdisk)
- except OSError, e:
- log.warning('unable to unlink kernel/ramdisk: %s' %(e,))
-
- if dom <= 0:
- raise VmError('Creating domain failed: name=%s memory=%d'
- % (self.name, memory))
- xc.domain_setcpuweight(dom, cpu_weight)
- xc.domain_setmaxmem(dom, memory)
- xc.domain_memory_increase_reservation(dom, memory)
- if cpu != -1:
- xc.domain_pincpu(dom, 0, 1<<int(cpu))
- log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, self.name, memory)
- self.setdom(dom)
-
- def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
- """Build the domain boot image.
- """
- if self.recreate or self.restore: return
- if not os.path.isfile(kernel):
- raise VmError('Kernel image does not exist: %s' % kernel)
- if ramdisk and not os.path.isfile(ramdisk):
- raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
- if len(cmdline) >= 256:
- log.warning('kernel cmdline too long, domain %d', self.dom)
- dom = self.dom
- buildfn = getattr(xc, '%s_build' % ostype)
- flags = 0
- if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
- if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
- #todo generalise this
- if ostype == "vmx":
- log.debug('building vmx domain')
- err = buildfn(dom = dom,
- image = kernel,
- control_evtchn = 0,
- memsize = self.memory,
- memmap = memmap,
- cmdline = cmdline,
- ramdisk = ramdisk,
- flags = flags)
- else:
- log.debug('building dom with %d vcpus', self.vcpus)
- err = buildfn(dom = dom,
- image = kernel,
- control_evtchn = self.channel.getRemotePort(),
- cmdline = cmdline,
- ramdisk = ramdisk,
- flags = flags,
- vcpus = self.vcpus)
- if err != 0:
- raise VmError('Building domain failed: type=%s dom=%d err=%d'
- % (ostype, dom, err))
-
- def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
- """Create a domain. Builds the image but does not configure it.
+ dom = self.image.initDomain(self.id, self.memory, cpu, self.cpu_weight)
+ log.debug('init_domain> Created domain=%d name=%s memory=%d',
+ dom, self.name, self.memory)
+ if not self.restore:
+ self.setdom(dom)
- @param ostype: OS type
- @param kernel: kernel image
- @param ramdisk: kernel ramdisk
- @param cmdline: kernel commandline
+ def openChannel(self, key, local, remote):
+ """Create a channel to the domain.
+ If saved info is available recreate the channel.
+
+ @param key db key for the saved data (if any)
+ @param local default local port
+ @param remote default remote port
"""
-
- self.create_channel()
- self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
- self.image = kernel
- self.ramdisk = ramdisk
- self.cmdline = cmdline
-
+ db = self.db.addChild(key)
+ chan = channelFactory().restoreFromDB(db, self.id, local, remote)
+ #todo: save here?
+ #chan.saveToDB(db)
+ return chan
+
+ def eventChannel(self, key):
+ db = self.db.addChild(key)
+ return EventChannel.restoreFromDB(db, 0, self.id)
+
def create_channel(self):
- """Create the control channel to the domain.
- If saved info is available recreate the channel using the saved ports.
+ """Create the channels to the domain.
"""
- local = 0
- remote = 1
- if self.savedinfo:
- info = sxp.child(self.savedinfo, "channel")
- if info:
- local = int(sxp.child_value(info, "local_port", 0))
- remote = int(sxp.child_value(info, "remote_port", 1))
- self.channel = channelFactory().openChannel(self.dom,
- local_port=local,
- remote_port=remote)
+ self.channel = self.openChannel("channel", 0, 1)
+ self.store_channel = self.eventChannel("store_channel")
def create_configured_devices(self):
devices = sxp.children(self.config, 'device')
- indexes = {}
for d in devices:
dev_config = sxp.child0(d)
if dev_config is None:
@@ -748,13 +689,7 @@ class XendDomainInfo:
ctrl_type = get_device_handler(dev_type)
if ctrl_type is None:
raise VmError('unknown device type: ' + dev_type)
- # Keep track of device indexes by type, so we can fish
- # out saved info for recreation.
- idx = indexes.get(dev_type, -1)
- idx += 1
- indexes[ctrl_type] = idx
- recreate = self.get_device_recreate(dev_type, idx)
- self.createDevice(ctrl_type, dev_config, recreate=recreate)
+ self.createDevice(ctrl_type, dev_config)
def create_devices(self):
"""Create the devices for a vm.
@@ -766,43 +701,6 @@ class XendDomainInfo:
ctrl.initController(reboot=True)
else:
self.create_configured_devices()
- if self.is_vmx:
- self.create_vmx_model()
-
- def create_vmx_model(self):
- #todo: remove special case for vmx
- device_model = sxp.child_value(self.config, 'device_model')
- if not device_model:
- raise VmError("vmx: missing device model")
- device_config = sxp.child_value(self.config, 'device_config')
- if not device_config:
- raise VmError("vmx: missing device config")
- #todo: self.memory?
- memory = sxp.child_value(self.config, "memory")
- # Create an event channel
- device_channel = channel.eventChannel(0, self.dom)
- # see if a vncviewer was specified
- # XXX RN: bit of a hack. should unify this, maybe stick in config space
- vncconnect=""
- image = sxp.child_value(self.config, "image")
- args = sxp.child_value(image, "args")
- if args:
- arg_list = string.split(args)
- for arg in arg_list:
- al = string.split(arg, '=')
- if al[0] == "VNC_VIEWER":
- vncconnect=" -v %s" % al[1]
- break
-
- # Execute device model.
- #todo: Error handling
- # XXX RN: note that the order of args matter!
- os.system(device_model
- + " -f %s" % device_config
- + vncconnect
- + " -d %d" % self.dom
- + " -p %d" % device_channel['port1']
- + " -m %s" % memory)
def device_create(self, dev_config):
"""Create a new device.
@@ -814,16 +712,14 @@ class XendDomainInfo:
self.config.append(['device', dev.getConfig()])
return dev.sxpr()
- def device_configure(self, dev_config, idx):
+ def device_configure(self, dev_config, id):
"""Configure an existing device.
@param dev_config: device configuration
- @param idx: device index
+ @param id: device id
"""
type = sxp.name(dev_config)
- dev = self.getDeviceByIndex(type, idx)
- if not dev:
- raise VmError('invalid device: %s %s' % (type, idx))
+ dev = self.getDevice(type, id)
old_config = dev.getConfig()
new_config = dev.configure(dev_config, change=True)
# Patch new config into vm config.
@@ -833,26 +729,22 @@ class XendDomainInfo:
self.config[old_index] = new_full_config
return new_config
- def device_refresh(self, type, idx):
+ def device_refresh(self, type, id):
"""Refresh a device.
@param type: device type
- @param idx: device index
+ @param id: device id
"""
- dev = self.getDeviceByIndex(type, idx)
- if not dev:
- raise VmError('invalid device: %s %s' % (type, idx))
+ dev = self.getDevice(type, id)
dev.refresh()
- def device_delete(self, type, idx):
+ def device_delete(self, type, id):
"""Destroy and remove a device.
@param type: device type
- @param idx: device index
+ @param id: device id
"""
- dev = self.getDeviceByIndex(type, idx)
- if not dev:
- raise VmError('invalid device: %s %s' % (type, idx))
+ dev = self.getDevice(type, id)
dev_config = dev.getConfig()
if dev_config:
self.config.remove(['device', dev_config])
@@ -861,9 +753,7 @@ class XendDomainInfo:
def configure_bootloader(self):
"""Configure boot loader.
"""
- bl = sxp.child_value(self.config, "bootloader")
- if bl is not None:
- self.bootloader = bl
+ self.bootloader = sxp.child_value(self.config, "bootloader")
def configure_console(self):
"""Configure the vm console port.
@@ -946,6 +836,7 @@ class XendDomainInfo:
if self.bootloader:
self.config = self.bootloader_config()
self.construct(self.config)
+ self.saveDB()
finally:
self.restart_state = None
@@ -1051,19 +942,6 @@ class XendDomainInfo:
log.warning("Unknown config field %s", field_name)
index[field_name] = field_index + 1
- def pgtable_size(self, memory):
- """Return the size of memory needed for 1:1 page tables for physical
- mode.
-
- @param memory: size in MB
- @return size in KB
- """
- if self.is_vmx:
- # Logic x86-32 specific.
- # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
- return (1 + ((memory + 3) >> 2)) * 4
- return 0
-
def mem_target_set(self, target):
"""Set domain memory target in pages.
"""
@@ -1090,83 +968,6 @@ class XendDomainInfo:
return 0
return timeout - (time.time() - self.shutdown_pending['start'])
-def vm_image_linux(vm, image):
- """Create a VM for a linux image.
-
- @param name: vm name
- @param memory: vm memory
- @param image: image config
- @return: vm
- """
- kernel = sxp.child_value(image, "kernel")
- cmdline = ""
- ip = sxp.child_value(image, "ip", None)
- if ip:
- cmdline += " ip=" + ip
- root = sxp.child_value(image, "root")
- if root:
- cmdline += " root=" + root
- args = sxp.child_value(image, "args")
- if args:
- cmdline += " " + args
- ramdisk = sxp.child_value(image, "ramdisk", '')
- log.debug("creating linux domain with cmdline: %s" %(cmdline,))
- vm.create_domain("linux", kernel, ramdisk, cmdline)
- return vm
-
-def vm_image_plan9(vm, image):
- """Create a VM for a Plan 9 image.
-
- name vm name
- memory vm memory
- image image config
-
- returns vm
- """
- kernel = sxp.child_value(image, "kernel")
- cmdline = ""
- ip = sxp.child_value(image, "ip", "dhcp")
- if ip:
- cmdline += "ip=" + ip
- root = sxp.child_value(image, "root")
- if root:
- cmdline += "root=" + root
- args = sxp.child_value(image, "args")
- if args:
- cmdline += " " + args
- ramdisk = sxp.child_value(image, "ramdisk", '')
- log.debug("creating plan9 domain with cmdline: %s" %(cmdline,))
- vm.create_domain("plan9", kernel, ramdisk, cmdline)
- return vm
-
-def vm_image_vmx(vm, image):
- """Create a VM for the VMX environment.
-
- @param name: vm name
- @param memory: vm memory
- @param image: image config
- @return: vm
- """
- kernel = sxp.child_value(image, "kernel")
- cmdline = ""
- ip = sxp.child_value(image, "ip", "dhcp")
- if ip:
- cmdline += " ip=" + ip
- root = sxp.child_value(image, "root")
- if root:
- cmdline += " root=" + root
- args = sxp.child_value(image, "args")
- if args:
- cmdline += " " + args
- ramdisk = sxp.child_value(image, "ramdisk", '')
- memmap = sxp.child_value(vm.config, "memmap", '')
- memmap = sxp.parse(open(memmap))[0]
- from xen.util.memmap import memmap_parse
- memmap = memmap_parse(memmap)
- log.debug("creating vmx domain with cmdline: %s" %(cmdline,))
- vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
- return vm
-
def vm_field_ignore(vm, config, val, index):
"""Dummy config field handler used for fields with built-in handling.
@@ -1192,13 +993,20 @@ def vm_field_maxmem(vm, config, val, index):
maxmem = int(maxmem)
except:
raise VmError("invalid maxmem: " + str(maxmem))
- xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
+ xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
#============================================================================
# Register image handlers.
-add_image_handler('linux', vm_image_linux)
-add_image_handler('plan9', vm_image_plan9)
-add_image_handler('vmx', vm_image_vmx)
+from image import \
+ addImageHandlerClass, \
+ ImageHandler, \
+ LinuxImageHandler, \
+ Plan9ImageHandler, \
+ VmxImageHandler
+
+addImageHandlerClass(LinuxImageHandler)
+addImageHandlerClass(Plan9ImageHandler)
+addImageHandlerClass(VmxImageHandler)
# Ignore the fields we already handle.
add_config_handler('name', vm_field_ignore)
diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py
index d1bd503f8a..045a5a5fa4 100644
--- a/tools/python/xen/xend/XendRoot.py
+++ b/tools/python/xen/xend/XendRoot.py
@@ -25,9 +25,6 @@ import sxp
class XendRoot:
"""Root of the management classes."""
- """Default path to the root of the database."""
- dbroot_default = "/var/lib/xen/xend-db"
-
"""Default path to the config file."""
config_default = "/etc/xen/xend-config.sxp"
@@ -82,7 +79,6 @@ class XendRoot:
components = {}
def __init__(self):
- self.dbroot = None
self.config_path = None
self.config = None
self.logging = None
@@ -171,13 +167,15 @@ class XendRoot:
def configure(self):
self.set_config()
self.configure_logger()
- self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
def configure_logger(self):
logfile = self.get_config_value("logfile", self.logfile_default)
loglevel = self.get_config_value("loglevel", self.loglevel_default)
self.logging = XendLogging(logfile, level=loglevel)
- #self.logging.addLogStderr()
+
+ from xen.xend.server import params
+ if params.XEND_DEBUG:
+ self.logging.addLogStderr()
def get_logging(self):
"""Get the XendLogging instance.
@@ -189,11 +187,6 @@ class XendRoot:
"""
return self.logging and self.logging.getLogger()
- def get_dbroot(self):
- """Get the path to the database root.
- """
- return self.dbroot
-
def set_config(self):
"""If the config file exists, read it. If not, ignore it.
@@ -241,9 +234,9 @@ class XendRoot:
def get_config_bool(self, name, val=None):
v = self.get_config_value(name, val)
- if v in ['yes', '1', 'on', 1, True]:
+ if v in ['yes', '1', 'on', 'true', 1, True]:
return True
- if v in ['no', '0', 'off', 0, False]:
+ if v in ['no', '0', 'off', 'false', 0, False]:
return False
raise XendError("invalid xend config %s: expected bool: %s" % (name, v))
@@ -325,7 +318,7 @@ class XendRoot:
return self.get_config_value('network-script', 'network')
def get_enable_dump(self):
- return self.get_config_value('enable-dump', 'false')
+ return self.get_config_bool('enable-dump', 'no')
def get_vif_bridge(self):
return self.get_config_value('vif-bridge', 'xen-br0')
diff --git a/tools/python/xen/xend/XendVnet.py b/tools/python/xen/xend/XendVnet.py
index d95fd204aa..3614127c49 100644
--- a/tools/python/xen/xend/XendVnet.py
+++ b/tools/python/xen/xend/XendVnet.py
@@ -4,11 +4,10 @@
"""
from xen.util import Brctl
-
-import sxp
-import XendDB
-from XendError import XendError
-from XendLogging import log
+from xen.xend import sxp
+from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
+from xen.xend.xenstore import XenNode, DBMap
def vnet_cmd(cmd):
out = None
@@ -63,14 +62,15 @@ class XendVnet:
"""Index of all vnets. Singleton.
"""
- dbpath = "vnet"
+ dbpath = "/vnet"
def __init__(self):
# Table of vnet info indexed by vnet id.
self.vnet = {}
- self.db = XendDB.XendDB(self.dbpath)
- vnets = self.db.fetchall("")
- for config in vnets.values():
+ self.dbmap = DBMap(db=XenNode(self.dbpath))
+ self.dbmap.readDB()
+ for vnetdb in self.dbmap.values():
+ config = vnetdb.config
info = XendVnetInfo(config)
self.vnet[info.id] = info
try:
@@ -115,7 +115,7 @@ class XendVnet:
"""
info = XendVnetInfo(config)
self.vnet[info.id] = info
- self.db.save(info.id, info.sxpr())
+ self.dbmap["%s/config" % info.id] = info.sxpr()
info.configure()
def vnet_delete(self, id):
@@ -126,7 +126,7 @@ class XendVnet:
info = self.vnet_get(id)
if info:
del self.vnet[id]
- self.db.delete(id)
+ self.dbmap.delete(id)
info.delete()
def instance():
diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py
new file mode 100644
index 0000000000..e0d70581bf
--- /dev/null
+++ b/tools/python/xen/xend/image.py
@@ -0,0 +1,339 @@
+import os
+
+import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
+from xen.xend import sxp
+from xen.xend.XendError import VmError
+from xen.xend.XendLogging import log
+from xen.xend.xenstore import DBVar
+
+class ImageHandler:
+ """Abstract base class for image handlers.
+
+ initDomain() is called to initialise the domain memory.
+
+ createImage() is called to configure and build the domain from its
+ kernel image and ramdisk etc.
+
+ The method buildDomain() is used to build the domain, and must be
+ defined in a subclass. Usually this is the only method that needs
+ defining in a subclass.
+
+ The method createDeviceModel() is called to create the domain device
+ model if it needs one. The default is to do nothing.
+
+ The method destroy() is called when the domain is destroyed.
+ The default is to do nothing.
+
+ """
+
+ #======================================================================
+ # Class vars and methods.
+
+ """Table of image handler classes for virtual machine images.
+ Indexed by image type.
+ """
+ imageHandlerClasses = {}
+
+ def addImageHandlerClass(cls, h):
+ """Add a handler class for an image type
+ @param h: handler: ImageHandler subclass
+ """
+ cls.imageHandlerClasses[h.ostype] = h
+
+ addImageHandlerClass = classmethod(addImageHandlerClass)
+
+ def findImageHandlerClass(cls, image):
+ """Find the image handler class for an image config.
+
+ @param image config
+ @return ImageHandler subclass or None
+ """
+ ty = sxp.name(image)
+ if ty is None:
+ raise VmError('missing image type')
+ imageClass = cls.imageHandlerClasses.get(ty)
+ if imageClass is None:
+ raise VmError('unknown image type: ' + ty)
+ return imageClass
+
+ findImageHandlerClass = classmethod(findImageHandlerClass)
+
+ def create(cls, vm, image):
+ """Create an image handler for a vm.
+
+ @param vm vm
+ @param image image config
+ @return ImageHandler instance
+ """
+ imageClass = cls.findImageHandlerClass(image)
+ return imageClass(vm, image)
+
+ create = classmethod(create)
+
+ #======================================================================
+ # Instance vars and methods.
+
+ db = None
+ ostype = None
+
+ config = None
+ kernel = None
+ ramdisk = None
+ cmdline = None
+ flags = 0
+
+ __exports__ = [
+ DBVar('ostype', ty='str'),
+ DBVar('config', ty='sxpr'),
+ DBVar('kernel', ty='str'),
+ DBVar('ramdisk', ty='str'),
+ DBVar('cmdline', ty='str'),
+ DBVar('flags', ty='int'),
+ ]
+
+ def __init__(self, vm, config):
+ self.vm = vm
+ self.db = vm.db.addChild('/image')
+ self.config = config
+
+ def exportToDB(self, save=False):
+ self.db.exportToDB(self, fields=self.__exports__, save=save)
+
+ def importFromDB(self):
+ self.db.importFromDB(self, fields=self.__exports__)
+
+ def unlink(self, f):
+ if not f: return
+ try:
+ os.unlink(f)
+ except OSError, ex:
+ log.warning("error removing bootloader file '%s': %s", f, ex)
+
+ def initDomain(self, dom, memory, cpu, cpu_weight):
+ """Initial domain create.
+
+ @return domain id
+ """
+
+ mem_kb = self.getDomainMemory(memory)
+ if not self.vm.restore:
+ dom = xc.domain_create(dom = dom or 0)
+ # if bootloader, unlink here. But should go after buildDomain() ?
+ if self.vm.bootloader:
+ self.unlink(self.kernel)
+ self.unlink(self.ramdisk)
+ if dom <= 0:
+ raise VmError('Creating domain failed: name=%s' % self.vm.name)
+ log.debug("initDomain: cpu=%d mem_kb=%d dom=%d", cpu, mem_kb, dom)
+ # xc.domain_setuuid(dom, uuid)
+ xc.domain_setcpuweight(dom, cpu_weight)
+ xc.domain_setmaxmem(dom, mem_kb)
+ xc.domain_memory_increase_reservation(dom, mem_kb)
+ if cpu != -1:
+ xc.domain_pincpu(dom, 0, 1<<int(cpu))
+ return dom
+
+ def createImage(self):
+ """Entry point to create domain memory image.
+ Override in subclass if needed.
+ """
+ self.configure()
+ self.createDomain()
+
+ def configure(self):
+ """Config actions common to all unix-like domains."""
+ self.kernel = sxp.child_value(self.config, "kernel")
+ self.cmdline = ""
+ ip = sxp.child_value(self.config, "ip", None)
+ if ip:
+ self.cmdline += " ip=" + ip
+ root = sxp.child_value(self.config, "root")
+ if root:
+ self.cmdline += " root=" + root
+ args = sxp.child_value(self.config, "args")
+ if args:
+ self.cmdline += " " + args
+ self.ramdisk = sxp.child_value(self.config, "ramdisk", '')
+
+ def createDomain(self):
+ """Build the domain boot image.
+ """
+ # Set params and call buildDomain().
+ self.flags = 0
+ if self.vm.netif_backend: self.flags |= SIF_NET_BE_DOMAIN
+ if self.vm.blkif_backend: self.flags |= SIF_BLK_BE_DOMAIN
+
+ if self.vm.recreate or self.vm.restore:
+ return
+ if not os.path.isfile(self.kernel):
+ raise VmError('Kernel image does not exist: %s' % self.kernel)
+ if self.ramdisk and not os.path.isfile(self.ramdisk):
+ raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
+ if len(self.cmdline) >= 256:
+ log.warning('kernel cmdline too long, domain %d', self.vm.getDomain())
+
+ log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
+ self.vm.getDomain(), self.vm.vcpus)
+ err = self.buildDomain()
+ if err != 0:
+ raise VmError('Building domain failed: ostype=%s dom=%d err=%d'
+ % (self.ostype, self.vm.getDomain(), err))
+
+ def getDomainMemory(self, mem_mb):
+ """Memory (in KB) the domain will need for mem_mb (in MB)."""
+ return mem_mb * 1024
+
+ def buildDomain(self):
+ """Build the domain. Define in subclass."""
+ raise NotImplementedError()
+
+ def createDeviceModel(self):
+ """Create device model for the domain (define in subclass if needed)."""
+ pass
+
+ def destroy(self):
+ """Extra cleanup on domain destroy (define in subclass if needed)."""
+ pass
+
+addImageHandlerClass = ImageHandler.addImageHandlerClass
+
+class LinuxImageHandler(ImageHandler):
+
+ ostype = "linux"
+
+ def buildDomain(self):
+ if self.vm.store_channel:
+ store_evtchn = self.vm.store_channel.port2
+ else:
+ store_evtchn = 0
+ ret = xc.linux_build(dom = self.vm.getDomain(),
+ image = self.kernel,
+ control_evtchn = self.vm.channel.getRemotePort(),
+ store_evtchn = store_evtchn,
+ cmdline = self.cmdline,
+ ramdisk = self.ramdisk,
+ flags = self.flags,
+ vcpus = self.vm.vcpus)
+ if isinstance(ret, dict):
+ self.vm.store_mfn = ret.get('store_mfn')
+ return 0
+ return ret
+
+class Plan9ImageHandler(ImageHandler):
+
+ ostype = "plan9"
+
+ def buildDomain(self):
+ return xc.plan9_build(dom = self.vm.getDomain(),
+ image = self.kernel,
+ control_evtchn = self.vm.channel.getRemotePort(),
+ cmdline = self.cmdline,
+ ramdisk = self.ramdisk,
+ flags = self.flags,
+ vcpus = self.vm.vcpus)
+
+class VmxImageHandler(ImageHandler):
+
+ __exports__ = ImageHandler.__exports__ + [
+ DBVar('memmap', ty='str'),
+ DBVar('memmap_value', ty='sxpr'),
+ # device channel?
+ ]
+
+ ostype = "vmx"
+ memmap = None
+ memmap_value = None
+ device_channel = None
+
+ def createImage(self):
+ """Create a VM for the VMX environment.
+ """
+ self.configure()
+ self.parseMemmap()
+ self.createDomain()
+
+ def buildDomain(self):
+ return xc.vmx_build(dom = self.vm.getDomain(),
+ image = self.kernel,
+ control_evtchn = 0,
+ memsize = self.vm.memory,
+ memmap = self.memmap_value,
+ cmdline = self.cmdline,
+ ramdisk = self.ramdisk,
+ flags = self.flags)
+
+ def parseMemmap(self):
+ self.memmap = sxp.child_value(self.vm.config, "memmap")
+ if self.memmap is None:
+ raise VmError("missing memmap")
+ memmap = sxp.parse(open(self.memmap))[0]
+ from xen.util.memmap import memmap_parse
+ self.memmap_value = memmap_parse(memmap)
+
+ def createDeviceModel_old(self):
+ device_model = sxp.child_value(self.vm.config, 'device_model')
+ if not device_model:
+ raise VmError("vmx: missing device model")
+ device_config = sxp.child_value(self.vm.config, 'device_config')
+ if not device_config:
+ raise VmError("vmx: missing device config")
+ # Create an event channel.
+ self.device_channel = channel.eventChannel(0, self.vm.getDomain())
+ # Execute device model.
+ #todo: Error handling
+ os.system(device_model
+ + " -f %s" % device_config
+ + " -d %d" % self.vm.getDomain()
+ + " -p %d" % self.device_channel['port1']
+ + " -m %s" % self.vm.memory)
+
+ def createDeviceModel(self):
+ device_model = sxp.child_value(self.vm.config, 'device_model')
+ if not device_model:
+ raise VmError("vmx: missing device model")
+ device_config = sxp.child_value(self.vm.config, 'device_config')
+ if not device_config:
+ raise VmError("vmx: missing device config")
+ # Create an event channel
+ self.device_channel = channel.eventChannel(0, self.vm.getDomain())
+ # Execute device model.
+ #todo: Error handling
+ # XXX RN: note that the order of args matter!
+ os.system(device_model
+ + " -f %s" % device_config
+ + self.vncParams()
+ + " -d %d" % self.vm.getDomain()
+ + " -p %d" % self.device_channel['port1']
+ + " -m %s" % self.vm.memory)
+
+ def vncParams(self):
+ # see if a vncviewer was specified
+ # XXX RN: bit of a hack. should unify this, maybe stick in config space
+ vncconnect=""
+ image = self.config
+ args = sxp.child_value(image, "args")
+ if args:
+ arg_list = string.split(args)
+ for arg in arg_list:
+ al = string.split(arg, '=')
+ if al[0] == "VNC_VIEWER":
+ vncconnect=" -v %s" % al[1]
+ break
+ return vncconnect
+
+ def destroy(self):
+ channel.eventChannelClose(self.device_channel)
+
+ def getDomainMemory(self, mem_mb):
+ return (mem_mb * 1024) + self.getPageTableSize(mem_mb)
+
+ def getPageTableSize(self, mem_mb):
+ """Return the size of memory needed for 1:1 page tables for physical
+ mode.
+
+ @param mem_mb: size in MB
+ @return size in KB
+ """
+ # Logic x86-32 specific.
+ # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
+ return (1 + ((mem_mb + 3) >> 2)) * 4
diff --git a/tools/python/xen/xend/server/SrvConsole.py b/tools/python/xen/xend/server/SrvConsole.py
index 233f62b968..f147f2810b 100644
--- a/tools/python/xen/xend/server/SrvConsole.py
+++ b/tools/python/xen/xend/server/SrvConsole.py
@@ -30,7 +30,7 @@ class SrvConsole(SrvDir):
#self.ls()
req.write('<p>%s</p>' % self.info)
req.write('<p><a href="%s">Connect to domain %d</a></p>'
- % (self.info.uri(), self.info.dom))
+ % (self.info.uri(), self.info.id))
self.form(req)
req.write('</body></html>')
diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py
index 061aa3dba7..133df206b9 100644
--- a/tools/python/xen/xend/server/SrvDaemon.py
+++ b/tools/python/xen/xend/server/SrvDaemon.py
@@ -32,9 +32,6 @@ import event
import relocate
from params import *
-DAEMONIZE = 0
-DEBUG = 1
-
class Daemon:
"""The xend daemon.
"""
@@ -128,9 +125,13 @@ class Daemon:
def cleanup_xend(self, kill=False):
return self.cleanup_process(XEND_PID_FILE, "xend", kill)
+ def cleanup_xenstored(self, kill=False):
+ return self.cleanup_process(XENSTORED_PID_FILE, "xenstored", kill)
+
def cleanup(self, kill=False):
self.cleanup_xend(kill=kill)
-
+ #self.cleanup_xenstored(kill=kill)
+
def status(self):
"""Returns the status of the xend daemon.
The return value is defined by the LSB:
@@ -166,8 +167,29 @@ class Daemon:
pidfile.close()
return pid
+ def start_xenstored(self):
+ """Fork and exec xenstored, writing its pid to XENSTORED_PID_FILE.
+ """
+ def mkdirs(p):
+ try:
+ os.makedirs(p)
+ except:
+ pass
+ mkdirs(XENSTORED_RUN_DIR)
+ mkdirs(XENSTORED_LIB_DIR)
+
+ pid = self.fork_pid(XENSTORED_PID_FILE)
+ if pid:
+ # Parent
+ log.info("Started xenstored, pid=%d", pid)
+ else:
+ # Child
+ if XEND_DAEMONIZE and (not XENSTORED_DEBUG):
+ self.daemonize()
+ os.execl("/usr/sbin/xenstored", "xenstored", "--no-fork")
+
def daemonize(self):
- if not DAEMONIZE: return
+ if not XEND_DAEMONIZE: return
# Detach from TTY.
os.setsid()
@@ -177,16 +199,16 @@ class Daemon:
os.close(0)
os.close(1)
os.close(2)
- if DEBUG:
+ if XEND_DEBUG:
os.open('/dev/null', os.O_RDONLY)
# XXX KAF: Why doesn't this capture output from C extensions that
# fprintf(stdout) or fprintf(stderr) ??
- os.open('/var/log/xend-debug.log', os.O_WRONLY|os.O_CREAT)
+ os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
os.dup(1)
else:
os.open('/dev/null', os.O_RDWR)
os.dup(0)
- os.open('/var/log/xend-debug.log', os.O_WRONLY|os.O_CREAT)
+ os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
def start(self, trace=0):
@@ -196,11 +218,15 @@ class Daemon:
4 Insufficient privileges
"""
xend_pid = self.cleanup_xend()
+ xenstored_pid = self.cleanup_xenstored()
if self.set_user():
return 4
os.chdir("/")
+ if xenstored_pid == 0:
+ self.start_xenstored()
+
if xend_pid > 0:
# Trying to run an already-running service is a success.
return 0
@@ -308,7 +334,7 @@ class Daemon:
servers.start()
except Exception, ex:
print >>sys.stderr, 'Exception starting xend:', ex
- if DEBUG:
+ if XEND_DEBUG:
traceback.print_exc()
log.exception("Exception starting xend")
self.exit(1)
diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py
index c9cf4fe603..255e6157bf 100644
--- a/tools/python/xen/xend/server/SrvDomain.py
+++ b/tools/python/xen/xend/server/SrvDomain.py
@@ -28,19 +28,19 @@ class SrvDomain(SrvDir):
fn = FormFn(self.xd.domain_configure,
[['dom', 'int'],
['config', 'sxpr']])
- return fn(req.args, {'dom': self.dom.dom})
+ return fn(req.args, {'dom': self.dom.id})
def op_unpause(self, op, req):
- val = self.xd.domain_unpause(self.dom.name)
+ val = self.xd.domain_unpause(self.dom.id)
return val
def op_pause(self, op, req):
- val = self.xd.domain_pause(self.dom.name)
+ val = self.xd.domain_pause(self.dom.id)
return val
def op_shutdown(self, op, req):
fn = FormFn(self.xd.domain_shutdown,
- [['dom', 'str'],
+ [['dom', 'int'],
['reason', 'str'],
['key', 'int']])
val = fn(req.args, {'dom': self.dom.id})
@@ -50,7 +50,7 @@ class SrvDomain(SrvDir):
def op_destroy(self, op, req):
fn = FormFn(self.xd.domain_destroy,
- [['dom', 'str'],
+ [['dom', 'int'],
['reason', 'str']])
val = fn(req.args, {'dom': self.dom.id})
req.setHeader("Location", "%s/.." % req.prePathURL())
@@ -61,7 +61,7 @@ class SrvDomain(SrvDir):
def do_save(self, op, req):
fn = FormFn(self.xd.domain_save,
- [['dom', 'str'],
+ [['dom', 'int'],
['file', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return 0
@@ -71,7 +71,7 @@ class SrvDomain(SrvDir):
def do_migrate(self, op, req):
fn = FormFn(self.xd.domain_migrate,
- [['dom', 'str'],
+ [['dom', 'int'],
['destination', 'str'],
['live', 'int'],
['resource', 'int']])
@@ -79,7 +79,7 @@ class SrvDomain(SrvDir):
def op_pincpu(self, op, req):
fn = FormFn(self.xd.domain_pincpu,
- [['dom', 'str'],
+ [['dom', 'int'],
['vcpu', 'int'],
['cpumap', 'int']])
val = fn(req.args, {'dom': self.dom.id})
@@ -87,7 +87,7 @@ class SrvDomain(SrvDir):
def op_cpu_bvt_set(self, op, req):
fn = FormFn(self.xd.domain_cpu_bvt_set,
- [['dom', 'str'],
+ [['dom', 'int'],
['mcuadv', 'int'],
['warpback', 'int'],
['warpvalue', 'int'],
@@ -99,7 +99,7 @@ class SrvDomain(SrvDir):
def op_cpu_sedf_set(self, op, req):
fn = FormFn(self.xd.domain_cpu_sedf_set,
- [['dom', 'str'],
+ [['dom', 'int'],
['period', 'int'],
['slice', 'int'],
['latency', 'int'],
@@ -110,28 +110,28 @@ class SrvDomain(SrvDir):
def op_maxmem_set(self, op, req):
fn = FormFn(self.xd.domain_maxmem_set,
- [['dom', 'str'],
+ [['dom', 'int'],
['memory', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_mem_target_set(self, op, req):
fn = FormFn(self.xd.domain_mem_target_set,
- [['dom', 'str'],
+ [['dom', 'int'],
['target', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_devices(self, op, req):
fn = FormFn(self.xd.domain_devtype_ls,
- [['dom', 'str'],
+ [['dom', 'int'],
['type', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_device(self, op, req):
fn = FormFn(self.xd.domain_devtype_get,
- [['dom', 'str'],
+ [['dom', 'int'],
['type', 'str'],
['idx', 'int']])
val = fn(req.args, {'dom': self.dom.id})
@@ -142,14 +142,14 @@ class SrvDomain(SrvDir):
def op_device_create(self, op, req):
fn = FormFn(self.xd.domain_device_create,
- [['dom', 'str'],
+ [['dom', 'int'],
['config', 'sxpr']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_device_refresh(self, op, req):
fn = FormFn(self.xd.domain_device_refresh,
- [['dom', 'str'],
+ [['dom', 'int'],
['type', 'str'],
['idx', 'str']])
val = fn(req.args, {'dom': self.dom.id})
@@ -157,7 +157,7 @@ class SrvDomain(SrvDir):
def op_device_destroy(self, op, req):
fn = FormFn(self.xd.domain_device_destroy,
- [['dom', 'str'],
+ [['dom', 'int'],
['type', 'str'],
['idx', 'str']])
val = fn(req.args, {'dom': self.dom.id})
@@ -165,7 +165,7 @@ class SrvDomain(SrvDir):
def op_device_configure(self, op, req):
fn = FormFn(self.xd.domain_device_configure,
- [['dom', 'str'],
+ [['dom', 'int'],
['config', 'sxpr'],
['idx', 'str']])
val = fn(req.args, {'dom': self.dom.id})
@@ -173,7 +173,7 @@ class SrvDomain(SrvDir):
def op_vif_limit_set(self, op, req):
fn = FormFn(self.xd.domain_vif_limit_set,
- [['dom', 'str'],
+ [['dom', 'int'],
['vif', 'int'],
['credit', 'int'],
['period', 'int']])
diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py
index e10561ee45..d6f6291716 100644
--- a/tools/python/xen/xend/server/SrvDomainDir.py
+++ b/tools/python/xen/xend/server/SrvDomainDir.py
@@ -24,7 +24,7 @@ class SrvDomainDir(SrvDir):
def domain(self, x):
val = None
- dom = self.xd.domain_lookup(x)
+ dom = self.xd.domain_lookup_by_name(x)
if not dom:
raise XendError('No such domain ' + str(x))
val = SrvDomain(dom)
diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py
index 5a179c23a0..75a76e8bda 100755
--- a/tools/python/xen/xend/server/blkif.py
+++ b/tools/python/xen/xend/server/blkif.py
@@ -5,14 +5,15 @@ import string
from xen.util import blkif
from xen.xend.XendError import XendError, VmError
-from xen.xend import XendRoot
+from xen.xend.XendRoot import get_component
from xen.xend.XendLogging import log
from xen.xend import sxp
from xen.xend import Blkctl
+from xen.xend.xenstore import DBVar
-import channel
-from controller import CtrlMsgRcvr, Dev, DevController
-from messages import *
+from xen.xend.server import channel
+from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
+from xen.xend.server.messages import *
class BlkifBackend:
""" Handler for the 'back-end' channel to a block device driver domain
@@ -56,7 +57,7 @@ class BlkifBackend:
def openEvtchn(self):
self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
-
+
def getEventChannelBackend(self):
val = 0
if self.evtchn:
@@ -158,6 +159,18 @@ class BlkDev(Dev):
"""Info record for a block device.
"""
+ __exports__ = Dev.__exports__ + [
+ DBVar('dev', ty='str'),
+ DBVar('vdev', ty='int'),
+ DBVar('mode', ty='str'),
+ DBVar('viftype', ty='str'),
+ DBVar('params', ty='str'),
+ DBVar('node', ty='str'),
+ DBVar('device', ty='long'),
+ DBVar('start_sector', ty='long'),
+ DBVar('nr_sectors', ty='long'),
+ ]
+
def __init__(self, controller, id, config, recreate=False):
Dev.__init__(self, controller, id, config, recreate=recreate)
self.dev = None
@@ -206,7 +219,8 @@ class BlkDev(Dev):
raise VmError('vbd: Device not found: %s' % self.dev)
try:
- self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
+ xd = get_component('xen.xend.XendDomain')
+ self.backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
except:
raise XendError('invalid backend domain')
@@ -214,8 +228,7 @@ class BlkDev(Dev):
def attach(self, recreate=False, change=False):
if recreate:
- node = sxp.child_value(recreate, 'node')
- self.setNode(node)
+ pass
else:
node = Blkctl.block('bind', self.type, self.params)
self.setNode(node)
@@ -263,7 +276,7 @@ class BlkDev(Dev):
def check_mounted(self, name):
mode = blkif.mount_mode(name)
- xd = XendRoot.get_component('xen.xend.XendDomain')
+ xd = get_component('xen.xend.XendDomain')
for vm in xd.list():
ctrl = vm.getDeviceController(self.getType(), error=False)
if (not ctrl): continue
@@ -292,14 +305,14 @@ class BlkDev(Dev):
val.append(['uname', self.uname])
if self.node:
val.append(['node', self.node])
- val.append(['index', self.getIndex()])
return val
def getBackend(self):
return self.controller.getBackend(self.backendDomain)
def refresh(self):
- log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, self.id)
+ log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain,
+ self.id)
self.interfaceChanged()
def destroy(self, change=False, reboot=False):
@@ -308,7 +321,8 @@ class BlkDev(Dev):
@param change: change flag
"""
self.destroyed = True
- log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, self.id)
+ log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain,
+ self.id)
self.send_be_vbd_destroy()
if change:
self.interfaceChanged()
@@ -445,5 +459,4 @@ class BlkifController(DevController):
log.error("Exception connecting backend: %s", ex)
else:
log.error('interface connect on unknown interface: id=%d', id)
-
diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py
index e2b2043e66..00f451a7b8 100755
--- a/tools/python/xen/xend/server/channel.py
+++ b/tools/python/xen/xend/server/channel.py
@@ -14,52 +14,129 @@ DEBUG = 0
RESPONSE_TIMEOUT = 20.0
-def eventChannel(dom1, dom2):
- """Create an event channel between domains.
- The returned dict contains dom1, dom2, port1 and port2 on success.
+class EventChannel(dict):
+ """An event channel between domains.
+ """
+
+ def interdomain(cls, dom1, dom2, port1=0, port2=0):
+ """Create an event channel between domains.
+
+ @return EventChannel (None on error)
+ """
+ v = xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2,
+ port1=port1, port2=port2)
+ if v:
+ v = cls(dom1, dom2, v)
+ return v
+
+ interdomain = classmethod(interdomain)
+
+ def restoreFromDB(cls, db, dom1, dom2, port1=0, port2=0):
+ """Create an event channel using db info if available.
+ Inverse to saveToDB().
+
+ @param db db
+ @param dom1
+ @param dom2
+ @param port1
+ @param port2
+ """
+ try:
+ dom1 = int(db['dom1'])
+ except: pass
+ try:
+ dom2 = int(db['dom2'])
+ except: pass
+ try:
+ port1 = int(db['port1'])
+ except: pass
+ try:
+ port2 = int(db['port2'])
+ except: pass
+ evtchn = cls.interdomain(dom1, dom2, port1=port1, port2=port2)
+ return evtchn
+
+ restoreFromDB = classmethod(restoreFromDB)
+
+ def __init__(self, dom1, dom2, d):
+ d['dom1'] = dom1
+ d['dom2'] = dom2
+ self.update(d)
+ self.dom1 = dom1
+ self.dom2 = dom2
+ self.port1 = d.get('port1')
+ self.port2 = d.get('port2')
+
+ def close(self):
+ """Close the event channel.
+ """
+ def evtchn_close(dom, port):
+ try:
+ xc.evtchn_close(dom=dom, port=port)
+ except Exception, ex:
+ pass
+
+ if DEBUG:
+ print 'EventChannel>close>', self
+ evtchn_close(self.dom1, self.port1)
+ evtchn_close(self.dom2, self.port2)
+
+ def saveToDB(self, db):
+ """Save the event channel to the db so it can be restored later,
+ using restoreFromDB() on the class.
+
+ @param db db
+ """
+ db['dom1'] = str(self.dom1)
+ db['dom2'] = str(self.dom2)
+ db['port1'] = str(self.port1)
+ db['port2'] = str(self.port2)
+ db.saveDB()
+
+ def sxpr(self):
+ return ['event-channel',
+ ['dom1', self.dom1 ],
+ ['port1', self.port1 ],
+ ['dom2', self.dom2 ],
+ ['port2', self.port2 ]
+ ]
+
+ def __repr__(self):
+ return ("<EventChannel dom1:%d:%d dom2:%d:%d>"
+ % (self.dom1, self.port1, self.dom2, self.port2))
- @return dict (empty on error)
+def eventChannel(dom1, dom2, port1=0, port2=0):
+ """Create an event channel between domains.
+
+ @return EventChannel (None on error)
"""
- evtchn = xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
- if evtchn:
- evtchn['dom1'] = dom1
- evtchn['dom2'] = dom2
- return evtchn
+ return EventChannel.interdomain(dom1, dom2, port1=port1, port2=port2)
def eventChannelClose(evtchn):
- """Close an event channel that was opened by eventChannel().
+ """Close an event channel.
"""
- def evtchn_close(dom, port):
- if (dom is None) or (port is None): return
- try:
- xc.evtchn_close(dom=dom, port=port)
- except Exception, ex:
- pass
-
if not evtchn: return
- if DEBUG:
- print 'eventChannelClose>', evtchn
- evtchn_close(evtchn.get('dom1'), evtchn.get('port1'))
- evtchn_close(evtchn.get('dom2'), evtchn.get('port2'))
-
+ evtchn.close()
class ChannelFactory:
- """Factory for creating channels.
+ """Factory for creating control channels.
Maintains a table of channels.
"""
""" Channels indexed by index. """
- channels = {}
+ channels = None
thread = None
notifier = None
"""Map of ports to the virq they signal."""
- virqPorts = {}
+ virqPorts = None
def __init__(self):
"""Constructor - do not use. Use the channelFactory function."""
+ self.channels = {}
+ self.virqPorts = {}
self.notifier = xu.notifier()
# Register interest in virqs.
self.bind_virq(xen.lowlevel.xc.VIRQ_DOM_EXC)
@@ -70,10 +147,6 @@ class ChannelFactory:
self.virqPorts[port] = virq
log.info("Virq %s on port %s", virq, port)
- def virq(self):
- log.error("virq")
- self.notifier.virq_send(self.virqPort)
-
def start(self):
"""Fork a thread to read messages.
"""
@@ -182,9 +255,13 @@ class ChannelFactory:
return None
def openChannel(self, dom, local_port=0, remote_port=0):
- return (self.findChannel(dom, local_port=local_port, remote_port=remote_port)
- or
- self.newChannel(dom, local_port, remote_port))
+ chan = self.findChannel(dom, local_port=local_port,
+ remote_port=remote_port)
+ if chan:
+ return chan
+ chan = self.newChannel(dom, local_port, remote_port)
+ return chan
+
def createPort(self, dom, local_port=0, remote_port=0):
"""Create a port for a channel to the given domain.
@@ -203,8 +280,31 @@ class ChannelFactory:
@type remote: int
@return: port object
"""
- return xu.port(dom, local_port=int(local_port),
- remote_port=int(remote_port))
+ return xu.port(dom, local_port=local_port, remote_port=remote_port)
+
+ def restoreFromDB(self, db, dom, local, remote):
+ """Create a channel using ports restored from the db (if available).
+ Otherwise use the given ports. This is the inverse operation to
+ saveToDB() on a channel.
+
+ @param db db
+ @param dom domain the channel connects to
+ @param local default local port
+ @param remote default remote port
+ """
+ try:
+ local_port = int(db['local_port'])
+ except:
+ local_port = local
+ try:
+ remote_port = int(db['remote_port'])
+ except:
+ remote_port = remote
+ try:
+ chan = self.openChannel(dom, local_port, remote_port)
+ except:
+ return None
+ return chan
def channelFactory():
"""Singleton constructor for the channel factory.
@@ -218,7 +318,7 @@ def channelFactory():
return inst
class Channel:
- """Chanel to a domain.
+ """Control channel to a domain.
Maintains a list of device handlers to dispatch requests to, based
on the request type.
"""
@@ -239,6 +339,17 @@ class Channel:
# Make sure the port will deliver all the messages.
self.port.register(TYPE_WILDCARD)
+ def saveToDB(self, db):
+ """Save the channel ports to the db so the channel can be restored later,
+ using restoreFromDB() on the factory.
+
+ @param db db
+ """
+ if self.closed: return
+ db['local_port'] = str(self.getLocalPort())
+ db['remote_port'] = str(self.getRemotePort())
+ db.saveDB()
+
def getKey(self):
"""Get the channel key.
"""
diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py
index f3dade883b..743ace4aec 100755
--- a/tools/python/xen/xend/server/console.py
+++ b/tools/python/xen/xend/server/console.py
@@ -13,10 +13,11 @@ from xen.xend import EventServer; eserver = EventServer.instance()
from xen.xend.XendLogging import log
from xen.xend import XendRoot; xroot = XendRoot.instance()
from xen.xend import sxp
+from xen.xend.xenstore import DBVar
-from controller import CtrlMsgRcvr, Dev, DevController
-from messages import *
-from params import *
+from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
+from xen.xend.server.messages import *
+from xen.xend.server.params import *
class ConsoleProtocol(protocol.Protocol):
"""Asynchronous handler for a console socket.
@@ -76,6 +77,12 @@ class ConsoleDev(Dev, protocol.ServerFactory):
STATUS_CONNECTED = 'connected'
STATUS_LISTENING = 'listening'
+ __exports__ = Dev.__exports__ + [
+ DBVar('status', ty='str'),
+ #DBVar('listening', ty='str'),
+ DBVar('console_port', ty='int'),
+ ]
+
def __init__(self, controller, id, config, recreate=False):
Dev.__init__(self, controller, id, config)
self.lock = threading.RLock()
@@ -129,7 +136,6 @@ class ConsoleDev(Dev, protocol.ServerFactory):
val.append(['local_port', self.getLocalPort() ])
val.append(['remote_port', self.getRemotePort() ])
val.append(['console_port', self.console_port ])
- val.append(['index', self.getIndex()])
if self.addr:
val.append(['connected', self.addr[0], self.addr[1]])
finally:
diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py
index 9205b2778e..d1e19efee1 100755
--- a/tools/python/xen/xend/server/controller.py
+++ b/tools/python/xen/xend/server/controller.py
@@ -4,7 +4,8 @@ for a domain.
"""
from xen.xend.XendError import XendError
-from messages import msgTypeName, printMsg, getMessageType
+from xen.xend.xenstore import DBVar
+from xen.xend.server.messages import msgTypeName, printMsg, getMessageType
DEBUG = 0
@@ -115,18 +116,18 @@ class DevControllerTable:
def getDevControllerClass(self, type):
return self.controllerClasses.get(type)
- def addDevControllerClass(self, klass):
- self.controllerClasses[klass.getType()] = klass
+ def addDevControllerClass(self, cls):
+ self.controllerClasses[cls.getType()] = cls
def delDevControllerClass(self, type):
if type in self.controllerClasses:
del self.controllerClasses[type]
def createDevController(self, type, vm, recreate=False):
- klass = self.getDevControllerClass(type)
- if not klass:
+ cls = self.getDevControllerClass(type)
+ if not cls:
raise XendError("unknown device type: " + type)
- return klass.createDevController(vm, recreate=recreate)
+ return cls.createDevController(vm, recreate=recreate)
def getDevControllerTable():
"""Singleton constructor for the controller table.
@@ -138,11 +139,11 @@ def getDevControllerTable():
devControllerTable = DevControllerTable()
return devControllerTable
-def addDevControllerClass(name, klass):
+def addDevControllerClass(name, cls):
"""Add a device controller class to the controller table.
"""
- klass.name = name
- getDevControllerTable().addDevControllerClass(klass)
+ cls.type = name
+ getDevControllerTable().addDevControllerClass(cls)
def createDevController(name, vm, recreate=False):
return getDevControllerTable().createDevController(name, vm, recreate=recreate)
@@ -155,29 +156,57 @@ class DevController:
"""
- name = None
+ # State:
+ # controller/<type> : for controller
+ # device/<type>/<id> : for each device
- def createDevController(klass, vm, recreate=False):
+ def createDevController(cls, vm, recreate=False):
"""Class method to create a dev controller.
"""
- ctrl = klass(vm, recreate=recreate)
+ ctrl = cls(vm, recreate=recreate)
ctrl.initController(recreate=recreate)
+ ctrl.exportToDB()
return ctrl
createDevController = classmethod(createDevController)
- def getType(klass):
- return klass.name
+ def getType(cls):
+ return cls.type
getType = classmethod(getType)
+ __exports__ = [
+ DBVar('type', 'str'),
+ DBVar('destroyed', 'bool'),
+ ]
+
+ # Set when registered.
+ type = None
+
def __init__(self, vm, recreate=False):
self.destroyed = False
self.vm = vm
+ self.db = self.getDB()
self.deviceId = 0
self.devices = {}
self.device_order = []
+ def getDB(self):
+ """Get the db node to use for a controller.
+ """
+ return self.vm.db.addChild("/controller/%s" % self.getType())
+
+ def getDevDB(self, id):
+ """Get the db node to use for a device.
+ """
+ return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id))
+
+ def exportToDB(self, save=False):
+ self.db.exportToDB(self, fields=self.__exports__, save=save)
+
+ def importFromDB(self):
+ self.db.importFromDB(self, fields=self.__exports__)
+
def getDevControllerType(self):
return self.dctype
@@ -229,18 +258,19 @@ class DevController:
i.e. it is being added at runtime rather than when the domain is created.
"""
dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate)
+ if self.vm.recreate:
+ dev.importFromDB()
dev.init(recreate=recreate)
self.addDevice(dev)
- idx = self.getDeviceIndex(dev)
- recreate = self.vm.get_device_recreate(self.getType(), idx)
+ if not recreate:
+ dev.exportToDB()
dev.attach(recreate=recreate, change=change)
+ dev.exportToDB()
def configureDevice(self, id, config, change=False):
"""Reconfigure an existing device.
May be defined in subclass."""
- dev = self.getDevice(id)
- if not dev:
- raise XendError("invalid device id: " + id)
+ dev = self.getDevice(id, error=True)
dev.configure(config, change=change)
def destroyDevice(self, id, change=False, reboot=False):
@@ -251,9 +281,7 @@ class DevController:
The device is not deleted, since it may be recreated later.
"""
- dev = self.getDevice(id)
- if not dev:
- raise XendError("invalid device id: " + id)
+ dev = self.getDevice(id, error=True)
dev.destroy(change=change, reboot=reboot)
return dev
@@ -278,24 +306,15 @@ class DevController:
def isDestroyed(self):
return self.destroyed
- def getDevice(self, id):
- return self.devices.get(id)
-
- def getDeviceByIndex(self, idx):
- if 0 <= idx < len(self.device_order):
- return self.device_order[idx]
- else:
- return None
-
- def getDeviceIndex(self, dev):
- return self.device_order.index(dev)
+ def getDevice(self, id, error=False):
+ dev = self.devices.get(id)
+ if error and not dev:
+ raise XendError("invalid device id: " + id)
+ return dev
def getDeviceIds(self):
return [ dev.getId() for dev in self.device_order ]
- def getDeviceIndexes(self):
- return range(0, len(self.device_order))
-
def getDevices(self):
return self.device_order
@@ -353,11 +372,42 @@ class Dev:
@type controller: DevController
"""
+ # ./status : need 2: actual and requested?
+ # down-down: initial.
+ # up-up: fully up.
+ # down-up: down requested, still up. Watch front and back, when both
+ # down go to down-down. But what if one (or both) is not connected?
+ # Still have front/back trees with status? Watch front/status, back/status?
+ # up-down: up requested, still down.
+ # Back-end watches ./status, front/status
+ # Front-end watches ./status, back/status
+ # i.e. each watches the other 2.
+ # Each is status/request status/actual?
+ #
+ # backend?
+ # frontend?
+
+ __exports__ = [
+ DBVar('id', ty='int'),
+ DBVar('type', ty='str'),
+ DBVar('config', ty='sxpr'),
+ DBVar('destroyed', ty='bool'),
+ ]
+
def __init__(self, controller, id, config, recreate=False):
self.controller = controller
self.id = id
self.config = config
self.destroyed = False
+ self.type = self.getType()
+
+ self.db = controller.getDevDB(id)
+
+ def exportToDB(self, save=False):
+ self.db.exportToDB(self, fields=self.__exports__, save=save)
+
+ def importFromDB(self):
+ self.db.importFromDB(self, fields=self.__exports__)
def getDomain(self):
return self.controller.getDomain()
@@ -380,9 +430,6 @@ class Dev:
def getId(self):
return self.id
- def getIndex(self):
- return self.controller.getDeviceIndex(self)
-
def getConfig(self):
return self.config
diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py
index 9d2dbc4f63..0a49842522 100755
--- a/tools/python/xen/xend/server/netif.py
+++ b/tools/python/xen/xend/server/netif.py
@@ -4,21 +4,75 @@
import random
+from xen.util.mac import macFromString, macToString
+
from xen.xend import sxp
from xen.xend import Vifctl
from xen.xend.XendError import XendError, VmError
from xen.xend.XendLogging import log
from xen.xend import XendVnet
from xen.xend.XendRoot import get_component
+from xen.xend.xenstore import DBVar
-import channel
-from controller import CtrlMsgRcvr, Dev, DevController
-from messages import *
+from xen.xend.server import channel
+from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
+from xen.xend.server.messages import *
class NetDev(Dev):
"""A network device.
"""
+ # State:
+ # inherited +
+ # ./config
+ # ./mac
+ # ./be_mac
+ # ./bridge
+ # ./script
+ # ./ipaddr ?
+ #
+ # ./credit
+ # ./period
+ #
+ # ./vifctl: up/down?
+ # ./vifname
+ #
+ #
+ # Poss should have no backend state here - except for ref to backend's own tree
+ # for the device? And a status - the one we want.
+ # ./back/dom
+ # ./back/devid - id for back-end (netif_handle) - same as front/devid
+ # ./back/id - backend id (if more than one b/e per domain)
+ # ./back/status
+ # ./back/tx_shmem_frame - actually these belong in back-end state
+ # ./back/rx_shmem_frame
+ #
+ # ./front/dom
+ # ./front/devid
+ # ./front/status - need 2: one for requested, one for actual? Or drive from dev status
+ # and this is front status only.
+ # ./front/tx_shmem_frame
+ # ./front/rx_shmem_frame
+ #
+ # ./evtchn/front - here or in front/back?
+ # ./evtchn/back
+ # ./evtchn/status ?
+ # At present created by dev: but should be created unbound by front/back
+ # separately and then bound (by back)?
+
+ __exports__ = Dev.__exports__ + [
+ DBVar('config', ty='sxpr'),
+ DBVar('mac', ty='mac'),
+ DBVar('be_mac', ty='mac'),
+ DBVar('bridge', ty='str'),
+ DBVar('script', ty='str'),
+ #DBVar('ipaddr'),
+ DBVar('credit', ty='int'),
+ DBVar('period', ty='int'),
+ DBVar('vifname', ty='str'),
+ DBVar('evtchn'), #todo: export fields (renamed)
+ ]
+
def __init__(self, controller, id, config, recreate=False):
Dev.__init__(self, controller, id, config, recreate=recreate)
self.vif = int(self.id)
@@ -49,15 +103,19 @@ class NetDev(Dev):
def _get_config_mac(self, config):
vmac = sxp.child_value(config, 'mac')
if not vmac: return None
- mac = [ int(x, 16) for x in vmac.split(':') ]
- if len(mac) != 6: raise XendError("invalid mac: %s" % vmac)
+ try:
+ mac = macFromString(vmac)
+ except:
+ raise XendError("invalid mac: %s" % vmac)
return mac
def _get_config_be_mac(self, config):
vmac = sxp.child_value(config, 'be_mac')
if not vmac: return None
- mac = [ int(x, 16) for x in vmac.split(':') ]
- if len(mac) != 6: raise XendError("invalid backend mac: %s" % vmac)
+ try:
+ mac = macFromString(vmac)
+ except:
+ raise XendError("invalid backend mac: %s" % vmac)
return mac
def _get_config_ipaddr(self, config):
@@ -102,7 +160,7 @@ class NetDev(Dev):
else:
#todo: Code below will fail on xend restart when backend is not domain 0.
xd = get_component('xen.xend.XendDomain')
- self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
+ self.backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
except:
raise XendError('invalid backend domain')
return self.config
@@ -127,13 +185,13 @@ class NetDev(Dev):
ipaddr = self._get_config_ipaddr(config)
xd = get_component('xen.xend.XendDomain')
- backendDomain = str(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
+ backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
if (mac is not None) and (mac != self.mac):
raise XendError("cannot change mac")
if (be_mac is not None) and (be_mac != self.be_mac):
raise XendError("cannot change backend mac")
- if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
+ if (backendDomain is not None) and (backendDomain != self.backendDomain):
raise XendError("cannot change backend")
if (bridge is not None) and (bridge != self.bridge):
changes['bridge'] = bridge
@@ -199,7 +257,6 @@ class NetDev(Dev):
val.append(['evtchn',
self.evtchn['port1'],
self.evtchn['port2']])
- val.append(['index', self.getIndex()])
return val
def get_vifname(self):
@@ -213,12 +270,12 @@ class NetDev(Dev):
def get_mac(self):
"""Get the MAC address as a string.
"""
- return ':'.join(map(lambda x: "%02x" % x, self.mac))
+ return macToString(self.mac)
def get_be_mac(self):
"""Get the backend MAC address as a string.
"""
- return ':'.join(map(lambda x: "%02x" % x, self.be_mac))
+ return macToString(self.be_mac)
def vifctl_params(self, vmname=None):
"""Get the parameters to pass to vifctl.
@@ -230,7 +287,7 @@ class NetDev(Dev):
vm = xd.domain_lookup(dom)
vmname = vm.name
except:
- vmname = 'DOM%d' % dom
+ vmname = 'Domain-%d' % dom
return { 'domain': vmname,
'vif' : self.get_vifname(),
'mac' : self.get_mac(),
diff --git a/tools/python/xen/xend/server/params.py b/tools/python/xen/xend/server/params.py
index 5c7fdf7bad..2565c2dfcd 100644
--- a/tools/python/xen/xend/server/params.py
+++ b/tools/python/xen/xend/server/params.py
@@ -1,6 +1,34 @@
-# The following parameters could be placed in a configuration file.
-XEND_PID_FILE = '/var/run/xend.pid'
-XEND_TRACE_FILE = '/var/log/xend.trace'
+import os
+
+def getenv(var, val, conv=None):
+ """Get a value from the environment, with optional conversion.
-XEND_USER = 'root'
+ @param var name of environment variable
+ @param val default value
+ @param conv conversion function to apply to env value
+ @return converted value or default
+ """
+ try:
+ v = os.getenv(var)
+ if v is None:
+ v = val
+ else:
+ print var, '=', v
+ if conv:
+ v = conv(v)
+ except:
+ v = val
+ return v
+
+# The following parameters could be placed in a configuration file.
+XEND_PID_FILE = '/var/run/xend.pid'
+XEND_TRACE_FILE = '/var/log/xend.trace'
+XEND_DEBUG_LOG = '/var/log/xend-debug.log'
+XEND_USER = 'root'
+XEND_DEBUG = getenv("XEND_DEBUG", 0, conv=int)
+XEND_DAEMONIZE = getenv("XEND_DAEMONIZE", not XEND_DEBUG, conv=int)
+XENSTORED_PID_FILE = '/var/run/xenstored.pid'
+XENSTORED_RUN_DIR = '/var/run/xenstored'
+XENSTORED_LIB_DIR = '/var/lib/xenstored'
+XENSTORED_DEBUG = getenv("XENSTORED_DEBUG", 0, conv=int)
diff --git a/tools/python/xen/xend/server/usbif.py b/tools/python/xen/xend/server/usbif.py
index 9535fdd202..d366985740 100644
--- a/tools/python/xen/xend/server/usbif.py
+++ b/tools/python/xen/xend/server/usbif.py
@@ -7,10 +7,11 @@
from xen.xend import sxp
from xen.xend.XendLogging import log
from xen.xend.XendError import XendError
+from xen.xend.xenstore import DBVar
-import channel
-from controller import Dev, DevController
-from messages import *
+from xen.xend.server import channel
+from xen.xend.server.controller import Dev, DevController
+from xen.xend.server.messages import *
class UsbBackend:
"""Handler for the 'back-end' channel to a USB device driver domain
@@ -141,6 +142,11 @@ class UsbBackend:
class UsbDev(Dev):
+
+ __exports__ = Dev.__exports__ + [
+ DBVar('port', ty='int'),
+ DBVar('path', ty='str'),
+ ]
def __init__(self, controller, id, config, recreate=False):
Dev.__init__(self, controller, id, config, recreate=recreate)
@@ -186,7 +192,6 @@ class UsbDev(Dev):
['port', self.port],
['path', self.path],
]
- val.append(['index', self.getIndex()])
return val
def getBackend(self):
diff --git a/tools/python/xen/xend/uuid.py b/tools/python/xen/xend/uuid.py
new file mode 100644
index 0000000000..096fef7f9f
--- /dev/null
+++ b/tools/python/xen/xend/uuid.py
@@ -0,0 +1,65 @@
+"""Universal(ly) Unique Identifiers (UUIDs).
+"""
+import commands
+import random
+
+def uuidgen(random=True):
+ """Generate a UUID using the command uuidgen.
+
+ If random is true (default) generates a random uuid.
+ If random is false generates a time-based uuid.
+ """
+ cmd = "uuidgen"
+ if random:
+ cmd += " -r"
+ else:
+ cmd += " -t"
+ return commands.getoutput(cmd)
+
+class UuidFactoryUuidgen:
+
+ """A uuid factory using uuidgen."""
+
+ def __init__(self):
+ pass
+
+ def getUuid(self):
+ return uuidgen()
+
+class UuidFactoryRandom:
+
+ """A random uuid factory."""
+
+ def __init__(self):
+ f = file("/dev/urandom", "r")
+ seed = f.read(16)
+ f.close()
+ self.rand = random.Random(seed)
+
+ def randBytes(self, n):
+ return [ self.rand.randint(0, 255) for i in range(0, n) ]
+
+ def getUuid(self):
+ bytes = self.randBytes(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) )
+
+def getFactory():
+ """Get the factory to use for creating uuids.
+ This is so it's easy to change the uuid factory.
+ For example, for testing we might want repeatable uuids
+ rather than the random ones we normally use.
+ """
+ global uuidFactory
+ try:
+ uuidFactory
+ except:
+ #uuidFactory = UuidFactoryUuidgen()
+ uuidFactory = UuidFactoryRandom()
+ return uuidFactory
+
+def getUuid():
+ return getFactory().getUuid()
diff --git a/tools/python/xen/xend/xenstore/__init__.py b/tools/python/xen/xend/xenstore/__init__.py
new file mode 100644
index 0000000000..6772d2ceca
--- /dev/null
+++ b/tools/python/xen/xend/xenstore/__init__.py
@@ -0,0 +1,2 @@
+from xsnode import *
+from xsobj import *
diff --git a/tools/python/xen/xend/xenstore/xsnode.py b/tools/python/xen/xend/xenstore/xsnode.py
new file mode 100644
index 0000000000..ae770219ab
--- /dev/null
+++ b/tools/python/xen/xend/xenstore/xsnode.py
@@ -0,0 +1,382 @@
+import errno
+import os
+import os.path
+import select
+import sys
+import time
+
+from xen.lowlevel import xs
+from xen.xend import sxp
+from xen.xend.PrettyPrint import prettyprint
+
+SELECT_TIMEOUT = 2.0
+
+def getEventPath(event):
+ return os.path.join("/_event", event)
+
+def getEventIdPath(event):
+ return os.path.join(eventPath(event), "@eid")
+
+class Subscription:
+
+ def __init__(self, event, fn, id):
+ self.event = event
+ self.watcher = None
+ self.fn = fn
+ self.id = id
+
+ def watch(self, watcher):
+ self.watcher = watcher
+ watcher.addSubs(self)
+
+ def unwatch(self):
+ watcher = self.watcher
+ if watcher:
+ self.watcher = None
+ watcher.delSubs(self)
+
+ def notify(self, event):
+ try:
+ self.fn(event, id)
+ except SystemExitException:
+ raise
+ except:
+ pass
+
+class Watcher:
+
+ def __init__(self, store, event):
+ self.path = getEventPath(event)
+ self.eidPath = getEventIdPath(event)
+ store.mkdirs(self.path)
+ if not store.exists(self.eidPath):
+ store.writeInt(self.eidPath, 0)
+ self.xs = None
+ self.subs = []
+
+ def __getattr__(self, k, v):
+ if k == "fileno":
+ if self.xs:
+ return self.xs.fileno
+ else:
+ return -1
+ else:
+ return self.__dict__.get(k, v)
+
+ def addSubs(self, subs):
+ self.subs.append(subs)
+ self.watch()
+
+ def delSubs(self, subs):
+ self.subs.remove(subs)
+ if len(self.subs) == 0:
+ self.unwatch()
+
+ def getEvent(self):
+ return self.event
+
+ def watch(self):
+ if self.xs: return
+ self.xs = xs.open()
+ self.xs.watch(path)
+
+ def unwatch(self):
+ if self.xs:
+ self.xs.unwatch(self.path)
+ self.xs.close()
+ self.xs = None
+
+ def watching(self):
+ return self.xs is not None
+
+ def getNotification(self):
+ p = self.xs.read_watch()
+ self.xs.acknowledge_watch()
+ eid = self.xs.readInt(self.eidPath)
+ return p
+
+ def notify(self, subs):
+ p = self.getNotification()
+ for s in subs:
+ s.notify(p)
+
+class XenStore:
+
+ def __init__(self):
+ self.xs = None
+ #self.xs = xs.open()
+ self.subscription = {}
+ self.subscription_id = 0
+ self.events = {}
+ self.write("/", "")
+
+ def getxs(self):
+ if self.xs is None:
+ ex = None
+ for i in range(0,20):
+ try:
+ self.xs = xs.open()
+ ex = None
+ break
+ except Exception, ex:
+ print >>stderr, "Exception connecting to xsdaemon:", ex
+ print >>stderr, "Trying again..."
+ time.sleep(1)
+ else:
+ raise ex
+
+ #todo would like to reconnect if xs conn closes (e.g. daemon restart).
+ return self.xs
+
+ def dump(self, path="/", out=sys.stdout):
+ print 'dump>', path
+ val = ['node']
+ val.append(['path', path])
+## perms = ['perms']
+## for p in self.getPerms(path):
+## l = ['perm']
+## l.append('dom', p.get['dom'])
+## for k in ['read', 'write', 'create', 'owner']:
+## v = p.get(k)
+## l.append([k, v])
+## perms.append(l)
+## val.append(perms)
+ data = self.read(path)
+ if data:
+ val.append(['data', data])
+ children = ['children']
+ for x in self.lsPaths(path):
+ print 'dump>', 'child=', x
+ children.append(self.dump(x))
+ if len(children) > 1:
+ val.append(children)
+ prettyprint(val, out=out)
+ return val
+
+ def getPerms(self, path):
+ return self.getxs().get_permissions(path)
+
+ def ls(self, path="/"):
+ return self.getxs().ls(path)
+
+ def lsPaths(self, path="/"):
+ return [ os.path.join(path, x) for x in self.ls(path) ]
+
+ def lsr(self, path="/", list=None):
+ if list is None:
+ list = []
+ list.append(path)
+ for x in self.lsPaths(path):
+ list.append(x)
+ self.lsr(x, list=list)
+ return list
+
+ def rm(self, path):
+ try:
+ #for x in self.lsPaths():
+ # self.getxs().rm(x)
+ self.getxs().rm(path)
+ except:
+ pass
+
+ def exists(self, path):
+ try:
+ self.getxs().ls(path)
+ return True
+ except RuntimeError, ex:
+ if ex.args[0] == errno.ENOENT:
+ return False
+ else:
+ raise
+
+ def mkdirs(self, path):
+ if self.exists(path):
+ return
+ elts = path.split("/")
+ p = "/"
+ for x in elts:
+ if x == "": continue
+ p = os.path.join(p, x)
+ if not self.exists(p):
+ self.getxs().write(p, "", create=True)
+
+ def read(self, path):
+ try:
+ return self.getxs().read(path)
+ except RuntimeError, ex:
+ if ex.args[0] == errno.EISDIR:
+ return None
+ else:
+ raise
+
+ def create(self, path, excl=False):
+ self.write(path, "", create=True, excl=excl)
+
+ def write(self, path, data, create=True, excl=False):
+ self.mkdirs(path)
+ self.getxs().write(path, data, create=create, excl=excl)
+
+ def begin(self, path):
+ self.getxs().begin_transaction(path)
+
+ def commit(self, abandon=False):
+ self.getxs().end_transaction(abort=abandon)
+
+ def subscribe(self, event, fn):
+ watcher = self.watchEvent(event)
+ self.subscription_id += 1
+ subs = Subscription(event, fn, self.subscription_id)
+ self.subscription[subs.id] = subs
+ subs.watch(watcher)
+ return subs.id
+
+ def unsubscribe(self, sid):
+ s = self.subscription.get(sid)
+ if not s: return
+ del self.subscription[s.id]
+ s.unwatch()
+ unwatchEvent(s.event)
+
+ def sendEvent(self, event, data):
+ eventPath = getEventPath(event)
+ eidPath = getEventIdPath(event)
+ try:
+ self.begin(eventPath)
+ self.mkdirs(eventPath)
+ if self.exists(eidPath):
+ eid = self.readInt(eidPath)
+ eid += 1
+ else:
+ eid = 1
+ self.writeInt(eidPath, eid)
+ self.write(os.path.join(eventPath, str(eid)), data)
+ finally:
+ self.commit()
+
+ def watchEvent(self, event):
+ if event in self.events:
+ return
+ watcher = Watcher(event)
+ self.watchers[watcher.getEvent()] = watcher
+ self.watchStart()
+ return watcher
+
+ def unwatchEvent(self, event):
+ watcher = self.watchers.get(event)
+ if not watcher:
+ return
+ if not watcher.watching():
+ del self.watchers[event]
+
+ def watchStart(self):
+ if self.watchThread: return
+
+ def watchMain(self):
+ try:
+ while True:
+ if self.watchThread is None: return
+ if not self.events:
+ return
+ rd = self.watchers.values()
+ try:
+ (rd, wr, er) = select.select(rd, [], [], SELECT_TIMEOUT)
+ for watcher in rd:
+ watcher.notify()
+ except socket.error, ex:
+ if ex.args[0] in (EAGAIN, EINTR):
+ pass
+ else:
+ raise
+ finally:
+ self.watchThread = None
+
+ def introduceDomain(self, dom, page, evtchn, path):
+ self.getxs().introduce_domain(dom, page, evtchn.port1, path)
+
+ def releaseDomain(self, dom):
+ self.getxs().release_domain(dom)
+
+def getXenStore():
+ global xenstore
+ try:
+ return xenstore
+ except:
+ xenstore = XenStore()
+ return xenstore
+
+class XenNode:
+
+ def __init__(self, path="/", create=True):
+ self.store = getXenStore()
+ self.path = path
+ if not self.store.exists(path):
+ if create:
+ self.store.create(path)
+ else:
+ raise ValueError("path does not exist: '%s'" % path)
+
+ def relPath(self, path=""):
+ if not path:
+ return self.path
+ if path and path.startswith("/"):
+ path = path[1:]
+ return os.path.join(self.path, path)
+
+ def delete(self, path=""):
+ self.store.rm(self.relPath(path))
+
+ def exists(self, path=""):
+ return self.store.exists(self.relPath(path))
+
+ def getNode(self, path="", create=True):
+ if path == "":
+ return self
+ else:
+ return XenNode(self.relPath(path=path), create=create)
+
+ getChild = getNode
+
+ def getData(self, path=""):
+ path = self.relPath(path)
+ try:
+ return self.store.read(path)
+ except:
+ return None
+
+ def setData(self, data, path=""):
+ path = self.relPath(path)
+ #print 'XenNode>setData>', 'path=', path, 'data=', data
+ return self.store.write(path, data)
+
+ def getLock(self):
+ return None
+
+ def lock(self, lockid):
+ return None
+
+ def unlock(self, lockid):
+ return None
+
+ def deleteChild(self, name):
+ self.delete(name)
+
+ def deleteChildren(self):
+ for name in self.ls():
+ self.deleteChild(name)
+
+ def getChildren(self):
+ return [ self.getNode(name) for name in self.ls() ]
+
+ def ls(self):
+ return self.store.ls(self.path)
+
+ def introduceDomain(self, dom, page, evtchn, path):
+ self.store.introduceDomain(dom, page, evtchn, path)
+
+ def releaseDomain(self, dom):
+ self.store.releaseDomain(dom)
+
+ def __repr__(self):
+ return "<XenNode %s>" % self.path
+
+
diff --git a/tools/python/xen/xend/xenstore/xsobj.py b/tools/python/xen/xend/xenstore/xsobj.py
new file mode 100644
index 0000000000..b1c9a4f1d1
--- /dev/null
+++ b/tools/python/xen/xend/xenstore/xsobj.py
@@ -0,0 +1,522 @@
+import string
+import types
+
+from xen.xend import sxp
+from xsnode import XenNode
+from xen.util.mac import macToString, macFromString
+
+VALID_KEY_CHARS = string.ascii_letters + string.digits + "_-@"
+
+def hasAttr(obj, attr):
+ if isinstance(obj, dict):
+ return obj.contains(attr)
+ else:
+ return hasattr(obj, attr)
+
+def getAttr(obj, attr):
+ if isinstance(obj, dict):
+ return dict.get(attr)
+ else:
+ return getattr(obj, attr, None)
+
+def setAttr(obj, attr, val):
+ if isinstance(obj, dict):
+ dict[attr] = val
+ else:
+ setattr(obj, attr, val)
+
+class DBConverter:
+ """Conversion of values to and from strings in xenstore.
+ """
+
+ converters = {}
+
+ def checkType(cls, ty):
+ if ty is None or ty in cls.converters:
+ return
+ raise ValueError("invalid converter type: '%s'" % ty)
+
+ checkType = classmethod(checkType)
+
+ def getConverter(cls, ty=None):
+ if ty is None:
+ ty = "str"
+ conv = cls.converters.get(ty)
+ if not conv:
+ raise ValueError("no converter for type: '%s'" % ty)
+ return conv
+
+ getConverter = classmethod(getConverter)
+
+ def convertToDB(cls, val, ty=None):
+ return cls.getConverter(ty).toDB(val)
+
+ convertToDB = classmethod(convertToDB)
+
+ def convertFromDB(cls, val, ty=None):
+ return cls.getConverter(ty).fromDB(val)
+
+ convertFromDB = classmethod(convertFromDB)
+
+ # Must define in subclass.
+ name = None
+
+ def __init__(self):
+ self.register()
+
+ def register(self):
+ if not self.name:
+ raise ValueError("invalid converter name: '%s'" % self.name)
+ self.converters[self.name] = self
+
+ def toDB(self, val):
+ raise NotImplementedError()
+
+ def fromDB(self, val):
+ raise NotImplementedError()
+
+class StrConverter(DBConverter):
+
+ name = "str"
+
+ def toDB(self, val):
+ # Convert True/False to 1/0, otherwise they convert to
+ # 'True' and 'False' rather than '1' and '0', even though
+ # isinstance(True/False, int) is true.
+ if isinstance(val, bool):
+ val = int(val)
+ return str(val)
+
+ def fromDB(self, data):
+ return data
+
+StrConverter()
+
+class BoolConverter(DBConverter):
+
+ name = "bool"
+
+ def toDB(self, val):
+ return str(int(bool(val)))
+
+ def fromDB(self, data):
+ return bool(int(data))
+
+BoolConverter()
+
+class SxprConverter(DBConverter):
+
+ name = "sxpr"
+
+ def toDB(self, val):
+ return sxp.to_string(val)
+
+ def fromDB(self, data):
+ return sxp.from_string(data)
+
+SxprConverter()
+
+class IntConverter(DBConverter):
+
+ name = "int"
+
+ def toDB(self, val):
+ return str(int(val))
+
+ def fromDB(self, data):
+ return int(data)
+
+IntConverter()
+
+class FloatConverter(DBConverter):
+
+ name = "float"
+
+ def toDB(self, val):
+ return str(float(val))
+
+ def fromDB(self, data):
+ return float(data)
+
+FloatConverter()
+
+class LongConverter(DBConverter):
+
+ name = "long"
+
+ def toDB(self, val):
+ return str(long(val))
+
+ def fromDB(self, data):
+ return long(data)
+
+LongConverter()
+
+class MacConverter(DBConverter):
+
+ name = "mac"
+
+ def toDB(self, val):
+ return macToString(val)
+
+ def fromDB(self, data):
+ return macFromString(data)
+
+MacConverter()
+
+class DBVar:
+
+ def __init__(self, var, ty=None, path=None):
+ DBConverter.checkType(ty)
+ if path is None:
+ path = var
+ self.var = var
+ self.ty = ty
+ self.path = path
+ varpath = filter(bool, self.var.split())
+ self.attrpath = varpath[:-1]
+ self.attr = varpath[-1]
+
+ def exportToDB(self, db, obj):
+ self.setDB(db, self.getObj(obj))
+
+ def importFromDB(self, db, obj):
+ self.setObj(obj, self.getDB(db))
+
+ def getObj(self, obj):
+ o = obj
+ for x in self.attrpath:
+ o = getAttr(o, x)
+ if o is None:
+ return None
+ return getAttr(o, self.attr)
+
+ def setObj(self, obj, val):
+ o = obj
+ for x in self.attrpath:
+ o = getAttr(o, x)
+ # Don't set obj attr if val is None.
+ if val is None and hasAttr(o, self.attr):
+ return
+ setAttr(o, self.attr, val)
+
+ def getDB(self, db):
+ try:
+ data = getattr(db, self.path)
+ except AttributeError:
+ return None
+ return DBConverter.convertFromDB(data, ty=self.ty)
+
+ def setDB(self, db, val):
+ # Don't set in db if val is None.
+ #print 'DBVar>setDB>', self.path, 'val=', val
+ if val is None:
+ return
+ data = DBConverter.convertToDB(val, ty=self.ty)
+ #print 'DBVar>setDB>', self.path, 'data=', data
+ setattr(db, self.path, data)
+
+
+class DBMap(dict):
+ """A persistent map. Extends dict with persistence.
+ Set and get values using the usual map syntax:
+
+ m[k], m.get(k)
+ m[k] = v
+
+ Also supports being treated as an object with attributes.
+ When 'k' is a legal identifier you may also use
+
+ m.k, getattr(m, k)
+ m.k = v, setattr(m, k)
+ k in m, hasattr(m, k)
+
+ When setting you can pass in a normal value, for example
+
+ m.x = 3
+
+ Getting works too:
+
+ m.x ==> 3
+
+ while m['x'] will return the map for x.
+
+ m['x'].getData() ==> 3
+
+ To get values from subdirs use get() to get the subdir first:
+
+ get(m, 'foo').x
+ m['foo'].x
+
+ instead of m.foo.x, because m.foo will return the data for field foo,
+ not the directory.
+
+ You can assign values into a subdir by passing a map:
+
+ m.foo = {'x': 1, 'y':2 }
+
+ You can also use paths as keys:
+
+ m['foo/x'] = 1
+
+ sets field x in subdir foo.
+
+ """
+
+ __db__ = None
+ __data__ = None
+ __perms__ = None
+ __parent__ = None
+ __name__ = ""
+
+ __transaction__ = False
+
+ # True if value set since saved (or never saved).
+ __dirty__ = True
+
+ def __init__(self, parent=None, name="", db=None):
+ if parent is None:
+ self.__name__ = name
+ else:
+ if not isinstance(parent, DBMap):
+ raise ValueError("invalid parent")
+ self.__parent__ = parent
+ self.__name__ = name
+ db = self.__parent__.getChildDB(name)
+ self.setDB(db)
+
+ def getName(self):
+ return self.__name__
+
+ def getPath(self):
+ return self.__db__ and self.__db__.relPath()
+
+ def introduceDomain(self, dom, page, evtchn, path=None):
+ db = self.__db__
+ if path is None:
+ path = db.relPath()
+ print 'DBMap>introduceDomain>', dom, page, evtchn, path
+ try:
+ db.introduceDomain(dom, page, evtchn, path)
+ except Exception, ex:
+ import traceback
+ traceback.print_exc()
+ print 'DBMap>introduceDomain>', ex
+ pass # todo: don't ignore
+
+ def releaseDomain(self, dom):
+ db = self.__db__
+ print 'DBMap>releaseDomain>', dom
+ try:
+ db.releaseDomain(dom)
+ except Exception, ex:
+ import traceback
+ traceback.print_exc()
+ print 'DBMap>releaseDomain>', ex
+ pass # todo: don't ignore
+
+ def transactionBegin(self):
+ # Begin a transaction.
+ pass
+
+ def transactionCommit(self):
+ # Commit writes to db.
+ pass
+
+ def transactionFail(self):
+ # Fail a transaction.
+ # We have changed values, what do we do?
+ pass
+
+ def watch(self, fn):
+ pass
+
+ def unwatch(self, watch):
+ pass
+
+ def checkName(self, k):
+ if k == "":
+ raise ValueError("invalid key, empty string")
+ for c in k:
+ if c in VALID_KEY_CHARS: continue
+ raise ValueError("invalid key char '%s'" % c)
+
+ def _setData(self, v):
+ #print 'DBMap>_setData>', self.getPath(), 'data=', v
+ if v != self.__data__:
+ self.__dirty__ = True
+ self.__data__ = v
+
+ def setData(self, v):
+ if isinstance(v, dict):
+ for (key, val) in v.items():
+ self[key] = val
+ else:
+ self._setData(v)
+
+ def getData(self):
+ return self.__data__
+
+ def _set(self, k, v):
+ dict.__setitem__(self, k, v)
+
+ def _get(self, k):
+ try:
+ return dict.__getitem__(self, k)
+ except:
+ return None
+
+ def _del(self, k, v):
+ try:
+ dict.__delitem__(self, k)
+ except:
+ pass
+
+ def _contains(self, k):
+ return dict.__contains__(self, k)
+
+ def __setitem__(self, k, v, save=False):
+ node = self.addChild(k)
+ node.setData(v)
+ if save:
+ node.saveDB()
+
+ def __getitem__(self, k):
+ if self._contains(k):
+ v = self._get(k)
+ else:
+ v = self.readChildDB(k)
+ self._set(k, v)
+ return v
+
+ def __delitem__(self, k):
+ self._del(k)
+ self.deleteChildDB(k)
+
+ def __repr__(self):
+ if len(self):
+ return dict.__repr__(self)
+ else:
+ return repr(self.__data__)
+
+ def __setattr__(self, k, v):
+ if k.startswith("__"):
+ object.__setattr__(self, k, v)
+ else:
+ self.__setitem__(k, v, save=True)
+ return v
+
+ def __getattr__(self, k):
+ if k.startswith("__"):
+ v = object.__getattr__(self, k)
+ else:
+ try:
+ v = self.__getitem__(k).getData()
+ except LookupError, ex:
+ raise AttributeError(ex.args)
+ return v
+
+ def __delattr__(self, k):
+ return self.__delitem__(k)
+
+ def delete(self):
+ dict.clear(self)
+ self.__data__ = None
+ if self.__db__:
+ self.__db__.delete()
+
+ def clear(self):
+ dict.clear(self)
+ if self.__db__:
+ self.__db__.deleteChildren()
+
+ def getChild(self, k):
+ return self._get(k)
+
+ def getChildDB(self, k):
+ self.checkName(k)
+ return self.__db__ and self.__db__.getChild(k)
+
+ def deleteChildDB(self, k):
+ if self.__db__:
+ self.__db__.deleteChild(k)
+
+ def _addChild(self, k):
+ kid = self._get(k)
+ if kid is None:
+ kid = DBMap(parent=self, name=k, db=self.getChildDB(k))
+ self._set(k, kid)
+ return kid
+
+ def addChild(self, path):
+ l = path.split("/")
+ n = self
+ for x in l:
+ if x == "": continue
+ n = n._addChild(x)
+ return n
+
+ def setDB(self, db):
+ if (db is not None) and not isinstance(db, XenNode):
+ raise ValueError("invalid db")
+ self.__db__ = db
+ for (k, v) in self.items():
+ if v is None: continue
+ if isinstance(v, DBMap):
+ v._setDB(self.addChild(k), restore)
+
+ def readDB(self):
+ if self.__db__ is None:
+ return
+ self.__data__ = self.__db__.getData()
+ for k in self.__db__.ls():
+ n = self.addChild(k)
+ n.readDB()
+ self.__dirty__ = False
+
+ def readChildDB(self, k):
+ if self.__db__ and (k in self.__db__.ls()):
+ n = self.addChild(k)
+ n.readDB()
+ raise LookupError("invalid key '%s'" % k)
+
+ def saveDB(self, sync=False, save=False):
+ """Save unsaved data to db.
+ If save or sync is true, saves whether dirty or not.
+ If sync is true, removes db entries not in the map.
+ """
+
+ if self.__db__ is None:
+ #print 'DBMap>saveDB>',self.getPath(), 'no db'
+ return
+ # Write data.
+ #print 'DBMap>saveDB>', self.getPath(), 'dirty=', self.__dirty__, 'data=', self.__data__
+ if ((self.__data__ is not None)
+ and (sync or save or self.__dirty__)):
+ self.__db__.setData(self.__data__)
+ self.__dirty__ = False
+ else:
+ #print 'DBMap>saveDB>', self.getPath(), 'not written'
+ pass
+ # Write children.
+ for (name, node) in self.items():
+ if not isinstance(node, DBMap): continue
+ node.saveDB(sync=sync, save=save)
+ # Remove db nodes not in children.
+ if sync:
+ for name in self.__db__.ls():
+ if name not in self:
+ self.__db__.delete(name)
+
+ def importFromDB(self, obj, fields):
+ """Set fields in obj from db fields.
+ """
+ for f in fields:
+ f.importFromDB(self, obj)
+
+ def exportToDB(self, obj, fields, save=False, sync=False):
+ """Set fields in db from obj fields.
+ """
+ for f in fields:
+ f.exportToDB(self, obj)
+ self.saveDB(save=save, sync=sync)
diff --git a/tools/python/xen/xend/xenstore/xsresource.py b/tools/python/xen/xend/xenstore/xsresource.py
new file mode 100644
index 0000000000..37011bdea3
--- /dev/null
+++ b/tools/python/xen/xend/xenstore/xsresource.py
@@ -0,0 +1,136 @@
+#============================================================================
+# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
+#============================================================================
+# HTTP interface onto xenstore (read-only).
+# Mainly intended for testing.
+
+import os
+import os.path
+
+from xen.web.httpserver import HttpServer, UnixHttpServer
+from xen.web.SrvBase import SrvBase
+from xen.web.SrvDir import SrvDir
+from xen.xend.Args import FormFn
+from xen.xend.xenstore import XenNode
+
+def pathurl(req):
+ url = req.prePathURL()
+ if not url.endswith('/'):
+ url += '/'
+ return url
+
+def writelist(req, l):
+ req.write('(')
+ for k in l:
+ req.write(' ' + k)
+ req.write(')')
+
+def lsData(dbnode, req, url):
+ v = dbnode.getData()
+ if v is None:
+ req.write('<p>No data')
+ else:
+ req.write('<p>Data: <pre>')
+ req.write(str(v))
+ req.write('</pre>')
+ v = dbnode.getLock()
+ if v is None:
+ req.write("<p>Unlocked")
+ else:
+ req.write("<p>Lock = %s" % v)
+
+def lsChildren(dbnode, req, url):
+ l = dbnode.ls()
+ if l:
+ req.write('<p>Children: <ul>')
+ for key in l:
+ child = dbnode.getChild(key)
+ data = child.getData()
+ if data is None: data = ""
+ req.write('<li><a href="%(url)s%(key)s">%(key)s</a> %(data)s</li>'
+ % { "url": url, "key": key, "data": data })
+ req.write('</ul>')
+ else:
+ req.write('<p>No children')
+
+
+class DBDataResource(SrvBase):
+ """Resource for the node data.
+ """
+
+ def __init__(self, dbnode):
+ SrvBase.__init__(self)
+ self.dbnode = dbnode
+
+ def render_GET(self, req):
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ req.write("<pre>")
+ req.write(self.getData() or self.getNoData())
+ req.write("</pre>")
+ req.write('</body></html>')
+
+ def getContentType(self):
+ # Use content-type from metadata.
+ return "text/plain"
+
+ def getData(self):
+ v = self.dbnode.getData()
+ if v is None: return v
+ return str(v)
+
+ def getNoData(self):
+ return ""
+
+class DBNodeResource(SrvDir):
+ """Resource for a DB node.
+ """
+
+ def __init__(self, dbnode):
+ SrvDir.__init__(self)
+ self.dbnode = dbnode
+
+ def get(self, x):
+ val = None
+ if x == "__data__":
+ val = DBDataResource(self.dbnode)
+ else:
+ if self.dbnode.exists(x):
+ child = self.dbnode.getChild(x, create=False)
+ else:
+ child = None
+ if child is not None:
+ val = DBNodeResource(child)
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def ls(self, req, use_sxp=0):
+ if use_sxp:
+ writelist(req, self.dbnode.getChildren())
+ else:
+ url = pathurl(req)
+ req.write("<fieldset>")
+ lsData(self.dbnode, req, url)
+ lsChildren(self.dbnode, req, url)
+ req.write("</fieldset>")
+
+ def form(self, req):
+ url = req.prePathURL()
+ pass
+
+class DBRootResource(DBNodeResource):
+ """Resource for the root of a DB.
+ """
+
+ def __init__(self):
+ DBNodeResource.__init__(self, XenNode())
+
+def main(argv):
+ root = SrvDir()
+ root.putChild('xenstore', DBRootResource())
+ interface = ''
+ port = 8003
+ server = HttpServer(root=root, interface=interface, port=port)
+ server.run()