mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
bitkeeper revision 1.1700 (42a854e79oBFuqa_DSY4Lr9IhenUQw)
Merge xenstore changes. Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
This commit is contained in:
commit
06940d306a
73 files changed
+12292
-826
No files matched your search
+13
-3
@@ -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')
|
||||
|
||||
@@ -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" },
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+152
-134
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
"""Construct the boot image for the domain.
|
||||
"""
|
||||
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
|
||||
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 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
|
||||
def delete(self):
|
||||
"""Delete the vm's db.
|
||||
"""
|
||||
self.configs.append(val)
|
||||
|
||||
def destroy(self):
|
||||
"""Completely destroy the vm.
|
||||
"""
|
||||
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,))
|
||||
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)
|
||||
|
||||
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.
|
||||
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
|
||||
"""
|
||||
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.
|
||||
|
||||
@param ostype: OS type
|
||||
@param kernel: kernel image
|
||||
@param ramdisk: kernel ramdisk
|
||||
@param cmdline: kernel commandline
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
@@ -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>')
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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']])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@return dict (empty on error)
|
||||
class EventChannel(dict):
|
||||
"""An event channel between domains.
|
||||
"""
|
||||
evtchn = xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
|
||||
if evtchn:
|
||||
evtchn['dom1'] = dom1
|
||||
evtchn['dom2'] = dom2
|
||||
return evtchn
|
||||
|
||||
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))
|
||||
|
||||
def eventChannel(dom1, dom2, port1=0, port2=0):
|
||||
"""Create an event channel between domains.
|
||||
|
||||
@return EventChannel (None on error)
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
import os
|
||||
|
||||
def getenv(var, val, conv=None):
|
||||
"""Get a value from the environment, with optional conversion.
|
||||
|
||||
@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_USER = 'root'
|
||||
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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
@@ -0,0 +1,2 @@
|
||||
from xsnode import *
|
||||
from xsobj import *
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||