aboutsummaryrefslogtreecommitdiffstats
path: root/tools/python
diff options
context:
space:
mode:
Diffstat (limited to 'tools/python')
-rw-r--r--tools/python/Makefile15
-rw-r--r--tools/python/setup.py48
-rw-r--r--tools/python/xen/__init__.py1
-rw-r--r--tools/python/xen/ext/__init__.py1
-rw-r--r--tools/python/xen/ext/xc/Makefile15
-rw-r--r--tools/python/xen/ext/xc/setup.py19
-rw-r--r--tools/python/xen/ext/xc/xc.c1160
-rw-r--r--tools/python/xen/ext/xu/__init__.py0
-rw-r--r--tools/python/xen/ext/xu/domain_controller.h532
-rw-r--r--tools/python/xen/ext/xu/xu.c1386
-rw-r--r--tools/python/xen/util/Brctl.py160
-rw-r--r--tools/python/xen/util/__init__.py1
-rw-r--r--tools/python/xen/util/console_client.py82
-rw-r--r--tools/python/xen/util/ip.py113
-rw-r--r--tools/python/xen/util/tempfile.py451
-rw-r--r--tools/python/xen/xend/Args.py126
-rw-r--r--tools/python/xen/xend/EventServer.py215
-rw-r--r--tools/python/xen/xend/EventTypes.py34
-rw-r--r--tools/python/xen/xend/PrettyPrint.py299
-rw-r--r--tools/python/xen/xend/Vifctl.py28
-rw-r--r--tools/python/xen/xend/XendClient.py311
-rw-r--r--tools/python/xen/xend/XendConsole.py179
-rw-r--r--tools/python/xen/xend/XendDB.py91
-rw-r--r--tools/python/xen/xend/XendDomain.py369
-rw-r--r--tools/python/xen/xend/XendDomainConfig.py44
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py908
-rw-r--r--tools/python/xen/xend/XendMigrate.py103
-rw-r--r--tools/python/xen/xend/XendNode.py71
-rw-r--r--tools/python/xen/xend/XendRoot.py156
-rw-r--r--tools/python/xen/xend/XendVnet.py69
-rw-r--r--tools/python/xen/xend/__init__.py1
-rw-r--r--tools/python/xen/xend/encode.py165
-rw-r--r--tools/python/xen/xend/server/SrvBase.py137
-rw-r--r--tools/python/xen/xend/server/SrvConsole.py42
-rw-r--r--tools/python/xen/xend/server/SrvConsoleDir.py59
-rw-r--r--tools/python/xen/xend/server/SrvDaemon.py751
-rw-r--r--tools/python/xen/xend/server/SrvDeviceDir.py9
-rw-r--r--tools/python/xen/xend/server/SrvDir.py91
-rw-r--r--tools/python/xen/xend/server/SrvDomain.py195
-rw-r--r--tools/python/xen/xend/server/SrvDomainDir.py144
-rw-r--r--tools/python/xen/xend/server/SrvEventDir.py41
-rw-r--r--tools/python/xen/xend/server/SrvNode.py54
-rw-r--r--tools/python/xen/xend/server/SrvRoot.py30
-rw-r--r--tools/python/xen/xend/server/SrvServer.py59
-rw-r--r--tools/python/xen/xend/server/SrvVnetDir.py9
-rw-r--r--tools/python/xen/xend/server/__init__.py1
-rwxr-xr-xtools/python/xen/xend/server/blkif.py341
-rwxr-xr-xtools/python/xen/xend/server/channel.py378
-rwxr-xr-xtools/python/xen/xend/server/console.py232
-rwxr-xr-xtools/python/xen/xend/server/controller.py169
-rwxr-xr-xtools/python/xen/xend/server/cstruct.py269
-rw-r--r--tools/python/xen/xend/server/domain.py41
-rw-r--r--tools/python/xen/xend/server/messages.py219
-rwxr-xr-xtools/python/xen/xend/server/netif.py316
-rw-r--r--tools/python/xen/xend/server/params.py10
-rw-r--r--tools/python/xen/xend/sxp.py580
-rw-r--r--tools/python/xen/xm/__init__.py0
-rw-r--r--tools/python/xen/xm/create.py366
-rw-r--r--tools/python/xen/xm/main.py448
-rw-r--r--tools/python/xen/xm/opts.py340
-rw-r--r--tools/python/xen/xm/shutdown.py75
61 files changed, 12559 insertions, 0 deletions
diff --git a/tools/python/Makefile b/tools/python/Makefile
new file mode 100644
index 0000000000..7dc74a1072
--- /dev/null
+++ b/tools/python/Makefile
@@ -0,0 +1,15 @@
+
+all:
+ python setup.py build
+
+install: all
+ if [ "$(prefix)" = "" ]; then \
+ python setup.py install; \
+ elif [ "$(dist)" = "yes" ]; then \
+ python setup.py install --home="$(prefix)"; \
+ else \
+ python setup.py install --root="$(prefix)"; \
+ fi
+
+clean:
+ rm -rf build *.pyc *.pyo *.o *.a *~
diff --git a/tools/python/setup.py b/tools/python/setup.py
new file mode 100644
index 0000000000..b0eb3f2ebd
--- /dev/null
+++ b/tools/python/setup.py
@@ -0,0 +1,48 @@
+
+from distutils.core import setup, Extension
+
+XEN_ROOT = "../.."
+
+extra_compile_args = ["-fno-strict-aliasing"]
+
+
+include_dirs = [ XEN_ROOT + "/xen/include/hypervisor-ifs",
+ XEN_ROOT + "/linux-xen-sparse/include",
+ XEN_ROOT + "/tools/python/xen/ext/xu",
+ XEN_ROOT + "/tools/libxc",
+ XEN_ROOT + "/tools/libxutil",
+ ]
+
+library_dirs = [ XEN_ROOT + "/tools/libxc",
+ XEN_ROOT + "/tools/libxutil",
+ ]
+
+libraries = [ "xc", "xutil" ]
+
+xc = Extension("xc",
+ extra_compile_args = extra_compile_args,
+ include_dirs = include_dirs + [ "xen/ext/xc" ],
+ library_dirs = library_dirs,
+ libraries = libraries,
+ sources = [ "xen/ext/xc/xc.c" ])
+
+xu = Extension("xu",
+ extra_compile_args = extra_compile_args,
+ include_dirs = include_dirs + [ "xen/ext/xu" ],
+ library_dirs = library_dirs,
+ libraries = libraries,
+ sources = [ "xen/ext/xu/xu.c" ])
+
+setup(name = 'xen',
+ version = '2.0',
+ description = 'Xen',
+ packages = ['xen',
+ 'xen.ext',
+ 'xen.util',
+ 'xen.xend',
+ 'xen.xend.server',
+ 'xen.xm',
+ ],
+ ext_package = "xen.ext",
+ ext_modules = [ xc, xu ]
+ )
diff --git a/tools/python/xen/__init__.py b/tools/python/xen/__init__.py
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/tools/python/xen/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tools/python/xen/ext/__init__.py b/tools/python/xen/ext/__init__.py
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/tools/python/xen/ext/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tools/python/xen/ext/xc/Makefile b/tools/python/xen/ext/xc/Makefile
new file mode 100644
index 0000000000..7dc74a1072
--- /dev/null
+++ b/tools/python/xen/ext/xc/Makefile
@@ -0,0 +1,15 @@
+
+all:
+ python setup.py build
+
+install: all
+ if [ "$(prefix)" = "" ]; then \
+ python setup.py install; \
+ elif [ "$(dist)" = "yes" ]; then \
+ python setup.py install --home="$(prefix)"; \
+ else \
+ python setup.py install --root="$(prefix)"; \
+ fi
+
+clean:
+ rm -rf build *.pyc *.pyo *.o *.a *~
diff --git a/tools/python/xen/ext/xc/setup.py b/tools/python/xen/ext/xc/setup.py
new file mode 100644
index 0000000000..8efe5ca1a0
--- /dev/null
+++ b/tools/python/xen/ext/xc/setup.py
@@ -0,0 +1,19 @@
+
+from distutils.core import setup, Extension
+
+module = Extension("xc",
+ extra_compile_args = ["-fno-strict-aliasing"],
+ include_dirs = ["../lib",
+ "../../../xen/include/hypervisor-ifs",
+ "../../../linux-xen-sparse/include",
+ "../../xu/lib",
+ "../../lib" ],
+ library_dirs = ["../lib",
+ "../../lib" ],
+ libraries = ["xc"],
+ sources = ["Xc.c"])
+
+setup(name = "xc",
+ version = "2.0",
+ ext_package = "xen.ext",
+ ext_modules = [module])
diff --git a/tools/python/xen/ext/xc/xc.c b/tools/python/xen/ext/xc/xc.c
new file mode 100644
index 0000000000..2eb70cf2a0
--- /dev/null
+++ b/tools/python/xen/ext/xc/xc.c
@@ -0,0 +1,1160 @@
+/******************************************************************************
+ * Xc.c
+ *
+ * Copyright (c) 2003-2004, K A Fraser (University of Cambridge)
+ */
+
+#include <Python.h>
+#include <xc.h>
+#include <zlib.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "xc_private.h"
+#include "gzip_stream.h"
+
+/* Needed for Python versions earlier than 2.3. */
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC DL_EXPORT(void)
+#endif
+
+#define XENPKG "xen.ext.xc"
+
+static PyObject *xc_error, *zero;
+
+typedef struct {
+ PyObject_HEAD;
+ int xc_handle;
+} XcObject;
+
+/*
+ * Definitions for the 'xc' object type.
+ */
+
+static PyObject *pyxc_domain_create(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ unsigned int mem_kb = 0;
+ char *name = "(anon)";
+ int cpu = -1;
+ u32 dom;
+ int ret;
+
+ static char *kwd_list[] = { "mem_kb", "name", "cpu", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|isi", kwd_list,
+ &mem_kb, &name, &cpu) )
+ return NULL;
+
+ if ( (ret = xc_domain_create(xc->xc_handle, mem_kb, name, cpu, &dom)) < 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return PyInt_FromLong(dom);
+}
+
+static PyObject *pyxc_domain_pause(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+
+ static char *kwd_list[] = { "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
+ return NULL;
+
+ if ( xc_domain_pause(xc->xc_handle, dom) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_domain_unpause(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+
+ static char *kwd_list[] = { "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
+ return NULL;
+
+ if ( xc_domain_unpause(xc->xc_handle, dom) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_domain_destroy(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+
+ static char *kwd_list[] = { "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
+ return NULL;
+
+ if ( xc_domain_destroy(xc->xc_handle, dom) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_domain_pincpu(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ int cpu = -1;
+
+ static char *kwd_list[] = { "dom", "cpu", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
+ &dom, &cpu) )
+ return NULL;
+
+ if ( xc_domain_pincpu(xc->xc_handle, dom, cpu) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_domain_getinfo(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ PyObject *list;
+
+ u32 first_dom = 0;
+ int max_doms = 1024, nr_doms, i;
+ xc_dominfo_t *info;
+
+ static char *kwd_list[] = { "first_dom", "max_doms", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list,
+ &first_dom, &max_doms) )
+ return NULL;
+
+ if ( (info = malloc(max_doms * sizeof(xc_dominfo_t))) == NULL )
+ return PyErr_NoMemory();
+
+ nr_doms = xc_domain_getinfo(xc->xc_handle, first_dom, max_doms, info);
+
+ list = PyList_New(nr_doms);
+ for ( i = 0 ; i < nr_doms; i++ )
+ {
+ PyList_SetItem(
+ list, i,
+ Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i"
+ ",s:l,s:L,s:s,s:l,s:i}",
+ "dom", info[i].domid,
+ "cpu", info[i].cpu,
+ "dying", info[i].dying,
+ "crashed", info[i].crashed,
+ "shutdown", info[i].shutdown,
+ "paused", info[i].paused,
+ "blocked", info[i].blocked,
+ "running", info[i].running,
+ "mem_kb", info[i].nr_pages*4,
+ "cpu_time", info[i].cpu_time,
+ "name", info[i].name,
+ "maxmem_kb", info[i].max_memkb,
+ "shutdown_reason", info[i].shutdown_reason
+ ));
+ }
+
+ free(info);
+
+ return list;
+}
+
+static int file_save(XcObject *xc, XcIOContext *ctxt, char *state_file){
+ int rc = -1;
+ int fd = -1;
+ int open_flags = (O_CREAT | O_EXCL | O_WRONLY);
+ int open_mode = 0644;
+
+ printf("%s>\n", __FUNCTION__);
+ fd = open(state_file, open_flags, open_mode);
+ if(fd < 0){
+ xcio_perror(ctxt, "Could not open file for writing");
+ goto exit;
+ }
+ /* Compression rate 1: we want speed over compression.
+ * We're mainly going for those zero pages, after all.
+ */
+ printf("%s>gzip_stream_fdopen... \n", __FUNCTION__);
+ ctxt->io = gzip_stream_fdopen(fd, "wb1");
+ if(!ctxt->io){
+ xcio_perror(ctxt, "Could not allocate compression state");
+ goto exit;
+ }
+ printf("%s> xc_linux_save...\n", __FUNCTION__);
+ rc = xc_linux_save(xc->xc_handle, ctxt);
+ exit:
+ if(ctxt->io) IOStream_close(ctxt->io);
+ if(fd >= 0) close(fd);
+ unlink(state_file);
+ printf("%s> rc=%d\n", __FUNCTION__, rc);
+ return rc;
+}
+
+static PyObject *pyxc_linux_save(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ char *state_file;
+ int progress = 1, debug = 0;
+ unsigned int flags = 0;
+ PyObject *val = NULL;
+ int rc = -1;
+ XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
+
+ static char *kwd_list[] = { "dom", "state_file", "vmconfig", "progress", "debug", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|sii", kwd_list,
+ &ioctxt.domain,
+ &state_file,
+ &ioctxt.vmconfig,
+ &progress,
+ &debug)){
+ goto exit;
+ }
+ ioctxt.vmconfig_n = (ioctxt.vmconfig ? strlen(ioctxt.vmconfig) : 0);
+ if (progress) ioctxt.flags |= XCFLAGS_VERBOSE;
+ if (debug) ioctxt.flags |= XCFLAGS_DEBUG;
+ if(!state_file || state_file[0] == '\0') goto exit;
+ rc = file_save(xc, &ioctxt, state_file);
+ if(rc){
+ PyErr_SetFromErrno(xc_error);
+ goto exit;
+ }
+ //xc_domain_destroy(xc->xc_handle, dom);
+ Py_INCREF(zero);
+ val = zero;
+ exit:
+ return val;
+}
+
+
+static int file_restore(XcObject *xc, XcIOContext *ioctxt, char *state_file){
+ int rc = -1;
+
+ ioctxt->io = gzip_stream_fopen(state_file, "rb");
+ if (!ioctxt->io) {
+ xcio_perror(ioctxt, "Could not open file for reading");
+ goto exit;
+ }
+
+ rc = xc_linux_restore(xc->xc_handle, ioctxt);
+ exit:
+ if(ioctxt->io) IOStream_close(ioctxt->io);
+ return rc;
+}
+
+static PyObject *pyxc_linux_restore(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ char *state_file;
+ int progress = 1, debug = 0;
+ u32 dom;
+ PyObject *val = NULL;
+ XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
+ int rc =-1;
+
+ static char *kwd_list[] = { "state_file", "progress", "debug", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|ii", kwd_list,
+ &ioctxt.domain,
+ &state_file,
+ &progress,
+ &debug)){
+ goto exit;
+ }
+ if (progress) ioctxt.flags |= XCFLAGS_VERBOSE;
+ if (debug) ioctxt.flags |= XCFLAGS_DEBUG;
+
+ if(!state_file || state_file[0] == '\0') goto exit;
+ rc = file_restore(xc, &ioctxt, state_file);
+ if(rc){
+ PyErr_SetFromErrno(xc_error);
+ goto exit;
+ }
+ val = Py_BuildValue("{s:i,s:s}",
+ "dom", ioctxt.domain,
+ "vmconfig", ioctxt.vmconfig);
+ //? free(ioctxt.vmconfig);
+ exit:
+ return val;
+}
+
+static PyObject *pyxc_linux_build(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ char *image, *ramdisk = NULL, *cmdline = "";
+ int control_evtchn, flags = 0;
+
+ static char *kwd_list[] = { "dom", "control_evtchn",
+ "image", "ramdisk", "cmdline", "flags",
+ NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssi", kwd_list,
+ &dom, &control_evtchn,
+ &image, &ramdisk, &cmdline, &flags) )
+ return NULL;
+
+ if ( xc_linux_build(xc->xc_handle, dom, image,
+ ramdisk, cmdline, control_evtchn, flags) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_netbsd_build(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ char *image, *ramdisk = NULL, *cmdline = "";
+ int control_evtchn;
+
+ static char *kwd_list[] = { "dom", "control_evtchn",
+ "image", "ramdisk", "cmdline", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssi", kwd_list,
+ &dom, &control_evtchn,
+ &image, &ramdisk, &cmdline) )
+ return NULL;
+
+ if ( xc_netbsd_build(xc->xc_handle, dom, image,
+ cmdline, control_evtchn) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_bvtsched_global_set(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ unsigned long ctx_allow;
+
+ static char *kwd_list[] = { "ctx_allow", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "l", kwd_list, &ctx_allow) )
+ return NULL;
+
+ if ( xc_bvtsched_global_set(xc->xc_handle, ctx_allow) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_bvtsched_global_get(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ unsigned long ctx_allow;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( xc_bvtsched_global_get(xc->xc_handle, &ctx_allow) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("s:l", "ctx_allow", ctx_allow);
+}
+
+static PyObject *pyxc_bvtsched_domain_set(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ unsigned long mcuadv, warp, warpl, warpu;
+
+ static char *kwd_list[] = { "dom", "mcuadv", "warp", "warpl",
+ "warpu", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "illll", kwd_list,
+ &dom, &mcuadv, &warp, &warpl, &warpu) )
+ return NULL;
+
+ if ( xc_bvtsched_domain_set(xc->xc_handle, dom, mcuadv,
+ warp, warpl, warpu) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_bvtsched_domain_get(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u32 dom;
+ unsigned long mcuadv, warp, warpl, warpu;
+
+ static char *kwd_list[] = { "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
+ return NULL;
+
+ if ( xc_bvtsched_domain_get(xc->xc_handle, dom, &mcuadv, &warp,
+ &warpl, &warpu) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("{s:i,s:l,s:l,s:l,s:l}",
+ "domain", dom,
+ "mcuadv", mcuadv,
+ "warp", warp,
+ "warpl", warpl,
+ "warpu", warpu);
+}
+
+static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom1 = DOMID_SELF, dom2 = DOMID_SELF;
+ int port1, port2;
+
+ static char *kwd_list[] = { "dom1", "dom2", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list,
+ &dom1, &dom2) )
+ return NULL;
+
+ if ( xc_evtchn_bind_interdomain(xc->xc_handle, dom1,
+ dom2, &port1, &port2) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("{s:i,s:i}",
+ "port1", port1,
+ "port2", port2);
+}
+
+static PyObject *pyxc_evtchn_bind_virq(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ int virq, port;
+
+ static char *kwd_list[] = { "virq", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &virq) )
+ return NULL;
+
+ if ( xc_evtchn_bind_virq(xc->xc_handle, virq, &port) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return PyInt_FromLong(port);
+}
+
+static PyObject *pyxc_evtchn_close(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom = DOMID_SELF;
+ int port;
+
+ static char *kwd_list[] = { "port", "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
+ &port, &dom) )
+ return NULL;
+
+ if ( xc_evtchn_close(xc->xc_handle, dom, port) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_evtchn_send(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ int port;
+
+ static char *kwd_list[] = { "port", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &port) )
+ return NULL;
+
+ if ( xc_evtchn_send(xc->xc_handle, port) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_evtchn_status(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ PyObject *dict;
+
+ u32 dom = DOMID_SELF;
+ int port, ret;
+ xc_evtchn_status_t status;
+
+ static char *kwd_list[] = { "port", "dom", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
+ &port, &dom) )
+ return NULL;
+
+ ret = xc_evtchn_status(xc->xc_handle, dom, port, &status);
+ if ( ret != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ switch ( status.status )
+ {
+ case EVTCHNSTAT_closed:
+ dict = Py_BuildValue("{s:s}",
+ "status", "closed");
+ break;
+ case EVTCHNSTAT_unbound:
+ dict = Py_BuildValue("{s:s}",
+ "status", "unbound");
+ break;
+ case EVTCHNSTAT_interdomain:
+ dict = Py_BuildValue("{s:s,s:i,s:i}",
+ "status", "interdomain",
+ "dom", status.u.interdomain.dom,
+ "port", status.u.interdomain.port);
+ break;
+ case EVTCHNSTAT_pirq:
+ dict = Py_BuildValue("{s:s,s:i}",
+ "status", "pirq",
+ "irq", status.u.pirq);
+ break;
+ case EVTCHNSTAT_virq:
+ dict = Py_BuildValue("{s:s,s:i}",
+ "status", "virq",
+ "irq", status.u.virq);
+ break;
+ default:
+ dict = Py_BuildValue("{}");
+ break;
+ }
+
+ return dict;
+}
+
+static PyObject *pyxc_physdev_pci_access_modify(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u32 dom;
+ int bus, dev, func, enable, ret;
+
+ static char *kwd_list[] = { "dom", "bus", "dev", "func", "enable", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiii", kwd_list,
+ &dom, &bus, &dev, &func, &enable) )
+ return NULL;
+
+ ret = xc_physdev_pci_access_modify(
+ xc->xc_handle, dom, bus, dev, func, enable);
+ if ( ret != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_readconsolering(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ unsigned int clear = 0;
+ char str[32768];
+ int ret;
+
+ static char *kwd_list[] = { "clear", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwd_list, &clear) )
+ return NULL;
+
+ ret = xc_readconsolering(xc->xc_handle, str, sizeof(str), clear);
+ if ( ret < 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return PyString_FromStringAndSize(str, ret);
+}
+
+static PyObject *pyxc_physinfo(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ xc_physinfo_t info;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( xc_physinfo(xc->xc_handle, &info) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("{s:i,s:i,s:l,s:l,s:l}",
+ "ht_per_core", info.ht_per_core,
+ "cores", info.cores,
+ "total_pages", info.total_pages,
+ "free_pages", info.free_pages,
+ "cpu_khz", info.cpu_khz);
+}
+
+static PyObject *pyxc_atropos_domain_set(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u32 domid;
+ u64 period, slice, latency;
+ int xtratime;
+
+ static char *kwd_list[] = { "dom", "period", "slice", "latency",
+ "xtratime", NULL };
+
+ if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLLLi", kwd_list, &domid,
+ &period, &slice, &latency, &xtratime) )
+ return NULL;
+
+ if ( xc_atropos_domain_set(xc->xc_handle, domid, period, slice,
+ latency, xtratime) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_atropos_domain_get(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u32 domid;
+ u64 period, slice, latency;
+ int xtratime;
+
+ static char *kwd_list[] = { "dom", NULL };
+
+ if( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &domid) )
+ return NULL;
+
+ if ( xc_atropos_domain_get( xc->xc_handle, domid, &period,
+ &slice, &latency, &xtratime ) )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i}",
+ "domain", domid,
+ "period", period,
+ "slice", slice,
+ "latency", latency,
+ "xtratime", xtratime);
+}
+
+
+static PyObject *pyxc_rrobin_global_set(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u64 slice;
+
+ static char *kwd_list[] = { "slice", NULL };
+
+ if( !PyArg_ParseTupleAndKeywords(args, kwds, "L", kwd_list, &slice) )
+ return NULL;
+
+ if ( xc_rrobin_global_set(xc->xc_handle, slice) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_shadow_control(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ int op=0;
+
+ static char *kwd_list[] = { "dom", "op", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
+ &dom, &op) )
+ return NULL;
+
+ if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, NULL) < 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_rrobin_global_get(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u64 slice;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( xc_rrobin_global_get(xc->xc_handle, &slice) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ return Py_BuildValue("{s:L}", "slice", slice);
+}
+
+static PyObject *pyxc_domain_setname(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+ u32 dom;
+ char *name;
+
+ static char *kwd_list[] = { "dom", "name", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list,
+ &dom, &name) )
+ return NULL;
+
+ if ( xc_domain_setname(xc->xc_handle, dom, name) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_domain_setmaxmem(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ unsigned long max_memkb;
+
+ static char *kwd_list[] = { "dom", "max_memkb", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list,
+ &dom, &max_memkb) )
+ return NULL;
+
+ if ( xc_domain_setmaxmem(xc->xc_handle, dom, max_memkb) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+
+static PyMethodDef pyxc_methods[] = {
+ { "domain_create",
+ (PyCFunction)pyxc_domain_create,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Create a new domain.\n"
+ " mem_kb [int, 0]: Memory allocation, in kilobytes.\n"
+ " name [str, '(anon)']: Informative textual name.\n\n"
+ "Returns: [int] new domain identifier; -1 on error.\n" },
+
+ { "domain_pause",
+ (PyCFunction)pyxc_domain_pause,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Temporarily pause execution of a domain.\n"
+ " dom [int]: Identifier of domain to be paused.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_unpause",
+ (PyCFunction)pyxc_domain_unpause,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "(Re)start execution of a domain.\n"
+ " dom [int]: Identifier of domain to be unpaused.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_destroy",
+ (PyCFunction)pyxc_domain_destroy,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Destroy a domain.\n"
+ " dom [int]: Identifier of domain to be destroyed.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_pincpu",
+ (PyCFunction)pyxc_domain_pincpu,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Pin a domain to a specified CPU.\n"
+ " dom [int]: Identifier of domain to be pinned.\n"
+ " cpu [int, -1]: CPU to pin to, or -1 to unpin\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_getinfo",
+ (PyCFunction)pyxc_domain_getinfo,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Get information regarding a set of domains, in increasing id order.\n"
+ " first_dom [int, 0]: First domain to retrieve info about.\n"
+ " max_doms [int, 1024]: Maximum number of domains to retrieve info"
+ " about.\n\n"
+ "Returns: [list of dicts] if list length is less than 'max_doms'\n"
+ " parameter then there was an error, or the end of the\n"
+ " domain-id space was reached.\n"
+ " dom [int]: Identifier of domain to which this info pertains\n"
+ " cpu [int]: CPU to which this domain is bound\n"
+ " dying [int]: Bool - is the domain dying?\n"
+ " crashed [int]: Bool - has the domain crashed?\n"
+ " shutdown [int]: Bool - has the domain shut itself down?\n"
+ " paused [int]: Bool - is the domain paused by control software?\n"
+ " blocked [int]: Bool - is the domain blocked waiting for an event?\n"
+ " running [int]: Bool - is the domain currently running?\n"
+ " mem_kb [int]: Memory reservation, in kilobytes\n"
+ " cpu_time [long]: CPU time consumed, in nanoseconds\n"
+ " name [str]: Identifying name\n"
+ " shutdown_reason [int]: Numeric code from guest OS, explaining "
+ "reason why it shut itself down.\n" },
+
+ { "linux_save",
+ (PyCFunction)pyxc_linux_save,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Save the CPU and memory state of a Linux guest OS.\n"
+ " dom [int]: Identifier of domain to be saved.\n"
+ " state_file [str]: Name of state file. Must not currently exist.\n"
+ " progress [int, 1]: Bool - display a running progress indication?\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "linux_restore",
+ (PyCFunction)pyxc_linux_restore,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Restore the CPU and memory state of a Linux guest OS.\n"
+ " state_file [str]: Name of state file. Must not currently exist.\n"
+ " progress [int, 1]: Bool - display a running progress indication?\n\n"
+ "Returns: [int] new domain identifier on success; -1 on error.\n" },
+
+ { "linux_build",
+ (PyCFunction)pyxc_linux_build,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Build a new Linux guest OS.\n"
+ " dom [int]: Identifier of domain to build into.\n"
+ " image [str]: Name of kernel image file. May be gzipped.\n"
+ " ramdisk [str, n/a]: Name of ramdisk file, if any.\n"
+ " cmdline [str, n/a]: Kernel parameters, if any.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "netbsd_build",
+ (PyCFunction)pyxc_netbsd_build,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Build a new NetBSD guest OS.\n"
+ " dom [int]: Identifier of domain to build into.\n"
+ " image [str]: Name of kernel image file. May be gzipped.\n"
+ " cmdline [str, n/a]: Kernel parameters, if any.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "bvtsched_global_set",
+ (PyCFunction)pyxc_bvtsched_global_set,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Set global tuning parameters for Borrowed Virtual Time scheduler.\n"
+ " ctx_allow [int]: Minimal guaranteed quantum.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "bvtsched_global_get",
+ (PyCFunction)pyxc_bvtsched_global_get,
+ METH_KEYWORDS, "\n"
+ "Get global tuning parameters for BVT scheduler.\n"
+ "Returns: [dict]:\n"
+ " ctx_allow [int]: context switch allowance\n" },
+
+ { "bvtsched_domain_set",
+ (PyCFunction)pyxc_bvtsched_domain_set,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Set per-domain tuning parameters for Borrowed Virtual Time scheduler.\n"
+ " dom [int]: Identifier of domain to be tuned.\n"
+ " mcuadv [int]: Proportional to the inverse of the domain's weight.\n"
+ " warp [int]: How far to warp domain's EVT on unblock.\n"
+ " warpl [int]: How long the domain can run warped.\n"
+ " warpu [int]: How long before the domain can warp again.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "bvtsched_domain_get",
+ (PyCFunction)pyxc_bvtsched_domain_get,
+ METH_KEYWORDS, "\n"
+ "Get per-domain tuning parameters under the BVT scheduler.\n"
+ " dom [int]: Identifier of domain to be queried.\n"
+ "Returns [dict]:\n"
+ " domain [int]: Domain ID.\n"
+ " mcuadv [long]: MCU Advance.\n"
+ " warp [long]: Warp.\n"
+ " warpu [long]: Unwarp requirement.\n"
+ " warpl [long]: Warp limit,\n"
+ },
+
+ { "atropos_domain_set",
+ (PyCFunction)pyxc_atropos_domain_set,
+ METH_KEYWORDS, "\n"
+ "Set the scheduling parameters for a domain when running with Atropos.\n"
+ " dom [int]: domain to set\n"
+ " period [long]: domain's scheduling period\n"
+ " slice [long]: domain's slice per period\n"
+ " latency [long]: wakeup latency hint\n"
+ " xtratime [int]: boolean\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "atropos_domain_get",
+ (PyCFunction)pyxc_atropos_domain_get,
+ METH_KEYWORDS, "\n"
+ "Get the current scheduling parameters for a domain when running with\n"
+ "the Atropos scheduler."
+ " dom [int]: domain to query\n"
+ "Returns: [dict]\n"
+ " domain [int]: domain ID\n"
+ " period [long]: scheduler period\n"
+ " slice [long]: CPU reservation per period\n"
+ " latency [long]: unblocking latency hint\n"
+ " xtratime [int] : 0 if not using slack time, nonzero otherwise\n" },
+
+ { "rrobin_global_set",
+ (PyCFunction)pyxc_rrobin_global_set,
+ METH_KEYWORDS, "\n"
+ "Set Round Robin scheduler slice.\n"
+ " slice [long]: Round Robin scheduler slice\n"
+ "Returns: [int] 0 on success, throws an exception on failure\n" },
+
+ { "rrobin_global_get",
+ (PyCFunction)pyxc_rrobin_global_get,
+ METH_KEYWORDS, "\n"
+ "Get Round Robin scheduler settings\n"
+ "Returns [dict]:\n"
+ " slice [long]: Scheduler time slice.\n" },
+
+ { "evtchn_bind_interdomain",
+ (PyCFunction)pyxc_evtchn_bind_interdomain,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Open an event channel between two domains.\n"
+ " dom1 [int, SELF]: First domain to be connected.\n"
+ " dom2 [int, SELF]: Second domain to be connected.\n\n"
+ "Returns: [dict] dictionary is empty on failure.\n"
+ " port1 [int]: Port-id for endpoint at dom1.\n"
+ " port2 [int]: Port-id for endpoint at dom2.\n" },
+
+ { "evtchn_bind_virq",
+ (PyCFunction)pyxc_evtchn_bind_virq,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Bind an event channel to the specified VIRQ.\n"
+ " virq [int]: VIRQ to bind.\n\n"
+ "Returns: [int] Bound event-channel port.\n" },
+
+ { "evtchn_close",
+ (PyCFunction)pyxc_evtchn_close,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Close an event channel.\n"
+ " dom [int, SELF]: Dom-id of one endpoint of the channel.\n"
+ " port [int]: Port-id of one endpoint of the channel.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "evtchn_send",
+ (PyCFunction)pyxc_evtchn_send,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Send an event along a locally-connected event channel.\n"
+ " port [int]: Port-id of a local channel endpoint.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "evtchn_status",
+ (PyCFunction)pyxc_evtchn_status,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Query the status of an event channel.\n"
+ " dom [int, SELF]: Dom-id of one endpoint of the channel.\n"
+ " port [int]: Port-id of one endpoint of the channel.\n\n"
+ "Returns: [dict] dictionary is empty on failure.\n"
+ " status [str]: 'closed', 'unbound', 'interdomain', 'pirq',"
+ " or 'virq'.\n"
+ "The following are returned if 'status' is 'interdomain':\n"
+ " dom [int]: Dom-id of remote endpoint.\n"
+ " port [int]: Port-id of remote endpoint.\n"
+ "The following are returned if 'status' is 'pirq' or 'virq':\n"
+ " irq [int]: IRQ number.\n" },
+
+ { "physdev_pci_access_modify",
+ (PyCFunction)pyxc_physdev_pci_access_modify,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Allow a domain access to a PCI device\n"
+ " dom [int]: Identifier of domain to be allowed access.\n"
+ " bus [int]: PCI bus\n"
+ " dev [int]: PCI slot\n"
+ " func [int]: PCI function\n"
+ " enable [int]: Non-zero means enable access; else disable access\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "readconsolering",
+ (PyCFunction)pyxc_readconsolering,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Read Xen's console ring.\n"
+ " clear [int, 0]: Bool - clear the ring after reading from it?\n\n"
+ "Returns: [str] string is empty on failure.\n" },
+
+ { "physinfo",
+ (PyCFunction)pyxc_physinfo,
+ METH_VARARGS, "\n"
+ "Get information about the physical host machine\n"
+ "Returns [dict]: information about the hardware"
+ " [None]: on failure.\n" },
+
+ { "shadow_control",
+ (PyCFunction)pyxc_shadow_control,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Set parameter for shadow pagetable interface\n"
+ " dom [int]: Identifier of domain.\n"
+ " op [int, 0]: operation\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_setname",
+ (PyCFunction)pyxc_domain_setname,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Set domain informative textual name\n"
+ " dom [int]: Identifier of domain.\n"
+ " name [str]: Text string.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { "domain_setmaxmem",
+ (PyCFunction)pyxc_domain_setname,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Set a domain's memory limit\n"
+ " dom [int]: Identifier of domain.\n"
+ " max_memkb [long]: .\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+
+/*
+ * Definitions for the 'Xc' module wrapper.
+ */
+
+staticforward PyTypeObject PyXcType;
+
+static PyObject *PyXc_new(PyObject *self, PyObject *args)
+{
+ XcObject *xc;
+
+ if ( !PyArg_ParseTuple(args, ":new") )
+ return NULL;
+
+ xc = PyObject_New(XcObject, &PyXcType);
+
+ if ( (xc->xc_handle = xc_interface_open()) == -1 )
+ {
+ PyObject_Del((PyObject *)xc);
+ return PyErr_SetFromErrno(xc_error);
+ }
+
+ return (PyObject *)xc;
+}
+
+static PyObject *PyXc_getattr(PyObject *obj, char *name)
+{
+ return Py_FindMethod(pyxc_methods, obj, name);
+}
+
+static void PyXc_dealloc(PyObject *self)
+{
+ XcObject *xc = (XcObject *)self;
+ (void)xc_interface_close(xc->xc_handle);
+ PyObject_Del(self);
+}
+
+static PyTypeObject PyXcType = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "Xc",
+ sizeof(XcObject),
+ 0,
+ PyXc_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ PyXc_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 PyMethodDef PyXc_methods[] = {
+ { "new", PyXc_new, METH_VARARGS, "Create a new " XENPKG " object." },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC initxc(void)
+{
+ PyObject *m, *d;
+
+ m = Py_InitModule(XENPKG, PyXc_methods);
+
+ d = PyModule_GetDict(m);
+ xc_error = PyErr_NewException(XENPKG ".error", NULL, NULL);
+ PyDict_SetItemString(d, "error", xc_error);
+
+ zero = PyInt_FromLong(0);
+}
diff --git a/tools/python/xen/ext/xu/__init__.py b/tools/python/xen/ext/xu/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/python/xen/ext/xu/__init__.py
diff --git a/tools/python/xen/ext/xu/domain_controller.h b/tools/python/xen/ext/xu/domain_controller.h
new file mode 100644
index 0000000000..76dd164fcb
--- /dev/null
+++ b/tools/python/xen/ext/xu/domain_controller.h
@@ -0,0 +1,532 @@
+/******************************************************************************
+ * domain_controller.h
+ *
+ * Interface to server controller (e.g., 'xend'). This header file defines the
+ * interface that is shared with guest OSes.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __DOMAIN_CONTROLLER_H__
+#define __DOMAIN_CONTROLLER_H__
+
+
+#ifndef BASIC_START_INFO
+#error "Xen header file hypervisor-if.h must already be included here."
+#endif
+
+
+/*
+ * EXTENDED BOOTSTRAP STRUCTURE FOR NEW DOMAINS.
+ */
+
+typedef struct {
+ BASIC_START_INFO;
+ u16 domain_controller_evtchn; /* 320 */
+} PACKED extended_start_info_t; /* 322 bytes */
+#define SIF_BLK_BE_DOMAIN (1<<4) /* Is this a block backend domain? */
+#define SIF_NET_BE_DOMAIN (1<<5) /* Is this a net backend domain? */
+
+
+/*
+ * Reason codes for SCHEDOP_shutdown. These are opaque to Xen but may be
+ * interpreted by control software to determine the appropriate action. These
+ * are only really advisories: the controller can actually do as it likes.
+ */
+#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */
+#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */
+#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */
+
+
+/*
+ * CONTROLLER MESSAGING INTERFACE.
+ */
+
+typedef struct {
+ u8 type; /* 0: echoed in response */
+ u8 subtype; /* 1: echoed in response */
+ u8 id; /* 2: echoed in response */
+ u8 length; /* 3: number of bytes in 'msg' */
+ u8 msg[60]; /* 4: type-specific message data */
+} PACKED control_msg_t; /* 64 bytes */
+
+#define CONTROL_RING_SIZE 8
+typedef u32 CONTROL_RING_IDX;
+#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1))
+
+typedef struct {
+ control_msg_t tx_ring[CONTROL_RING_SIZE]; /* 0: guest -> controller */
+ control_msg_t rx_ring[CONTROL_RING_SIZE]; /* 512: controller -> guest */
+ CONTROL_RING_IDX tx_req_prod, tx_resp_prod; /* 1024, 1028 */
+ CONTROL_RING_IDX rx_req_prod, rx_resp_prod; /* 1032, 1036 */
+} PACKED control_if_t; /* 1040 bytes */
+
+/*
+ * Top-level command types.
+ */
+#define CMSG_CONSOLE 0 /* Console */
+#define CMSG_BLKIF_BE 1 /* Block-device backend */
+#define CMSG_BLKIF_FE 2 /* Block-device frontend */
+#define CMSG_NETIF_BE 3 /* Network-device backend */
+#define CMSG_NETIF_FE 4 /* Network-device frontend */
+#define CMSG_SHUTDOWN 6 /* Shutdown messages */
+
+
+/******************************************************************************
+ * CONSOLE DEFINITIONS
+ */
+
+/*
+ * Subtypes for console messages.
+ */
+#define CMSG_CONSOLE_DATA 0
+
+
+/******************************************************************************
+ * BLOCK-INTERFACE FRONTEND DEFINITIONS
+ */
+
+/* Messages from domain controller to guest. */
+#define CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED 0
+
+/* Messages from guest to domain controller. */
+#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32
+#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33
+#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34
+
+/* These are used by both front-end and back-end drivers. */
+#define blkif_vdev_t u16
+#define blkif_pdev_t u16
+#define blkif_sector_t u64
+
+/*
+ * CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED:
+ * Notify a guest about a status change on one of its block interfaces.
+ * If the interface is DESTROYED or DOWN then the interface is disconnected:
+ * 1. The shared-memory frame is available for reuse.
+ * 2. Any unacknowledged messgaes pending on the interface were dropped.
+ */
+#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
+#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
+#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */
+typedef struct {
+ u32 handle; /* 0 */
+ u32 status; /* 4 */
+ u16 evtchn; /* 8: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */
+} PACKED blkif_fe_interface_status_changed_t; /* 10 bytes */
+
+/*
+ * CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
+ * Notify the domain controller that the front-end driver is DOWN or UP.
+ * When the driver goes DOWN then the controller will send no more
+ * status-change notifications. When the driver comes UP then the controller
+ * will send a notification for each interface that currently exists.
+ * If the driver goes DOWN while interfaces are still UP, the domain
+ * will automatically take the interfaces DOWN.
+ */
+#define BLKIF_DRIVER_STATUS_DOWN 0
+#define BLKIF_DRIVER_STATUS_UP 1
+typedef struct {
+ /* IN */
+ u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */
+ /* OUT */
+ /*
+ * Tells driver how many interfaces it should expect to immediately
+ * receive notifications about.
+ */
+ u32 nr_interfaces; /* 4 */
+} PACKED blkif_fe_driver_status_changed_t; /* 8 bytes */
+
+/*
+ * CMSG_BLKIF_FE_INTERFACE_CONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_CONNECTED message.
+ */
+typedef struct {
+ u32 handle; /* 0 */
+ u32 __pad;
+ memory_t shmem_frame; /* 8 */
+ MEMORY_PADDING;
+} PACKED blkif_fe_interface_connect_t; /* 16 bytes */
+
+/*
+ * CMSG_BLKIF_FE_INTERFACE_DISCONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_DISCONNECTED message.
+ */
+typedef struct {
+ u32 handle; /* 0 */
+} PACKED blkif_fe_interface_disconnect_t; /* 4 bytes */
+
+
+/******************************************************************************
+ * BLOCK-INTERFACE BACKEND DEFINITIONS
+ */
+
+/* Messages from domain controller. */
+#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */
+#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */
+#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */
+#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */
+#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */
+#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */
+#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */
+#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */
+
+/* Messages to domain controller. */
+#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32
+
+/*
+ * Message request/response definitions for block-device messages.
+ */
+
+typedef struct {
+ blkif_sector_t sector_start; /* 0 */
+ blkif_sector_t sector_length; /* 8 */
+ blkif_pdev_t device; /* 16 */
+ u16 __pad; /* 18 */
+} PACKED blkif_extent_t; /* 20 bytes */
+
+/* Non-specific 'okay' return. */
+#define BLKIF_BE_STATUS_OKAY 0
+/* Non-specific 'error' return. */
+#define BLKIF_BE_STATUS_ERROR 1
+/* The following are specific error returns. */
+#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2
+#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3
+#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4
+#define BLKIF_BE_STATUS_VBD_EXISTS 5
+#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6
+#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7
+#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8
+#define BLKIF_BE_STATUS_MAPPING_ERROR 9
+
+/* This macro can be used to create an array of descriptive error strings. */
+#define BLKIF_BE_STATUS_ERRORS { \
+ "Okay", \
+ "Non-specific error", \
+ "Interface already exists", \
+ "Interface not found", \
+ "Interface is still connected", \
+ "VBD already exists", \
+ "VBD not found", \
+ "Out of memory", \
+ "Extent not found for VBD", \
+ "Could not map domain memory" }
+
+/*
+ * CMSG_BLKIF_BE_CREATE:
+ * When the driver sends a successful response then the interface is fully
+ * created. The controller will send a DOWN notification to the front-end
+ * driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 blkif_handle; /* 4: Domain-specific interface handle. */
+ /* OUT */
+ u32 status; /* 8 */
+} PACKED blkif_be_create_t; /* 12 bytes */
+
+/*
+ * CMSG_BLKIF_BE_DESTROY:
+ * When the driver sends a successful response then the interface is fully
+ * torn down. The controller will send a DESTROYED notification to the
+ * front-end driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify interface to be destroyed. */
+ u32 blkif_handle; /* 4: ...ditto... */
+ /* OUT */
+ u32 status; /* 8 */
+} PACKED blkif_be_destroy_t; /* 12 bytes */
+
+/*
+ * CMSG_BLKIF_BE_CONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * connected. The controller will send a CONNECTED notification to the
+ * front-end driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 blkif_handle; /* 4: Domain-specific interface handle. */
+ memory_t shmem_frame; /* 8: Page cont. shared comms window. */
+ MEMORY_PADDING;
+ u32 evtchn; /* 16: Event channel for notifications. */
+ /* OUT */
+ u32 status; /* 20 */
+} PACKED blkif_be_connect_t; /* 24 bytes */
+
+/*
+ * CMSG_BLKIF_BE_DISCONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * disconnected. The controller will send a DOWN notification to the front-end
+ * driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 blkif_handle; /* 4: Domain-specific interface handle. */
+ /* OUT */
+ u32 status; /* 8 */
+} PACKED blkif_be_disconnect_t; /* 12 bytes */
+
+/* CMSG_BLKIF_BE_VBD_CREATE */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify blkdev interface. */
+ u32 blkif_handle; /* 4: ...ditto... */
+ blkif_vdev_t vdevice; /* 8: Interface-specific id for this VBD. */
+ u16 readonly; /* 10: Non-zero -> VBD isn't writeable. */
+ /* OUT */
+ u32 status; /* 12 */
+} PACKED blkif_be_vbd_create_t; /* 16 bytes */
+
+/* CMSG_BLKIF_BE_VBD_DESTROY */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify blkdev interface. */
+ u32 blkif_handle; /* 4: ...ditto... */
+ blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */
+ u16 __pad; /* 10 */
+ /* OUT */
+ u32 status; /* 12 */
+} PACKED blkif_be_vbd_destroy_t; /* 16 bytes */
+
+/* CMSG_BLKIF_BE_VBD_GROW */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify blkdev interface. */
+ u32 blkif_handle; /* 4: ...ditto... */
+ blkif_extent_t extent; /* 8: Physical extent to append to VBD. */
+ blkif_vdev_t vdevice; /* 28: Interface-specific id of the VBD. */
+ u16 __pad; /* 30 */
+ /* OUT */
+ u32 status; /* 32 */
+} PACKED blkif_be_vbd_grow_t; /* 36 bytes */
+
+/* CMSG_BLKIF_BE_VBD_SHRINK */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify blkdev interface. */
+ u32 blkif_handle; /* 4: ...ditto... */
+ blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */
+ u16 __pad; /* 10 */
+ /* OUT */
+ u32 status; /* 12 */
+} PACKED blkif_be_vbd_shrink_t; /* 16 bytes */
+
+/*
+ * CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED:
+ * Notify the domain controller that the back-end driver is DOWN or UP.
+ * If the driver goes DOWN while interfaces are still UP, the controller
+ * will automatically send DOWN notifications.
+ */
+typedef struct {
+ u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */
+} PACKED blkif_be_driver_status_changed_t; /* 4 bytes */
+
+
+/******************************************************************************
+ * NETWORK-INTERFACE FRONTEND DEFINITIONS
+ */
+
+/* Messages from domain controller to guest. */
+#define CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED 0
+
+/* Messages from guest to domain controller. */
+#define CMSG_NETIF_FE_DRIVER_STATUS_CHANGED 32
+#define CMSG_NETIF_FE_INTERFACE_CONNECT 33
+#define CMSG_NETIF_FE_INTERFACE_DISCONNECT 34
+
+/*
+ * CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED:
+ * Notify a guest about a status change on one of its network interfaces.
+ * If the interface is DESTROYED or DOWN then the interface is disconnected:
+ * 1. The shared-memory frame is available for reuse.
+ * 2. Any unacknowledged messgaes pending on the interface were dropped.
+ */
+#define NETIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
+#define NETIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
+#define NETIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */
+typedef struct {
+ u32 handle; /* 0 */
+ u32 status; /* 4 */
+ u16 evtchn; /* 8: status == NETIF_INTERFACE_STATUS_CONNECTED */
+ u8 mac[6]; /* 10: status == NETIF_INTERFACE_STATUS_CONNECTED */
+} PACKED netif_fe_interface_status_changed_t; /* 16 bytes */
+
+/*
+ * CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
+ * Notify the domain controller that the front-end driver is DOWN or UP.
+ * When the driver goes DOWN then the controller will send no more
+ * status-change notifications. When the driver comes UP then the controller
+ * will send a notification for each interface that currently exists.
+ * If the driver goes DOWN while interfaces are still UP, the domain
+ * will automatically take the interfaces DOWN.
+ */
+#define NETIF_DRIVER_STATUS_DOWN 0
+#define NETIF_DRIVER_STATUS_UP 1
+typedef struct {
+ /* IN */
+ u32 status; /* 0: NETIF_DRIVER_STATUS_??? */
+ /* OUT */
+ /*
+ * Tells driver how many interfaces it should expect to immediately
+ * receive notifications about.
+ */
+ u32 nr_interfaces; /* 4 */
+} PACKED netif_fe_driver_status_changed_t; /* 8 bytes */
+
+/*
+ * CMSG_NETIF_FE_INTERFACE_CONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_CONNECTED message.
+ */
+typedef struct {
+ u32 handle; /* 0 */
+ u32 __pad; /* 4 */
+ memory_t tx_shmem_frame; /* 8 */
+ MEMORY_PADDING;
+ memory_t rx_shmem_frame; /* 16 */
+ MEMORY_PADDING;
+} PACKED netif_fe_interface_connect_t; /* 24 bytes */
+
+/*
+ * CMSG_NETIF_FE_INTERFACE_DISCONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_DISCONNECTED message.
+ */
+typedef struct {
+ u32 handle; /* 0 */
+} PACKED netif_fe_interface_disconnect_t; /* 4 bytes */
+
+
+/******************************************************************************
+ * NETWORK-INTERFACE BACKEND DEFINITIONS
+ */
+
+/* Messages from domain controller. */
+#define CMSG_NETIF_BE_CREATE 0 /* Create a new net-device interface. */
+#define CMSG_NETIF_BE_DESTROY 1 /* Destroy a net-device interface. */
+#define CMSG_NETIF_BE_CONNECT 2 /* Connect i/f to remote driver. */
+#define CMSG_NETIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */
+
+/* Messages to domain controller. */
+#define CMSG_NETIF_BE_DRIVER_STATUS_CHANGED 32
+
+/*
+ * Message request/response definitions for net-device messages.
+ */
+
+/* Non-specific 'okay' return. */
+#define NETIF_BE_STATUS_OKAY 0
+/* Non-specific 'error' return. */
+#define NETIF_BE_STATUS_ERROR 1
+/* The following are specific error returns. */
+#define NETIF_BE_STATUS_INTERFACE_EXISTS 2
+#define NETIF_BE_STATUS_INTERFACE_NOT_FOUND 3
+#define NETIF_BE_STATUS_INTERFACE_CONNECTED 4
+#define NETIF_BE_STATUS_OUT_OF_MEMORY 5
+#define NETIF_BE_STATUS_MAPPING_ERROR 6
+
+/* This macro can be used to create an array of descriptive error strings. */
+#define NETIF_BE_STATUS_ERRORS { \
+ "Okay", \
+ "Non-specific error", \
+ "Interface already exists", \
+ "Interface not found", \
+ "Interface is still connected", \
+ "Out of memory", \
+ "Could not map domain memory" }
+
+/*
+ * CMSG_NETIF_BE_CREATE:
+ * When the driver sends a successful response then the interface is fully
+ * created. The controller will send a DOWN notification to the front-end
+ * driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 netif_handle; /* 4: Domain-specific interface handle. */
+ u8 mac[6]; /* 8 */
+ u16 __pad; /* 14 */
+ /* OUT */
+ u32 status; /* 16 */
+} PACKED netif_be_create_t; /* 20 bytes */
+
+/*
+ * CMSG_NETIF_BE_DESTROY:
+ * When the driver sends a successful response then the interface is fully
+ * torn down. The controller will send a DESTROYED notification to the
+ * front-end driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Identify interface to be destroyed. */
+ u32 netif_handle; /* 4: ...ditto... */
+ /* OUT */
+ u32 status; /* 8 */
+} PACKED netif_be_destroy_t; /* 12 bytes */
+
+/*
+ * CMSG_NETIF_BE_CONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * connected. The controller will send a CONNECTED notification to the
+ * front-end driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 netif_handle; /* 4: Domain-specific interface handle. */
+ memory_t tx_shmem_frame; /* 8: Page cont. tx shared comms window. */
+ MEMORY_PADDING;
+ memory_t rx_shmem_frame; /* 16: Page cont. rx shared comms window. */
+ MEMORY_PADDING;
+ u16 evtchn; /* 24: Event channel for notifications. */
+ u16 __pad; /* 26 */
+ /* OUT */
+ u32 status; /* 28 */
+} PACKED netif_be_connect_t; /* 32 bytes */
+
+/*
+ * CMSG_NETIF_BE_DISCONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * disconnected. The controller will send a DOWN notification to the front-end
+ * driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* 0: Domain attached to new interface. */
+ u32 netif_handle; /* 4: Domain-specific interface handle. */
+ /* OUT */
+ u32 status; /* 8 */
+} PACKED netif_be_disconnect_t; /* 12 bytes */
+
+/*
+ * CMSG_NETIF_BE_DRIVER_STATUS_CHANGED:
+ * Notify the domain controller that the back-end driver is DOWN or UP.
+ * If the driver goes DOWN while interfaces are still UP, the domain
+ * will automatically send DOWN notifications.
+ */
+typedef struct {
+ u32 status; /* 0: NETIF_DRIVER_STATUS_??? */
+} PACKED netif_be_driver_status_changed_t; /* 4 bytes */
+
+
+/******************************************************************************
+ * SHUTDOWN DEFINITIONS
+ */
+
+/*
+ * Subtypes for shutdown messages.
+ */
+#define CMSG_SHUTDOWN_POWEROFF 0 /* Clean shutdown (SHUTDOWN_poweroff). */
+#define CMSG_SHUTDOWN_REBOOT 1 /* Clean shutdown (SHUTDOWN_reboot). */
+#define CMSG_SHUTDOWN_SUSPEND 2 /* Create suspend info, then */
+ /* SHUTDOWN_suspend. */
+
+#endif /* __DOMAIN_CONTROLLER_H__ */
diff --git a/tools/python/xen/ext/xu/xu.c b/tools/python/xen/ext/xu/xu.c
new file mode 100644
index 0000000000..48c975912d
--- /dev/null
+++ b/tools/python/xen/ext/xu/xu.c
@@ -0,0 +1,1386 @@
+/******************************************************************************
+ * utils.c
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#include <Python.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <xc.h>
+
+#include <hypervisor-if.h>
+#include "domain_controller.h"
+
+#include <asm-xen/proc_cmd.h>
+
+#define XENPKG "xen.ext.xu"
+
+/* Needed for Python versions earlier than 2.3. */
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC DL_EXPORT(void)
+#endif
+
+/* NB. The following should be kept in sync with the kernel's evtchn driver. */
+#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
+#define EVTCHN_DEV_MAJOR 10
+#define EVTCHN_DEV_MINOR 200
+#define PORT_NORMAL 0x0000 /* A standard event notification. */
+#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */
+#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */
+/* /dev/xen/evtchn ioctls: */
+/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
+#define EVTCHN_RESET _IO('E', 1)
+/* EVTCHN_BIND: Bind to teh specified event-channel port. */
+#define EVTCHN_BIND _IO('E', 2)
+/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */
+#define EVTCHN_UNBIND _IO('E', 3)
+
+/* Size of a machine page frame. */
+#define PAGE_SIZE 4096
+
+
+/*
+ * *********************** NOTIFIER ***********************
+ */
+
+typedef struct {
+ PyObject_HEAD;
+ int evtchn_fd;
+} xu_notifier_object;
+
+static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ u16 v;
+ int bytes;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ if ( errno == EAGAIN )
+ goto none;
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ if ( bytes == sizeof(v) )
+ return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK);
+
+ none:
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ u16 v;
+ int idx;
+
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
+ return NULL;
+
+ v = (u16)idx;
+
+ (void)write(xun->evtchn_fd, &v, sizeof(v));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_bind(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ int idx;
+
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
+ return NULL;
+
+ if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 )
+ return PyErr_SetFromErrno(PyExc_IOError);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ int idx;
+
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
+ return NULL;
+
+ if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 )
+ return PyErr_SetFromErrno(PyExc_IOError);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ return PyInt_FromLong(xun->evtchn_fd);
+}
+
+static PyMethodDef xu_notifier_methods[] = {
+ { "read",
+ (PyCFunction)xu_notifier_read,
+ METH_VARARGS,
+ "Read a (@port, @type) pair.\n" },
+
+ { "unmask",
+ (PyCFunction)xu_notifier_unmask,
+ METH_VARARGS,
+ "Unmask notifications for a @port.\n" },
+
+ { "bind",
+ (PyCFunction)xu_notifier_bind,
+ METH_VARARGS,
+ "Get notifications for a @port.\n" },
+
+ { "unbind",
+ (PyCFunction)xu_notifier_unbind,
+ METH_VARARGS,
+ "No longer get notifications for a @port.\n" },
+
+ { "fileno",
+ (PyCFunction)xu_notifier_fileno,
+ METH_VARARGS,
+ "Return the file descriptor for the notification channel.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_notifier_type;
+
+static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
+
+ reopen:
+ xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
+ if ( xun->evtchn_fd == -1 )
+ {
+ if ( (errno == ENOENT) &&
+ ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) &&
+ (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600,
+ (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) )
+ goto reopen;
+ PyObject_Del((PyObject *)xun);
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ return (PyObject *)xun;
+}
+
+static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
+{
+ if ( strcmp(name, "EXCEPTION") == 0 )
+ return PyInt_FromLong(PORT_EXCEPTION);
+ if ( strcmp(name, "NORMAL") == 0 )
+ return PyInt_FromLong(PORT_NORMAL);
+ return Py_FindMethod(xu_notifier_methods, obj, name);
+}
+
+static void xu_notifier_dealloc(PyObject *self)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ (void)close(xun->evtchn_fd);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_notifier_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "notifier",
+ sizeof(xu_notifier_object),
+ 0,
+ xu_notifier_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_notifier_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 */
+};
+
+
+
+/*
+ * *********************** MESSAGE ***********************
+ */
+
+#define TYPE(_x,_y) (((_x)<<8)|(_y))
+#define P2C(_struct, _field, _ctype) \
+ do { \
+ PyObject *obj; \
+ if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \
+ { \
+ if ( PyInt_Check(obj) ) \
+ { \
+ ((_struct *)&xum->msg.msg[0])->_field = \
+ (_ctype)PyInt_AsLong(obj); \
+ dict_items_parsed++; \
+ } \
+ else if ( PyLong_Check(obj) ) \
+ { \
+ ((_struct *)&xum->msg.msg[0])->_field = \
+ (_ctype)PyLong_AsUnsignedLongLong(obj); \
+ dict_items_parsed++; \
+ } \
+ } \
+ xum->msg.length = sizeof(_struct); \
+ } while ( 0 )
+#define C2P(_struct, _field, _pytype, _ctype) \
+ do { \
+ PyObject *obj = Py ## _pytype ## _From ## _ctype \
+ (((_struct *)&xum->msg.msg[0])->_field); \
+ if ( dict == NULL ) dict = PyDict_New(); \
+ PyDict_SetItemString(dict, #_field, obj); \
+ } while ( 0 )
+
+typedef struct {
+ PyObject_HEAD;
+ control_msg_t msg;
+} xu_message_object;
+
+static PyObject *xu_message_append_payload(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+ char *str;
+ int len;
+
+ if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+ return NULL;
+
+ if ( (len + xum->msg.length) > sizeof(xum->msg.msg) )
+ {
+ PyErr_SetString(PyExc_RuntimeError, "out of space in control message");
+ return NULL;
+ }
+
+ memcpy(&xum->msg.msg[xum->msg.length], str, len);
+ xum->msg.length += len;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+ PyObject *payload;
+ int dict_items_parsed = 0;
+
+ if ( !PyArg_ParseTuple(args, "O", &payload) )
+ return NULL;
+
+ if ( !PyDict_Check(payload) )
+ {
+ PyErr_SetString(PyExc_TypeError, "payload is not a dictionary");
+ return NULL;
+ }
+
+ switch ( TYPE(xum->msg.type, xum->msg.subtype) )
+ {
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED):
+ P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32);
+ break;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
+ P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32);
+ break;
+ }
+
+ if ( dict_items_parsed != PyDict_Size(payload) )
+ {
+ PyErr_SetString(PyExc_TypeError, "payload contains bad items");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+ PyObject *dict = NULL;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ switch ( TYPE(xum->msg.type, xum->msg.subtype) )
+ {
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED):
+ C2P(blkif_fe_interface_status_changed_t, handle, Int, Long);
+ C2P(blkif_fe_interface_status_changed_t, status, Int, Long);
+ C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED):
+ C2P(blkif_fe_driver_status_changed_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT):
+ C2P(blkif_fe_interface_connect_t, handle, Int, Long);
+ C2P(blkif_fe_interface_connect_t, shmem_frame, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_DISCONNECT):
+ C2P(blkif_fe_interface_disconnect_t, handle, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE):
+ C2P(blkif_be_create_t, domid, Int, Long);
+ C2P(blkif_be_create_t, blkif_handle, Int, Long);
+ C2P(blkif_be_create_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY):
+ C2P(blkif_be_destroy_t, domid, Int, Long);
+ C2P(blkif_be_destroy_t, blkif_handle, Int, Long);
+ C2P(blkif_be_destroy_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT):
+ C2P(blkif_be_connect_t, domid, Int, Long);
+ C2P(blkif_be_connect_t, blkif_handle, Int, Long);
+ C2P(blkif_be_connect_t, shmem_frame, Int, Long);
+ C2P(blkif_be_connect_t, evtchn, Int, Long);
+ C2P(blkif_be_connect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT):
+ C2P(blkif_be_disconnect_t, domid, Int, Long);
+ C2P(blkif_be_disconnect_t, blkif_handle, Int, Long);
+ C2P(blkif_be_disconnect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE):
+ C2P(blkif_be_vbd_create_t, domid, Int, Long);
+ C2P(blkif_be_vbd_create_t, blkif_handle, Int, Long);
+ C2P(blkif_be_vbd_create_t, vdevice, Int, Long);
+ C2P(blkif_be_vbd_create_t, readonly, Int, Long);
+ C2P(blkif_be_vbd_create_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY):
+ C2P(blkif_be_vbd_destroy_t, domid, Int, Long);
+ C2P(blkif_be_vbd_destroy_t, blkif_handle, Int, Long);
+ C2P(blkif_be_vbd_destroy_t, vdevice, Int, Long);
+ C2P(blkif_be_vbd_destroy_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW):
+ C2P(blkif_be_vbd_grow_t, domid, Int, Long);
+ C2P(blkif_be_vbd_grow_t, blkif_handle, Int, Long);
+ C2P(blkif_be_vbd_grow_t, vdevice, Int, Long);
+ C2P(blkif_be_vbd_grow_t, extent.sector_start,
+ Long, UnsignedLongLong);
+ C2P(blkif_be_vbd_grow_t, extent.sector_length,
+ Long, UnsignedLongLong);
+ C2P(blkif_be_vbd_grow_t, extent.device, Int, Long);
+ C2P(blkif_be_vbd_grow_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK):
+ C2P(blkif_be_vbd_shrink_t, domid, Int, Long);
+ C2P(blkif_be_vbd_shrink_t, blkif_handle, Int, Long);
+ C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long);
+ C2P(blkif_be_vbd_shrink_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED):
+ C2P(blkif_be_driver_status_changed_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED):
+ C2P(netif_fe_interface_status_changed_t, handle, Int, Long);
+ C2P(netif_fe_interface_status_changed_t, status, Int, Long);
+ C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
+ C2P(netif_fe_driver_status_changed_t, status, Int, Long);
+ C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT):
+ C2P(netif_fe_interface_connect_t, handle, Int, Long);
+ C2P(netif_fe_interface_connect_t, tx_shmem_frame, Int, Long);
+ C2P(netif_fe_interface_connect_t, rx_shmem_frame, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_DISCONNECT):
+ C2P(netif_fe_interface_disconnect_t, handle, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE):
+ C2P(netif_be_create_t, domid, Int, Long);
+ C2P(netif_be_create_t, netif_handle, Int, Long);
+ C2P(netif_be_create_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY):
+ C2P(netif_be_destroy_t, domid, Int, Long);
+ C2P(netif_be_destroy_t, netif_handle, Int, Long);
+ C2P(netif_be_destroy_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT):
+ C2P(netif_be_connect_t, domid, Int, Long);
+ C2P(netif_be_connect_t, netif_handle, Int, Long);
+ C2P(netif_be_connect_t, tx_shmem_frame, Int, Long);
+ C2P(netif_be_connect_t, rx_shmem_frame, Int, Long);
+ C2P(netif_be_connect_t, evtchn, Int, Long);
+ C2P(netif_be_connect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT):
+ C2P(netif_be_disconnect_t, domid, Int, Long);
+ C2P(netif_be_disconnect_t, netif_handle, Int, Long);
+ C2P(netif_be_disconnect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED):
+ C2P(netif_be_driver_status_changed_t, status, Int, Long);
+ return dict;
+ }
+
+ return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length);
+}
+
+static PyObject *xu_message_get_header(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ return Py_BuildValue("{s:i,s:i,s:i}",
+ "type", xum->msg.type,
+ "subtype", xum->msg.subtype,
+ "id", xum->msg.id);
+}
+
+static PyMethodDef xu_message_methods[] = {
+ { "append_payload",
+ (PyCFunction)xu_message_append_payload,
+ METH_VARARGS,
+ "Append @str to the message payload.\n" },
+
+ { "set_response_fields",
+ (PyCFunction)xu_message_set_response_fields,
+ METH_VARARGS,
+ "Fill in the response fields in a message that was passed to us.\n" },
+
+ { "get_payload",
+ (PyCFunction)xu_message_get_payload,
+ METH_VARARGS,
+ "Return the message payload in string form.\n" },
+
+ { "get_header",
+ (PyCFunction)xu_message_get_header,
+ METH_VARARGS,
+ "Returns a dictionary of values for @type, @subtype, and @id.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_message_type;
+
+static PyObject *xu_message_new(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum;
+ int type, subtype, id, dict_items_parsed = 0;
+ PyObject *payload = NULL;
+
+ if ( !PyArg_ParseTuple(args, "iii|O", &type, &subtype, &id, &payload) )
+ return NULL;
+
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+
+ xum->msg.type = type;
+ xum->msg.subtype = subtype;
+ xum->msg.id = id;
+ xum->msg.length = 0;
+
+ if ( payload == NULL )
+ return (PyObject *)xum;
+
+ if ( !PyDict_Check(payload) )
+ {
+ PyErr_SetString(PyExc_TypeError, "payload is not a dictionary");
+ PyObject_Del((PyObject *)xum);
+ return NULL;
+ }
+
+ switch ( TYPE(type, subtype) )
+ {
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED):
+ P2C(blkif_fe_interface_status_changed_t, handle, u32);
+ P2C(blkif_fe_interface_status_changed_t, status, u32);
+ P2C(blkif_fe_interface_status_changed_t, evtchn, u16);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE):
+ P2C(blkif_be_create_t, domid, u32);
+ P2C(blkif_be_create_t, blkif_handle, u32);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY):
+ P2C(blkif_be_destroy_t, domid, u32);
+ P2C(blkif_be_destroy_t, blkif_handle, u32);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT):
+ P2C(blkif_be_connect_t, domid, u32);
+ P2C(blkif_be_connect_t, blkif_handle, u32);
+ P2C(blkif_be_connect_t, shmem_frame, memory_t);
+ P2C(blkif_be_connect_t, evtchn, u16);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT):
+ P2C(blkif_be_disconnect_t, domid, u32);
+ P2C(blkif_be_disconnect_t, blkif_handle, u32);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE):
+ P2C(blkif_be_vbd_create_t, domid, u32);
+ P2C(blkif_be_vbd_create_t, blkif_handle, u32);
+ P2C(blkif_be_vbd_create_t, vdevice, blkif_vdev_t);
+ P2C(blkif_be_vbd_create_t, readonly, u16);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY):
+ P2C(blkif_be_vbd_destroy_t, domid, u32);
+ P2C(blkif_be_vbd_destroy_t, blkif_handle, u32);
+ P2C(blkif_be_vbd_destroy_t, vdevice, blkif_vdev_t);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW):
+ P2C(blkif_be_vbd_grow_t, domid, u32);
+ P2C(blkif_be_vbd_grow_t, blkif_handle, u32);
+ P2C(blkif_be_vbd_grow_t, vdevice, blkif_vdev_t);
+ P2C(blkif_be_vbd_grow_t, extent.sector_start, blkif_sector_t);
+ P2C(blkif_be_vbd_grow_t, extent.sector_length, blkif_sector_t);
+ P2C(blkif_be_vbd_grow_t, extent.device, blkif_pdev_t);
+ break;
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK):
+ P2C(blkif_be_vbd_shrink_t, domid, u32);
+ P2C(blkif_be_vbd_shrink_t, blkif_handle, u32);
+ P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t);
+ break;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED):
+ P2C(netif_fe_interface_status_changed_t, handle, u32);
+ P2C(netif_fe_interface_status_changed_t, status, u32);
+ P2C(netif_fe_interface_status_changed_t, evtchn, u16);
+ P2C(netif_fe_interface_status_changed_t, mac[0], u8);
+ P2C(netif_fe_interface_status_changed_t, mac[1], u8);
+ P2C(netif_fe_interface_status_changed_t, mac[2], u8);
+ P2C(netif_fe_interface_status_changed_t, mac[3], u8);
+ P2C(netif_fe_interface_status_changed_t, mac[4], u8);
+ P2C(netif_fe_interface_status_changed_t, mac[5], u8);
+ break;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE):
+ P2C(netif_be_create_t, domid, u32);
+ P2C(netif_be_create_t, netif_handle, u32);
+ P2C(netif_be_create_t, mac[0], u8);
+ P2C(netif_be_create_t, mac[1], u8);
+ P2C(netif_be_create_t, mac[2], u8);
+ P2C(netif_be_create_t, mac[3], u8);
+ P2C(netif_be_create_t, mac[4], u8);
+ P2C(netif_be_create_t, mac[5], u8);
+ break;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY):
+ P2C(netif_be_destroy_t, domid, u32);
+ P2C(netif_be_destroy_t, netif_handle, u32);
+ break;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT):
+ P2C(netif_be_connect_t, domid, u32);
+ P2C(netif_be_connect_t, netif_handle, u32);
+ P2C(netif_be_connect_t, tx_shmem_frame, memory_t);
+ P2C(netif_be_connect_t, rx_shmem_frame, memory_t);
+ P2C(netif_be_connect_t, evtchn, u16);
+ break;
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT):
+ P2C(netif_be_disconnect_t, domid, u32);
+ P2C(netif_be_disconnect_t, netif_handle, u32);
+ break;
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
+ P2C(netif_fe_driver_status_changed_t, status, u32);
+ P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32);
+ break;
+ }
+
+ if ( dict_items_parsed != PyDict_Size(payload) )
+ {
+ PyErr_SetString(PyExc_TypeError, "payload contains bad items");
+ PyObject_Del((PyObject *)xum);
+ return NULL;
+ }
+
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_message_getattr(PyObject *obj, char *name)
+{
+ xu_message_object *xum;
+ if ( strcmp(name, "MAX_PAYLOAD") == 0 )
+ return PyInt_FromLong(sizeof(xum->msg.msg));
+ return Py_FindMethod(xu_message_methods, obj, name);
+}
+
+static void xu_message_dealloc(PyObject *self)
+{
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_message_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "message",
+ sizeof(xu_message_object),
+ 0,
+ xu_message_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_message_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 */
+};
+
+
+
+/*
+ * *********************** PORT ***********************
+ */
+
+static control_if_t *map_control_interface(int fd, unsigned long pfn)
+{
+ char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, pfn * PAGE_SIZE);
+ if ( vaddr == MAP_FAILED )
+ return NULL;
+ return (control_if_t *)(vaddr + 2048);
+}
+static void unmap_control_interface(int fd, control_if_t *c)
+{
+ char *vaddr = (char *)c - 2048;
+ (void)munmap(vaddr, PAGE_SIZE);
+}
+
+typedef struct xu_port_object {
+ PyObject_HEAD;
+ int mem_fd;
+ int xc_handle;
+ u32 remote_dom;
+ int local_port, remote_port;
+ control_if_t *interface;
+ CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
+ CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
+} xu_port_object;
+
+static PyObject *port_error;
+
+static int xup_connect(xu_port_object *xup, domid_t dom,
+ int local_port, int remote_port){
+ // From our prespective rx = producer, tx = consumer.
+ int err = 0;
+ printf("%s> dom=%u %d:%d\n", __FUNCTION__, (unsigned int)dom,
+ local_port, remote_port);
+
+ // Consumer = tx.
+ //xup->interface->tx_resp_prod = 0;
+ //xup->interface->tx_req_prod = 0;
+ xup->tx_resp_prod = xup->interface->tx_resp_prod;
+ xup->tx_req_cons = xup->interface->tx_resp_prod;
+ printf("%s> tx: %u %u : %u %u\n", __FUNCTION__,
+ (unsigned int)xup->interface->tx_resp_prod,
+ (unsigned int)xup->tx_resp_prod,
+ (unsigned int)xup->tx_req_cons,
+ (unsigned int)xup->interface->tx_req_prod);
+
+ // Producer = rx.
+ //xup->interface->rx_req_prod = 0;
+ //xup->interface->rx_resp_prod = 0;
+ xup->rx_req_prod = xup->interface->rx_req_prod;
+ xup->rx_resp_cons = xup->interface->rx_resp_prod;
+ printf("%s> rx: %u %u : %u %u\n", __FUNCTION__,
+ (unsigned int)xup->rx_resp_cons,
+ (unsigned int)xup->interface->rx_resp_prod,
+ (unsigned int)xup->interface->rx_req_prod,
+ (unsigned int)xup->rx_req_prod);
+
+ xup->remote_dom = dom;
+ xup->local_port = local_port;
+ xup->remote_port = remote_port;
+
+ printf("%s< err=%d\n", __FUNCTION__, err);
+ return err;
+}
+
+static PyObject *xu_port_notify(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX c = xup->tx_req_cons;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->tx_req_prod) ||
+ ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+ {
+ PyErr_SetString(port_error, "no request to read");
+ return NULL;
+ }
+
+ cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+ memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+ if ( xum->msg.length > sizeof(xum->msg.msg) )
+ xum->msg.length = sizeof(xum->msg.msg);
+ xup->tx_req_cons++;
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX p = xup->rx_req_prod;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+ return NULL;
+
+ if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+ {
+ PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message");
+ return NULL;
+ }
+
+ if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+ {
+ PyErr_SetString(port_error, "no space to write request");
+ return NULL;
+ }
+
+ cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
+ memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+ xup->rx_req_prod = cif->rx_req_prod = p + 1;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX c = xup->rx_resp_cons;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+ {
+ PyErr_SetString(port_error, "no response to read");
+ return NULL;
+ }
+
+ cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+ memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+ if ( xum->msg.length > sizeof(xum->msg.msg) )
+ xum->msg.length = sizeof(xum->msg.msg);
+ xup->rx_resp_cons++;
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX p = xup->tx_resp_prod;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+ return NULL;
+
+ if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+ {
+ PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message");
+ return NULL;
+ }
+
+ if ( p == xup->tx_req_cons )
+ {
+ PyErr_SetString(port_error, "no space to write response");
+ return NULL;
+ }
+
+ cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
+ memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+ xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX c = xup->tx_req_cons;
+ control_if_t *cif = xup->interface;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->tx_req_prod) ||
+ ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX p = xup->rx_req_prod;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX c = xup->rx_resp_cons;
+ control_if_t *cif = xup->interface;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_response(
+ PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX p = xup->tx_resp_prod;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( p == xup->tx_req_cons )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyMethodDef xu_port_methods[] = {
+ { "notify",
+ (PyCFunction)xu_port_notify,
+ METH_VARARGS,
+ "Send a notification to the remote end.\n" },
+
+ { "read_request",
+ (PyCFunction)xu_port_read_request,
+ METH_VARARGS,
+ "Read a request message from the control interface.\n" },
+
+ { "write_request",
+ (PyCFunction)xu_port_write_request,
+ METH_VARARGS,
+ "Write a request message to the control interface.\n" },
+
+ { "read_response",
+ (PyCFunction)xu_port_read_response,
+ METH_VARARGS,
+ "Read a response message from the control interface.\n" },
+
+ { "write_response",
+ (PyCFunction)xu_port_write_response,
+ METH_VARARGS,
+ "Write a response message to the control interface.\n" },
+
+ { "request_to_read",
+ (PyCFunction)xu_port_request_to_read,
+ METH_VARARGS,
+ "Returns TRUE if there is a request message to read.\n" },
+
+ { "space_to_write_request",
+ (PyCFunction)xu_port_space_to_write_request,
+ METH_VARARGS,
+ "Returns TRUE if there is space to write a request message.\n" },
+
+ { "response_to_read",
+ (PyCFunction)xu_port_response_to_read,
+ METH_VARARGS,
+ "Returns TRUE if there is a response message to read.\n" },
+
+ { "space_to_write_response",
+ (PyCFunction)xu_port_space_to_write_response,
+ METH_VARARGS,
+ "Returns TRUE if there is space to write a response message.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_port_type;
+
+static PyObject *xu_port_new(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup;
+ u32 dom;
+ int port1, port2;
+ xc_dominfo_t info;
+
+ if ( !PyArg_ParseTuple(args, "i", &dom) )
+ return NULL;
+
+ xup = PyObject_New(xu_port_object, &xu_port_type);
+
+ if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
+ {
+ PyErr_SetString(port_error, "Could not open '/dev/mem'");
+ goto fail1;
+ }
+
+ /* Set the General-Purpose Subject whose page frame will be mapped. */
+ (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)dom);
+
+ if ( (xup->xc_handle = xc_interface_open()) == -1 )
+ {
+ PyErr_SetString(port_error, "Could not open Xen control interface");
+ goto fail2;
+ }
+
+ if ( dom == 0 )
+ {
+ /*
+ * The control-interface event channel for DOM0 is already set up.
+ * We use an ioctl to discover the port at our end of the channel.
+ */
+ port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL);
+ port2 = -1; /* We don't need the remote end of the DOM0 link. */
+ if ( port1 < 0 )
+ {
+ PyErr_SetString(port_error, "Could not open channel to DOM0");
+ goto fail3;
+ }
+ }
+ else if ( xc_evtchn_bind_interdomain(xup->xc_handle,
+ DOMID_SELF, dom,
+ &port1, &port2) != 0 )
+ {
+ PyErr_SetString(port_error, "Could not open channel to domain");
+ goto fail3;
+ }
+
+ if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) ||
+ (info.domid != dom) )
+ {
+ PyErr_SetString(port_error, "Failed to obtain domain status");
+ goto fail4;
+ }
+
+ xup->interface =
+ map_control_interface(xup->mem_fd, info.shared_info_frame);
+ if ( xup->interface == NULL )
+ {
+ PyErr_SetString(port_error, "Failed to map domain control interface");
+ goto fail4;
+ }
+
+ xup_connect(xup, dom, port1, port2);
+ return (PyObject *)xup;
+
+
+ fail4:
+ (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
+ fail3:
+ (void)xc_interface_close(xup->xc_handle);
+ fail2:
+ (void)close(xup->mem_fd);
+ fail1:
+ PyObject_Del((PyObject *)xup);
+ return NULL;
+}
+
+static PyObject *xu_port_getattr(PyObject *obj, char *name)
+{
+ xu_port_object *xup = (xu_port_object *)obj;
+ if ( strcmp(name, "local_port") == 0 )
+ return PyInt_FromLong(xup->local_port);
+ if ( strcmp(name, "remote_port") == 0 )
+ return PyInt_FromLong(xup->remote_port);
+ if ( strcmp(name, "remote_dom") == 0 )
+ return PyInt_FromLong(xup->remote_dom);
+ return Py_FindMethod(xu_port_methods, obj, name);
+}
+
+static void xu_port_dealloc(PyObject *self)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ unmap_control_interface(xup->mem_fd, xup->interface);
+ if ( xup->remote_dom != 0 )
+ (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
+ (void)xc_interface_close(xup->xc_handle);
+ (void)close(xup->mem_fd);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_port_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "port",
+ sizeof(xu_port_object),
+ 0,
+ xu_port_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_port_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 */
+};
+
+
+
+/*
+ * *********************** BUFFER ***********************
+ */
+
+#define BUFSZ 65536
+#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1))
+typedef unsigned int BUF_IDX;
+
+typedef struct {
+ PyObject_HEAD;
+ char *buf;
+ unsigned int prod, cons;
+} xu_buffer_object;
+
+static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max)
+{
+ PyObject *str1, *str2;
+ int len1, len2, c = MASK_BUF_IDX(xub->cons);
+
+ len1 = xub->prod - xub->cons;
+ if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */
+ len1 = BUFSZ - c;
+ if ( len1 > max ) /* clip to specified maximum */
+ len1 = max;
+ if ( len1 < 0 ) /* sanity */
+ len1 = 0;
+
+ if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL )
+ return NULL;
+
+ if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) )
+ {
+ len2 = max - len1;
+ if ( len2 > MASK_BUF_IDX(xub->prod) )
+ len2 = MASK_BUF_IDX(xub->prod);
+ if ( len2 > 0 )
+ {
+ str2 = PyString_FromStringAndSize(&xub->buf[0], len2);
+ if ( str2 == NULL )
+ return NULL;
+ PyString_ConcatAndDel(&str1, str2);
+ if ( str1 == NULL )
+ return NULL;
+ }
+ }
+
+ return str1;
+}
+
+static PyObject *xu_buffer_peek(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ int max = 1024;
+
+ if ( !PyArg_ParseTuple(args, "|i", &max) )
+ return NULL;
+
+ return __xu_buffer_peek(xub, max);
+}
+
+static PyObject *xu_buffer_read(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ PyObject *str;
+ int max = 1024;
+
+ if ( !PyArg_ParseTuple(args, "|i", &max) )
+ return NULL;
+
+ if ( (str = __xu_buffer_peek(xub, max)) != NULL )
+ xub->cons += PyString_Size(str);
+
+ return str;
+}
+
+static PyObject *xu_buffer_discard(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ int max, len;
+
+ if ( !PyArg_ParseTuple(args, "i", &max) )
+ return NULL;
+
+ len = xub->prod - xub->cons;
+ if ( len > max )
+ len = max;
+ if ( len < 0 )
+ len = 0;
+
+ xub->cons += len;
+
+ return PyInt_FromLong(len);
+}
+
+static PyObject *xu_buffer_write(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ char *str;
+ int len, len1, len2;
+
+ if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+ return NULL;
+
+ len1 = len;
+ if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+ len1 = BUFSZ - MASK_BUF_IDX(xub->prod);
+ if ( len1 > (BUFSZ - (xub->prod - xub->cons)) )
+ len1 = BUFSZ - (xub->prod - xub->cons);
+
+ if ( len1 == 0 )
+ return PyInt_FromLong(0);
+
+ memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1);
+ xub->prod += len1;
+
+ if ( len1 < len )
+ {
+ len2 = len - len1;
+ if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+ len2 = BUFSZ - MASK_BUF_IDX(xub->prod);
+ if ( len2 > (BUFSZ - (xub->prod - xub->cons)) )
+ len2 = BUFSZ - (xub->prod - xub->cons);
+ if ( len2 != 0 )
+ {
+ memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2);
+ xub->prod += len2;
+ return PyInt_FromLong(len1 + len2);
+ }
+ }
+
+ return PyInt_FromLong(len1);
+}
+
+static PyObject *xu_buffer_empty(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( xub->cons == xub->prod )
+ return PyInt_FromLong(1);
+
+ return PyInt_FromLong(0);
+}
+
+static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (xub->prod - xub->cons) == BUFSZ )
+ return PyInt_FromLong(1);
+
+ return PyInt_FromLong(0);
+}
+
+static PyMethodDef xu_buffer_methods[] = {
+ { "peek",
+ (PyCFunction)xu_buffer_peek,
+ METH_VARARGS,
+ "Peek up to @max bytes from the buffer. Returns a string.\n" },
+
+ { "read",
+ (PyCFunction)xu_buffer_read,
+ METH_VARARGS,
+ "Read up to @max bytes from the buffer. Returns a string.\n" },
+
+ { "discard",
+ (PyCFunction)xu_buffer_discard,
+ METH_VARARGS,
+ "Discard up to @max bytes from the buffer. Returns number of bytes.\n" },
+
+ { "write",
+ (PyCFunction)xu_buffer_write,
+ METH_VARARGS,
+ "Write @string into buffer. Return number of bytes written.\n" },
+
+ { "empty",
+ (PyCFunction)xu_buffer_empty,
+ METH_VARARGS,
+ "Return TRUE if the buffer is empty.\n" },
+
+ { "full",
+ (PyCFunction)xu_buffer_full,
+ METH_VARARGS,
+ "Return TRUE if the buffer is full.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_buffer_type;
+
+static PyObject *xu_buffer_new(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ xub = PyObject_New(xu_buffer_object, &xu_buffer_type);
+
+ if ( (xub->buf = malloc(BUFSZ)) == NULL )
+ {
+ PyObject_Del((PyObject *)xub);
+ return NULL;
+ }
+
+ xub->prod = xub->cons = 0;
+
+ return (PyObject *)xub;
+}
+
+static PyObject *xu_buffer_getattr(PyObject *obj, char *name)
+{
+ return Py_FindMethod(xu_buffer_methods, obj, name);
+}
+
+static void xu_buffer_dealloc(PyObject *self)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ free(xub->buf);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_buffer_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "buffer",
+ sizeof(xu_buffer_object),
+ 0,
+ xu_buffer_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_buffer_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 */
+};
+
+
+
+/*
+ * *********************** MODULE WRAPPER ***********************
+ */
+
+static void handle_child_death(int dummy)
+{
+ while ( waitpid(-1, NULL, WNOHANG) > 0 )
+ continue;
+}
+
+static PyObject *xu_autoreap(PyObject *self, PyObject *args)
+{
+ struct sigaction sa;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handle_child_death;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef xu_methods[] = {
+ { "notifier", xu_notifier_new, METH_VARARGS,
+ "Create a new notifier." },
+ { "message", xu_message_new, METH_VARARGS,
+ "Create a new communications message." },
+ { "port", xu_port_new, METH_VARARGS,
+ "Create a new communications port." },
+ { "buffer", xu_buffer_new, METH_VARARGS,
+ "Create a new ring buffer." },
+ { "autoreap", xu_autoreap, METH_VARARGS,
+ "Ensure that zombie children are automatically reaped by the OS." },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC initxu(void)
+{
+ PyObject *m, *d;
+
+ m = Py_InitModule(XENPKG, xu_methods);
+
+ d = PyModule_GetDict(m);
+ port_error = PyErr_NewException(XENPKG ".PortError", NULL, NULL);
+ PyDict_SetItemString(d, "PortError", port_error);
+}
diff --git a/tools/python/xen/util/Brctl.py b/tools/python/xen/util/Brctl.py
new file mode 100644
index 0000000000..9d3eba1e5f
--- /dev/null
+++ b/tools/python/xen/util/Brctl.py
@@ -0,0 +1,160 @@
+"""Bridge control utilities.
+"""
+import os
+import os.path
+import re
+import sys
+
+os.defpath = os.defpath + ':/sbin:/usr/sbin:/usr/local/sbin'
+CMD_IFCONFIG = 'ifconfig'
+CMD_ROUTE = 'route'
+CMD_BRCTL = 'brctl'
+CMD_IPTABLES = "iptables"
+
+opts = None
+
+class Opts:
+
+ def __init__(self, defaults):
+ for (k, v) in defaults.items():
+ setattr(self, k, v)
+ pass
+
+def cmd(p, s):
+ """Print and execute command 'p' with args 's'.
+ """
+ global opts
+ c = p + ' ' + s
+ if opts.verbose: print c
+ if not opts.dryrun:
+ os.system(c)
+
+def vif_bridge_add(params):
+ """Add the network interface for vif on dom to a bridge.
+ """
+ cmd(CMD_BRCTL, 'addif %(bridge)s %(vif)s' % params)
+
+def vif_bridge_rem(params):
+ """Remove the network interface for vif on dom from a bridge.
+ """
+ cmd(CMD_BRCTL, 'delif %(bridge)s %(vif)s' % params)
+
+def vif_restrict_addr(vif, addr, delete=0):
+ d = { 'vif': vif, 'addr': addr}
+ if delete:
+ d['flag'] = '-D'
+ else:
+ d['flag'] = '-A'
+ cmd(CMD_IPTABLES, '-P FORWARD DROP')
+ cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-in %(vif)s -s %(addr)s -j ACCEPT' % d)
+ cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-out %(vif)s -d %(addr)s -j ACCEPT' % d)
+
+def bridge_create(bridge, **kwd):
+ """Create a bridge.
+ Defaults hello time to 0, forward delay to 0 and stp off.
+ """
+ cmd(CMD_BRCTL, 'addbr %s' % bridge)
+ if kwd.get('hello', None) is None:
+ kwd['hello'] = 0
+ if kwd.get('fd', None) is None:
+ kwd['fd'] = 0
+ if kwd.get('stp', None) is None:
+ kwd['stp'] = 'off'
+ bridge_set(bridge, **kwd)
+
+def bridge_set(bridge, hello=None, fd=None, stp=None):
+ """Set bridge parameters.
+ """
+ if hello is not None:
+ cmd(CMD_BRCTL, 'sethello %s %d' % (bridge, hello))
+ if fd is not None:
+ cmd(CMD_BRCTL, 'setfd %s %d' % (bridge, fd))
+ if stp is not None:
+ cmd(CMD_BRCTL, 'stp %s %s' % (bridge, stp))
+
+def bridge_del(bridge):
+ """Delete a bridge.
+ """
+ cmd(CMD_BRCTL, 'delbr %s' % bridge)
+
+def routes():
+ """Return a list of the routes.
+ """
+ fin = os.popen(CMD_ROUTE + ' -n', 'r')
+ routes = []
+ for x in fin:
+ if x.startswith('Kernel'): continue
+ if x.startswith('Destination'): continue
+ x = x.strip()
+ y = x.split()
+ z = { 'destination': y[0],
+ 'gateway' : y[1],
+ 'mask' : y[2],
+ 'flags' : y[3],
+ 'metric' : y[4],
+ 'ref' : y[5],
+ 'use' : y[6],
+ 'interface' : y[7] }
+ routes.append(z)
+ return routes
+
+def ifconfig(interface):
+ """Return the ip config for an interface,
+ """
+ fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r')
+ inetre = re.compile('\s*inet\s*addr:(?P<address>\S*)\s*Bcast:(?P<broadcast>\S*)\s*Mask:(?P<mask>\S*)')
+ info = None
+ for x in fin:
+ m = inetre.match(x)
+ if not m: continue
+ info = m.groupdict()
+ info['interface'] = interface
+ break
+ return info
+
+def reconfigure(interface, bridge):
+ """Reconfigure an interface to be attached to a bridge, and give the bridge
+ the IP address etc. from interface. Move the default route to the interface
+ to the bridge.
+
+ """
+ global opts
+ intf_info = ifconfig(interface)
+ if not intf_info:
+ print >>sys.stderr, 'Interface not found:', interface
+ return
+ #bridge_info = ifconfig(bridge)
+ #if not bridge_info:
+ # print >>sys.stderr, 'Bridge not found:', bridge
+ # return
+ route_info = routes()
+ intf_info['bridge'] = bridge
+ intf_info['gateway'] = None
+ for r in route_info:
+ if (r['destination'] == '0.0.0.0' and
+ 'G' in r['flags'] and
+ r['interface'] == interface):
+ intf_info['gateway'] = r['gateway']
+ if not intf_info['gateway']:
+ print >>sys.stderr, 'Gateway not found: ', interface
+ return
+ cmd(CMD_IFCONFIG,
+ '%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up'
+ % intf_info)
+ cmd(CMD_ROUTE,
+ 'add default gateway %(gateway)s dev %(bridge)s'
+ % intf_info)
+ cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info)
+ cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info)
+
+defaults = {
+ 'verbose' : 1,
+ 'dryrun' : 0,
+ }
+
+opts = Opts(defaults)
+
+def set_opts(val):
+ global opts
+ opts = val
+ return opts
diff --git a/tools/python/xen/util/__init__.py b/tools/python/xen/util/__init__.py
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/tools/python/xen/util/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tools/python/xen/util/console_client.py b/tools/python/xen/util/console_client.py
new file mode 100644
index 0000000000..7ac63aeb75
--- /dev/null
+++ b/tools/python/xen/util/console_client.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+##############################################
+# Console client for Xen guest OSes
+# Copyright (c) 2004, K A Fraser
+##############################################
+
+import errno, os, signal, socket, struct, sys
+
+from termios import *
+# Indexes into termios.tcgetattr() list.
+IFLAG = 0
+OFLAG = 1
+CFLAG = 2
+LFLAG = 3
+ISPEED = 4
+OSPEED = 5
+CC = 6
+
+def __child_death(signum, frame):
+ global stop
+ stop = True
+
+def __recv_from_sock(sock):
+ global stop
+ stop = False
+ while not stop:
+ try:
+ data = sock.recv(1024)
+ os.write(1, data)
+ except socket.error, error:
+ if error[0] != errno.EINTR:
+ raise
+ os.wait()
+
+def __send_to_sock(sock):
+ while 1:
+ data = os.read(0,1024)
+ if ord(data[0]) == ord(']')-64:
+ break
+ try:
+ sock.send(data)
+ except socket.error, error:
+ if error[0] == errno.EPIPE:
+ sys.exit(0)
+ if error[0] != errno.EINTR:
+ raise
+ sys.exit(0)
+
+def connect(host,port):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ sock.connect((host,port))
+
+ oattrs = tcgetattr(0)
+ nattrs = tcgetattr(0)
+ nattrs[IFLAG] = nattrs[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)
+ nattrs[OFLAG] = nattrs[OFLAG] & ~(OPOST)
+ nattrs[CFLAG] = nattrs[CFLAG] & ~(CSIZE | PARENB)
+ nattrs[CFLAG] = nattrs[CFLAG] | CS8
+ nattrs[LFLAG] = nattrs[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG)
+ nattrs[CC][VMIN] = 1
+ nattrs[CC][VTIME] = 0
+
+ if os.fork():
+ signal.signal(signal.SIGCHLD, __child_death)
+ print "************ REMOTE CONSOLE: CTRL-] TO QUIT ********"
+ tcsetattr(0, TCSAFLUSH, nattrs)
+ try:
+ __recv_from_sock(sock)
+ finally:
+ tcsetattr(0, TCSAFLUSH, oattrs)
+ print
+ print "************ REMOTE CONSOLE EXITED *****************"
+ else:
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+ __send_to_sock(sock)
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ print sys.argv[0] + " <host> <port>"
+ sys.exit(1)
+ connect(str(sys.argv[1]),int(sys.argv[2]))
diff --git a/tools/python/xen/util/ip.py b/tools/python/xen/util/ip.py
new file mode 100644
index 0000000000..8396e0d014
--- /dev/null
+++ b/tools/python/xen/util/ip.py
@@ -0,0 +1,113 @@
+import os
+import re
+import socket
+import struct
+
+def readlines(fd):
+ """Version of readlines safe against EINTR.
+ """
+ import errno
+
+ lines = []
+ while 1:
+ try:
+ line = fd.readline()
+ except IOError, ex:
+ if ex.errno == errno.EINTR:
+ continue
+ else:
+ raise
+ if line == '': break
+ lines.append(line)
+ return lines
+
+def readline(fd):
+ """Version of readline safe against EINTR.
+ """
+ while 1:
+ try:
+ return fd.readline()
+ except IOError, ex:
+ if ex.errno == errno.EINTR:
+ continue
+ else:
+ raise
+
+##### Networking-related functions
+
+"""Bridge for network backend.
+When bridging is used, eth0 may not have an IP address,
+as it may have been moved onto the bridge.
+"""
+NBE_BRIDGE = 'nbe-br'
+
+def get_current_ipaddr(dev='eth0'):
+ """Return a string containing the primary IP address for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+ lines = readlines(fd)
+ for line in lines:
+ m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+ line )
+ if m:
+ return m.group(1)
+ if dev == 'eth0':
+ return get_current_ipaddr(NBE_BRIDGE)
+ return None
+
+def get_current_ipmask(dev='eth0'):
+ """Return a string containing the primary IP netmask for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+ lines = readlines(fd)
+ for line in lines:
+ m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+ line )
+ if m:
+ return m.group(1)
+ if dev == 'eth0':
+ return get_current_ipmask(NBE_BRIDGE)
+ return None
+
+def get_current_ipgw(dev='eth0'):
+ """Return a string containing the IP gateway for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/route -n' )
+ lines = readlines(fd)
+ for line in lines:
+ m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
+ '\s+\S+\s+\S*G.*' + dev + '.*', line )
+ if m:
+ return m.group(1)
+ if dev == 'eth0':
+ return get_current_ipgw(NBE_BRIDGE)
+ return None
+
+def inet_aton(addr):
+ """Convert an IP addr in IPv4 dot notation into an int.
+ """
+ b = socket.inet_aton(addr)
+ return struct.unpack('!I', b)[0]
+
+def inet_ntoa(n):
+ """Convert an int into an IP addr in IPv4 dot notation.
+ """
+ b = struct.pack('!I', n)
+ return socket.inet_ntoa(b)
+
+def add_offset_to_ip(addr, offset):
+ """Add a numerical offset to an IP addr in IPv4 dot notation.
+ """
+ n = inet_aton(addr)
+ n += offset
+ return inet_ntoa(n)
+
+def check_subnet( ip, network, netmask ):
+ n_ip = inet_aton(ip)
+ n_net = inet_aton(network)
+ n_mask = inet_aton(netmask)
+ return (n_ip & n_mask) == (n_net & n_mask)
+
diff --git a/tools/python/xen/util/tempfile.py b/tools/python/xen/util/tempfile.py
new file mode 100644
index 0000000000..756d8c8727
--- /dev/null
+++ b/tools/python/xen/util/tempfile.py
@@ -0,0 +1,451 @@
+"""Temporary files.
+
+This module provides generic, low- and high-level interfaces for
+creating temporary files and directories. The interfaces listed
+as "safe" just below can be used without fear of race conditions.
+Those listed as "unsafe" cannot, and are provided for backward
+compatibility only.
+
+This module also provides some data items to the user:
+
+ TMP_MAX - maximum number of names that will be tried before
+ giving up.
+ template - the default prefix for all temporary names.
+ You may change this to control the default prefix.
+ tempdir - If this is set to a string before the first use of
+ any routine from this module, it will be considered as
+ another candidate location to store temporary files.
+"""
+
+__all__ = [
+ "NamedTemporaryFile", "TemporaryFile", # high level safe interfaces
+ "mkstemp", "mkdtemp", # low level safe interfaces
+ "mktemp", # deprecated unsafe interface
+ "TMP_MAX", "gettempprefix", # constants
+ "tempdir", "gettempdir"
+ ]
+
+
+# Imports.
+
+import os as _os
+import errno as _errno
+from random import Random as _Random
+
+if _os.name == 'mac':
+ import Carbon.Folder as _Folder
+ import Carbon.Folders as _Folders
+
+try:
+ import fcntl as _fcntl
+ # If PYTHONCASEOK is set on Windows, stinking FCNTL.py gets
+ # imported, and we don't get an ImportError then. Provoke
+ # an AttributeError instead in that case.
+ _fcntl.fcntl
+except (ImportError, AttributeError):
+ def _set_cloexec(fd):
+ pass
+else:
+ def _set_cloexec(fd):
+ flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0)
+ if flags >= 0:
+ # flags read successfully, modify
+ flags |= _fcntl.FD_CLOEXEC
+ _fcntl.fcntl(fd, _fcntl.F_SETFD, flags)
+
+
+try:
+ import thread as _thread
+except ImportError:
+ import dummy_thread as _thread
+_allocate_lock = _thread.allocate_lock
+
+_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
+if hasattr(_os, 'O_NOINHERIT'):
+ _text_openflags |= _os.O_NOINHERIT
+if hasattr(_os, 'O_NOFOLLOW'):
+ _text_openflags |= _os.O_NOFOLLOW
+
+_bin_openflags = _text_openflags
+if hasattr(_os, 'O_BINARY'):
+ _bin_openflags |= _os.O_BINARY
+
+if hasattr(_os, 'TMP_MAX'):
+ TMP_MAX = _os.TMP_MAX
+else:
+ TMP_MAX = 10000
+
+template = "tmp"
+
+tempdir = None
+
+# Internal routines.
+
+_once_lock = _allocate_lock()
+
+class _RandomNameSequence:
+ """An instance of _RandomNameSequence generates an endless
+ sequence of unpredictable strings which can safely be incorporated
+ into file names. Each string is six characters long. Multiple
+ threads can safely use the same instance at the same time.
+
+ _RandomNameSequence is an iterator."""
+
+ characters = ("abcdefghijklmnopqrstuvwxyz" +
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "0123456789-_")
+
+ def __init__(self):
+ self.mutex = _allocate_lock()
+ self.rng = _Random()
+ self.normcase = _os.path.normcase
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ m = self.mutex
+ c = self.characters
+ choose = self.rng.choice
+
+ m.acquire()
+ try:
+ letters = [choose(c) for dummy in "123456"]
+ finally:
+ m.release()
+
+ return self.normcase(''.join(letters))
+
+def _candidate_tempdir_list():
+ """Generate a list of candidate temporary directories which
+ _get_default_tempdir will try."""
+
+ dirlist = []
+
+ # First, try the environment.
+ for envname in 'TMPDIR', 'TEMP', 'TMP':
+ dirname = _os.getenv(envname)
+ if dirname: dirlist.append(dirname)
+
+ # Failing that, try OS-specific locations.
+ if _os.name == 'mac':
+ try:
+ fsr = _Folder.FSFindFolder(_Folders.kOnSystemDisk,
+ _Folders.kTemporaryFolderType, 1)
+ dirname = fsr.as_pathname()
+ dirlist.append(dirname)
+ except _Folder.error:
+ pass
+ elif _os.name == 'riscos':
+ dirname = _os.getenv('Wimp$ScrapDir')
+ if dirname: dirlist.append(dirname)
+ elif _os.name == 'nt':
+ dirlist.extend([ r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])
+ else:
+ dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])
+
+ # As a last resort, the current directory.
+ try:
+ dirlist.append(_os.getcwd())
+ except (AttributeError, _os.error):
+ dirlist.append(_os.curdir)
+
+ return dirlist
+
+def _get_default_tempdir():
+ """Calculate the default directory to use for temporary files.
+ This routine should be called exactly once.
+
+ We determine whether or not a candidate temp dir is usable by
+ trying to create and write to a file in that directory. If this
+ is successful, the test file is deleted. To prevent denial of
+ service, the name of the test file must be randomized."""
+
+ namer = _RandomNameSequence()
+ dirlist = _candidate_tempdir_list()
+ flags = _text_openflags
+
+ for dir in dirlist:
+ if dir != _os.curdir:
+ dir = _os.path.normcase(_os.path.abspath(dir))
+ # Try only a few names per directory.
+ for seq in xrange(100):
+ name = namer.next()
+ filename = _os.path.join(dir, name)
+ try:
+ fd = _os.open(filename, flags, 0600)
+ fp = _os.fdopen(fd, 'w')
+ fp.write('blat')
+ fp.close()
+ _os.unlink(filename)
+ del fp, fd
+ return dir
+ except (OSError, IOError), e:
+ if e[0] != _errno.EEXIST:
+ break # no point trying more names in this directory
+ pass
+ raise IOError, (_errno.ENOENT,
+ ("No usable temporary directory found in %s" % dirlist))
+
+_name_sequence = None
+
+def _get_candidate_names():
+ """Common setup sequence for all user-callable interfaces."""
+
+ global _name_sequence
+ if _name_sequence is None:
+ _once_lock.acquire()
+ try:
+ if _name_sequence is None:
+ _name_sequence = _RandomNameSequence()
+ finally:
+ _once_lock.release()
+ return _name_sequence
+
+
+def _mkstemp_inner(dir, pre, suf, flags):
+ """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
+
+ names = _get_candidate_names()
+
+ for seq in xrange(TMP_MAX):
+ name = names.next()
+ file = _os.path.join(dir, pre + name + suf)
+ try:
+ fd = _os.open(file, flags, 0600)
+ _set_cloexec(fd)
+ return (fd, file)
+ except OSError, e:
+ if e.errno == _errno.EEXIST:
+ continue # try again
+ raise
+
+ raise IOError, (_errno.EEXIST, "No usable temporary file name found")
+
+
+# User visible interfaces.
+
+def gettempprefix():
+ """Accessor for tempdir.template."""
+ return template
+
+tempdir = None
+
+def gettempdir():
+ """Accessor for tempdir.tempdir."""
+ global tempdir
+ if tempdir is None:
+ _once_lock.acquire()
+ try:
+ if tempdir is None:
+ tempdir = _get_default_tempdir()
+ finally:
+ _once_lock.release()
+ return tempdir
+
+def mkstemp(suffix="", prefix=template, dir=None, text=False):
+ """mkstemp([suffix, [prefix, [dir, [text]]]])
+ User-callable function to create and return a unique temporary
+ file. The return value is a pair (fd, name) where fd is the
+ file descriptor returned by os.open, and name is the filename.
+
+ If 'suffix' is specified, the file name will end with that suffix,
+ otherwise there will be no suffix.
+
+ If 'prefix' is specified, the file name will begin with that prefix,
+ otherwise a default prefix is used.
+
+ If 'dir' is specified, the file will be created in that directory,
+ otherwise a default directory is used.
+
+ If 'text' is specified and true, the file is opened in text
+ mode. Else (the default) the file is opened in binary mode. On
+ some operating systems, this makes no difference.
+
+ The file is readable and writable only by the creating user ID.
+ If the operating system uses permission bits to indicate whether a
+ file is executable, the file is executable by no one. The file
+ descriptor is not inherited by children of this process.
+
+ Caller is responsible for deleting the file when done with it.
+ """
+
+ if dir is None:
+ dir = gettempdir()
+
+ if text:
+ flags = _text_openflags
+ else:
+ flags = _bin_openflags
+
+ return _mkstemp_inner(dir, prefix, suffix, flags)
+
+
+def mkdtemp(suffix="", prefix=template, dir=None):
+ """mkdtemp([suffix, [prefix, [dir]]])
+ User-callable function to create and return a unique temporary
+ directory. The return value is the pathname of the directory.
+
+ Arguments are as for mkstemp, except that the 'text' argument is
+ not accepted.
+
+ The directory is readable, writable, and searchable only by the
+ creating user.
+
+ Caller is responsible for deleting the directory when done with it.
+ """
+
+ if dir is None:
+ dir = gettempdir()
+
+ names = _get_candidate_names()
+
+ for seq in xrange(TMP_MAX):
+ name = names.next()
+ file = _os.path.join(dir, prefix + name + suffix)
+ try:
+ _os.mkdir(file, 0700)
+ return file
+ except OSError, e:
+ if e.errno == _errno.EEXIST:
+ continue # try again
+ raise
+
+ raise IOError, (_errno.EEXIST, "No usable temporary directory name found")
+
+def mktemp(suffix="", prefix=template, dir=None):
+ """mktemp([suffix, [prefix, [dir]]])
+ User-callable function to return a unique temporary file name. The
+ file is not created.
+
+ Arguments are as for mkstemp, except that the 'text' argument is
+ not accepted.
+
+ This function is unsafe and should not be used. The file name
+ refers to a file that did not exist at some point, but by the time
+ you get around to creating it, someone else may have beaten you to
+ the punch.
+ """
+
+## from warnings import warn as _warn
+## _warn("mktemp is a potential security risk to your program",
+## RuntimeWarning, stacklevel=2)
+
+ if dir is None:
+ dir = gettempdir()
+
+ names = _get_candidate_names()
+ for seq in xrange(TMP_MAX):
+ name = names.next()
+ file = _os.path.join(dir, prefix + name + suffix)
+ if not _os.path.exists(file):
+ return file
+
+ raise IOError, (_errno.EEXIST, "No usable temporary filename found")
+
+class _TemporaryFileWrapper:
+ """Temporary file wrapper
+
+ This class provides a wrapper around files opened for
+ temporary use. In particular, it seeks to automatically
+ remove the file when it is no longer needed.
+ """
+
+ def __init__(self, file, name):
+ self.file = file
+ self.name = name
+ self.close_called = False
+
+ def __getattr__(self, name):
+ file = self.__dict__['file']
+ a = getattr(file, name)
+ if type(a) != type(0):
+ setattr(self, name, a)
+ return a
+
+ # NT provides delete-on-close as a primitive, so we don't need
+ # the wrapper to do anything special. We still use it so that
+ # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
+ if _os.name != 'nt':
+
+ # Cache the unlinker so we don't get spurious errors at
+ # shutdown when the module-level "os" is None'd out. Note
+ # that this must be referenced as self.unlink, because the
+ # name TemporaryFileWrapper may also get None'd out before
+ # __del__ is called.
+ unlink = _os.unlink
+
+ def close(self):
+ if not self.close_called:
+ self.close_called = True
+ self.file.close()
+ self.unlink(self.name)
+
+ def __del__(self):
+ self.close()
+
+def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="",
+ prefix=template, dir=None):
+ """Create and return a temporary file.
+ Arguments:
+ 'prefix', 'suffix', 'dir' -- as for mkstemp.
+ 'mode' -- the mode argument to os.fdopen (default "w+b").
+ 'bufsize' -- the buffer size argument to os.fdopen (default -1).
+ The file is created as mkstemp() would do it.
+
+ Returns a file object; the name of the file is accessible as
+ file.name. The file will be automatically deleted when it is
+ closed.
+ """
+
+ if dir is None:
+ dir = gettempdir()
+
+ if 'b' in mode:
+ flags = _bin_openflags
+ else:
+ flags = _text_openflags
+
+ # Setting O_TEMPORARY in the flags causes the OS to delete
+ # the file when it is closed. This is only supported by Windows.
+ if _os.name == 'nt':
+ flags |= _os.O_TEMPORARY
+
+ (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
+ file = _os.fdopen(fd, mode, bufsize)
+ return _TemporaryFileWrapper(file, name)
+
+if _os.name != 'posix' or _os.sys.platform == 'cygwin':
+ # On non-POSIX and Cygwin systems, assume that we cannot unlink a file
+ # while it is open.
+ TemporaryFile = NamedTemporaryFile
+
+else:
+ def TemporaryFile(mode='w+b', bufsize=-1, suffix="",
+ prefix=template, dir=None):
+ """Create and return a temporary file.
+ Arguments:
+ 'prefix', 'suffix', 'directory' -- as for mkstemp.
+ 'mode' -- the mode argument to os.fdopen (default "w+b").
+ 'bufsize' -- the buffer size argument to os.fdopen (default -1).
+ The file is created as mkstemp() would do it.
+
+ Returns a file object. The file has no name, and will cease to
+ exist when it is closed.
+ """
+
+ if dir is None:
+ dir = gettempdir()
+
+ if 'b' in mode:
+ flags = _bin_openflags
+ else:
+ flags = _text_openflags
+
+ (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
+ try:
+ _os.unlink(name)
+ return _os.fdopen(fd, mode, bufsize)
+ except:
+ _os.close(fd)
+ raise
diff --git a/tools/python/xen/xend/Args.py b/tools/python/xen/xend/Args.py
new file mode 100644
index 0000000000..527e841d3d
--- /dev/null
+++ b/tools/python/xen/xend/Args.py
@@ -0,0 +1,126 @@
+import sxp
+
+class ArgError(StandardError):
+ pass
+
+class Args:
+ """Argument encoding support for HTTP.
+ """
+
+ def __init__(self, paramspec, keyspec):
+ self.arg_ord = []
+ self.arg_dict = {}
+ self.key_ord = []
+ self.key_dict = {}
+ for (name, type) in paramspec:
+ self.arg_ord.append(name)
+ self.arg_dict[name] = type
+ for (name, type) in keyspec:
+ self.key_ord.append(name)
+ self.key_dict[name] = type
+
+ def get_args(self, d, xargs=None):
+ args = {}
+ keys = {}
+ params = []
+ if xargs:
+ self.split_args(xargs, args, keys)
+ self.split_args(d, args, keys)
+ for a in self.arg_ord:
+ if a in args:
+ params.append(args[a])
+ else:
+ raise ArgError('Missing parameter: %s' % a)
+ return (params, keys)
+
+ def split_args(self, d, args, keys):
+ for (k, v) in d.items():
+ if k in self.arg_dict:
+ type = self.arg_dict[k]
+ val = self.coerce(type, v)
+ args[k] = val
+ elif k in self.key_dict:
+ type = self.key_dict[k]
+ val = self.coerce(type, v)
+ keys[k] = val
+ else:
+ raise ArgError('Invalid parameter: %s' % k)
+
+ def get_form_args(self, f, xargs=None):
+ d = {}
+ for (k, v) in f.items():
+ n = len(v)
+ if ((k not in self.arg_dict) and
+ (k not in self.key_dict)):
+ continue
+ if n == 0:
+ continue
+ elif n == 1:
+ d[k] = v[0]
+ else:
+ raise ArgError('Too many values for %s' % k)
+ return self.get_args(d, xargs=xargs)
+
+ def coerce(self, type, v):
+ try:
+ if type == 'int':
+ return int(v)
+ if type == 'str':
+ return str(v)
+ if type == 'sxpr':
+ return self.sxpr(v)
+ except ArgError:
+ raise
+ except StandardError, ex:
+ raise ArgError(str(ex))
+
+ def sxpr(self, v):
+ if instanceof(v, types.ListType):
+ return v
+ if instanceof(v, types.File) or hasattr(v, 'readline'):
+ return sxpr_file(v)
+ if instanceof(v, types.StringType):
+ return sxpr_file(StringIO(v))
+ return str(v)
+
+ def sxpr_file(self, fin):
+ try:
+ vals = sxp.parse(fin)
+ except:
+ raise ArgError('Coercion to sxpr failed')
+ if len(vals) == 1:
+ return vals[0]
+ else:
+ raise ArgError('Too many sxprs')
+
+ def call_with_args(self, fn, args, xargs=None):
+ (params, keys) = self.get_args(args, xargs=xargs)
+ fn(*params, **keys)
+
+ def call_with_form_args(self, fn, fargs, xargs=None):
+ (params, keys) = self.get_form_args(fargs, xargs=xargs)
+ fn(*params, **keys)
+
+class ArgFn(Args):
+ """Represent a remote HTTP operation as a function.
+ Used on the client.
+ """
+
+ def __init__(self, fn, paramspec, keyspec={}):
+ Args.__init__(self, paramspec, keyspec)
+ self.fn = fn
+
+ def __call__(self, fargs, xargs=None):
+ return self.call_with_args(self.fn, fargs, xargs=xargs)
+
+class FormFn(Args):
+ """Represent an operation as a function over a form.
+ Used in the HTTP server.
+ """
+
+ def __init__(self, fn, paramspec, keyspec={}):
+ Args.__init__(self, paramspec, keyspec)
+ self.fn = fn
+
+ def __call__(self, fargs, xargs=None):
+ return self.call_with_form_args(self.fn, fargs, xargs=xargs)
diff --git a/tools/python/xen/xend/EventServer.py b/tools/python/xen/xend/EventServer.py
new file mode 100644
index 0000000000..20c567ada7
--- /dev/null
+++ b/tools/python/xen/xend/EventServer.py
@@ -0,0 +1,215 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Simple publish/subscribe event server.
+
+"""
+import string
+
+from twisted.internet import reactor
+
+# subscribe a.b.c h: map a.b.c -> h
+# subscribe a.b.* h: map a.b.* -> h
+# subscribe a.b.? h: map a.b.? -> h
+#
+# for event a.b.c.d:
+#
+# lookup a.b.c.d, call handlers
+#
+# lookup a.b.c.?, call handlers
+#
+# lookup a.b.c.d.*, call handlers
+# lookup a.b.c.*, call handlers
+# lookup a.b.*, call handlers
+# lookup a.*, call handlers
+# lookup *, call handlers
+
+# a.b.c.d = (a b c d)
+# a.b.c.? = (a b c _)
+# a.b.c.* = (a b c . _)
+
+class EventServer:
+
+ DOT = '.'
+ QUERY = '?'
+ DOT_QUERY = DOT + QUERY
+ STAR = '*'
+ DOT_STAR = DOT + STAR
+
+ def __init__(self, run=0):
+ self.handlers = {}
+ self.run = run
+ self.queue = []
+
+ def start(self):
+ """Enable event handling. Sends any queued events.
+ """
+ self.run = 1
+ for (e,v) in self.queue:
+ self.inject(e, v)
+ self.queue = []
+
+ def stop(self):
+ """Suspend event handling. Events injected while suspended
+ are queued until we are started again.
+ """
+ self.run = 0
+
+ def subscribe(self, event, handler):
+ """Subscribe to an event. For example 'a.b.c.d'.
+ A subcription like 'a.b.c.?' ending in '?' matches any value
+ for the '?'. A subscription like 'a.b.c.*' ending in '*' matches
+ any event type with the same prefix, 'a.b.c' in this case.
+
+ event event name
+ handler event handler fn(event, val)
+ """
+ hl = self.handlers.get(event)
+ if hl is None:
+ self.handlers[event] = [handler]
+ else:
+ hl.append(handler)
+
+ def unsubscribe_all(self, event=None):
+ """Unsubscribe all handlers for a given event, or all handlers.
+
+ event event (optional)
+ """
+ if event == None:
+ self.handlers.clear()
+ elif event in self.handlers:
+ del self.handlers[event]
+
+ def unsubscribe(self, event, handler):
+ """Unsubscribe a given event and handler.
+
+ event event
+ handler handler
+ """
+ hl = self.handlers.get(event)
+ if hl is None:
+ return
+ if handler in hl:
+ hl.remove(handler)
+
+ def inject(self, event, val, async=1):
+ """Inject an event. Handlers for it are called if running, otherwise
+ it is queued.
+
+ event event type
+ val event value
+ """
+ if self.run:
+ if async:
+ reactor.callLater(0, self.call_handlers, event, val)
+ else:
+ self.notify_handlers(event, val)
+ else:
+ self.queue.append( (event, val) )
+
+ def call_handlers(self, event, val):
+ """Internal method to call event handlers.
+ """
+ #print ">event", event, val
+ self.call_event_handlers(event, event, val)
+ self.call_query_handlers(event, val)
+ self.call_star_handlers(event, val)
+
+ def call_event_handlers(self, key, event, val):
+ """Call the handlers for an event.
+ It is safe for handlers to subscribe or unsubscribe.
+
+ key key for handler list
+ event event type
+ val event value
+ """
+ hl = self.handlers.get(key)
+ if hl is None:
+ return
+ # Copy the handler list so that handlers can call
+ # subscribe/unsubscribe safely - python list iteration
+ # is not safe against list modification.
+ for h in hl[:]:
+ try:
+ h(event, val)
+ except:
+ pass
+
+ def call_query_handlers(self, event, val):
+ """Call regex handlers for events matching 'event' that end in '?'.
+
+ event event type
+ val event value
+ """
+ dot_idx = event.rfind(self.DOT)
+ if dot_idx == -1:
+ self.call_event_handlers(self.QUERY, event, val)
+ else:
+ event_query = event[0:dot_idx] + self.DOT_QUERY
+ self.call_event_handlers(event_query, event, val)
+
+ def call_star_handlers(self, event, val):
+ """Call regex handlers for events matching 'event' that end in '*'.
+
+ event event type
+ val event value
+ """
+ etype = string.split(event, self.DOT)
+ for i in range(len(etype), 0, -1):
+ event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR
+ self.call_event_handlers(event_star, event, val)
+ self.call_event_handlers(self.STAR, event, val)
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = EventServer()
+ inst.start()
+ return inst
+
+def main():
+ def sys_star(event, val):
+ print 'sys_star', event, val
+
+ def sys_foo(event, val):
+ print 'sys_foo', event, val
+ s.unsubscribe('sys.foo', sys_foo)
+
+ def sys_foo2(event, val):
+ print 'sys_foo2', event, val
+
+ def sys_bar(event, val):
+ print 'sys_bar', event, val
+
+ def sys_foo_bar(event, val):
+ print 'sys_foo_bar', event, val
+
+ def foo_bar(event, val):
+ print 'foo_bar', event, val
+
+ s = EventServer()
+ s.start()
+ s.subscribe('sys.*', sys_star)
+ s.subscribe('sys.foo', sys_foo)
+ s.subscribe('sys.foo', sys_foo2)
+ s.subscribe('sys.bar', sys_bar)
+ s.subscribe('sys.foo.bar', sys_foo_bar)
+ s.subscribe('foo.bar', foo_bar)
+ s.inject('sys.foo', 'hello')
+ print
+ s.inject('sys.bar', 'hello again')
+ print
+ s.inject('sys.foo.bar', 'hello again')
+ print
+ s.inject('foo.bar', 'hello again')
+ print
+ s.inject('foo', 'hello again')
+ print
+ s.start()
+ s.unsubscribe('sys.*', sys_star)
+ s.unsubscribe_all('sys.*')
+ s.inject('sys.foo', 'hello')
+
+if __name__ == "__main__":
+ main()
+
diff --git a/tools/python/xen/xend/EventTypes.py b/tools/python/xen/xend/EventTypes.py
new file mode 100644
index 0000000000..6350baa5dd
--- /dev/null
+++ b/tools/python/xen/xend/EventTypes.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+## XEND_DOMAIN_CREATE = "xend.domain.create": dom
+## create:
+## xend.domain.destroy: dom, reason:died/crashed
+## xend.domain.up ?
+
+## xend.domain.unpause: dom
+## xend.domain.pause: dom
+## xend.domain.shutdown: dom
+## xend.domain.destroy: dom
+
+## xend.domain.migrate.begin: dom, to
+## Begin tells: src host, src domain uri, dst host. Dst id known?
+## err: src host, src domain uri, dst host, dst id if known, status (of domain: ok, dead,...), reason
+## end: src host, src domain uri, dst host, dst uri
+
+## Events for both ends of migrate: for exporter and importer?
+## Include migrate id so can tie together.
+## Have uri /xend/migrate/<id> for migrate info (migrations in progress).
+
+## (xend.domain.migrate.begin (src <host>) (src.domain <id>)
+## (dst <host>) (id <migrate id>))
+
+## xend.domain.migrate.end:
+## (xend.domain.migrate.end (domain <id>) (to <host>)
+
+## xend.node.up: xend uri
+## xend.node.down: xend uri
+
+## xend.error ?
+
+## format:
+
diff --git a/tools/python/xen/xend/PrettyPrint.py b/tools/python/xen/xend/PrettyPrint.py
new file mode 100644
index 0000000000..9e91b11448
--- /dev/null
+++ b/tools/python/xen/xend/PrettyPrint.py
@@ -0,0 +1,299 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""General pretty-printer, including support for SXP.
+
+"""
+import sys
+import types
+import StringIO
+import sxp
+
+class PrettyItem:
+
+ def __init__(self, width):
+ self.width = width
+
+ def insert(self, block):
+ block.addtoline(self)
+
+ def get_width(self):
+ return self.width
+
+ def output(self, out):
+ print '***PrettyItem>output>', self
+ pass
+
+ def prettyprint(self, out, width):
+ print '***PrettyItem>prettyprint>', self
+ return width
+
+class PrettyString(PrettyItem):
+
+ def __init__(self, x):
+ PrettyItem.__init__(self, len(x))
+ self.value = x
+
+ def output(self, out):
+ out.write(self.value)
+
+ def prettyprint(self, line):
+ line.output(self)
+
+ def show(self, out):
+ print >> out, ("(string (width %d) '%s')" % (self.width, self.value))
+
+class PrettySpace(PrettyItem):
+
+ def output(self, out):
+ out.write(' ' * self.width)
+
+ def prettyprint(self, line):
+ line.output(self)
+
+ def show(self, out):
+ print >> out, ("(space (width %d))" % self.width)
+
+class PrettyBreak(PrettyItem):
+
+ def __init__(self, width, indent):
+ PrettyItem.__init__(self, width)
+ self.indent = indent
+ self.space = 0
+ self.active = 0
+
+ def output(self, out):
+ out.write(' ' * self.width)
+
+ def prettyprint(self, line):
+ if line.breaks(self.space):
+ self.active = 1
+ line.newline(self.indent)
+ else:
+ line.output(self)
+
+ def show(self, out):
+ print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))"
+ % (self.width, self.indent, self.space, self.lspace, self.active))
+
+class PrettyNewline(PrettySpace):
+
+ def __init__(self, indent):
+ PrettySpace.__init__(self, indent)
+
+ def insert(self, block):
+ block.newline()
+ block.addtoline(self)
+
+ def output(self, out):
+ out.write(' ' * self.width)
+
+ def prettyprint(self, line):
+ line.newline(0)
+ line.output(self)
+
+ def show(self, out):
+ print >> out, ("(nl (indent %d))" % self.indent)
+
+class PrettyLine(PrettyItem):
+ def __init__(self):
+ PrettyItem.__init__(self, 0)
+ self.content = []
+
+ def write(self, x):
+ self.content.append(x)
+
+ def end(self):
+ width = 0
+ lastwidth = 0
+ lastbreak = None
+ for x in self.content:
+ if isinstance(x, PrettyBreak):
+ if lastbreak:
+ lastbreak.space = (width - lastwidth)
+ lastbreak = x
+ lastwidth = width
+ width += x.get_width()
+ if lastbreak:
+ lastbreak.space = (width - lastwidth)
+ self.width = width
+
+ def prettyprint(self, line):
+ for x in self.content:
+ x.prettyprint(line)
+
+ def show(self, out):
+ print >> out, '(LINE (width %d)' % self.width
+ for x in self.content:
+ x.show(out)
+ print >> out, ')'
+
+class PrettyBlock(PrettyItem):
+
+ def __init__(self, all=0, parent=None):
+ self.width = 0
+ self.lines = []
+ self.parent = parent
+ self.indent = 0
+ self.all = all
+ self.broken = 0
+ self.newline()
+
+ def add(self, item):
+ item.insert(self)
+
+ def end(self):
+ self.width = 0
+ for l in self.lines:
+ l.end()
+ if self.width < l.width:
+ self.width = l.width
+
+ def breaks(self, n):
+ return self.all and self.broken
+
+ def newline(self):
+ self.lines.append(PrettyLine())
+
+ def addtoline(self, x):
+ self.lines[-1].write(x)
+
+ def prettyprint(self, line):
+ self.indent = line.used
+ line.block = self
+ if not line.fits(self.width):
+ self.broken = 1
+ for l in self.lines:
+ l.prettyprint(line)
+ line.block = self.parent
+
+ def show(self, out):
+ print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' %
+ (self.width, self.indent, self.all, self.broken))
+ for l in self.lines:
+ l.show(out)
+ print >> out, ')'
+
+class Line:
+
+ def __init__(self, out, width):
+ self.out = out
+ self.width = width
+ self.used = 0
+ self.space = self.width
+
+ def newline(self, indent):
+ indent += self.block.indent
+ self.out.write('\n')
+ self.out.write(' ' * indent)
+ self.used = indent
+ self.space = self.width - self.used
+
+ def fits(self, n):
+ return self.space - n >= 0
+
+ def breaks(self, n):
+ return self.block.breaks(n) or not self.fits(n)
+
+ def output(self, x):
+ n = x.get_width()
+ self.space -= n
+ self.used += n
+ if self.space < 0:
+ self.space = 0
+ x.output(self.out)
+
+class PrettyPrinter:
+ """A prettyprinter based on what I remember of Derek Oppen's
+ prettyprint algorithm from TOPLAS way back.
+ """
+
+ def __init__(self, width=40):
+ self.width = width
+ self.block = None
+ self.top = None
+
+ def write(self, x):
+ self.block.add(PrettyString(x))
+
+ def add(self, item):
+ self.block.add(item)
+
+ def addbreak(self, width=1, indent=4):
+ self.add(PrettyBreak(width, indent))
+
+ def addspace(self, width=1):
+ self.add(PrettySpace(width))
+
+ def addnl(self, indent=0):
+ self.add(PrettyNewline(indent))
+
+ def begin(self, all=0):
+ block = PrettyBlock(all=all, parent=self.block)
+ self.block = block
+
+ def end(self):
+ self.block.end()
+ if self.block.parent:
+ self.block.parent.add(self.block)
+ else:
+ self.top = self.block
+ self.block = self.block.parent
+
+ def prettyprint(self, out=sys.stdout):
+ line = Line(out, self.width)
+ self.top.prettyprint(line)
+
+class SXPPrettyPrinter(PrettyPrinter):
+ """An SXP prettyprinter.
+ """
+
+ def pstring(self, x):
+ io = StringIO.StringIO()
+ sxp.show(x, out=io)
+ io.seek(0)
+ val = io.getvalue()
+ io.close()
+ return val
+
+ def pprint(self, l):
+ if isinstance(l, types.ListType):
+ self.begin(all=1)
+ self.write('(')
+ i = 0
+ for x in l:
+ if(i): self.addbreak()
+ self.pprint(x)
+ i += 1
+ self.addbreak(width=0, indent=0)
+ self.write(')')
+ self.end()
+ else:
+ self.write(self.pstring(l))
+
+def prettyprint(sxpr, out=sys.stdout, width=80):
+ """Prettyprint an SXP form.
+
+ sxpr s-expression
+ out destination
+ width maximum output width
+ """
+ if isinstance(sxpr, types.ListType):
+ pp = SXPPrettyPrinter(width=width)
+ pp.pprint(sxpr)
+ pp.prettyprint(out=out)
+ else:
+ sxp.show(sxpr, out=out)
+ print >> out
+
+def main():
+ pin = sxp.Parser()
+ while 1:
+ buf = sys.stdin.read(100)
+ pin.input(buf)
+ if buf == '': break
+ l = pin.get_val()
+ prettyprint(l, width=80)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/tools/python/xen/xend/Vifctl.py b/tools/python/xen/xend/Vifctl.py
new file mode 100644
index 0000000000..49df8f6bd0
--- /dev/null
+++ b/tools/python/xen/xend/Vifctl.py
@@ -0,0 +1,28 @@
+import os
+import os.path
+import sys
+
+VIFCTL = '/etc/xen/xend/vifctl'
+
+def init():
+ os.system(VIFCTL + ' init ')
+
+def up(vif, mac=None, bridge=None, ipaddr=[]):
+ args = ['vif=%s' % vif]
+ if mac:
+ args.append('mac=%s' % mac)
+ if bridge:
+ args.append('bridge=%s' % bridge)
+ if ipaddr:
+ args.append('ipaddr=%s' % ','.join(ipaddr))
+ os.system(VIFCTL + ' up ' + ' '.join(args))
+
+def down(vif, mac=None, bridge=None, ipaddr=[]):
+ args = ['vif=%s' % vif]
+ if mac:
+ args.append('mac=%s' % mac)
+ if bridge:
+ args.append('bridge=%s' % bridge)
+ if ipaddr:
+ args.append('ipaddr=%s' % ','.join(ipaddr))
+ os.system(VIFCTL + ' down ' + ' '.join(args))
diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py
new file mode 100644
index 0000000000..13dc3dbb1e
--- /dev/null
+++ b/tools/python/xen/xend/XendClient.py
@@ -0,0 +1,311 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Client API for the HTTP interface on xend.
+Callable as a script - see main().
+"""
+import sys
+import httplib
+import types
+from StringIO import StringIO
+import urlparse
+
+from encode import *
+import sxp
+import PrettyPrint
+
+DEBUG = 0
+
+class Foo(httplib.HTTPResponse):
+
+ def begin(self):
+ fin = self.fp
+ while(1):
+ buf = fin.readline()
+ print "***", buf
+ if buf == '':
+ print
+ sys.exit()
+
+
+def sxprio(sxpr):
+ io = StringIO()
+ sxp.show(sxpr, out=io)
+ print >> io
+ io.seek(0)
+ return io
+
+def fileof(val):
+ """Converter for passing configs.
+ Handles lists, files directly.
+ Assumes a string is a file name and passes its contents.
+ """
+ if isinstance(val, types.ListType):
+ return sxprio(val)
+ if isinstance(val, types.StringType):
+ return file(val)
+ if hasattr(val, 'readlines'):
+ return val
+
+# todo: need to sort of what urls/paths are using for objects.
+# e.g. for domains at the moment return '0'.
+# should probably return abs path w.r.t. server, e.g. /xend/domain/0.
+# As an arg, assume abs path is obj uri, otherwise just id.
+
+# Function to convert to full url: Xend.uri(path), e.g.
+# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0
+# And should accept urls for ids?
+
+def urljoin(location, root, prefix='', rest=''):
+ prefix = str(prefix)
+ rest = str(rest)
+ base = 'http://' + location + root + prefix
+ url = urlparse.urljoin(base, rest)
+ return url
+
+def nodeurl(location, root, id=''):
+ return urljoin(location, root, 'node/', id)
+
+def domainurl(location, root, id=''):
+ return urljoin(location, root, 'domain/', id)
+
+def consoleurl(location, root, id=''):
+ return urljoin(location, root, 'console/', id)
+
+def deviceurl(location, root, id=''):
+ return urljoin(location, root, 'device/', id)
+
+def vneturl(location, root, id=''):
+ return urljoin(location, root, 'vnet/', id)
+
+def eventurl(location, root, id=''):
+ return urljoin(location, root, 'event/', id)
+
+def xend_request(url, method, data=None):
+ urlinfo = urlparse.urlparse(url)
+ (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo
+ if DEBUG: print url, urlinfo
+ if uproto != 'http':
+ raise StandardError('Invalid protocol: ' + uproto)
+ if DEBUG: print '>xend_request', ulocation, upath, method, data
+ (hdr, args) = encode_data(data)
+ if data and method == 'GET':
+ upath += '?' + args
+ args = None
+ if method == "POST" and upath.endswith('/'):
+ upath = upath[:-1]
+ if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args
+ #hdr['User-Agent'] = 'Mozilla'
+ #hdr['Accept'] = 'text/html,text/plain'
+ conn = httplib.HTTPConnection(ulocation)
+ #conn.response_class = Foo
+ if DEBUG: conn.set_debuglevel(1)
+ conn.request(method, upath, args, hdr)
+ resp = conn.getresponse()
+ if DEBUG: print resp.status, resp.reason
+ if DEBUG: print resp.msg.headers
+ if resp.status in [204, 404]:
+ return None
+ if resp.status not in [200, 201, 202, 203]:
+ raise RuntimeError(resp.reason)
+ pin = sxp.Parser()
+ data = resp.read()
+ if DEBUG: print "***data" , data
+ if DEBUG: print "***"
+ pin.input(data);
+ pin.input_eof()
+ conn.close()
+ val = pin.get_val()
+ #if isinstance(val, types.ListType) and sxp.name(val) == 'val':
+ # val = val[1]
+ if isinstance(val, types.ListType) and sxp.name(val) == 'err':
+ raise RuntimeError(val[1])
+ if DEBUG: print '**val='; sxp.show(val); print
+ return val
+
+def xend_get(url, args=None):
+ return xend_request(url, "GET", args)
+
+def xend_call(url, data):
+ return xend_request(url, "POST", data)
+
+class Xend:
+
+ SRV_DEFAULT = "localhost:8000"
+ ROOT_DEFAULT = "/xend/"
+
+ def __init__(self, srv=None, root=None):
+ self.bind(srv, root)
+
+ def bind(self, srv=None, root=None):
+ if srv is None: srv = self.SRV_DEFAULT
+ if root is None: root = self.ROOT_DEFAULT
+ if not root.endswith('/'): root += '/'
+ self.location = srv
+ self.root = root
+
+ def nodeurl(self, id=''):
+ return nodeurl(self.location, self.root, id)
+
+ def domainurl(self, id=''):
+ return domainurl(self.location, self.root, id)
+
+ def consoleurl(self, id=''):
+ return consoleurl(self.location, self.root, id)
+
+ def deviceurl(self, id=''):
+ return deviceurl(self.location, self.root, id)
+
+ def vneturl(self, id=''):
+ return vneturl(self.location, self.root, id)
+
+ def eventurl(self, id=''):
+ return eventurl(self.location, self.root, id)
+
+ def xend(self):
+ return xend_get(urljoin(self.location, self.root))
+
+ def xend_node(self):
+ return xend_get(self.nodeurl())
+
+ def xend_node_cpu_rrobin_slice_set(self, slice):
+ return xend_call(self.nodeurl(),
+ {'op' : 'cpu_rrobin_slice_set',
+ 'slice' : slice })
+
+ def xend_node_cpu_bvt_slice_set(self, slice):
+ return xend_call(self.nodeurl(),
+ {'op' : 'cpu_bvt_slice_set',
+ 'slice' : slice })
+
+ def xend_domains(self):
+ return xend_get(self.domainurl())
+
+ def xend_domain_create(self, conf):
+ return xend_call(self.domainurl(),
+ {'op' : 'create',
+ 'config' : fileof(conf) })
+
+ def xend_domain(self, id):
+ return xend_get(self.domainurl(id))
+
+ def xend_domain_unpause(self, id):
+ return xend_call(self.domainurl(id),
+ {'op' : 'unpause'})
+
+ def xend_domain_pause(self, id):
+ return xend_call(self.domainurl(id),
+ {'op' : 'pause'})
+
+ def xend_domain_shutdown(self, id):
+ return xend_call(self.domainurl(id),
+ {'op' : 'shutdown'})
+
+ def xend_domain_destroy(self, id):
+ return xend_call(self.domainurl(id),
+ {'op' : 'destroy'})
+
+ def xend_domain_save(self, id, filename):
+ return xend_call(self.domainurl(id),
+ {'op' : 'save',
+ 'file' : filename})
+
+ def xend_domain_restore(self, id, filename):
+ return xend_call(self.domainurl(id),
+ {'op' : 'restore',
+ 'file' : filename })
+
+ def xend_domain_migrate(self, id, dst):
+ return xend_call(self.domainurl(id),
+ {'op' : 'migrate',
+ 'destination': dst})
+
+ def xend_domain_pincpu(self, id, cpu):
+ return xend_call(self.domainurl(id),
+ {'op' : 'pincpu',
+ 'cpu' : cpu})
+
+ def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu):
+ return xend_call(self.domainurl(id),
+ {'op' : 'cpu_bvt_set',
+ 'mcuadv' : mvuadv,
+ 'warp' : warp,
+ 'warpl' : warpl,
+ 'warpu' : warpu })
+
+ def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
+ return xend_call(self.domainurl(id),
+ {'op' : 'cpu_atropos_set',
+ 'period' : period,
+ 'slice' : slice,
+ 'latency' : latency,
+ 'xtratime': xtratime })
+
+ def xend_domain_vifs(self, id):
+ return xend_get(self.domainurl(id),
+ { 'op' : 'vifs' })
+
+ def xend_domain_vif_ip_add(self, id, vif, ipaddr):
+ return xend_call(self.domainurl(id),
+ {'op' : 'vif_ip_add',
+ 'vif' : vif,
+ 'ip' : ipaddr })
+
+ def xend_domain_vbds(self, id):
+ return xend_get(self.domainurl(id),
+ {'op' : 'vbds'})
+
+ def xend_domain_vbd(self, id, vbd):
+ return xend_get(self.domainurl(id),
+ {'op' : 'vbd',
+ 'vbd' : vbd})
+
+ def xend_consoles(self):
+ return xend_get(self.consoleurl())
+
+ def xend_console(self, id):
+ return xend_get(self.consoleurl(id))
+
+ def xend_vnets(self):
+ return xend_get(self.vneturl())
+
+ def xend_vnet_create(self, conf):
+ return xend_call(self.vneturl(),
+ {'op': 'create', 'config': fileof(conf) })
+
+ def xend_vnet(self, id):
+ return xend_get(self.vneturl(id))
+
+ def xend_vnet_delete(self, id):
+ return xend_call(self.vneturl(id),
+ {'op': 'delete'})
+
+ def xend_event_inject(self, sxpr):
+ val = xend_call(self.eventurl(),
+ {'op': 'inject', 'event': fileof(sxpr) })
+
+
+def main(argv):
+ """Call an API function:
+
+ python XendClient.py fn args...
+
+ The leading 'xend_' on the function can be omitted.
+ Example:
+
+ > python XendClient.py domains
+ (domain 0 8)
+ > python XendClient.py domain 0
+ (domain (id 0) (name Domain-0) (memory 128))
+ """
+ server = Xend()
+ fn = argv[1]
+ if not fn.startswith('xend'):
+ fn = 'xend_' + fn
+ args = argv[2:]
+ val = getattr(server, fn)(*args)
+ PrettyPrint.prettyprint(val)
+ print
+
+if __name__ == "__main__":
+ main(sys.argv)
+else:
+ server = Xend()
diff --git a/tools/python/xen/xend/XendConsole.py b/tools/python/xen/xend/XendConsole.py
new file mode 100644
index 0000000000..4420c388f2
--- /dev/null
+++ b/tools/python/xen/xend/XendConsole.py
@@ -0,0 +1,179 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import socket
+import xen.ext.xc
+xc = xen.ext.xc.new()
+
+import sxp
+import XendRoot
+xroot = XendRoot.instance()
+import XendDB
+
+import EventServer
+eserver = EventServer.instance()
+
+from xen.xend.server import SrvDaemon
+daemon = SrvDaemon.instance()
+
+class XendConsoleInfo:
+ """Console information record.
+ """
+
+ def __init__(self, console, dom1, port1, dom2, port2, conn=None):
+ self.console = console
+ self.dom1 = int(dom1)
+ self.port1 = int(port1)
+ self.dom2 = int(dom2)
+ self.port2 = int(port2)
+ self.conn = conn
+ #self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2)
+ self.id = str(port1)
+
+ def __str__(self):
+ s = "console"
+ s += " id=%s" % self.id
+ s += " src=%d.%d" % (self.dom1, self.port1)
+ s += " dst=%d.%d" % (self.dom2, self.port2)
+ s += " port=%s" % self.console
+ if self.conn:
+ s += " conn=%s:%s" % (self.conn[0], self.conn[1])
+ return s
+
+ def sxpr(self):
+ sxpr = ['console',
+ ['id', self.id],
+ ['src', self.dom1, self.port1],
+ ['dst', self.dom2, self.port2],
+ ['port', self.console],
+ ]
+ if self.conn:
+ sxpr.append(['connected', self.conn[0], self.conn[1]])
+ return sxpr
+
+ def connection(self):
+ return self.conn
+
+ def update(self, consinfo):
+ conn = sxp.child(consinfo, 'connected')
+ if conn:
+ self.conn = conn[1:]
+ else:
+ self.conn = None
+
+ def uri(self):
+ """Get the uri to use to connect to the console.
+ This will be a telnet: uri.
+
+ return uri
+ """
+ host = socket.gethostname()
+ return "telnet://%s:%s" % (host, self.console)
+
+class XendConsole:
+
+ dbpath = "console"
+
+ def __init__(self):
+ self.db = XendDB.XendDB(self.dbpath)
+ self.console = {}
+ self.console_db = self.db.fetchall("")
+ if xroot.get_rebooted():
+ print 'XendConsole> rebooted: removing all console info'
+ self.rm_all()
+ eserver.subscribe('xend.domain.died', self.onDomainDied)
+ eserver.subscribe('xend.domain.destroy', self.onDomainDied)
+
+ def rm_all(self):
+ """Remove all console info. Used after reboot.
+ """
+ for (k, v) in self.console_db.items():
+ self._delete_console(k)
+
+ def refresh(self):
+ consoles = daemon.consoles()
+ cons = {}
+ for consinfo in consoles:
+ id = str(sxp.child_value(consinfo, 'id'))
+ cons[id] = consinfo
+ if id not in self.console:
+ self._new_console(consinfo)
+ for c in self.console.values():
+ consinfo = cons.get(c.id)
+ if consinfo:
+ c.update(consinfo)
+ else:
+ self._delete_console(c.id)
+
+ def onDomainDied(self, event, val):
+ dom = int(val)
+ #print 'XendConsole>onDomainDied', 'event', event, "dom=", dom
+ for c in self.consoles():
+ #print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2
+ if (c.dom1 == dom) or (c.dom2 == dom):
+ 'XendConsole>onDomainDied', 'delete console dom=', dom
+ ctrl = daemon.get_domain_console(dom)
+ if ctrl:
+ ctrl.close()
+ self._delete_console(c.id)
+
+ def sync(self):
+ self.db.saveall("", self.console_db)
+
+ def sync_console(self, id):
+ self.db.save(id, self.console_db[id])
+
+ def _new_console(self, consinfo):
+ # todo: xen needs a call to get current domain id.
+ dom1 = 0
+ port1 = sxp.child_value(consinfo, 'local_port')
+ dom2 = sxp.child_value(consinfo, 'domain')
+ port2 = sxp.child_value(consinfo, 'remote_port')
+ console = sxp.child_value(consinfo, 'console_port')
+ info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2))
+ info.update(consinfo)
+ self._add_console(info.id, info)
+ return info
+
+ def _add_console(self, id, info):
+ self.console[id] = info
+ self.console_db[id] = info.sxpr()
+ self.sync_console(id)
+
+ def _delete_console(self, id):
+ if id in self.console:
+ del self.console[id]
+ if id in self.console_db:
+ del self.console_db[id]
+ self.db.delete(id)
+
+ def console_ls(self):
+ self.refresh()
+ return self.console.keys()
+
+ def consoles(self):
+ self.refresh()
+ return self.console.values()
+
+ def console_create(self, dom):
+ consinfo = daemon.console_create(dom)
+ info = self._new_console(consinfo)
+ return info
+
+ def console_get(self, id):
+ self.refresh()
+ return self.console.get(id)
+
+ def console_delete(self, id):
+ self._delete_console(id)
+
+ def console_disconnect(self, id):
+ id = int(id)
+ daemon.console_disconnect(id)
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendConsole()
+ return inst
diff --git a/tools/python/xen/xend/XendDB.py b/tools/python/xen/xend/XendDB.py
new file mode 100644
index 0000000000..6a27e65b58
--- /dev/null
+++ b/tools/python/xen/xend/XendDB.py
@@ -0,0 +1,91 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import os
+import os.path
+import errno
+import dircache
+import time
+
+import sxp
+import XendRoot
+xroot = XendRoot.instance()
+
+class XendDB:
+ """Persistence for Xend. Stores data in files and directories.
+ """
+
+ def __init__(self, path=None):
+ self.dbpath = xroot.get_dbroot()
+ if path:
+ self.dbpath = os.path.join(self.dbpath, path)
+ pass
+
+ def filepath(self, path):
+ return os.path.join(self.dbpath, path)
+
+ def fetch(self, path):
+ fpath = self.filepath(path)
+ return self.fetchfile(fpath)
+
+ def fetchfile(self, fpath):
+ pin = sxp.Parser()
+ fin = file(fpath, "rb")
+ try:
+ while 1:
+ try:
+ buf = fin.read(1024)
+ except IOError, ex:
+ if ex.errno == errno.EINTR:
+ continue
+ else:
+ raise
+ pin.input(buf)
+ if buf == '':
+ pin.input_eof()
+ break
+ finally:
+ fin.close()
+ return pin.get_val()
+
+ def save(self, path, sxpr):
+ fpath = self.filepath(path)
+ return self.savefile(fpath, sxpr)
+
+ def savefile(self, fpath, sxpr):
+ fdir = os.path.dirname(fpath)
+ if not os.path.isdir(fdir):
+ os.makedirs(fdir)
+ fout = file(fpath, "wb+")
+ try:
+ t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
+ fout.write("# %s %s\n" % (fpath, t))
+ sxp.show(sxpr, out=fout)
+ finally:
+ fout.close()
+
+ def fetchall(self, path):
+ dpath = self.filepath(path)
+ d = {}
+ for k in dircache.listdir(dpath):
+ try:
+ v = self.fetchfile(os.path.join(dpath, k))
+ d[k] = v
+ except:
+ pass
+ return d
+
+ def saveall(self, path, d):
+ for (k, v) in d.items():
+ self.save(os.path.join(path, k), v)
+
+ def delete(self, path):
+ dpath = self.filepath(path)
+ os.unlink(dpath)
+
+ def ls(self, path):
+ dpath = self.filepath(path)
+ return dircache.listdir(dpath)
+
+
+
+
diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py
new file mode 100644
index 0000000000..3aaf080c36
--- /dev/null
+++ b/tools/python/xen/xend/XendDomain.py
@@ -0,0 +1,369 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Handler for domain operations.
+ Nothing here is persistent (across reboots).
+ Needs to be persistent for one uptime.
+"""
+import sys
+
+from twisted.internet import defer
+
+import xen.ext.xc; xc = xen.ext.xc.new()
+
+import sxp
+import XendRoot
+xroot = XendRoot.instance()
+import XendDB
+import XendDomainInfo
+import XendConsole
+import EventServer
+
+from xen.xend.server import SrvDaemon
+xend = SrvDaemon.instance()
+
+eserver = EventServer.instance()
+
+__all__ = [ "XendDomain" ]
+
+class XendDomain:
+ """Index of all domains. Singleton.
+ """
+
+ dbpath = "domain"
+ domain = {}
+
+ def __init__(self):
+ self.xconsole = XendConsole.instance()
+ # Table of domain info indexed by domain id.
+ self.db = XendDB.XendDB(self.dbpath)
+ #self.domain = {}
+ self.domain_db = self.db.fetchall("")
+ if xroot.get_rebooted():
+ print 'XendDomain> rebooted: removing all domain info'
+ self.rm_all()
+ eserver.subscribe('xend.virq', self.onVirq)
+ self.initial_refresh()
+
+ def onVirq(self, event, val):
+ print 'XendDomain> virq', val
+ self.reap()
+
+ def rm_all(self):
+ """Remove all domain info. Used after reboot.
+ """
+ for (k, v) in self.domain_db.items():
+ self._delete_domain(k, notify=0)
+
+ def initial_refresh(self):
+ """Refresh initial domain info from domain_db.
+ """
+ print "initial_refresh>"
+ for d in self.domain_db.values(): print 'db dom=', d
+ domlist = xc.domain_getinfo()
+ for d in domlist: print 'xc dom=', d
+ doms = {}
+ for d in domlist:
+ domid = str(d['dom'])
+ doms[domid] = d
+ dlist = []
+ for config in self.domain_db.values():
+ domid = str(sxp.child_value(config, 'id'))
+ print "dom=", domid, "config=", config
+ if domid in doms:
+ print "dom=", domid, "new"
+ deferred = self._new_domain(config, doms[domid])
+ dlist.append(deferred)
+ else:
+ print "dom=", domid, "del"
+ self._delete_domain(domid)
+ deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
+ def cbok(val):
+ #print "doms:"
+ #for d in self.domain.values(): print 'dom', d
+ self.refresh()
+ print "XendDomain>initial_refresh> doms:"
+ for d in self.domain.values(): print 'dom', d
+ deferred.addCallback(cbok)
+
+ def sync(self):
+ """Sync domain db to disk.
+ """
+ self.db.saveall("", self.domain_db)
+
+ def sync_domain(self, dom):
+ """Sync info for a domain to disk.
+
+ dom domain id (string)
+ """
+ self.db.save(dom, self.domain_db[dom])
+
+ def close(self):
+ pass
+
+ def _new_domain(self, savedinfo, info):
+ """Create a domain entry from saved info.
+ """
+## console = None
+## kernel = None
+## id = sxp.child_value(info, 'id')
+## dom = int(id)
+## name = sxp.child_value(info, 'name')
+## memory = int(sxp.child_value(info, 'memory'))
+## consoleinfo = sxp.child(info, 'console')
+## if consoleinfo:
+## consoleid = sxp.child_value(consoleinfo, 'id')
+## console = self.xconsole.console_get(consoleid)
+## if dom and console is None:
+## # Try to connect a console.
+## console = self.xconsole.console_create(dom)
+## config = sxp.child(info, 'config')
+## if config:
+## image = sxp.child(info, 'image')
+## if image:
+## image = sxp.child0(image)
+## kernel = sxp.child_value(image, 'kernel')
+## dominfo = XendDomainInfo.XendDomainInfo(
+## config, dom, name, memory, kernel, console)
+ config = sxp.child_value(savedinfo, 'config')
+ deferred = XendDomainInfo.vm_recreate(config, info)
+ def fn(dominfo):
+ self.domain[dominfo.id] = dominfo
+ deferred.addCallback(fn)
+ return deferred
+
+ def _add_domain(self, id, info, notify=1):
+ self.domain[id] = info
+ self.domain_db[id] = info.sxpr()
+ self.sync_domain(id)
+ if notify: eserver.inject('xend.domain.created', id)
+
+ def _delete_domain(self, id, notify=1):
+ if id in self.domain:
+ if notify: eserver.inject('xend.domain.died', id)
+ del self.domain[id]
+ if id in self.domain_db:
+ del self.domain_db[id]
+ self.db.delete(id)
+
+ def reap(self):
+ """Go through the domains looking for ones that have crashed or stopped.
+ Tidy them up.
+ """
+ print 'XendDomain>reap>'
+ domlist = xc.domain_getinfo()
+ casualties = []
+ for d in domlist:
+ #print 'dom', d
+ dead = 0
+ dead = dead or (d['crashed'] or d['shutdown'])
+ dead = dead or (d['dying'] and
+ not(d['running'] or d['paused'] or d['blocked']))
+ if dead:
+ casualties.append(d)
+ for d in casualties:
+ id = str(d['dom'])
+ print 'XendDomain>reap> died id=', id, d
+ dominfo = self.domain.get(id)
+ if not dominfo: continue
+ dominfo.died()
+ self.domain_destroy(id, refresh=0)
+ print 'XendDomain>reap<'
+
+ def refresh(self):
+ """Refresh domain list from Xen.
+ """
+ domlist = xc.domain_getinfo()
+ # Index the domlist by id.
+ # Add entries for any domains we don't know about.
+ doms = {}
+ for d in domlist:
+ id = str(d['dom'])
+ doms[id] = d
+ if id not in self.domain:
+ config = None
+ #image = None
+ #newinfo = XendDomainInfo.XendDomainInfo(
+ # config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d)
+ deferred = XendDomainInfo.vm_recreate(config, d)
+ def fn(dominfo):
+ self._add_domain(dominfo.id, dominfo)
+ deferred.addCallback(fn)
+ # Remove entries for domains that no longer exist.
+ for d in self.domain.values():
+ dominfo = doms.get(d.id)
+ if dominfo:
+ d.update(dominfo)
+ else:
+ self._delete_domain(d.id)
+ self.reap()
+
+ def refresh_domain(self, id):
+ dom = int(id)
+ dominfo = xc.domain_getinfo(dom, 1)
+ if dominfo == [] or dominfo[0]['dom'] != dom:
+ try:
+ self._delete_domain(id)
+ except:
+ print 'refresh_domain: error'
+ raise
+ pass
+ else:
+ d = self.domain.get(id)
+ if d:
+ d.update(dominfo[0])
+
+ def domain_ls(self):
+ # List domains.
+ # Update info from kernel first.
+ self.refresh()
+ return self.domain.keys()
+
+ def domains(self):
+ self.refresh()
+ return self.domain.values()
+
+ def domain_create(self, config):
+ # Create domain, log it.
+ deferred = XendDomainInfo.vm_create(config)
+ def fn(dominfo):
+ self._add_domain(dominfo.id, dominfo)
+ return dominfo
+ deferred.addCallback(fn)
+ return deferred
+
+ def domain_get(self, id):
+ id = str(id)
+ self.refresh_domain(id)
+ return self.domain.get(id)
+
+ def domain_unpause(self, id):
+ """(Re)start domain running.
+ """
+ dom = int(id)
+ eserver.inject('xend.domain.unpause', id)
+ return xc.domain_unpause(dom=dom)
+
+ def domain_pause(self, id):
+ """Pause domain execution.
+ """
+ dom = int(id)
+ eserver.inject('xend.domain.pause', id)
+ return xc.domain_pause(dom=dom)
+
+ def domain_shutdown(self, id, reason='poweroff'):
+ """Shutdown domain (nicely).
+ """
+ dom = int(id)
+ if dom <= 0:
+ return 0
+ eserver.inject('xend.domain.shutdown', [id, reason])
+ val = xend.domain_shutdown(dom, reason)
+ self.refresh()
+ return val
+
+ def domain_destroy(self, id, refresh=1):
+ """Terminate domain immediately.
+ """
+ dom = int(id)
+ if dom <= 0:
+ return 0
+ eserver.inject('xend.domain.destroy', id)
+ val = xc.domain_destroy(dom=dom)
+ if refresh: self.refresh()
+ return val
+
+ def domain_migrate(self, id, dst):
+ """Start domain migration.
+ """
+ # Need a cancel too?
+ pass
+
+ def domain_save(self, id, dst, progress=0):
+ """Save domain state to file, destroy domain.
+ """
+ dom = int(id)
+ dominfo = self.domain_get(id)
+ if not dominfo:
+ return -1
+ vmconfig = sxp.to_string(dominfo.sxpr())
+ self.domain_pause(id)
+ eserver.inject('xend.domain.save', id)
+ rc = xc.linux_save(dom=dom, state_file=dst, vmconfig=vmconfig, progress=progress)
+ if rc == 0:
+ self.domain_destroy(id)
+ return rc
+
+ def domain_restore(self, src, progress=0):
+ """Restore domain from file.
+ """
+ dominfo = XendDomainInfo.vm_restore(src, progress=progress)
+ self._add_domain(dominfo.id, dominfo)
+ return dominfo
+
+ #============================================================================
+ # Backward compatibility stuff from here on.
+
+ def domain_pincpu(self, dom, cpu):
+ dom = int(dom)
+ return xc.domain_pincpu(dom, cpu)
+
+ def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu):
+ dom = int(dom)
+ return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv,
+ warp=warp, warpl=warpl, warpu=warpu)
+
+ def domain_cpu_bvt_get(self, dom):
+ dom = int(dom)
+ return xc.bvtsched_domain_get(dom)
+
+ def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime):
+ dom = int(dom)
+ return xc.atropos_domain_set(dom, period, slice, latency, xtratime)
+
+ def domain_cpu_atropos_get(self, dom):
+ dom = int(dom)
+ return xc.atropos_domain_get(dom)
+
+ def domain_vif_ls(self, dom):
+ dominfo = self.domain_get(dom)
+ if not dominfo: return None
+ devs = dominfo.get_devices('vif')
+ return range(0, len(devs))
+
+ def domain_vif_get(self, dom, vif):
+ dominfo = self.domain_get(dom)
+ if not dominfo: return None
+ return dominfo.get_device_by_index(vif)
+
+## def domain_vif_ip_add(self, dom, vif, ip):
+## dom = int(dom)
+## return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip)
+
+ def domain_vbd_ls(self, dom):
+ dominfo = self.domain_get(dom)
+ if not dominfo: return []
+ devs = dominfo.get_devices('vbd')
+ return [ sxp.child_value(v, 'dev') for v in devs ]
+
+ def domain_vbd_get(self, dom, vbd):
+ dominfo = self.domain_get(dom)
+ if not dominfo: return None
+ devs = dominfo.get_devices('vbd')
+ for v in devs:
+ if sxp.child_value(v, 'dev') == vbd:
+ return v
+ return None
+
+ def domain_shadow_control(self, dom, op):
+ dom = int(dom)
+ return xc.shadow_control(dom, op)
+
+ #============================================================================
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendDomain()
+ return inst
diff --git a/tools/python/xen/xend/XendDomainConfig.py b/tools/python/xen/xend/XendDomainConfig.py
new file mode 100644
index 0000000000..35db31ff51
--- /dev/null
+++ b/tools/python/xen/xend/XendDomainConfig.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Handler for persistent domain configs.
+
+"""
+
+import sxp
+import XendDB
+import XendDomain
+
+__all__ = [ "XendDomainConfig" ]
+
+class XendDomainConfig:
+
+ dbpath = 'config'
+
+ def __init__(self):
+ self.db = XendDB.XendDB(self.dbpath)
+
+ def domain_config_ls(self, path):
+ return self.db.ls(path)
+
+ def domain_config_create(self, path, sxpr):
+ self.db.save(path, sxpr)
+ pass
+
+ def domain_config_delete(self, path):
+ self.db.delete(path)
+
+ def domain_config_instance(self, path):
+ """Create a domain from a config.
+ """
+ config = self.db.fetch(path)
+ xd = XendDomain.instance()
+ newdom = xd.domain_create(config)
+ return newdom
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendDomainConfig()
+ return inst
diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
new file mode 100644
index 0000000000..a7e5aa3b2f
--- /dev/null
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -0,0 +1,908 @@
+#!/usr/bin/python
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Representation of a single domain.
+Includes support for domain construction, using
+open-ended configurations.
+
+Author: Mike Wray <mike.wray@hpl.hp.com>
+
+"""
+
+import string
+import re
+import sys
+import os
+
+from twisted.internet import defer
+
+import xen.ext.xc; xc = xen.ext.xc.new()
+import xen.util.ip
+
+import sxp
+
+import XendConsole
+xendConsole = XendConsole.instance()
+
+import server.SrvDaemon
+xend = server.SrvDaemon.instance()
+
+SIF_BLK_BE_DOMAIN = (1<<4)
+SIF_NET_BE_DOMAIN = (1<<5)
+
+def readlines(fd):
+ """Version of readlines safe against EINTR.
+ """
+ import errno
+
+ lines = []
+ while 1:
+ try:
+ line = fd.readline()
+ except IOError, ex:
+ if ex.errno == errno.EINTR:
+ continue
+ else:
+ raise
+ if line == '': break
+ lines.append(line)
+ return lines
+
+class VmError(ValueError):
+ """Vm construction error."""
+
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+
+def blkdev_name_to_number(name):
+ """Take the given textual block-device name (e.g., '/dev/sda1',
+ 'hda') and return the device number used by the OS. """
+
+ if not re.match( '/dev/', name ):
+ name = '/dev/' + name
+
+ return os.stat(name).st_rdev
+
+def lookup_raw_partn(partition):
+ """Take the given block-device name (e.g., '/dev/sda1', 'hda')
+ and return a dictionary { device, start_sector,
+ nr_sectors, type }
+ device: Device number of the given partition
+ start_sector: Index of first sector of the partition
+ nr_sectors: Number of sectors comprising this partition
+ type: 'Disk' or identifying name for partition type
+ """
+
+ if not re.match( '/dev/', partition ):
+ partition = '/dev/' + partition
+
+ drive = re.split( '[0-9]', partition )[0]
+
+ if drive == partition:
+ fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
+ line = readline(fd)
+ if line:
+ return [ { 'device' : blkdev_name_to_number(drive),
+ 'start_sector' : long(0),
+ 'nr_sectors' : long(line) * 2,
+ 'type' : 'Disk' } ]
+ return None
+
+ # determine position on disk
+ fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
+
+ #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
+ lines = readlines(fd)
+ for line in lines:
+ m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
+ 'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
+ if m:
+ return [ { 'device' : blkdev_name_to_number(drive),
+ 'start_sector' : long(m.group(1)),
+ 'nr_sectors' : long(m.group(2)),
+ 'type' : m.group(3) } ]
+
+ return None
+
+def lookup_disk_uname(uname):
+ """Lookup a list of segments for a physical device.
+ uname [string]: name of the device in the format \'phy:dev\' for a physical device
+ returns [list of dicts]: list of extents that make up the named device
+ """
+ ( type, d_name ) = string.split( uname, ':' )
+
+ if type == "phy":
+ segments = lookup_raw_partn( d_name )
+ else:
+ segments = None
+ return segments
+
+def make_disk(dom, uname, dev, mode, recreate=0):
+ """Create a virtual disk device for a domain.
+
+ @returns Deferred
+ """
+ segments = lookup_disk_uname(uname)
+ if not segments:
+ raise VmError("vbd: Segments not found: uname=%s" % uname)
+ if len(segments) > 1:
+ raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
+ segment = segments[0]
+ vdev = blkdev_name_to_number(dev)
+ ctrl = xend.blkif_create(dom, recreate=recreate)
+
+ def fn(ctrl):
+ return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate)
+ ctrl.addCallback(fn)
+ return ctrl
+
+def make_vif(dom, vif, vmac, recreate=0):
+ """Create a virtual network device for a domain.
+
+
+ @returns Deferred
+ """
+ xend.netif_create(dom, recreate=recreate)
+ d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate)
+ return d
+
+def vif_up(iplist):
+ """send an unsolicited ARP reply for all non link-local IP addresses.
+
+ iplist IP addresses
+ """
+
+ IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
+
+ def get_ip_nonlocal_bind():
+ return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
+
+ def set_ip_nonlocal_bind(v):
+ print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
+
+ def link_local(ip):
+ return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
+
+ def arping(ip, gw):
+ cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
+ print cmd
+ os.system(cmd)
+
+ gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
+ nlb = get_ip_nonlocal_bind()
+ if not nlb: set_ip_nonlocal_bind(1)
+ try:
+ for ip in iplist:
+ if not link_local(ip):
+ arping(ip, gateway)
+ finally:
+ if not nlb: set_ip_nonlocal_bind(0)
+
+config_handlers = {}
+
+def add_config_handler(name, h):
+ """Add a handler for a config field.
+
+ name field name
+ h handler: fn(vm, config, field, index)
+ """
+ config_handlers[name] = h
+
+def get_config_handler(name):
+ """Get a handler for a config field.
+
+ returns handler or None
+ """
+ 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
+ name image type
+ h handler: fn(config, name, memory, image)
+ """
+ image_handlers[name] = h
+
+def get_image_handler(name):
+ """Get the handler for an image type.
+ name image type
+
+ returns handler or None
+ """
+ return image_handlers.get(name)
+
+"""Table of handlers for devices.
+Indexed by device type.
+"""
+device_handlers = {}
+
+def add_device_handler(name, h):
+ """Add a handler for a device type.
+
+ name device type
+ h handler: fn(vm, dev)
+ """
+ device_handlers[name] = h
+
+def get_device_handler(name):
+ """Get the handler for a device type.
+
+ name device type
+
+ returns handler or None
+ """
+ return device_handlers.get(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.
+
+ config configuration
+
+ returns Deferred
+ raises VmError for invalid configuration
+ """
+ print 'vm_create>'
+ vm = XendDomainInfo()
+ return vm.construct(config)
+
+def vm_recreate(config, info):
+ """Create the VM object for an existing domain.
+ """
+ vm = XendDomainInfo()
+ vm.recreate = 1
+ vm.setdom(info['dom'])
+ vm.name = info['name']
+ vm.memory = info['mem_kb']/1024
+ if config:
+ d = vm.construct(config)
+ else:
+ d = defer.Deferred()
+ d.callback(vm)
+ return d
+
+def vm_restore(src, progress=0):
+ """Restore a VM from a disk image.
+
+ src saved state to restore
+ progress progress reporting flag
+ returns deferred
+ raises VmError for invalid configuration
+ """
+ vm = XendDomainInfo()
+ ostype = "linux" #todo Set from somewhere (store in the src?).
+ restorefn = getattr(xc, "%s_restore" % ostype)
+ d = restorefn(state_file=src, progress=progress)
+ dom = int(d['dom'])
+ if dom < 0:
+ raise VMError('restore failed')
+ vmconfig = sxp.from_string(d['vmconfig'])
+ vm.config = sxp.child_value(vmconfig, 'config')
+ deferred = vm.dom_configure(dom)
+ def vifs_cb(val, vm):
+ vif_up(vm.ipaddrs)
+ deferred.addCallback(vifs_cb, vm)
+ return deferred
+
+def dom_get(dom):
+ domlist = xc.domain_getinfo(dom=dom)
+ if domlist and dom == domlist[0]['dom']:
+ return domlist[0]
+ return None
+
+
+def append_deferred(dlist, v):
+ if isinstance(v, defer.Deferred):
+ dlist.append(v)
+
+def _vm_configure1(val, vm):
+ d = vm.create_devices()
+ print '_vm_configure1> made devices...'
+ def cbok(x):
+ print '_vm_configure1> cbok', x
+ return x
+ d.addCallback(cbok)
+ d.addCallback(_vm_configure2, vm)
+ print '_vm_configure1<'
+ return d
+
+def _vm_configure2(val, vm):
+ print '>callback _vm_configure2...'
+ d = vm.configure_fields()
+ def cbok(results):
+ print '_vm_configure2> cbok', results
+ return vm
+ def cberr(err):
+ print '_vm_configure2> cberr', err
+ vm.destroy()
+ return err
+ d.addCallback(cbok)
+ d.addErrback(cberr)
+ print '<_vm_configure2'
+ return d
+
+class XendDomainInfo:
+ """Virtual machine object."""
+
+ def __init__(self):
+ self.recreate = 0
+ self.config = None
+ self.id = None
+ self.dom = None
+ self.name = None
+ self.memory = None
+ self.image = None
+ self.ramdisk = None
+ self.cmdline = None
+ self.console = None
+ self.devices = {}
+ self.configs = []
+ self.info = None
+ self.ipaddrs = []
+ self.blkif_backend = 0
+ self.netif_backend = 0
+ #todo: state: running, suspended
+ self.state = 'running'
+ #todo: set to migrate info if migrating
+ self.migrate = None
+
+ def setdom(self, dom):
+ self.dom = int(dom)
+ self.id = str(dom)
+
+ def update(self, info):
+ """Update with info from xc.domain_getinfo().
+ """
+ self.info = info
+ self.memory = self.info['mem_kb'] / 1024
+
+ def __str__(self):
+ s = "domain"
+ s += " id=" + self.id
+ s += " name=" + self.name
+ s += " memory=" + str(self.memory)
+ if self.console:
+ s += " console=" + self.console.id
+ if self.image:
+ s += " image=" + self.image
+ s += ""
+ return s
+
+ __repr__ = __str__
+
+ def sxpr(self):
+ sxpr = ['domain',
+ ['id', self.id],
+ ['name', self.name],
+ ['memory', self.memory] ]
+ if self.info:
+ run = (self.info['running'] and 'r') or '-'
+ block = (self.info['blocked'] and 'b') or '-'
+ stop = (self.info['paused'] and 'p') or '-'
+ susp = (self.info['shutdown'] and 's') or '-'
+ crash = (self.info['crashed'] and 'c') or '-'
+ state = run + block + stop + susp + crash
+ sxpr.append(['state', state])
+ if self.info['shutdown']:
+ reasons = ["poweroff", "reboot", "suspend"]
+ reason = reasons[self.info['shutdown_reason']]
+ sxpr.append(['shutdown_reason', reason])
+ sxpr.append(['cpu', self.info['cpu']])
+ sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
+ if self.console:
+ sxpr.append(self.console.sxpr())
+ if self.config:
+ sxpr.append(['config', self.config])
+ return sxpr
+
+ def construct(self, config):
+ # todo - add support for scheduling params?
+ self.config = config
+ try:
+ self.name = sxp.child_value(config, 'name')
+ self.memory = int(sxp.child_value(config, 'memory', '128'))
+ self.configure_backends()
+ image = sxp.child_value(config, 'image')
+ image_name = sxp.name(image)
+ image_handler = get_image_handler(image_name)
+ if image_handler is None:
+ raise VmError('unknown image type: ' + image_name)
+ image_handler(self, image)
+ deferred = self.configure()
+ except StandardError, ex:
+ # Catch errors, cleanup and re-raise.
+ self.destroy()
+ raise
+ def cbok(x):
+ print 'vm_create> cbok', x
+ return x
+ deferred.addCallback(cbok)
+ print 'vm_create<'
+ return deferred
+
+ def config_devices(self, name):
+ """Get a list of the 'device' nodes of a given type from the config.
+
+ name device type
+ return list of device configs
+ """
+ devices = []
+ for d in sxp.children(self.config, 'device'):
+ dev = sxp.child0(d)
+ if dev is None: continue
+ if name == sxp.name(dev):
+ devices.append(dev)
+ return devices
+
+ def add_device(self, type, dev):
+ """Add a device to a virtual machine.
+
+ dev device to add
+ """
+ dl = self.devices.get(type, [])
+ dl.append(dev)
+ self.devices[type] = dl
+
+ def get_devices(self, type):
+ val = self.devices.get(type, [])
+ return val
+
+ def get_device_by_id(self, type, id):
+ """Get the device with the given id.
+
+ id device id
+
+ returns device or None
+ """
+ dl = self.get_devices(type)
+ for d in dl:
+ if d.getprop('id') == id:
+ return d
+ return None
+
+ def get_device_by_index(self, type, idx):
+ """Get the device with the given index.
+
+ idx device index
+
+ returns device or None
+ """
+ dl = self.get_devices(type)
+ if 0 <= idx < len(dl):
+ return dl[idx]
+ else:
+ return None
+
+ def add_config(self, val):
+ """Add configuration data to a virtual machine.
+
+ val data to add
+ """
+ self.configs.append(val)
+
+ def destroy(self):
+ if self.dom <= 0:
+ return 0
+ return xc.domain_destroy(dom=self.dom)
+
+ def died(self):
+ print 'died>', self.dom
+ self.release_devices()
+
+ def release_devices(self):
+ print 'release_devices>', self.dom
+ self.release_vifs()
+ self.release_vbds()
+ self.devices = {}
+
+ def release_vifs(self):
+ print 'release_vifs>', self.dom
+ if self.dom is None: return
+ ctrl = xend.netif_get(self.dom)
+ if ctrl:
+ ctrl.destroy()
+
+ def release_vbds(self):
+ print 'release_vbds>', self.dom
+ if self.dom is None: return
+ ctrl = xend.blkif_get(self.dom)
+ if ctrl:
+ ctrl.destroy()
+
+ def show(self):
+ """Print virtual machine info.
+ """
+ print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
+ print "image:"
+ sxp.show(self.image)
+ print
+ for dl in self.devices:
+ for dev in dl:
+ print "device:"
+ sxp.show(dev)
+ print
+ for val in self.configs:
+ print "config:"
+ sxp.show(val)
+ print
+ print "]"
+
+ def init_domain(self):
+ """Initialize the domain memory.
+ """
+ if self.recreate: return
+ memory = self.memory
+ name = self.name
+ cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
+ print 'init_domain>', memory, name, cpu
+ dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
+ if dom <= 0:
+ raise VmError('Creating domain failed: name=%s memory=%d'
+ % (name, memory))
+ self.setdom(dom)
+
+ def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
+ """Build the domain boot image.
+ """
+ if self.recreate: return
+ if len(cmdline) >= 256:
+ print 'Warning: kernel cmdline too long'
+ dom = self.dom
+ buildfn = getattr(xc, '%s_build' % ostype)
+ print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk
+ flags = 0
+ if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
+ if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
+ err = buildfn(dom = dom,
+ image = kernel,
+ control_evtchn = self.console.port2,
+ cmdline = cmdline,
+ ramdisk = ramdisk,
+ flags = flags)
+ 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, vifs_n):
+ """Create a domain. Builds the image but does not configure it.
+
+ ostype OS type
+ kernel kernel image
+ ramdisk kernel ramdisk
+ cmdline kernel commandline
+ vifs_n number of network interfaces
+ """
+ print 'create_domain>', ostype, kernel
+ if not self.recreate:
+ 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)
+ print 'create-domain> init_domain...'
+ self.init_domain()
+ print 'create_domain>', 'dom=', self.dom
+ self.console = xendConsole.console_create(self.dom)
+ self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
+ self.image = kernel
+ self.ramdisk = ramdisk
+ self.cmdline = cmdline
+
+ def create_devices(self):
+ """Create the devices for a vm.
+
+ returns Deferred
+ raises VmError for invalid devices
+ """
+ print '>create_devices'
+ dlist = []
+ devices = sxp.children(self.config, 'device')
+ index = {}
+ for d in devices:
+ dev = sxp.child0(d)
+ if dev is None:
+ raise VmError('invalid device')
+ dev_name = sxp.name(dev)
+ dev_index = index.get(dev_name, 0)
+ dev_handler = get_device_handler(dev_name)
+ if dev_handler is None:
+ raise VmError('unknown device type: ' + dev_name)
+ v = dev_handler(self, dev, dev_index)
+ append_deferred(dlist, v)
+ index[dev_name] = dev_index + 1
+ deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
+ print '<create_devices'
+ return deferred
+
+ def configure_backends(self):
+ """Set configuration flags if the vm is a backend for netif of blkif.
+ """
+ for c in sxp.children(self.config, 'backend'):
+ name = sxp.name(sxp.child0(c))
+ if name == 'blkif':
+ self.blkif_backend = 1
+ elif name == 'netif':
+ self.netif_backend = 1
+ else:
+ raise VmError('invalid backend type:' + str(name))
+
+ def create_backends(self):
+ """Setup the netif and blkif backends.
+ """
+ if self.blkif_backend:
+ xend.blkif_set_control_domain(self.dom, recreate=self.recreate)
+ if self.netif_backend:
+ xend.netif_set_control_domain(self.dom, recreate=self.recreate)
+
+ def configure(self):
+ """Configure a vm.
+
+ vm virtual machine
+ config configuration
+
+ returns Deferred - calls callback with vm
+ """
+ if self.blkif_backend:
+ d = defer.Deferred()
+ d.callback(1)
+ else:
+ d = xend.blkif_create(self.dom, recreate=self.recreate)
+ d.addCallback(_vm_configure1, self)
+ return d
+
+ def dom_configure(self, dom):
+ """Configure a domain.
+
+ dom domain id
+ returns deferred
+ """
+ d = dom_get(dom)
+ if not d:
+ raise VMError("Domain not found: %d" % dom)
+ try:
+ self.setdom(dom)
+ self.name = d['name']
+ self.memory = d['memory']/1024
+ deferred = self.configure()
+ except StandardError, ex:
+ self.destroy()
+ raise
+ return deferred
+
+ def configure_fields(self):
+ dlist = []
+ index = {}
+ for field in sxp.children(self.config):
+ field_name = sxp.name(field)
+ field_index = index.get(field_name, 0)
+ field_handler = get_config_handler(field_name)
+ # Ignore unknown fields. Warn?
+ if field_handler:
+ v = field_handler(self, self.config, field, field_index)
+ append_deferred(dlist, v)
+ index[field_name] = field_index + 1
+ d = defer.DeferredList(dlist, fireOnOneErrback=1)
+ return d
+
+
+def vm_image_linux(vm, image):
+ """Create a VM for a linux 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", '')
+ vifs = vm.config_devices("vif")
+ vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
+ return vm
+
+def vm_image_netbsd(vm, image):
+ """Create a VM for a bsd image.
+
+ name vm name
+ memory vm memory
+ image image config
+
+ returns vm
+ """
+ #todo: Same as for linux. Is that right? If so can unify them.
+ 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")
+ vifs = vm.config_devices("vif")
+ vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
+ return vm
+
+
+def vm_dev_vif(vm, val, index):
+ """Create a virtual network interface (vif).
+
+ vm virtual machine
+ val vif config
+ index vif index
+ """
+ if vm.netif_backend:
+ raise VmError('vif: vif in netif backend domain')
+ vif = index #todo
+ vmac = sxp.child_value(val, "mac")
+ defer = make_vif(vm.dom, vif, vmac, vm.recreate)
+ def fn(id):
+ dev = xend.netif_dev(vm.dom, vif)
+ devid = sxp.attribute(val, 'id')
+ if devid:
+ dev.setprop('id', devid)
+ bridge = sxp.child_value(val, "bridge")
+ dev.up(bridge)
+ vm.add_device('vif', dev)
+ print 'vm_dev_vif> created', dev
+ return id
+ defer.addCallback(fn)
+ return defer
+
+def vm_dev_vbd(vm, val, index):
+ """Create a virtual block device (vbd).
+
+ vm virtual machine
+ val vbd config
+ index vbd index
+ """
+ if vm.blkif_backend:
+ raise VmError('vbd: vbd in blkif backend domain')
+ vdev = index
+ uname = sxp.child_value(val, 'uname')
+ if not uname:
+ raise VMError('vbd: Missing uname')
+ dev = sxp.child_value(val, 'dev')
+ if not dev:
+ raise VMError('vbd: Missing dev')
+ mode = sxp.child_value(val, 'mode', 'r')
+ defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
+ def fn(vbd):
+ dev = xend.blkif_dev(vm.dom, vdev)
+ vm.add_device('vbd', dev)
+ return vbd
+ defer.addCallback(fn)
+ return defer
+
+def parse_pci(val):
+ if isinstance(val, StringType):
+ radix = 10
+ if val.startswith('0x') or val.startswith('0X'):
+ radix = 16
+ v = int(val, radix)
+ else:
+ v = val
+ return v
+
+def vm_dev_pci(vm, val, index):
+ bus = sxp.child_value(val, 'bus')
+ if not bus:
+ raise VMError('pci: Missing bus')
+ dev = sxp.child_value(val, 'dev')
+ if not dev:
+ raise VMError('pci: Missing dev')
+ func = sxp.child_value(val, 'func')
+ if not func:
+ raise VMError('pci: Missing func')
+ try:
+ bus = parse_pci(bus)
+ dev = parse_pci(dev)
+ func = parse_pci(func)
+ except:
+ raise VMError('pci: invalid parameter')
+ rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
+ func=func, enable=1)
+ if rc < 0:
+ #todo non-fatal
+ raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
+ (bus, dev, func))
+ return rc
+
+
+def vm_field_vfr(vm, config, val, index):
+ """Handle a vfr field in a config.
+
+ vm virtual machine
+ config vm config
+ val vfr field
+ """
+ # Get the rules and add them.
+ # (vfr (vif (id foo) (ip x.x.x.x)) ... )
+ list = sxp.children(val, 'vif')
+ ipaddrs = []
+ for v in list:
+ id = sxp.child_value(v, 'id')
+ if id is None:
+ raise VmError('vfr: missing vif id')
+ id = int(id)
+ dev = vm.get_device_by_index('vif', id)
+ if not dev:
+ raise VmError('vfr: invalid vif id %d' % id)
+ vif = sxp.child_value(dev, 'vif')
+ ip = sxp.child_value(v, 'ip')
+ if not ip:
+ raise VmError('vfr: missing ip address')
+ ipaddrs.append(ip);
+ # todo: Configure the ipaddrs.
+ vm.ipaddrs = ipaddrs
+
+def vnet_bridge(vnet, vmac, dom, idx):
+ """Add the device for the vif to the bridge for its vnet.
+ """
+ vif = "vif%d.%d" % (dom, idx)
+ try:
+ cmd = "(vif.conn (vif %s) (vnet %s) (vmac %s))" % (vif, vnet, vmac)
+ print "*** vnet_bridge>", cmd
+ out = file("/proc/vnet/policy", "wb")
+ out.write(cmd)
+ err = out.close()
+ print "vnet_bridge>", "err=", err
+ except IOError, ex:
+ print "vnet_bridge>", ex
+
+def vm_field_vnet(vm, config, val, index):
+ """Handle a vnet field in a config.
+
+ vm virtual machine
+ config vm config
+ val vnet field
+ index index
+ """
+ # Get the vif children. For each vif look up the vif device
+ # with the given id and configure its vnet.
+ # (vnet (vif (id foo) (vnet 2) (mac x:x:x:x:x:x)) ... )
+ vif_vnets = sxp.children(val, 'vif')
+ for v in vif_vnets:
+ id = sxp.child_value(v, 'id')
+ if id is None:
+ raise VmError('vnet: missing vif id')
+ dev = vm.get_device_by_id('vif', id)
+ #vnet = sxp.child_value(v, 'vnet', 1)
+ #mac = sxp.child_value(dev, 'mac')
+ #vif = sxp.child_value(dev, 'vif')
+ #vnet_bridge(vnet, mac, vm.dom, 0)
+ #vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]])
+
+# Register image handlers for linux and bsd.
+add_image_handler('linux', vm_image_linux)
+add_image_handler('netbsd', vm_image_netbsd)
+
+# Register device handlers for vifs and vbds.
+add_device_handler('vif', vm_dev_vif)
+add_device_handler('vbd', vm_dev_vbd)
+add_device_handler('pci', vm_dev_pci)
+
+# Register config handlers for vfr and vnet.
+add_config_handler('vfr', vm_field_vfr)
+add_config_handler('vnet', vm_field_vnet)
diff --git a/tools/python/xen/xend/XendMigrate.py b/tools/python/xen/xend/XendMigrate.py
new file mode 100644
index 0000000000..1580ba83ed
--- /dev/null
+++ b/tools/python/xen/xend/XendMigrate.py
@@ -0,0 +1,103 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import sys
+import socket
+
+import sxp
+import XendDB
+import EventServer; eserver = EventServer.instance()
+
+class XendMigrateInfo:
+
+ # states: begin, active, failed, succeeded?
+
+ def __init__(self, id, dom, dst):
+ self.id = id
+ self.state = 'begin'
+ self.src_host = socket.gethostname()
+ self.src_dom = dom
+ self.dst_host = dst
+ self.dst_dom = None
+
+ def set_state(self, state):
+ self.state = state
+
+ def get_state(self):
+ return self.state
+
+ def sxpr(self):
+ sxpr = ['migrate', ['id', self.id], ['state', self.state] ]
+ sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
+ sxpr.append(sxpr_src)
+ sxpr_dst = ['dst', ['host', self.dst] ]
+ if self.dst_dom:
+ sxpr_dst.append(['domain', self.dst_dom])
+ sxpr.append(sxpr_dst)
+ return sxpr
+
+
+class XendMigrate:
+ # Represents migration in progress.
+ # Use log for indications of begin/end/errors?
+ # Need logging of: domain create/halt, migrate begin/end/fail
+ # Log via event server?
+
+ dbpath = "migrate"
+
+ def __init__(self):
+ self.db = XendDB.XendDB(self.dbpath)
+ self.migrate = {}
+ self.migrate_db = self.db.fetchall("")
+ self.id = 0
+
+ def nextid(self):
+ self.id += 1
+ return "%d" % self.id
+
+ def sync(self):
+ self.db.saveall("", self.migrate_db)
+
+ def sync_migrate(self, id):
+ self.db.save(id, self.migrate_db[id])
+
+ def close(self):
+ pass
+
+ def _add_migrate(self, id, info):
+ self.migrate[id] = info
+ self.migrate_db[id] = info.sxpr()
+ self.sync_migrate(id)
+ #eserver.inject('xend.migrate.begin', info.sxpr())
+
+ def _delete_migrate(self, id):
+ #eserver.inject('xend.migrate.end', id)
+ del self.migrate[id]
+ del self.migrate_db[id]
+ self.db.delete(id)
+
+ def migrate_ls(self):
+ return self.migrate.keys()
+
+ def migrates(self):
+ return self.migrate.values()
+
+ def migrate_get(self, id):
+ return self.migrate.get(id)
+
+ def migrate_begin(self, dom, dst):
+ # Check dom for existence, not migrating already.
+ # Create migrate info, tell xend to migrate it?
+ # - or fork migrate command ourselves?
+ # Subscribe to migrate notifications (for updating).
+ id = self.nextid()
+ info = XenMigrateInfo(id, dom, dst)
+ self._add_migrate(id, info)
+ return id
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendMigrate()
+ return inst
diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py
new file mode 100644
index 0000000000..7221785aff
--- /dev/null
+++ b/tools/python/xen/xend/XendNode.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Handler for node operations.
+ Has some persistent state:
+ - logs
+ - notification urls
+
+"""
+
+import os
+import xen.ext.xc
+
+class XendNode:
+
+ def __init__(self):
+ self.xc = xen.ext.xc.new()
+
+ def shutdown(self):
+ return 0
+
+ def reboot(self):
+ return 0
+
+ def notify(self, uri):
+ return 0
+
+ def cpu_bvt_slice_set(self, slice):
+ ret = 0
+ #ret = self.xc.bvtsched_global_set(ctx_allow=slice)
+ return ret
+
+ def cpu_bvt_slice_get(self, slice):
+ ret = 0
+ #ret = self.xc.bvtsched_global_get()
+ return ret
+
+ def cpu_rrobin_slice_set(self, slice):
+ ret = 0
+ #ret = self.xc.rrobin_global_set(slice)
+ return ret
+
+ def info(self):
+ return self.nodeinfo() + self.physinfo()
+
+ def nodeinfo(self):
+ (sys, host, rel, ver, mch) = os.uname()
+ return [['system', sys],
+ ['host', host],
+ ['release', rel],
+ ['version', ver],
+ ['machine', mch]]
+
+ def physinfo(self):
+ pinfo = self.xc.physinfo()
+ info = [['cores', pinfo['cores']],
+ ['hyperthreads_per_core', pinfo['ht_per_core']],
+ ['cpu_mhz', pinfo['cpu_khz']/1000],
+ ['memory', pinfo['total_pages']/256],
+ ['free_memory', pinfo['free_pages']/256]]
+ return info
+
+
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendNode()
+ return inst
+
diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py
new file mode 100644
index 0000000000..665f5df29e
--- /dev/null
+++ b/tools/python/xen/xend/XendRoot.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Xend root class.
+Creates the event server and handles configuration.
+"""
+
+import os
+import os.path
+import sys
+import EventServer
+
+# Initial create of the event server.
+eserver = EventServer.instance()
+
+import sxp
+
+def reboots():
+ """Get a list of system reboots from wtmp.
+ """
+ out = os.popen('/usr/bin/last reboot', 'r')
+ list = [ x.strip() for x in out if x.startswith('reboot') ]
+ return list
+
+def last_reboot():
+ """Get the last known system reboot.
+ """
+ l = reboots()
+ return (l and l[-1]) or None
+
+class XendRoot:
+ """Root of the management classes."""
+
+ lastboot_default = "/var/xen/lastboot"
+
+ """Default path to the root of the database."""
+ dbroot_default = "/var/xen/xend-db"
+
+ """Default path to the config file."""
+ config_default = "/etc/xen/xend-config.sxp"
+
+ """Environment variable used to override config_default."""
+ config_var = "XEND_CONFIG"
+
+ def __init__(self):
+ self.rebooted = 0
+ self.last_reboot = None
+ self.dbroot = None
+ self.config_path = None
+ self.config = None
+ self.configure()
+ self.check_lastboot()
+ eserver.subscribe('xend.*', self.event_handler)
+ #eserver.subscribe('xend.domain.created', self.event_handler)
+ #eserver.subscribe('xend.domain.died', self.event_handler)
+
+ def start(self):
+ eserver.inject('xend.start', self.rebooted)
+
+ def event_handler(self, event, val):
+ print >> sys.stderr, "EVENT>", event, val
+
+ def read_lastboot(self):
+ try:
+ val = file(self.lastboot, 'rb').readlines()[0]
+ except StandardError, ex:
+ print 'warning: Error reading', self.lastboot, ex
+ val = None
+ return val
+
+ def write_lastboot(self, val):
+ if not val: return
+ try:
+ fdir = os.path.dirname(self.lastboot)
+ if not os.path.isdir(fdir):
+ os.makedirs(fdir)
+ out = file(self.lastboot, 'wb+')
+ out.write(val)
+ out.close()
+ except IOError, ex:
+ print 'warning: Error writing', self.lastboot, ex
+ pass
+
+ def check_lastboot(self):
+ """Check if there has been a system reboot since we saved lastboot.
+ """
+ last_val = self.read_lastboot()
+ this_val = last_reboot()
+ if this_val == last_val:
+ self.rebooted = 0
+ else:
+ self.rebooted = 1
+ self.write_lastboot(this_val)
+ self.last_reboot = this_val
+
+ def get_last_reboot(self):
+ return self.last_reboot
+
+ def get_rebooted(self):
+ return self.rebooted
+
+ def configure(self):
+ self.set_config()
+ self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
+ self.lastboot = self.get_config_value("lastboot", self.lastboot_default)
+
+ 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.
+
+ The config file is a sequence of sxp forms.
+ """
+ self.config_path = os.getenv(self.config_var, self.config_default)
+ if os.path.exists(self.config_path):
+ fin = file(self.config_path, 'rb')
+ try:
+ config = sxp.parse(fin)
+ config.insert(0, 'config')
+ self.config = config
+ finally:
+ fin.close()
+ else:
+ self.config = ['config']
+
+ def get_config(self, name=None):
+ """Get the configuration element with the given name, or
+ the whole configuration if no name is given.
+
+ name element name (optional)
+ returns config or none
+ """
+ if name is None:
+ val = self.config
+ else:
+ val = sxp.child(self.config, name)
+ return val
+
+ def get_config_value(self, name, val=None):
+ """Get the value of an atomic configuration element.
+
+ name element name
+ val default value (optional, defaults to None)
+ returns value
+ """
+ return sxp.child_value(self.config, name, val=val)
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendRoot()
+ return inst
diff --git a/tools/python/xen/xend/XendVnet.py b/tools/python/xen/xend/XendVnet.py
new file mode 100644
index 0000000000..213408e111
--- /dev/null
+++ b/tools/python/xen/xend/XendVnet.py
@@ -0,0 +1,69 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Handler for vnet operations.
+"""
+
+import sxp
+import XendDB
+
+class XendVnet:
+ """Index of all vnets. Singleton.
+ """
+
+ dbpath = "vnet"
+
+ def __init__(self):
+ # Table of vnet info indexed by vnet id.
+ self.vnet = {}
+ self.db = XendDB.XendDB(self.dbpath)
+ self.vnet = self.db.fetchall("")
+
+ def vnet_ls(self):
+ """List all vnets.
+ """
+ return self.vnet.keys()
+
+ def vnets(self):
+ return self.vnet.values()
+
+ def vnet_get(self, id):
+ """Get a vnet.
+
+ id vnet id
+ """
+ return self.vnet.get(id)
+
+ def vnet_create(self, info):
+ """Create a vnet.
+
+ info config
+ """
+ self.vnet_configure(info)
+
+ def vnet_configure(self, info):
+ """Configure a vnet.
+ id vnet id
+ info config
+ """
+ # Need to configure for real.
+ # Only sync if succeeded - otherwise need to back out.
+ self.vnet[info.id] = info
+ self.db.save(info.id, info)
+
+ def vnet_delete(self, id):
+ """Delete a vnet.
+
+ id vnet id
+ """
+ # Need to delete for real. What if fails?
+ if id in self.vnet:
+ del self.vnet[id]
+ self.db.delete(id)
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = XendVnet()
+ return inst
diff --git a/tools/python/xen/xend/__init__.py b/tools/python/xen/xend/__init__.py
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/tools/python/xen/xend/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tools/python/xen/xend/encode.py b/tools/python/xen/xend/encode.py
new file mode 100644
index 0000000000..38c9351db7
--- /dev/null
+++ b/tools/python/xen/xend/encode.py
@@ -0,0 +1,165 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Encoding for arguments to HTTP calls.
+ Uses the url-encoding with MIME type 'application/x-www-form-urlencoded'
+ if the data does not include files. Otherwise it uses the encoding with
+ MIME type 'multipart/form-data'. See the HTML4 spec for details.
+
+ """
+import sys
+import types
+from StringIO import StringIO
+
+import urllib
+import httplib
+import random
+import md5
+
+# Extract from HTML4 spec.
+## The following example illustrates "multipart/form-data"
+## encoding. Suppose we have the following form:
+
+## <FORM action="http://server.com/cgi/handle"
+## enctype="multipart/form-data"
+## method="post">
+## <P>
+## What is your name? <INPUT type="text" name="submit-name"><BR>
+## What files are you sending? <INPUT type="file" name="files"><BR>
+## <INPUT type="submit" value="Send"> <INPUT type="reset">
+## </FORM>
+
+## If the user enters "Larry" in the text input, and selects the text
+## file "file1.txt", the user agent might send back the following data:
+
+## Content-Type: multipart/form-data; boundary=AaB03x
+
+## --AaB03x
+## Content-Disposition: form-data; name="submit-name"
+
+## Larry
+## --AaB03x
+## Content-Disposition: form-data; name="files"; filename="file1.txt"
+## Content-Type: text/plain
+
+## ... contents of file1.txt ...
+## --AaB03x--
+
+## If the user selected a second (image) file "file2.gif", the user agent
+## might construct the parts as follows:
+
+## Content-Type: multipart/form-data; boundary=AaB03x
+
+## --AaB03x
+## Content-Disposition: form-data; name="submit-name"
+
+## Larry
+## --AaB03x
+## Content-Disposition: form-data; name="files"
+## Content-Type: multipart/mixed; boundary=BbC04y
+
+## --BbC04y
+## Content-Disposition: file; filename="file1.txt"
+## Content-Type: text/plain
+
+## ... contents of file1.txt ...
+## --BbC04y
+## Content-Disposition: file; filename="file2.gif"
+## Content-Type: image/gif
+## Content-Transfer-Encoding: binary
+
+## ...contents of file2.gif...
+## --BbC04y--
+## --AaB03x--
+
+__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ]
+
+def data_values(d):
+ if isinstance(d, types.DictType):
+ return d.items()
+ else:
+ return d
+
+def encode_data(d):
+ """Encode some data for HTTP transport.
+ The encoding used is stored in 'Content-Type' in the headers.
+
+ d data - sequence of tuples or dictionary
+ returns a 2-tuple of the headers and the encoded data
+ """
+ val = ({}, None)
+ if d is None: return val
+ multipart = 0
+ for (k, v) in data_values(d):
+ if encode_isfile(v):
+ multipart = 1
+ break
+ if multipart:
+ val = encode_multipart(d)
+ else:
+ val = encode_form(d)
+ return val
+
+def encode_isfile(v):
+ if isinstance(v, types.FileType):
+ return 1
+ if hasattr(v, 'readlines'):
+ return 1
+ return 0
+
+def encode_multipart(d):
+ boundary = mime_boundary()
+ hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary }
+ out = StringIO()
+ for (k,v) in data_values(d):
+ out.write('--')
+ out.write(boundary)
+ out.write('\r\n')
+ if encode_isfile(v):
+ out.write('Content-Disposition: form-data; name="')
+ out.write(k)
+ if hasattr(v, 'name'):
+ out.write('"; filename="')
+ out.write(v.name)
+ out.write('"\r\n')
+ out.write('Content-Type: application/octet-stream\r\n')
+ out.write('\r\n')
+ for l in v.readlines():
+ out.write(l)
+ else:
+ out.write('Content-Disposition: form-data; name="')
+ out.write(k)
+ out.write('"\r\n')
+ out.write('\r\n')
+ out.write(str(v))
+ out.write('\r\n')
+ out.write('--')
+ out.write(boundary)
+ out.write('--')
+ out.write('\r\n')
+ return (hdr, out.getvalue())
+
+def mime_boundary():
+ random.seed()
+ m = md5.new()
+ for i in range(0, 10):
+ c = chr(random.randint(1, 255))
+ m.update(c)
+ b = m.hexdigest()
+ return b[0:16]
+
+def encode_form(d):
+ hdr = { 'Content-Type': 'application/x-www-form-urlencoded' }
+ val = urllib.urlencode(d)
+ return (hdr, val)
+
+def main():
+ #d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') }
+ #d = {'a': 1, 'b': 'x y' }
+ d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ]
+ #d = [ ('a', 1), ('b', 'x y')]
+ v = encode_data(d)
+ print v[0]
+ sys.stdout.write(v[1])
+ print
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/python/xen/xend/server/SrvBase.py b/tools/python/xen/xend/server/SrvBase.py
new file mode 100644
index 0000000000..bcff1bc3a0
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvBase.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import cgi
+
+import os
+import sys
+import types
+import StringIO
+
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.web import error
+from twisted.web import resource
+from twisted.web import server
+
+from xen.xend import sxp
+from xen.xend import PrettyPrint
+
+def uri_pathlist(p):
+ """Split a path into a list.
+ p path
+ return list of path elements
+ """
+ l = []
+ for x in p.split('/'):
+ if x == '': continue
+ l.append(x)
+ return l
+
+class SrvBase(resource.Resource):
+ """Base class for services.
+ """
+
+ def parse_form(self, req, method):
+ """Parse the data for a request, GET using the URL, POST using encoded data.
+ Posts should use enctype='multipart/form-data' in the <form> tag,
+ rather than 'application/x-www-form-urlencoded'. Only 'multipart/form-data'
+ handles file upload.
+
+ req request
+ returns a cgi.FieldStorage instance
+ """
+ env = {}
+ env['REQUEST_METHOD'] = method
+ if self.query:
+ env['QUERY_STRING'] = self.query
+ val = cgi.FieldStorage(fp=req.rfile, headers=req.headers, environ=env)
+ return val
+
+ def use_sxp(self, req):
+ """Determine whether to send an SXP response to a request.
+ Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept.
+
+ req request
+ returns 1 for SXP, 0 otherwise
+ """
+ ok = 0
+ user_agent = req.getHeader('User-Agent')
+ accept = req.getHeader('Accept')
+ if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0):
+ ok = 1
+ return ok
+
+ def get_op_method(self, op):
+ """Get the method for an operation.
+ For operation 'foo' looks for 'op_foo'.
+
+ op operation name
+ returns method or None
+ """
+ op_method_name = 'op_' + op
+ return getattr(self, op_method_name, None)
+
+ def perform(self, req):
+ """General operation handler for posted operations.
+ For operation 'foo' looks for a method op_foo and calls
+ it with op_foo(op, req). Replies with code 500 if op_foo
+ is not found.
+
+ The method must return a list when req.use_sxp is true
+ and an HTML string otherwise (or list).
+ Methods may also return a Deferred (for incomplete processing).
+
+ req request
+ """
+ op = req.args.get('op')
+ if op is None or len(op) != 1:
+ req.setResponseCode(404, "Invalid")
+ return ''
+ op = op[0]
+ op_method = self.get_op_method(op)
+ if op_method is None:
+ req.setResponseCode(501, "Not implemented")
+ req.setHeader("Content-Type", "text/plain")
+ req.write("Not implemented: " + op)
+ return ''
+ else:
+ val = op_method(op, req)
+ if isinstance(val, defer.Deferred):
+ val.addCallback(self._cb_perform, req, 1)
+ return server.NOT_DONE_YET
+ else:
+ self._cb_perform(val, req, 0)
+ return ''
+
+ def _cb_perform(self, val, req, dfr):
+ """Callback to complete the request.
+ May be called from a Deferred.
+ """
+ if isinstance(val, error.ErrorPage):
+ req.write(val.render(req))
+ elif self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ sxp.show(val, req)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ if isinstance(val, types.ListType):
+ req.write('<code><pre>')
+ PrettyPrint.prettyprint(val, out=req)
+ req.write('</pre></code>')
+ else:
+ req.write(str(val))
+ req.write('</body></html>')
+ if dfr:
+ req.finish()
+
+ def print_path(self, req):
+ """Print the path with hyperlinks.
+ """
+ pathlist = [x for x in req.prepath if x != '' ]
+ s = "/"
+ req.write('<h1><a href="/">/</a>')
+ for x in pathlist:
+ s += x + "/"
+ req.write(' <a href="%s">%s</a>/' % (s, x))
+ req.write("</h1>")
diff --git a/tools/python/xen/xend/server/SrvConsole.py b/tools/python/xen/xend/server/SrvConsole.py
new file mode 100644
index 0000000000..59d0e5f11c
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvConsole.py
@@ -0,0 +1,42 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from xen.xend import sxp
+from xen.xend import XendConsole
+from SrvDir import SrvDir
+
+class SrvConsole(SrvDir):
+ """An individual console.
+ """
+
+ def __init__(self, info):
+ SrvDir.__init__(self)
+ self.info = info
+ self.xc = XendConsole.instance()
+
+ def op_disconnect(self, op, req):
+ val = self.xc.console_disconnect(self.info.id)
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def render_GET(self, req):
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ sxp.show(self.info.sxpr(), out=req)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ #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.dom2))
+ self.form(req)
+ req.write('</body></html>')
+ return ''
+
+ def form(self, req):
+ req.write('<form method="post" action="%s">' % req.prePathURL())
+ if self.info.connection():
+ req.write('<input type="submit" name="op" value="disconnect">')
+ req.write('</form>')
diff --git a/tools/python/xen/xend/server/SrvConsoleDir.py b/tools/python/xen/xend/server/SrvConsoleDir.py
new file mode 100644
index 0000000000..814b448370
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvConsoleDir.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from SrvDir import SrvDir
+from SrvConsole import SrvConsole
+from xen.xend import XendConsole
+from xen.xend import sxp
+
+class SrvConsoleDir(SrvDir):
+ """Console directory.
+ """
+
+ def __init__(self):
+ SrvDir.__init__(self)
+ self.xconsole = XendConsole.instance()
+
+ def console(self, x):
+ val = None
+ try:
+ info = self.xconsole.console_get(x)
+ val = SrvConsole(info)
+ except KeyError, ex:
+ print 'SrvConsoleDir>', ex
+ pass
+ return val
+
+ def get(self, x):
+ v = SrvDir.get(self, x)
+ if v is not None:
+ return v
+ v = self.console(x)
+ return v
+
+ def render_GET(self, req):
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ self.ls_console(req, 1)
+ else:
+ req.write("<html><head></head><body>")
+ self.print_path(req)
+ self.ls(req)
+ self.ls_console(req)
+ #self.form(req.wfile)
+ req.write("</body></html>")
+ return ''
+
+ def ls_console(self, req, use_sxp=0):
+ url = req.prePathURL()
+ if not url.endswith('/'):
+ url += '/'
+ if use_sxp:
+ consoles = self.xconsole.console_ls()
+ sxp.show(consoles, out=req)
+ else:
+ consoles = self.xconsole.consoles()
+ consoles.sort(lambda x, y: cmp(x.id, y.id))
+ req.write('<ul>')
+ for c in consoles:
+ req.write('<li><a href="%s%s"> %s</a></li>' % (url, c.id, c))
+ req.write('</ul>')
diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py
new file mode 100644
index 0000000000..c8284dc485
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvDaemon.py
@@ -0,0 +1,751 @@
+###########################################################
+## Xen controller daemon
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
+###########################################################
+
+import os
+import os.path
+import signal
+import sys
+import threading
+import linecache
+import socket
+import pwd
+import re
+import StringIO
+
+from twisted.internet import pollreactor
+pollreactor.install()
+
+from twisted.internet import reactor
+from twisted.internet import protocol
+from twisted.internet import abstract
+from twisted.internet import defer
+
+from xen.ext import xu
+
+from xen.xend import sxp
+from xen.xend import PrettyPrint
+from xen.xend import EventServer
+eserver = EventServer.instance()
+
+from xen.xend.server import SrvServer
+
+import channel
+import blkif
+import netif
+import console
+import domain
+from params import *
+
+DEBUG = 1
+
+class MgmtProtocol(protocol.DatagramProtocol):
+ """Handler for the management socket (unix-domain).
+ """
+
+ def __init__(self, daemon):
+ #protocol.DatagramProtocol.__init__(self)
+ self.daemon = daemon
+
+ def write(self, data, addr):
+ return self.transport.write(data, addr)
+
+ def datagramReceived(self, data, addr):
+ if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data
+ io = StringIO.StringIO(data)
+ try:
+ vals = sxp.parse(io)
+ res = self.dispatch(vals[0])
+ self.send_result(addr, res)
+ except SystemExit:
+ raise
+ except:
+ if DEBUG:
+ raise
+ else:
+ self.send_error(addr)
+
+ def send_reply(self, addr, sxpr):
+ io = StringIO.StringIO()
+ sxp.show(sxpr, out=io)
+ io.seek(0)
+ self.write(io.getvalue(), addr)
+
+ def send_result(self, addr, res):
+
+ def fn(res, self=self, addr=addr):
+ self.send_reply(addr, ['ok', res])
+
+ if isinstance(res, defer.Deferred):
+ res.addCallback(fn)
+ else:
+ fn(res)
+
+ def send_error(self, addr):
+ (extype, exval) = sys.exc_info()[:2]
+ self.send_reply(addr, ['err',
+ ['type', str(extype) ],
+ ['value', str(exval) ] ] )
+
+ def opname(self, name):
+ """Get the name of the method for an operation.
+ """
+ return 'op_' + name.replace('.', '_')
+
+ def operror(self, name, v):
+ """Default operation handler - signals an error.
+ """
+ raise NotImplementedError('Invalid operation: ' +name)
+
+ def dispatch(self, req):
+ """Dispatch a request to its handler.
+ """
+ op_name = sxp.name(req)
+ op_method_name = self.opname(op_name)
+ op_method = getattr(self, op_method_name, self.operror)
+ return op_method(op_name, req)
+
+ def op_console_create(self, name, req):
+ """Create a new control interface - console for a domain.
+ """
+ print name, req
+ dom = sxp.child_value(req, 'domain')
+ if not dom: raise ValueError('Missing domain')
+ dom = int(dom)
+ console_port = sxp.child_value(req, 'console_port')
+ if console_port:
+ console_port = int(console_port)
+ resp = self.daemon.console_create(dom, console_port)
+ print name, resp
+ return resp
+
+ def op_consoles(self, name, req):
+ """Get a list of the consoles.
+ """
+ return self.daemon.consoles()
+
+ def op_console_disconnect(self, name, req):
+ id = sxp.child_value(req, 'id')
+ if not id:
+ raise ValueError('Missing console id')
+ id = int(id)
+ console = self.daemon.get_console(id)
+ if not console:
+ raise ValueError('Invalid console id')
+ if console.conn:
+ console.conn.loseConnection()
+ return ['ok']
+
+ def op_blkifs(self, name, req):
+ pass
+
+ def op_blkif_devs(self, name, req):
+ pass
+
+ def op_blkif_create(self, name, req):
+ pass
+
+ def op_blkif_dev_create(self, name, req):
+ pass
+
+ def op_netifs(self, name, req):
+ pass
+
+ def op_netif_devs(self, name, req):
+ pass
+
+ def op_netif_create(self, name, req):
+ pass
+
+ def op_netif_dev_create(self, name, req):
+ pass
+
+class NotifierProtocol(protocol.Protocol):
+ """Asynchronous handler for i/o on the notifier (event channel).
+ """
+
+ def __init__(self, channelFactory):
+ self.channelFactory = channelFactory
+
+ def notificationReceived(self, idx, type):
+ #print 'NotifierProtocol>notificationReceived>', idx, type
+ channel = self.channelFactory.getChannel(idx)
+ if not channel:
+ return
+ #print 'NotifierProtocol>notificationReceived> channel', channel
+ channel.notificationReceived(type)
+
+ def connectionLost(self, reason=None):
+ pass
+
+ def doStart(self):
+ pass
+
+ def doStop(self):
+ pass
+
+ def startProtocol(self):
+ pass
+
+ def stopProtocol(self):
+ pass
+
+class NotifierPort(abstract.FileDescriptor):
+ """Transport class for the event channel.
+ """
+
+ def __init__(self, daemon, notifier, proto, reactor=None):
+ assert isinstance(proto, NotifierProtocol)
+ abstract.FileDescriptor.__init__(self, reactor)
+ self.daemon = daemon
+ self.notifier = notifier
+ self.protocol = proto
+
+ def startListening(self):
+ self._bindNotifier()
+ self._connectToProtocol()
+
+ def stopListening(self):
+ if self.connected:
+ result = self.d = defer.Deferred()
+ else:
+ result = None
+ self.loseConnection()
+ return result
+
+ def fileno(self):
+ return self.notifier.fileno()
+
+ def _bindNotifier(self):
+ self.connected = 1
+
+ def _connectToProtocol(self):
+ self.protocol.makeConnection(self)
+ self.startReading()
+
+ def loseConnection(self):
+ if self.connected:
+ self.stopReading()
+ self.disconnecting = 1
+ reactor.callLater(0, self.connectionLost)
+
+ def connectionLost(self, reason=None):
+ abstract.FileDescriptor.connectionLost(self, reason)
+ if hasattr(self, 'protocol'):
+ self.protocol.doStop()
+ self.connected = 0
+ #self.notifier.close() # Not implemented.
+ os.close(self.fileno())
+ del self.notifier
+ if hasattr(self, 'd'):
+ self.d.callback(None)
+ del self.d
+
+ def doRead(self):
+ #print 'NotifierPort>doRead>', self
+ count = 0
+ while 1:
+ #print 'NotifierPort>doRead>', count
+ notification = self.notifier.read()
+ if not notification:
+ break
+ (idx, type) = notification
+ self.protocol.notificationReceived(idx, type)
+ self.notifier.unmask(idx)
+ count += 1
+ #print 'NotifierPort>doRead<'
+
+class EventProtocol(protocol.Protocol):
+ """Asynchronous handler for a connected event socket.
+ """
+
+ def __init__(self, daemon):
+ #protocol.Protocol.__init__(self)
+ self.daemon = daemon
+ # Event queue.
+ self.queue = []
+ # Subscribed events.
+ self.events = []
+ self.parser = sxp.Parser()
+ self.pretty = 0
+
+ # For debugging subscribe to everything and make output pretty.
+ self.subscribe(['*'])
+ self.pretty = 1
+
+ def dataReceived(self, data):
+ try:
+ self.parser.input(data)
+ if self.parser.ready():
+ val = self.parser.get_val()
+ res = self.dispatch(val)
+ self.send_result(res)
+ if self.parser.at_eof():
+ self.loseConnection()
+ except SystemExit:
+ raise
+ except:
+ if DEBUG:
+ raise
+ else:
+ self.send_error()
+
+ def loseConnection(self):
+ if self.transport:
+ self.transport.loseConnection()
+ if self.connected:
+ reactor.callLater(0, self.connectionLost)
+
+ def connectionLost(self, reason=None):
+ self.unsubscribe()
+
+ def send_reply(self, sxpr):
+ io = StringIO.StringIO()
+ if self.pretty:
+ PrettyPrint.prettyprint(sxpr, out=io)
+ else:
+ sxp.show(sxpr, out=io)
+ print >> io
+ io.seek(0)
+ return self.transport.write(io.getvalue())
+
+ def send_result(self, res):
+ return self.send_reply(['ok', res])
+
+ def send_error(self):
+ (extype, exval) = sys.exc_info()[:2]
+ return self.send_reply(['err',
+ ['type', str(extype)],
+ ['value', str(exval)]])
+
+ def send_event(self, val):
+ return self.send_reply(['event', val[0], val[1]])
+
+ def unsubscribe(self):
+ for event in self.events:
+ eserver.unsubscribe(event, self.queue_event)
+
+ def subscribe(self, events):
+ self.unsubscribe()
+ for event in events:
+ eserver.subscribe(event, self.queue_event)
+ self.events = events
+
+ def queue_event(self, name, v):
+ # Despite the name we dont' queue the event here.
+ # We send it because the transport will queue it.
+ self.send_event([name, v])
+
+ def opname(self, name):
+ return 'op_' + name.replace('.', '_')
+
+ def operror(self, name, req):
+ raise NotImplementedError('Invalid operation: ' +name)
+
+ def dispatch(self, req):
+ op_name = sxp.name(req)
+ op_method_name = self.opname(op_name)
+ op_method = getattr(self, op_method_name, self.operror)
+ return op_method(op_name, req)
+
+ def op_help(self, name, req):
+ def nameop(x):
+ if x.startswith('op_'):
+ return x[3:].replace('_', '.')
+ else:
+ return x
+
+ l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
+ return l
+
+ def op_quit(self, name, req):
+ self.loseConnection()
+
+ def op_exit(self, name, req):
+ sys.exit(0)
+
+ def op_pretty(self, name, req):
+ self.pretty = 1
+ return ['ok']
+
+ def op_console_disconnect(self, name, req):
+ id = sxp.child_value(req, 'id')
+ if not id:
+ raise ValueError('Missing console id')
+ self.daemon.console_disconnect(id)
+ return ['ok']
+
+ def op_info(self, name, req):
+ val = ['info']
+ val += self.daemon.consoles()
+ val += self.daemon.blkifs()
+ val += self.daemon.netifs()
+ return val
+
+ def op_sys_subscribe(self, name, v):
+ # (sys.subscribe event*)
+ # Subscribe to the events:
+ self.subscribe(v[1:])
+ return ['ok']
+
+ def op_sys_inject(self, name, v):
+ # (sys.inject event)
+ event = v[1]
+ eserver.inject(sxp.name(event), event)
+ return ['ok']
+
+
+class EventFactory(protocol.Factory):
+ """Asynchronous handler for the event server socket.
+ """
+ protocol = EventProtocol
+ service = None
+
+ def __init__(self, daemon):
+ #protocol.Factory.__init__(self)
+ self.daemon = daemon
+
+ def buildProtocol(self, addr):
+ proto = self.protocol(self.daemon)
+ proto.factory = self
+ return proto
+
+class VirqClient:
+ def __init__(self, daemon):
+ self.daemon = daemon
+
+ def virqReceived(self, virq):
+ print 'VirqClient.virqReceived>', virq
+ eserver.inject('xend.virq', virq)
+
+ def lostChannel(self, channel):
+ print 'VirqClient.lostChannel>', channel
+
+class Daemon:
+ """The xend daemon.
+ """
+ def __init__(self):
+ self.shutdown = 0
+
+ def daemon_pids(self):
+ pids = []
+ pidex = '(?P<pid>\d+)'
+ pythonex = '(?P<python>\S*python\S*)'
+ cmdex = '(?P<cmd>.*)'
+ procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$')
+ xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$')
+ procs = os.popen('ps -e -o pid,args 2>/dev/null')
+ for proc in procs:
+ pm = procre.match(proc)
+ if not pm: continue
+ xm = xendre.match(pm.group('cmd'))
+ if not xm: continue
+ #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd')
+ pids.append(int(pm.group('pid')))
+ return pids
+
+ def new_cleanup(self, kill=0):
+ err = 0
+ pids = self.daemon_pids()
+ if kill:
+ for pid in pids:
+ print "Killing daemon pid=%d" % pid
+ os.kill(pid, signal.SIGHUP)
+ elif pids:
+ err = 1
+ print "Daemon already running: ", pids
+ return err
+
+ def cleanup(self, kill=False):
+ # No cleanup to do if PID_FILE is empty.
+ if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE):
+ return 0
+ # Read the pid of the previous invocation and search active process list.
+ pid = open(PID_FILE, 'r').read()
+ lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+ for line in lines:
+ if re.search('^ *' + pid + '.+xend', line):
+ if not kill:
+ print "Daemon is already running (pid %d)" % int(pid)
+ return 1
+ # Old daemon is still active: terminate it.
+ os.kill(int(pid), 1)
+ # Delete the stale PID_FILE.
+ os.remove(PID_FILE)
+ return 0
+
+ def install_child_reaper(self):
+ #signal.signal(signal.SIGCHLD, self.onSIGCHLD)
+ # Ensure that zombie children are automatically reaped.
+ xu.autoreap()
+
+ def onSIGCHLD(self, signum, frame):
+ code = 1
+ while code > 0:
+ code = os.waitpid(-1, os.WNOHANG)
+
+ def start(self,trace=0):
+ if self.cleanup(kill=False):
+ return 1
+
+ # Detach from TTY.
+ if not DEBUG:
+ os.setsid()
+
+ if self.set_user():
+ return 1
+
+ self.install_child_reaper()
+
+ # Fork -- parent writes PID_FILE and exits.
+ pid = os.fork()
+ if pid:
+ # Parent
+ pidfile = open(PID_FILE, 'w')
+ pidfile.write(str(pid))
+ pidfile.close()
+ return 0
+ # Child
+ logfile = self.open_logfile()
+ self.redirect_output(logfile)
+ if trace:
+ self.tracefile = open('/var/log/xend.trace', 'w+', 1)
+ self.traceindent = 0
+ sys.settrace(self.trace)
+ try:
+ threading.settrace(self.trace) # Only in Python >= 2.3
+ except:
+ pass
+ self.run()
+ return 0
+
+ def print_trace(self,str):
+ for i in range(self.traceindent):
+ self.tracefile.write(" ")
+ self.tracefile.write(str)
+
+ def trace(self, frame, event, arg):
+ if event == 'call':
+ code = frame.f_code
+ filename = code.co_filename
+ m = re.search('.*xenmgr/(.*)', code.co_filename)
+ if not m:
+ return None
+ modulename = m.group(1)
+ if re.search('sxp.py', modulename):
+ return None
+ self.traceindent += 1
+ self.print_trace("++++ %s:%s\n"
+ % (modulename, code.co_name))
+ elif event == 'line':
+ filename = frame.f_code.co_filename
+ lineno = frame.f_lineno
+ self.print_trace("%4d %s" %
+ (lineno, linecache.getline(filename, lineno)))
+ elif event == 'return':
+ code = frame.f_code
+ filename = code.co_filename
+ m = re.search('.*xenmgr/(.*)', code.co_filename)
+ if not m:
+ return None
+ modulename = m.group(1)
+ self.print_trace("---- %s:%s\n"
+ % (modulename, code.co_name))
+ self.traceindent -= 1
+ elif event == 'exception':
+ pass
+ return self.trace
+
+ def open_logfile(self):
+ if not os.path.exists(CONTROL_DIR):
+ os.makedirs(CONTROL_DIR)
+
+ # Open log file. Truncate it if non-empty, and request line buffering.
+ if os.path.isfile(LOG_FILE):
+ os.rename(LOG_FILE, LOG_FILE+'.old')
+ logfile = open(LOG_FILE, 'w+', 1)
+ return logfile
+
+ def set_user(self):
+ # Set the UID.
+ try:
+ os.setuid(pwd.getpwnam(USER)[2])
+ return 0
+ except KeyError, error:
+ print "Error: no such user '%s'" % USER
+ return 1
+
+ def redirect_output(self, logfile):
+ if DEBUG: return
+ # Close down standard file handles
+ try:
+ os.close(0) # stdin
+ os.close(1) # stdout
+ os.close(2) # stderr
+ except:
+ pass
+ # Redirect output to log file.
+ sys.stdout = sys.stderr = logfile
+
+ def stop(self):
+ return self.cleanup(kill=True)
+
+ def run(self):
+ self.createFactories()
+ self.listenMgmt()
+ self.listenEvent()
+ self.listenNotifier()
+ self.listenVirq()
+ SrvServer.create(bridge=1)
+ reactor.run()
+
+ def createFactories(self):
+ self.channelF = channel.channelFactory()
+ self.domainCF = domain.DomainControllerFactory()
+ self.blkifCF = blkif.BlkifControllerFactory()
+ self.netifCF = netif.NetifControllerFactory()
+ self.consoleCF = console.ConsoleControllerFactory()
+
+ def listenMgmt(self):
+ protocol = MgmtProtocol(self)
+ s = os.path.join(CONTROL_DIR, MGMT_SOCK)
+ if os.path.exists(s):
+ os.unlink(s)
+ return reactor.listenUNIXDatagram(s, protocol)
+
+ def listenEvent(self):
+ protocol = EventFactory(self)
+ return reactor.listenTCP(EVENT_PORT, protocol)
+
+ def listenNotifier(self):
+ protocol = NotifierProtocol(self.channelF)
+ p = NotifierPort(self, self.channelF.notifier, protocol, reactor)
+ p.startListening()
+ return p
+
+ def listenVirq(self):
+ virqChan = self.channelF.virqChannel(channel.VIRQ_DOM_EXC)
+ virqChan.registerClient(VirqClient(self))
+
+ def exit(self):
+ reactor.diconnectAll()
+ sys.exit(0)
+
+ def blkif_set_control_domain(self, dom, recreate=0):
+ """Set the block device backend control domain.
+ """
+ return self.blkifCF.setControlDomain(dom, recreate=recreate)
+
+ def blkif_get_control_domain(self, dom):
+ """Get the block device backend control domain.
+ """
+ return self.blkifCF.getControlDomain()
+
+ def blkif_create(self, dom, recreate=0):
+ """Create a block device interface controller.
+
+ Returns Deferred
+ """
+ d = self.blkifCF.createInstance(dom, recreate=recreate)
+ return d
+
+ def blkifs(self):
+ return [ x.sxpr() for x in self.blkifCF.getInstances() ]
+
+ def blkif_get(self, dom):
+ return self.blkifCF.getInstanceByDom(dom)
+
+ def blkif_dev(self, dom, vdev):
+ return self.blkifCF.getDomainDevice(dom, vdev)
+
+ def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0):
+ """Create a block device.
+
+ Returns Deferred
+ """
+ ctrl = self.blkifCF.getInstanceByDom(dom)
+ if not ctrl:
+ raise ValueError('No blkif controller: %d' % dom)
+ print 'blkif_dev_create>', dom, vdev, mode, segment
+ d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate)
+ return d
+
+ def netif_set_control_domain(self, dom, recreate=0):
+ """Set the network interface backend control domain.
+ """
+ return self.netifCF.setControlDomain(dom, recreate=recreate)
+
+ def netif_get_control_domain(self, dom):
+ """Get the network interface backend control domain.
+ """
+ return self.netifCF.getControlDomain()
+
+ def netif_create(self, dom, recreate=0):
+ """Create a network interface controller.
+
+ """
+ return self.netifCF.createInstance(dom, recreate=recreate)
+
+ def netifs(self):
+ return [ x.sxpr() for x in self.netifCF.getInstances() ]
+
+ def netif_get(self, dom):
+ return self.netifCF.getInstanceByDom(dom)
+
+ def netif_dev_create(self, dom, vif, vmac, recreate=0):
+ """Create a network device.
+
+ todo
+ """
+ ctrl = self.netifCF.getInstanceByDom(dom)
+ if not ctrl:
+ raise ValueError('No netif controller: %d' % dom)
+ d = ctrl.attachDevice(vif, vmac, recreate=recreate)
+ return d
+
+ def netif_dev(self, dom, vif):
+ return self.netifCF.getDomainDevice(dom, vif)
+
+ def console_create(self, dom, console_port=None):
+ """Create a console for a domain.
+ """
+ console = self.consoleCF.getInstanceByDom(dom)
+ if console is None:
+ console = self.consoleCF.createInstance(dom, console_port)
+ return console.sxpr()
+
+ def consoles(self):
+ return [ c.sxpr() for c in self.consoleCF.getInstances() ]
+
+ def get_console(self, id):
+ return self.consoleCF.getInstance(id)
+
+ def get_domain_console(self, dom):
+ return self.consoleCF.getInstanceByDom(dom)
+
+ def console_disconnect(self, id):
+ """Disconnect any connected console client.
+ """
+ console = self.get_console(id)
+ if not console:
+ raise ValueError('Invalid console id')
+ console.disconnect()
+
+ def domain_shutdown(self, dom, reason):
+ """Shutdown a domain.
+ """
+ ctrl = self.domainCF.getInstanceByDom(dom)
+ if not ctrl:
+ raise ValueError('No domain controller: %d' % dom)
+ ctrl.shutdown(reason)
+ return 0
+
+def instance():
+ global inst
+ try:
+ inst
+ except:
+ inst = Daemon()
+ return inst
diff --git a/tools/python/xen/xend/server/SrvDeviceDir.py b/tools/python/xen/xend/server/SrvDeviceDir.py
new file mode 100644
index 0000000000..52f428540d
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvDeviceDir.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from SrvDir import SrvDir
+
+class SrvDeviceDir(SrvDir):
+ """Device directory.
+ """
+
+ pass
diff --git a/tools/python/xen/xend/server/SrvDir.py b/tools/python/xen/xend/server/SrvDir.py
new file mode 100644
index 0000000000..c49c0b36ba
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvDir.py
@@ -0,0 +1,91 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from twisted.web import error
+from xen.xend import sxp
+from SrvBase import SrvBase
+
+class SrvConstructor:
+ """Delayed constructor for sub-servers.
+ Does not import the sub-server class or create the object until needed.
+ """
+
+ def __init__(self, klass):
+ """Create a constructor. It is assumed that the class
+ should be imported as 'import klass from klass'.
+
+ klass name of its class
+ """
+ self.klass = klass
+ self.obj = None
+
+ def getobj(self):
+ """Get the sub-server object, importing its class and instantiating it if
+ necessary.
+ """
+ if not self.obj:
+ exec 'from %s import %s' % (self.klass, self.klass)
+ klassobj = eval(self.klass)
+ self.obj = klassobj()
+ return self.obj
+
+class SrvDir(SrvBase):
+ """Base class for directory servlets.
+ """
+ isLeaf = False
+
+ def __init__(self):
+ SrvBase.__init__(self)
+ self.table = {}
+ self.order = []
+
+ def getChild(self, x, req):
+ if x == '': return self
+ val = self.get(x)
+ if val is None:
+ return error.NoResource('Not found')
+ else:
+ return val
+
+ def get(self, x):
+ val = self.table.get(x)
+ if val is not None:
+ val = val.getobj()
+ return val
+
+ def add(self, x, xclass = None):
+ if xclass is None:
+ xclass = 'SrvDir'
+ self.table[x] = SrvConstructor(xclass)
+ self.order.append(x)
+
+ def render_GET(self, 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()
+ if not url.endswith('/'):
+ url += '/'
+ if use_sxp:
+ req.write('(ls ')
+ for k in self.order:
+ req.write(' ' + k)
+ req.write(')')
+ else:
+ req.write('<ul>')
+ for k in self.order:
+ v = self.get(k)
+ req.write('<li><a href="%s%s">%s</a></li>'
+ % (url, k, k))
+ req.write('</ul>')
+
+ def form(self, req):
+ pass
diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py
new file mode 100644
index 0000000000..156198bd70
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvDomain.py
@@ -0,0 +1,195 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from xen.xend import sxp
+from xen.xend import XendDomain
+from xen.xend import XendConsole
+from xen.xend import PrettyPrint
+from xen.xend.Args import FormFn
+
+from SrvDir import SrvDir
+
+class SrvDomain(SrvDir):
+ """Service managing a single domain.
+ """
+
+ def __init__(self, dom):
+ SrvDir.__init__(self)
+ self.dom = dom
+ self.xd = XendDomain.instance()
+ self.xconsole = XendConsole.instance()
+
+ def op_unpause(self, op, req):
+ val = self.xd.domain_unpause(self.dom.id)
+ return val
+
+ def op_pause(self, op, req):
+ val = self.xd.domain_pause(self.dom.id)
+ return val
+
+ def op_shutdown(self, op, req):
+ val = self.xd.domain_shutdown(self.dom.id)
+ req.setResponseCode(202)
+ req.setHeader("Location", "%s/.." % req.prePathURL())
+ return val
+
+ def op_destroy(self, op, req):
+ val = self.xd.domain_destroy(self.dom.id)
+ req.setHeader("Location", "%s/.." % req.prePathURL())
+ return val
+
+ def op_save(self, op, req):
+ fn = FormFn(self.xd.domain_save,
+ [['dom', 'int'],
+ ['file', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_migrate(self, op, req):
+ fn = FormFn(self.xd.domain_migrate,
+ [['dom', 'int'],
+ ['destination', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ val = 0 # Some migrate id.
+ req.setResponseCode(202)
+ #req.send_header("Location", "%s/.." % self.path) # Some migrate url.
+ return val
+
+ def op_pincpu(self, op, req):
+ fn = FormFn(self.xd.domain_pincpu,
+ [['dom', 'int'],
+ ['cpu', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_cpu_bvt_set(self, op, req):
+ fn = FormFn(self.xd.domain_cpu_bvt_set,
+ [['dom', 'int'],
+ ['mcuadv', 'int'],
+ ['warp', 'int'],
+ ['warpl', 'int'],
+ ['warpu', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_cpu_atropos_set(self, op, req):
+ fn = FormFn(self.xd.domain_cpu_atropos_set,
+ [['dom', 'int'],
+ ['period', 'int'],
+ ['slice', 'int'],
+ ['latency', 'int'],
+ ['xtratime', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vifs(self, op, req):
+ return self.xd.domain_vif_ls(self.dom.id)
+
+ def op_vif(self, op, req):
+ fn = FormFn(self.xd.domain_vif_get,
+ [['dom', 'int'],
+ ['vif', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vif_stats(self, op, req):
+ #todo
+ fn = FormFn(self.xd.domain_vif_stats,
+ [['dom', 'int'],
+ ['vif', 'int']])
+ #val = fn(req.args, {'dom': self.dom.id})
+ val = 999
+ #return val
+ return val
+
+ def op_vif_ip_add(self, op, req):
+ fn = FormFn(self.xd.domain_vif_ip_add,
+ [['dom', 'int'],
+ ['vif', 'int'],
+ ['ip', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vif_scheduler_set(self, op, req):
+ fn = FormFn(self.xd.domain_vif_scheduler_set,
+ [['dom', 'int'],
+ ['vif', 'int'],
+ ['bytes', 'int'],
+ ['usecs', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vif_scheduler_get(self, op, req):
+ fn = FormFn(self.xd.domain_vif_scheduler_set,
+ [['dom', 'int'],
+ ['vif', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vbds(self, op, req):
+ return self.xd.domain_vbd_ls(self.dom.id)
+
+ def op_vbd(self, op, req):
+ fn = FormFn(self.xd.domain_vbd_get,
+ [['dom', 'int'],
+ ['vbd', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vbd_add(self, op, req):
+ fn = FormFn(self.xd.domain_vbd_add,
+ [['dom', 'int'],
+ ['uname', 'str'],
+ ['dev', 'str'],
+ ['mode', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vbd_remove(self, op, req):
+ fn = FormFn(self.xd.domain_vbd_remove,
+ [['dom', 'int'],
+ ['dev', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def render_GET(self, req):
+ op = req.args.get('op')
+ if op and op[0] in ['vifs', 'vif', 'vif_stats', 'vbds', 'vbd']:
+ return self.perform(req)
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ sxp.show(self.dom.sxpr(), out=req)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ #self.ls()
+ req.write('<p>%s</p>' % self.dom)
+ if self.dom.console:
+ cinfo = self.dom.console
+ cid = cinfo.id
+ #todo: Local xref: need to know server prefix.
+ req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
+ % (cid, cid))
+ req.write('<p><a href="%s">Connect to console</a></p>'
+ % cinfo.uri())
+ if self.dom.config:
+ req.write("<code><pre>")
+ PrettyPrint.prettyprint(self.dom.config, out=req)
+ req.write("</pre></code>")
+ req.write('<a href="%s?op=vif_stats&vif=0">vif 0 stats</a>'
+ % req.prePathURL())
+ self.form(req)
+ req.write('</body></html>')
+ return ''
+
+ def form(self, req):
+ req.write('<form method="post" action="%s">' % req.prePathURL())
+ req.write('<input type="submit" name="op" value="unpause">')
+ req.write('<input type="submit" name="op" value="pause">')
+ req.write('<input type="submit" name="op" value="shutdown">')
+ req.write('<input type="submit" name="op" value="destroy">')
+ req.write('<br><input type="submit" name="op" value="migrate">')
+ req.write('To: <input type="text" name="destination">')
+ req.write('</form>')
diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py
new file mode 100644
index 0000000000..af4bc7a15c
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvDomainDir.py
@@ -0,0 +1,144 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from StringIO import StringIO
+
+from twisted.protocols import http
+from twisted.web import error
+
+from xen.xend import sxp
+from xen.xend import XendDomain
+from xen.xend.Args import FormFn
+
+from SrvDir import SrvDir
+from SrvDomain import SrvDomain
+
+class SrvDomainDir(SrvDir):
+ """Service that manages the domain directory.
+ """
+
+ def __init__(self):
+ SrvDir.__init__(self)
+ self.xd = XendDomain.instance()
+
+ def domain(self, x):
+ val = None
+ try:
+ dom = self.xd.domain_get(x)
+ val = SrvDomain(dom)
+ except KeyError, ex:
+ print 'SrvDomainDir>', ex
+ pass
+ return val
+
+ def get(self, x):
+ v = SrvDir.get(self, x)
+ if v is not None:
+ return v
+ v = self.domain(x)
+ return v
+
+ def op_create(self, op, req):
+ ok = 0
+ try:
+ configstring = req.args.get('config')[0]
+ print 'config:', configstring
+ pin = sxp.Parser()
+ pin.input(configstring)
+ pin.input_eof()
+ config = pin.get_val()
+ ok = 1
+ except Exception, ex:
+ print 'op_create>', ex
+ if not ok:
+ req.setResponseCode(http.BAD_REQUEST, "Invalid configuration")
+ return "Invalid configuration"
+ return error.ErrorPage(http.BAD_REQUEST,
+ "Invalid",
+ "Invalid configuration")
+ try:
+ deferred = self.xd.domain_create(config)
+ deferred.addCallback(self._cb_op_create, configstring, req)
+ return deferred
+ except Exception, ex:
+ raise
+ #return ['err', str(ex) ]
+ #req.setResponseCode(http.BAD_REQUEST, "Error creating domain")
+ #return str(ex)
+ #return error.ErrorPage(http.BAD_REQUEST,
+ # "Error creating domain",
+ # str(ex))
+
+
+ def _cb_op_create(self, dominfo, configstring, req):
+ """Callback to handle deferred domain creation.
+ """
+ dom = dominfo.id
+ domurl = "%s/%s" % (req.prePathURL(), dom)
+ req.setResponseCode(201, "created")
+ req.setHeader("Location", domurl)
+ if self.use_sxp(req):
+ return dominfo.sxpr()
+ else:
+ out = StringIO()
+ print >> out, ('<p> Created <a href="%s">Domain %s</a></p>'
+ % (domurl, dom))
+ print >> out, '<p><pre>'
+ print >> out, configstring
+ print >> out, '</pre></p>'
+ val = out.getvalue()
+ out.close()
+ return val
+
+ def op_restore(self, op, req):
+ fn = FormFn(self.xd.domain_restore,
+ [['file', 'str']])
+ val = fn(req.args)
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def render_GET(self, req):
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ self.ls_domain(req, 1)
+ else:
+ req.write("<html><head></head><body>")
+ self.print_path(req)
+ self.ls(req)
+ self.ls_domain(req)
+ self.form(req)
+ req.write("</body></html>")
+ return ''
+
+ def ls_domain(self, req, use_sxp=0):
+ url = req.prePathURL()
+ if not url.endswith('/'):
+ url += '/'
+ if use_sxp:
+ domains = self.xd.domain_ls()
+ sxp.show(domains, out=req)
+ else:
+ domains = self.xd.domains()
+ domains.sort(lambda x, y: cmp(x.id, y.id))
+ req.write('<ul>')
+ for d in domains:
+ req.write('<li><a href="%s%s"> Domain %s</a>'
+ % (url, d.id, d.id))
+ req.write('name=%s' % d.name)
+ req.write('memory=%d'% d.memory)
+ req.write('</li>')
+ req.write('</ul>')
+
+ def form(self, req):
+ req.write('<form method="post" action="%s" enctype="multipart/form-data">'
+ % req.prePathURL())
+ req.write('<button type="submit" name="op" value="create">Create Domain</button>')
+ req.write('Config <input type="file" name="config"><br>')
+ req.write('</form>')
+ req.write('<form method="post" action="%s" enctype="multipart/form-data">'
+ % req.prePathURL())
+ req.write('<button type="submit" name="op" value="create">Restore Domain</button>')
+ req.write('State <input type="string" name="state"><br>')
+ req.write('</form>')
+
diff --git a/tools/python/xen/xend/server/SrvEventDir.py b/tools/python/xen/xend/server/SrvEventDir.py
new file mode 100644
index 0000000000..02871a426a
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvEventDir.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from xen.xend import sxp
+from xen.xend import EventServer
+from SrvDir import SrvDir
+
+class SrvEventDir(SrvDir):
+ """Event directory.
+ """
+
+ def __init__(self):
+ SrvDir.__init__(self)
+ self.eserver = EventServer.instance()
+
+ def op_inject(self, op, req):
+ eventstring = req.args.get('event')
+ pin = sxp.Parser()
+ pin.input(eventstring)
+ pin.input_eof()
+ sxpr = pin.get_val()
+ self.eserver.inject(sxp.name(sxpr), sxpr)
+ if req.use_sxp:
+ sxp.name(sxpr)
+ else:
+ return '<code>' + eventstring + '</code>'
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def form(self, req):
+ action = req.prePathURL()
+ req.write('<form method="post" action="%s" enctype="multipart/form-data">'
+ % action)
+ req.write('<button type="submit" name="op" value="inject">Inject</button>')
+ req.write('Event <input type="text" name="event" size="40"><br>')
+ req.write('</form>')
+ req.write('<form method="post" action="%s" enctype="multipart/form-data">'
+ % action)
+ req.write('<button type="submit" name="op" value="inject">Inject</button>')
+ req.write('Event file<input type="file" name="event"><br>')
+ req.write('</form>')
diff --git a/tools/python/xen/xend/server/SrvNode.py b/tools/python/xen/xend/server/SrvNode.py
new file mode 100644
index 0000000000..69747d80c1
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvNode.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import os
+from SrvDir import SrvDir
+from xen.xend import sxp
+from xen.xend import XendNode
+
+class SrvNode(SrvDir):
+ """Information about the node.
+ """
+
+ def __init__(self):
+ SrvDir.__init__(self)
+ self.xn = XendNode.instance()
+
+ def op_shutdown(self, op, req):
+ val = self.xn.shutdown()
+ return val
+
+ def op_reboot(self, op, req):
+ val = self.xn.reboot()
+ return val
+
+ def op_cpu_rrobin_slice_set(self, op, req):
+ fn = FormFn(self.xn.cpu_rrobin_slice_set,
+ [['slice', 'int']])
+ val = fn(req.args, {})
+ return val
+
+ def op_cpu_bvt_slice_set(self, op, req):
+ fn = FormFn(self.xn.cpu_bvt_slice_set,
+ [['slice', 'int']])
+ val = fn(req.args, {})
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def render_GET(self, req):
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ sxp.show(['node'] + self.info(), out=req)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ req.write('<ul>')
+ for d in self.info():
+ req.write('<li> %10s: %s' % (d[0], str(d[1])))
+ req.write('</ul>')
+ req.write('</body></html>')
+ return ''
+
+ def info(self):
+ return self.xn.info()
diff --git a/tools/python/xen/xend/server/SrvRoot.py b/tools/python/xen/xend/server/SrvRoot.py
new file mode 100644
index 0000000000..8d38937b72
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvRoot.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from xen.xend import XendRoot
+xroot = XendRoot.instance()
+from SrvDir import SrvDir
+
+class SrvRoot(SrvDir):
+ """The root of the xend server.
+ """
+
+ """Server sub-components. Each entry is (name, class), where
+ 'name' is the entry name and 'class' is the name of its class.
+ """
+ #todo Get this list from the XendRoot config.
+ subdirs = [
+ ('node', 'SrvNode' ),
+ ('domain', 'SrvDomainDir' ),
+ ('console', 'SrvConsoleDir' ),
+ ('event', 'SrvEventDir' ),
+ ('device', 'SrvDeviceDir' ),
+ ('vnet', 'SrvVnetDir' ),
+ ]
+
+ def __init__(self):
+ SrvDir.__init__(self)
+ for (name, klass) in self.subdirs:
+ self.add(name, klass)
+ for (name, klass) in self.subdirs:
+ self.get(name)
+ xroot.start()
diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py
new file mode 100644
index 0000000000..ac201dd10d
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvServer.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python2
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Example xend HTTP and console server.
+
+ Can be accessed from a browser or from a program.
+ Do 'python SrvServer.py' to run the server.
+ Then point a web browser at http://localhost:8000/xend and follow the links.
+ Most are stubs, except /domain which has a list of domains and a 'create domain'
+ button.
+
+ You can also access the server from a program.
+ Do 'python XendClient.py' to run a few test operations.
+
+ The data served differs depending on the client (as defined by User-Agent
+ and Accept in the HTTP headers). If the client is a browser, data
+ is returned in HTML, with interactive forms. If the client is a program,
+ data is returned in SXP format, with no forms.
+
+ The server serves to the world by default. To restrict it to the local host
+ change 'interface' in main().
+
+ Mike Wray <mike.wray@hp.com>
+"""
+# todo Support security settings etc. in the config file.
+# todo Support command-line args.
+
+from twisted.web import server
+from twisted.web import resource
+from twisted.internet import reactor
+
+from xen.xend import XendRoot
+xroot = XendRoot.instance()
+
+from xen.xend import Vifctl
+
+from SrvRoot import SrvRoot
+
+def create(port=None, interface=None, bridge=0):
+ if port is None: port = 8000
+ if interface is None: interface = ''
+ if bridge or xroot.rebooted:
+ init_bridge()
+ root = resource.Resource()
+ xend = SrvRoot()
+ root.putChild('xend', xend)
+ site = server.Site(root)
+ reactor.listenTCP(port, site, interface=interface)
+
+def init_bridge():
+ Vifctl.init()
+
+def main(port=None, interface=None):
+ create(port, interface)
+ reactor.run()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/python/xen/xend/server/SrvVnetDir.py b/tools/python/xen/xend/server/SrvVnetDir.py
new file mode 100644
index 0000000000..a8a814192d
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvVnetDir.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from SrvDir import SrvDir
+
+class SrvVnetDir(SrvDir):
+ """Vnet directory.
+ """
+
+ pass
diff --git a/tools/python/xen/xend/server/__init__.py b/tools/python/xen/xend/server/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tools/python/xen/xend/server/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py
new file mode 100755
index 0000000000..4e2a49f7d8
--- /dev/null
+++ b/tools/python/xen/xend/server/blkif.py
@@ -0,0 +1,341 @@
+from twisted.internet import defer
+
+from xen.xend import sxp
+from xen.xend import PrettyPrint
+
+import channel
+import controller
+from messages import *
+
+class BlkifControllerFactory(controller.ControllerFactory):
+ """Factory for creating block device interface controllers.
+ Also handles the 'back-end' channel to the device driver domain.
+ """
+
+ def __init__(self):
+ controller.ControllerFactory.__init__(self)
+
+ self.majorTypes = [ CMSG_BLKIF_BE ]
+
+ self.subTypes = {
+ CMSG_BLKIF_BE_CREATE : self.recv_be_create,
+ CMSG_BLKIF_BE_CONNECT : self.recv_be_connect,
+ CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create,
+ CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow,
+ CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
+ }
+ self.attached = 1
+ self.registerChannel()
+
+ def createInstance(self, dom, recreate=0):
+ d = self.addDeferred()
+ blkif = self.getInstanceByDom(dom)
+ if blkif:
+ self.callDeferred(blkif)
+ else:
+ blkif = BlkifController(self, dom)
+ self.addInstance(blkif)
+ if recreate:
+ self.callDeferred(blkif)
+ else:
+ blkif.send_be_create()
+ return d
+
+ def getDomainDevices(self, dom):
+ blkif = self.getInstanceByDom(dom)
+ return (blkif and blkif.getDevices()) or []
+
+ def getDomainDevice(self, dom, vdev):
+ blkif = self.getInstanceByDom(dom)
+ return (blkif and blkif.getDevice(vdev)) or None
+
+ def setControlDomain(self, dom, recreate=0):
+ if self.dom == dom: return
+ self.deregisterChannel()
+ if not recreate:
+ self.attached = 0
+ self.dom = dom
+ self.registerChannel()
+ #
+ #if xend.blkif.be_port:
+ # xend.blkif.recovery = True
+ #xend.blkif.be_port = xend.main.port_from_dom(dom)
+
+ def getControlDomain(self):
+ return self.dom
+
+ def reattachDevice(self, dom, vdev):
+ blkif = self.getInstanceByDom(dom)
+ if blkif:
+ blkif.reattachDevice(vdev)
+ self.attached = self.devicesAttached()
+ if self.attached:
+ self.reattached()
+
+ def devicesAttached(self):
+ """Check if all devices are attached.
+ """
+ attached = 1
+ for blkif in self.getInstances():
+ if not blkif.attached:
+ attached = 0
+ break
+ return attached
+
+ def reattached(self):
+ for blkif in self.getInstances():
+ blkif.reattached()
+
+ def recv_be_create(self, msg, req):
+ #print 'recv_be_create>'
+ val = unpackMsg('blkif_be_create_t', msg)
+ blkif = self.getInstanceByDom(val['domid'])
+ self.callDeferred(blkif)
+
+ def recv_be_connect(self, msg, req):
+ #print 'recv_be_create>'
+ val = unpackMsg('blkif_be_connect_t', msg)
+ blkif = self.getInstanceByDom(val['domid'])
+ if blkif:
+ blkif.send_fe_interface_status_changed()
+ else:
+ pass
+
+ def recv_be_vbd_create(self, msg, req):
+ #print 'recv_be_vbd_create>'
+ val = unpackMsg('blkif_be_vbd_create_t', msg)
+ blkif = self.getInstanceByDom(val['domid'])
+ if blkif:
+ blkif.send_be_vbd_grow(val['vdevice'])
+ else:
+ pass
+
+ def recv_be_vbd_grow(self, msg, req):
+ #print 'recv_be_vbd_grow>'
+ val = unpackMsg('blkif_be_vbd_grow_t', msg)
+ # Check status?
+ if self.attached:
+ self.callDeferred(0)
+ else:
+ self.reattachDevice(val['domid'], val['vdevice'])
+
+ def recv_be_driver_status_changed(self, msg, req):
+ val = unpackMsg('blkif_be_driver_status_changed_t', msg)
+ status = val['status']
+ if status == BLKIF_DRIVER_STATUS_UP and not self.attached:
+ for blkif in self.getInstances():
+ blkif.detach()
+
+class BlkDev(controller.Dev):
+ """Info record for a block device.
+ """
+
+ def __init__(self, ctrl, vdev, mode, segment):
+ controller.Dev.__init__(self, ctrl)
+ self.vdev = vdev
+ self.mode = mode
+ self.device = segment['device']
+ self.start_sector = segment['start_sector']
+ self.nr_sectors = segment['nr_sectors']
+ self.attached = 1
+
+ def readonly(self):
+ return 'w' not in self.mode
+
+ def sxpr(self):
+ val = ['blkdev', ['vdev', self.vdev], ['mode', self.mode] ]
+ return val
+
+ def destroy(self):
+ print 'BlkDev>destroy>', self.vdev
+ PrettyPrint.prettyprint(self.sxpr())
+ self.controller.send_be_vbd_destroy(self.vdev)
+
+class BlkifController(controller.Controller):
+ """Block device interface controller. Handles all block devices
+ for a domain.
+ """
+
+ def __init__(self, factory, dom):
+ #print 'BlkifController> dom=', dom
+ controller.Controller.__init__(self, factory, dom)
+ self.devices = {}
+
+ self.majorTypes = [ CMSG_BLKIF_FE ]
+
+ self.subTypes = {
+ CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
+ self.recv_fe_driver_status_changed,
+ CMSG_BLKIF_FE_INTERFACE_CONNECT :
+ self.recv_fe_interface_connect,
+ }
+ self.attached = 1
+ self.evtchn = None
+ self.registerChannel()
+ #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx
+
+ def sxpr(self):
+ val = ['blkif', ['dom', self.dom]]
+ if self.evtchn:
+ val.append(['evtchn',
+ self.evtchn['port1'],
+ self.evtchn['port2']])
+ return val
+
+ def lostChannel(self):
+ print 'BlkifController>lostChannel>', 'dom=', self.dom
+ #self.destroyDevices()
+ controller.Controller.lostChannel(self)
+
+ def getDevices(self):
+ return self.devices.values()
+
+ def getDevice(self, vdev):
+ return self.devices.get(vdev)
+
+ def addDevice(self, vdev, mode, segment):
+ if vdev in self.devices: return None
+ dev = BlkDev(self, vdev, mode, segment)
+ self.devices[vdev] = dev
+ return dev
+
+ def attachDevice(self, vdev, mode, segment, recreate=0):
+ """Attach a device to the specified interface.
+ """
+ #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment
+ dev = self.addDevice(vdev, mode, segment)
+ if not dev: return -1
+ if recreate:
+ d = defer.Deferred()
+ d.callback(self)
+ else:
+ self.send_be_vbd_create(vdev)
+ d = self.factory.addDeferred()
+ return d
+
+ def destroy(self):
+ print 'BlkifController>destroy> dom=', self.dom
+ def cb_destroy(val):
+ self.send_be_destroy()
+ d = self.factory.addDeferred()
+ d.addCallback(cb_destroy)
+ self.send_be_disconnect()
+ #self.destroyDevices()
+
+ def destroyDevices(self):
+ for dev in self.getDevices():
+ dev.destroy()
+
+ def detach(self):
+ """Detach all devices, when the back-end control domain has changed.
+ """
+ self.attached = 0
+ for dev in self.devices.values():
+ dev.attached = 0
+ self.send_be_vbd_create(vdev)
+
+ def reattachDevice(self, vdev):
+ """Reattach a device, when the back-end control domain has changed.
+ """
+ dev = self.devices[vdev]
+ dev.attached = 1
+ attached = 1
+ for dev in self.devices.values():
+ if not dev.attached:
+ attached = 0
+ break
+ self.attached = attached
+ return self.attached
+
+ def reattached(self):
+ """All devices have been reattached after the back-end control
+ domain has changed.
+ """
+ msg = packMsg('blkif_fe_interface_status_changed_t',
+ { 'handle' : 0,
+ 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED})
+ self.writeRequest(msg)
+
+ def recv_fe_driver_status_changed(self, msg, req):
+ msg = packMsg('blkif_fe_interface_status_changed_t',
+ { 'handle' : 0,
+ 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED,
+ 'evtchn' : 0 })
+ self.writeRequest(msg)
+
+ def recv_fe_interface_connect(self, msg, req):
+ val = unpackMsg('blkif_fe_interface_connect_t', msg)
+ self.evtchn = channel.eventChannel(0, self.dom)
+ print 'recv_fe_interface_connect>'
+ PrettyPrint.prettyprint(self.sxpr())
+ msg = packMsg('blkif_be_connect_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : val['handle'],
+ 'evtchn' : self.evtchn['port1'],
+ 'shmem_frame' : val['shmem_frame'] })
+ self.factory.writeRequest(msg)
+ pass
+
+ #def recv_fe_interface_status_changed(self, msg, req):
+ # (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg)
+ # print 'recv_fe_interface_status_changed>', hnd, status, chan
+ # pass
+
+ def send_fe_interface_status_changed(self):
+ msg = packMsg('blkif_fe_interface_status_changed_t',
+ { 'handle' : 0,
+ 'status' : BLKIF_INTERFACE_STATUS_CONNECTED,
+ 'evtchn' : self.evtchn['port2'] })
+ self.writeRequest(msg)
+
+ def send_be_create(self):
+ msg = packMsg('blkif_be_create_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0 })
+ self.factory.writeRequest(msg)
+
+ def send_be_disconnect(self):
+ print '>BlkifController>send_be_disconnect>', 'dom=', self.dom
+ msg = packMsg('blkif_be_disconnect_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0 })
+ self.factory.writeRequest(msg)
+
+ def send_be_destroy(self):
+ print '>BlkifController>send_be_destroy>', 'dom=', self.dom
+ msg = packMsg('blkif_be_destroy_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0 })
+ self.factory.writeRequest(msg)
+
+ def send_be_vbd_create(self, vdev):
+ dev = self.devices[vdev]
+ msg = packMsg('blkif_be_vbd_create_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0,
+ 'vdevice' : dev.vdev,
+ 'readonly' : dev.readonly() })
+ self.factory.writeRequest(msg)
+
+ def send_be_vbd_grow(self, vdev):
+ dev = self.devices[vdev]
+ msg = packMsg('blkif_be_vbd_grow_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0,
+ 'vdevice' : dev.vdev,
+ 'extent.device' : dev.device,
+ 'extent.sector_start' : dev.start_sector,
+ 'extent.sector_length' : dev.nr_sectors })
+ self.factory.writeRequest(msg)
+
+ def send_be_vbd_destroy(self, vdev):
+ print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev
+ PrettyPrint.prettyprint(self.sxpr())
+ dev = self.devices[vdev]
+ msg = packMsg('blkif_be_vbd_destroy_t',
+ { 'domid' : self.dom,
+ 'blkif_handle' : 0,
+ 'vdevice' : dev.vdev })
+ del self.devices[vdev]
+ self.factory.writeRequest(msg)
+
diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py
new file mode 100755
index 0000000000..be98a37fd5
--- /dev/null
+++ b/tools/python/xen/xend/server/channel.py
@@ -0,0 +1,378 @@
+import xen.ext.xc; xc = xen.ext.xc.new()
+from xen.ext import xu
+from messages import msgTypeName
+
+VIRQ_MISDIRECT = 0 # Catch-all interrupt for unbound VIRQs.
+VIRQ_TIMER = 1 # Timebase update, and/or requested timeout.
+VIRQ_DEBUG = 2 # Request guest to dump debug info.
+VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console.
+VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain.
+
+def eventChannel(dom1, dom2):
+ return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
+
+class ChannelFactory:
+ """Factory for creating channels.
+ Maintains a table of channels.
+ """
+
+ """ Channels indexed by index. """
+ channels = {}
+
+ def __init__(self):
+ """Constructor - do not use. Use the channelFactory function."""
+ self.notifier = xu.notifier()
+
+ def addChannel(self, channel):
+ """Add a channel.
+ """
+ idx = channel.idx
+ self.channels[idx] = channel
+ self.notifier.bind(idx)
+ # Try to wake it up
+ #self.notifier.unmask(idx)
+ #channel.notify()
+
+ def getChannel(self, idx):
+ """Get the channel with the given index (if any).
+ """
+ return self.channels.get(idx)
+
+ def delChannel(self, idx):
+ """Remove the channel with the given index (if any).
+ """
+ if idx in self.channels:
+ del self.channels[idx]
+ self.notifier.unbind(idx)
+
+ def domChannel(self, dom):
+ """Get the channel for the given domain.
+ Construct if necessary.
+ """
+ dom = int(dom)
+ for chan in self.channels.values():
+ if not isinstance(chan, Channel): continue
+ if chan.dom == dom:
+ return chan
+ chan = Channel(self, dom)
+ self.addChannel(chan)
+ return chan
+
+ def virqChannel(self, virq):
+ """Get the channel for the given virq.
+ Construct if necessary.
+ """
+ for chan in self.channels.values():
+ if not isinstance(chan, VirqChannel): continue
+ if chan.virq == virq:
+ return chan
+ chan = VirqChannel(self, virq)
+ self.addChannel(chan)
+ return chan
+
+ def channelClosed(self, channel):
+ """The given channel has been closed - remove it.
+ """
+ self.delChannel(channel.idx)
+
+ def createPort(self, dom):
+ """Create a port for a channel to the given domain.
+ """
+ return xu.port(dom)
+
+def channelFactory():
+ """Singleton constructor for the channel factory.
+ Use this instead of the class constructor.
+ """
+ global inst
+ try:
+ inst
+ except:
+ inst = ChannelFactory()
+ return inst
+
+class BaseChannel:
+ """Abstract superclass for channels.
+
+ The subclass constructor must set idx to the port to use.
+ """
+
+ def __init__(self, factory):
+ self.factory = factory
+ self.idx = -1
+ self.closed = 0
+
+ def getIndex(self):
+ """Get the channel index.
+ """
+ return self.idx
+
+ def notificationReceived(self, type):
+ """Called when a notification is received.
+ Closes the channel on error, otherwise calls
+ handleNotification(type), which should be defined
+ in a subclass.
+ """
+ #print 'notificationReceived> type=', type, self
+ if self.closed: return
+ if type == self.factory.notifier.EXCEPTION:
+ print 'notificationReceived> EXCEPTION'
+ info = xc.evtchn_status(self.idx)
+ if info['status'] == 'unbound':
+ print 'notificationReceived> EXCEPTION closing...'
+ self.close()
+ return
+ self.handleNotification(type)
+
+ def close(self):
+ """Close the channel. Calls channelClosed() on the factory.
+ Override in subclass.
+ """
+ self.factory.channelClosed(self)
+
+ def handleNotification(self, type):
+ """Handle notification.
+ Define in subclass.
+ """
+ pass
+
+
+class VirqChannel(BaseChannel):
+ """A channel for handling a virq.
+ """
+
+ def __init__(self, factory, virq):
+ """Create a channel for the given virq using the given factory.
+
+ Do not call directly, use virqChannel on the factory.
+ """
+ BaseChannel.__init__(self, factory)
+ self.virq = virq
+ # Notification port (int).
+ self.port = xc.evtchn_bind_virq(virq)
+ self.idx = self.port
+ # Clients to call when a virq arrives.
+ self.clients = []
+
+ def __repr__(self):
+ return ('<VirqChannel virq=%d port=%d>'
+ % (self.virq, self.port))
+
+ def getVirq(self):
+ """Get the channel's virq.
+ """
+ return self.virq
+
+ def close(self):
+ """Close the channel. Calls lostChannel(self) on all its clients and
+ channelClosed() on the factory.
+ """
+ for c in self.clients:
+ c.lostChannel(self)
+ del self.clients
+ BaseChannel.close(self)
+
+ def registerClient(self, client):
+ """Register a client. The client will be called with
+ client.virqReceived(virq) when a virq is received.
+ The client will be called with client.lostChannel(self) if the
+ channel is closed.
+ """
+ self.clients.append(client)
+
+ def handleNotification(self, type):
+ for c in self.clients:
+ c.virqReceived(self.virq)
+
+ def notify(self):
+ xc.evtchn_send(self.port)
+
+
+class Channel(BaseChannel):
+ """A control channel to a domain. Messages for the domain device controllers
+ are multiplexed over the channel (console, block devs, net devs).
+ """
+
+ def __init__(self, factory, dom):
+ """Create a channel to the given domain using the given factory.
+
+ Do not call directly, use domChannel on the factory.
+ """
+ BaseChannel.__init__(self, factory)
+ # Domain.
+ self.dom = int(dom)
+ # Domain port (object).
+ self.port = self.factory.createPort(dom)
+ # Channel port (int).
+ self.idx = self.port.local_port
+ # Registered devices.
+ self.devs = []
+ # Devices indexed by the message types they handle.
+ self.devs_by_type = {}
+ # Output queue.
+ self.queue = []
+ self.closed = 0
+
+ def getLocalPort(self):
+ """Get the local port.
+ """
+ return self.port.local_port
+
+ def getRemotePort(self):
+ """Get the remote port.
+ """
+ return self.port.remote_port
+
+ def close(self):
+ """Close the channel. Calls lostChannel() on all its devices and
+ channelClosed() on the factory.
+ """
+ self.closed = 1
+ for d in self.devs:
+ d.lostChannel()
+ self.factory.channelClosed(self)
+ self.devs = []
+ self.devs_by_type = {}
+
+ def registerDevice(self, types, dev):
+ """Register a device controller.
+
+ @param types message types the controller handles
+ @param dev device controller
+ """
+ if self.closed: return
+ self.devs.append(dev)
+ for ty in types:
+ self.devs_by_type[ty] = dev
+
+ def deregisterDevice(self, dev):
+ """Remove the registration for a device controller.
+
+ @param dev device controller
+ """
+ if dev in self.devs:
+ self.devs.remove(dev)
+ types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ]
+ for ty in types:
+ del self.devs_by_type[ty]
+
+ def getDevice(self, type):
+ """Get the device controller handling a message type.
+
+ @param type message type
+ @returns controller or None
+ """
+ return self.devs_by_type.get(type)
+
+ def getMessageType(self, msg):
+ """Get a 2-tuple of the message type and subtype.
+ """
+ hdr = msg.get_header()
+ return (hdr['type'], hdr.get('subtype'))
+
+ def __repr__(self):
+ return ('<Channel dom=%d ports=%d:%d>'
+ % (self.dom,
+ self.port.local_port,
+ self.port.remote_port))
+
+ def handleNotification(self, type):
+ work = 0
+ work += self.handleRequests()
+ work += self.handleResponses()
+ work += self.handleWrites()
+ if work:
+ self.notify()
+
+ def notify(self):
+ self.port.notify()
+
+ def handleRequests(self):
+ work = 0
+ while 1:
+ msg = self.readRequest()
+ if not msg: break
+ self.requestReceived(msg)
+ work += 1
+ return work
+
+ def requestReceived(self, msg):
+ (ty, subty) = self.getMessageType(msg)
+ #todo: Must respond before writing any more messages.
+ #todo: Should automate this (respond on write)
+ self.port.write_response(msg)
+ dev = self.getDevice(ty)
+ if dev:
+ dev.requestReceived(msg, ty, subty)
+ else:
+ print ("requestReceived> No device: Message type %s %d:%d"
+ % (msgTypeName(ty, subty), ty, subty)), self
+
+ def handleResponses(self):
+ work = 0
+ while 1:
+ msg = self.readResponse()
+ if not msg: break
+ self.responseReceived(msg)
+ work += 1
+ return work
+
+ def responseReceived(self, msg):
+ (ty, subty) = self.getMessageType(msg)
+ dev = self.getDevice(ty)
+ if dev:
+ dev.responseReceived(msg, ty, subty)
+ else:
+ print ("responseReceived> No device: Message type %d:%d"
+ % (msgTypeName(ty, subty), ty, subty)), self
+
+ def handleWrites(self):
+ work = 0
+ # Pull data from producers.
+ for dev in self.devs:
+ work += dev.produceRequests()
+ # Flush the queue.
+ while self.queue and self.port.space_to_write_request():
+ msg = self.queue.pop(0)
+ self.port.write_request(msg)
+ work += 1
+ return work
+
+ def writeRequest(self, msg, notify=1):
+ if self.closed:
+ val = -1
+ elif self.writeReady():
+ self.port.write_request(msg)
+ if notify: self.notify()
+ val = 1
+ else:
+ self.queue.append(msg)
+ val = 0
+ return val
+
+ def writeResponse(self, msg):
+ if self.closed: return -1
+ self.port.write_response(msg)
+ return 1
+
+ def writeReady(self):
+ if self.closed or self.queue: return 0
+ return self.port.space_to_write_request()
+
+ def readRequest(self):
+ if self.closed:
+ return None
+ if self.port.request_to_read():
+ val = self.port.read_request()
+ else:
+ val = None
+ return val
+
+ def readResponse(self):
+ if self.closed:
+ return None
+ if self.port.response_to_read():
+ val = self.port.read_response()
+ else:
+ val = None
+ return val
diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py
new file mode 100755
index 0000000000..ab8b22e41e
--- /dev/null
+++ b/tools/python/xen/xend/server/console.py
@@ -0,0 +1,232 @@
+
+from twisted.internet import reactor
+from twisted.internet import protocol
+from twisted.protocols import telnet
+
+from xen.ext import xu
+
+from xen.xend import EventServer
+eserver = EventServer.instance()
+
+import controller
+from messages import *
+from params import *
+
+"""Telnet binary option."""
+TRANSMIT_BINARY = '0'
+WILL = chr(251)
+IAC = chr(255)
+
+class ConsoleProtocol(protocol.Protocol):
+ """Asynchronous handler for a console TCP socket.
+ """
+
+ def __init__(self, controller, idx):
+ self.controller = controller
+ self.idx = idx
+ self.addr = None
+ self.binary = 0
+
+ def connectionMade(self):
+ peer = self.transport.getPeer()
+ self.addr = (peer.host, peer.port)
+ if self.controller.connect(self.addr, self):
+ self.transport.write("Cannot connect to console %d on domain %d\n"
+ % (self.idx, self.controller.dom))
+ self.loseConnection()
+ return
+ else:
+ # KAF: A nice quiet successful connect. Don't bother with telnet
+ # control sequence -- telnet is not the appropriate protocol here.
+ #self.transport.write("Connected to console %d on domain %d\n"
+ # % (self.idx, self.controller.dom))
+ #self.setTelnetTransmitBinary()
+ eserver.inject('xend.console.connect',
+ [self.idx, self.addr[0], self.addr[1]])
+
+ def setTelnetTransmitBinary(self):
+ """Send the sequence to set the telnet TRANSMIT-BINARY option.
+ """
+ self.write(IAC + WILL + TRANSMIT_BINARY)
+
+ def dataReceived(self, data):
+ if self.controller.handleInput(self, data):
+ self.loseConnection()
+
+ def write(self, data):
+ #if not self.connected: return -1
+ self.transport.write(data)
+ return len(data)
+
+ def connectionLost(self, reason=None):
+ eserver.inject('xend.console.disconnect',
+ [self.idx, self.addr[0], self.addr[1]])
+ self.controller.disconnect()
+
+ def loseConnection(self):
+ self.transport.loseConnection()
+
+class ConsoleFactory(protocol.ServerFactory):
+ """Asynchronous handler for a console server socket.
+ """
+ protocol = ConsoleProtocol
+
+ def __init__(self, controller, idx):
+ #protocol.ServerFactory.__init__(self)
+ self.controller = controller
+ self.idx = idx
+
+ def buildProtocol(self, addr):
+ proto = self.protocol(self.controller, self.idx)
+ proto.factory = self
+ return proto
+
+class ConsoleControllerFactory(controller.ControllerFactory):
+ """Factory for creating console controllers.
+ """
+
+ def createInstance(self, dom, console_port=None):
+ if console_port is None:
+ console_port = CONSOLE_PORT_BASE + dom
+ console = ConsoleController(self, dom, console_port)
+ self.addInstance(console)
+ eserver.inject('xend.console.create',
+ [console.idx, console.dom, console.console_port])
+ return console
+
+ def consoleClosed(self, console):
+ eserver.inject('xend.console.close', console.idx)
+ self.delInstance(console)
+
+class ConsoleController(controller.Controller):
+ """Console controller for a domain.
+ Does not poll for i/o itself, but relies on the notifier to post console
+ output and the connected TCP sockets to post console input.
+ """
+
+ def __init__(self, factory, dom, console_port):
+ #print 'ConsoleController> dom=', dom, type(dom)
+ controller.Controller.__init__(self, factory, dom)
+ self.majorTypes = [ CMSG_CONSOLE ]
+ self.status = "new"
+ self.addr = None
+ self.conn = None
+ self.rbuf = xu.buffer()
+ self.wbuf = xu.buffer()
+ self.console_port = console_port
+
+ self.registerChannel()
+ self.listener = None
+ self.listen()
+ #print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx
+
+ def sxpr(self):
+ val =['console',
+ ['status', self.status ],
+ ['id', self.idx ],
+ ['domain', self.dom ],
+ ['local_port', self.channel.getLocalPort() ],
+ ['remote_port', self.channel.getRemotePort() ],
+ ['console_port', self.console_port ] ]
+ if self.addr:
+ val.append(['connected', self.addr[0], self.addr[1]])
+ return val
+
+ def ready(self):
+ return not (self.closed() or self.rbuf.empty())
+
+ def closed(self):
+ return self.status == 'closed'
+
+ def connected(self):
+ return self.status == 'connected'
+
+ def close(self):
+ try:
+ #print 'ConsoleController> close dom=', self.dom
+ self.status = "closed"
+ if self.conn:
+ self.conn.loseConnection()
+ self.listener.stopListening()
+ self.deregisterChannel()
+ self.lostChannel()
+ except Exception, ex:
+ print 'ConsoleController>close>', ex
+ raise
+
+ def listen(self):
+ """Listen for TCP connections to the console port..
+ """
+ if self.closed(): return
+ self.status = "listening"
+ if self.listener:
+ #self.listener.startListening()
+ pass
+ else:
+ f = ConsoleFactory(self, self.idx)
+ self.listener = reactor.listenTCP(self.console_port, f)
+
+ def connect(self, addr, conn):
+ if self.closed(): return -1
+ if self.connected(): return -1
+ self.addr = addr
+ self.conn = conn
+ self.status = "connected"
+ self.handleOutput()
+ return 0
+
+ def disconnect(self):
+ if self.conn:
+ self.conn.loseConnection()
+ self.addr = None
+ self.conn = None
+ self.listen()
+
+ def requestReceived(self, msg, type, subtype):
+ #print '***Console', self.dom, msg.get_payload()
+ self.rbuf.write(msg.get_payload())
+ self.handleOutput()
+
+ def responseReceived(self, msg, type, subtype):
+ pass
+
+ def produceRequests(self):
+ # Send as much pending console data as there is room for.
+ work = 0
+ while not self.wbuf.empty() and self.channel.writeReady():
+ msg = xu.message(CMSG_CONSOLE, 0, 0)
+ msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
+ work += self.channel.writeRequest(msg, notify=0)
+ return work
+
+ def handleInput(self, conn, data):
+ """Handle some external input aimed at the console.
+ Called from a TCP connection (conn).
+ """
+ if self.closed(): return -1
+ if conn != self.conn: return 0
+ self.wbuf.write(data)
+ if self.produceRequests():
+ self.channel.notify()
+ return 0
+
+ def handleOutput(self):
+ """Handle buffered output from the console.
+ Sends it to the connected console (if any).
+ """
+ if self.closed():
+ #print 'Console>handleOutput> closed'
+ return -1
+ if not self.conn:
+ #print 'Console>handleOutput> not connected'
+ return 0
+ while not self.rbuf.empty():
+ try:
+ #print 'Console>handleOutput> writing...'
+ bytes = self.conn.write(self.rbuf.peek())
+ if bytes > 0:
+ self.rbuf.discard(bytes)
+ except socket.error, error:
+ pass
+ #print 'Console>handleOutput<'
+ return 0
diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py
new file mode 100755
index 0000000000..900c2d55b0
--- /dev/null
+++ b/tools/python/xen/xend/server/controller.py
@@ -0,0 +1,169 @@
+from twisted.internet import defer
+
+import channel
+from messages import msgTypeName
+
+class CtrlMsgRcvr:
+ """Abstract class for things that deal with a control interface to a domain.
+ """
+
+
+ def __init__(self):
+ self.channelFactory = channel.channelFactory()
+ self.majorTypes = [ ]
+ self.subTypes = {}
+ self.dom = None
+ self.channel = None
+ self.idx = None
+
+ def requestReceived(self, msg, type, subtype):
+ method = self.subTypes.get(subtype)
+ if method:
+ method(msg, 1)
+ else:
+ print ('requestReceived> No handler: Message type %s %d:%d'
+ % (msgTypeName(type, subtype), type, subtype)), self
+
+ def responseReceived(self, msg, type, subtype):
+ method = self.subTypes.get(subtype)
+ if method:
+ method(msg, 0)
+ else:
+ print ('responseReceived> No handler: Message type %s %d:%d'
+ % (msgTypeName(type, subtype), type, subtype)), self
+
+ def lostChannel(self):
+ pass
+
+ def registerChannel(self):
+ #print 'CtrlMsgRcvr>registerChannel>', self
+ self.channel = self.channelFactory.domChannel(self.dom)
+ self.idx = self.channel.getIndex()
+ if self.majorTypes:
+ self.channel.registerDevice(self.majorTypes, self)
+
+ def deregisterChannel(self):
+ #print 'CtrlMsgRcvr>deregisterChannel>', self
+ if self.channel:
+ self.channel.deregisterDevice(self)
+ del self.channel
+
+ def produceRequests(self):
+ return 0
+
+ def writeRequest(self, msg):
+ if self.channel:
+ self.channel.writeRequest(msg)
+ else:
+ print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self
+
+ def writeResponse(self, msg):
+ if self.channel:
+ self.channel.writeResponse(msg)
+ else:
+ print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
+
+class ControllerFactory(CtrlMsgRcvr):
+ """Abstract class for factories creating controllers.
+ Maintains a table of instances.
+ """
+
+ def __init__(self):
+ CtrlMsgRcvr.__init__(self)
+ self.instances = {}
+ self.dlist = []
+ self.dom = 0
+ # Timeout (in seconds) for deferreds.
+ self.timeout = 10
+
+ def addInstance(self, instance):
+ self.instances[instance.idx] = instance
+
+ def getInstance(self, idx):
+ return self.instances.get(idx)
+
+ def getInstances(self):
+ return self.instances.values()
+
+ def getInstanceByDom(self, dom):
+ for inst in self.instances.values():
+ if inst.dom == dom:
+ return inst
+ return None
+
+ def delInstance(self, instance):
+ #print 'ControllerFactory>delInstance>', instance.idx
+ if instance.idx in self.instances:
+ #print 'ControllerFactory>delInstance> remove', instance.idx
+ del self.instances[instance.idx]
+
+ def createInstance(self, dom, recreate=0):
+ raise NotImplementedError()
+
+ def instanceClosed(self, instance):
+ #print 'ControllerFactory>instanceClosed>', instance.idx, instance
+ self.delInstance(instance)
+
+ def addDeferred(self):
+ d = defer.Deferred()
+ if self.timeout > 0:
+ # The deferred will error if not called before timeout.
+ d.setTimeout(self.timeout)
+ self.dlist.append(d)
+ return d
+
+ def callDeferred(self, *args):
+ if self.dlist:
+ d = self.dlist.pop(0)
+ d.callback(*args)
+
+ def errDeferred(self, *args):
+ if self.dlist:
+ d = self.dlist.pop(0)
+ d.errback(*args)
+
+class Controller(CtrlMsgRcvr):
+ """Abstract class for a device controller attached to a domain.
+ """
+
+ def __init__(self, factory, dom):
+ CtrlMsgRcvr.__init__(self)
+ self.factory = factory
+ self.dom = int(dom)
+ self.channel = None
+ self.idx = None
+
+ def close(self):
+ self.deregisterChannel()
+ self.lostChannel()
+
+ def lostChannel(self):
+ #print 'Controller>lostChannel>', self, self.factory
+ self.factory.instanceClosed(self)
+
+class Dev:
+
+ def __init__(self, controller):
+ self.controller = controller
+ self.props = {}
+
+ def setprop(self, k, v):
+ self.props[k] = v
+
+ def getprop(self, k, v=None):
+ return self.props.get(k, v)
+
+ def hasprop(self, k):
+ return k in self.props
+
+ def delprop(self, k):
+ if k in self.props:
+ del self.props[k]
+
+ #def __repr__(self):
+ # return str(self.sxpr())
+
+ def sxpr(self):
+ raise NotImplementedError()
+
+
diff --git a/tools/python/xen/xend/server/cstruct.py b/tools/python/xen/xend/server/cstruct.py
new file mode 100755
index 0000000000..880931b41f
--- /dev/null
+++ b/tools/python/xen/xend/server/cstruct.py
@@ -0,0 +1,269 @@
+import struct
+
+class Struct:
+
+ maxDepth = 10
+
+ base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ]
+
+ sizes = {'B': 1,
+ 'H': 2,
+ 'I': 4,
+ 'L': 4,
+ 'Q': 8,
+ 'c': 1,
+ 'h': 2,
+ 'i': 4,
+ 'l': 4,
+ 'q': 8,
+ 'x': 1,
+ }
+
+ formats = {
+ 'int8' : 'B',
+ 'int16' : 'H',
+ 'int32' : 'I',
+ 'int64' : 'Q',
+ 'u8' : 'B',
+ 'u16' : 'H',
+ 'u32' : 'I',
+ 'u64' : 'Q'
+ }
+
+ def typedef(self, name, val):
+ self.formats[name] = val
+
+ def struct(self, name, *f):
+ self.typedef(name, StructInfo(self, f))
+
+ def getType(self, name):
+ return self.formats[name]
+
+ def format(self, ty):
+ d = 0
+ f = ty
+ while d < self.maxDepth:
+ d += 1
+ f = self.formats[f]
+ if isinstance(f, StructInfo):
+ return f.format()
+ if f in self.base:
+ return f
+ return -1
+
+ def alignedformat(self, ty):
+ fmt = self.format(ty)
+ #print 'alignedformat> %s |%s|' %(ty, fmt)
+ afmt = self.align(fmt)
+ #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt)
+ return afmt
+
+ def align(self, fmt):
+ n1 = 0
+ afmt = ''
+ for a in fmt:
+ n2 = self.getSize(a)
+ m = n1 % n2
+ if m:
+ d = (n2 - m)
+ afmt += 'x' * d
+ n1 += d
+ afmt += a
+ n1 += n2
+ return afmt
+
+ def fmtsize(self, fmt):
+ s = 0
+ for f in fmt:
+ s += self.getSize(f)
+ return s
+
+ def getSize(self, f):
+ return self.sizes[f]
+
+ def pack(self, ty, data):
+ return self.getType(ty).pack(data)
+
+ def unpack(self, ty, data):
+ return self.getType(ty).unpack(data)
+
+ def show(self):
+ l = self.formats.keys()
+ l.sort()
+ for v in l:
+ print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v))
+
+
+class StructInfo:
+
+ def __init__(self, s, f):
+ self.fmt = None
+ self.structs = s
+ self.fields = f
+
+ def alignedformat(self):
+ if self.afmt: return self.afmt
+ self.afmt = self.structs.align(self.format())
+ return self.afmt
+
+ def format(self):
+ if self.fmt: return self.fmt
+ fmt = ""
+ for (ty, name) in self.fields:
+ fmt += self.formatString(ty)
+ self.fmt = fmt
+ return fmt
+
+ def formatString(self, ty):
+ if ty in self.fields:
+ ty = self.fields[ty]
+ return self.structs.format(ty)
+
+ def pack(self, *args):
+ return struct.pack(self.alignedformat(), *args)
+
+ def unpack(self, data):
+ return struct.unpack(self.alignedformat(), data)
+
+types = Struct()
+
+types.typedef('short' , 'h')
+types.typedef('int' , 'i')
+types.typedef('long' , 'l')
+types.typedef('unsigned short', 'H')
+types.typedef('unsigned int' , 'I')
+types.typedef('unsigned long' , 'L')
+types.typedef('domid_t' , 'u64')
+types.typedef('blkif_vdev_t' , 'u16')
+types.typedef('blkif_pdev_t' , 'u16')
+types.typedef('blkif_sector_t', 'u64')
+
+types.struct('u8[6]',
+ ('u8', 'a1'),
+ ('u8', 'a2'),
+ ('u8', 'a3'),
+ ('u8', 'a4'),
+ ('u8', 'a5'),
+ ('u8', 'a6'))
+
+types.struct('blkif_fe_interface_status_changed_t',
+ ('unsigned int', 'handle'),
+ ('unsigned int', 'status'),
+ ('unsigned int', 'evtchn'))
+
+types.struct('blkif_fe_driver_status_changed_t',
+ ('unsigned int', 'status'),
+ ('unsigned int', 'nr_interfaces'))
+
+types.struct('blkif_fe_interface_connect_t',
+ ('unsigned int' , 'handle'),
+ ('unsigned long', 'shmem_frame'))
+
+types.struct('blkif_fe_interface_disconnect_t',
+ ('unsigned int', 'handle'))
+
+types.struct('blkif_extent_t',
+ ('blkif_pdev_t' , 'device'),
+ ('blkif_sector_t', 'sector_start'),
+ ('blkif_sector_t', 'sector_length'))
+
+types.struct('blkif_be_create_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'blkif_handle'),
+ ('unsigned int', 'status'))
+
+types.struct('blkif_be_destroy_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'blkif_handle'),
+ ('unsigned int', 'status'))
+
+types.struct('blkif_be_connect_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int' , 'blkif_handle'),
+ ('unsigned int' , 'evtchn'),
+ ('unsigned long', 'shmem_frame'),
+ ('unsigned int' , 'status'))
+
+types.struct('blkif_be_disconnect_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'blkif_handle'),
+ ('unsigned int', 'status'))
+
+types.struct('blkif_be_vbd_create_t',
+ ('domid_t' , 'domid'), #Q
+ ('unsigned int', 'blkif_handle'), #I
+ ('blkif_vdev_t', 'vdevice'), #H
+ ('int' , 'readonly'), #i
+ ('unsigned int', 'status')) #I
+
+types.struct('blkif_be_vbd_destroy_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'blkif_handle'),
+ ('blkif_vdev_t', 'vdevice'),
+ ('unsigned int', 'status'))
+
+types.struct('blkif_be_vbd_grow_t',
+ ('domid_t' , 'domid'), #Q
+ ('unsigned int' , 'blkif_handle'), #I
+ ('blkif_vdev_t' , 'vdevice'), #H
+ ('blkif_extent_t', 'extent'), #HQQ
+ ('unsigned int' , 'status')) #I
+
+types.struct('blkif_be_vbd_shrink_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'blkif_handle'),
+ ('blkif_vdev_t', 'vdevice'),
+ ('unsigned int', 'status'))
+
+types.struct('blkif_be_driver_status_changed_t',
+ ('unsigned int', 'status'),
+ ('unsigned int', 'nr_interfaces'))
+
+types.struct('netif_fe_interface_status_changed_t',
+ ('unsigned int', 'handle'),
+ ('unsigned int', 'status'),
+ ('unsigned int', 'evtchn'),
+ ('u8[6]', 'mac'))
+
+types.struct('netif_fe_driver_status_changed_t',
+ ('unsigned int', 'status'),
+ ('unsigned int', 'nr_interfaces'))
+
+types.struct('netif_fe_interface_connect_t',
+ ('unsigned int', 'handle'),
+ ('unsigned long', 'tx_shmem_frame'),
+ ('unsigned long', 'rx_shmem_frame'))
+
+types.struct('netif_fe_interface_disconnect_t',
+ ('unsigned int', 'handle'))
+
+types.struct('netif_be_create_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'netif_handle'),
+ ('u8[6]' , 'mac'),
+ ('unsigned int', 'status'))
+
+types.struct('netif_be_destroy_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'netif_handle'),
+ ('unsigned int', 'status'))
+
+types.struct('netif_be_connect_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int' , 'netif_handle'),
+ ('unsigned int' , 'evtchn'),
+ ('unsigned long', 'tx_shmem_frame'),
+ ('unsigned long', 'rx_shmem_frame'),
+ ('unsigned int' , 'status'))
+
+types.struct('netif_be_disconnect_t',
+ ('domid_t' , 'domid'),
+ ('unsigned int', 'netif_handle'),
+ ('unsigned int', 'status'))
+
+types.struct('netif_be_driver_status_changed_t',
+ ('unsigned int', 'status'),
+ ('unsigned int', 'nr_interfaces'))
+
+if 1 or __name__ == "__main__":
+ types.show()
diff --git a/tools/python/xen/xend/server/domain.py b/tools/python/xen/xend/server/domain.py
new file mode 100644
index 0000000000..ab22234480
--- /dev/null
+++ b/tools/python/xen/xend/server/domain.py
@@ -0,0 +1,41 @@
+import channel
+import controller
+from messages import *
+
+class DomainControllerFactory(controller.ControllerFactory):
+ """Factory for creating domain controllers.
+ """
+
+ def createInstance(self, dom):
+ d = DomainController(self, dom)
+ self.addInstance(d)
+ return d
+
+ def getInstanceByDom(self, dom):
+ for inst in self.instances.values():
+ if inst.dom == dom:
+ return inst
+ inst = self.createInstance(dom)
+ return inst
+
+
+class DomainController(controller.Controller):
+ """Generic controller for a domain.
+ """
+
+ reasons = {'poweroff' : 'shutdown_poweroff_t',
+ 'reboot' : 'shutdown_reboot_t',
+ 'suspend' : 'shutdown_suspend_t' }
+
+ def __init__(self, factory, dom):
+ controller.Controller.__init__(self, factory, dom)
+ self.majorTypes = [ CMSG_SHUTDOWN ]
+ self.registerChannel()
+ print 'DomainController>', self, self.channel, self.idx
+
+ def shutdown(self, reason):
+ msgtype = self.reasons.get(reason)
+ if not msgtype:
+ raise ValueError('invalid reason:' + reason)
+ msg = packMsg(msgtype, {})
+ self.writeRequest(msg)
diff --git a/tools/python/xen/xend/server/messages.py b/tools/python/xen/xend/server/messages.py
new file mode 100644
index 0000000000..e12d7b6e24
--- /dev/null
+++ b/tools/python/xen/xend/server/messages.py
@@ -0,0 +1,219 @@
+import struct
+
+from xen.ext import xu
+
+DEBUG = 0
+
+""" All message formats.
+Added to incrementally for the various message types.
+See below.
+"""
+msg_formats = {}
+
+#============================================================================
+# Console message types.
+#============================================================================
+
+CMSG_CONSOLE = 0
+
+console_formats = { 'console_data': (CMSG_CONSOLE, 0) }
+
+msg_formats.update(console_formats)
+
+#============================================================================
+# Block interface message types.
+#============================================================================
+
+CMSG_BLKIF_BE = 1
+CMSG_BLKIF_FE = 2
+
+CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0
+CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32
+CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
+CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
+
+CMSG_BLKIF_BE_CREATE = 0
+CMSG_BLKIF_BE_DESTROY = 1
+CMSG_BLKIF_BE_CONNECT = 2
+CMSG_BLKIF_BE_DISCONNECT = 3
+CMSG_BLKIF_BE_VBD_CREATE = 4
+CMSG_BLKIF_BE_VBD_DESTROY = 5
+CMSG_BLKIF_BE_VBD_GROW = 6
+CMSG_BLKIF_BE_VBD_SHRINK = 7
+CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32
+
+BLKIF_DRIVER_STATUS_DOWN = 0
+BLKIF_DRIVER_STATUS_UP = 1
+
+BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
+BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+
+BLKIF_BE_STATUS_OKAY = 0
+BLKIF_BE_STATUS_ERROR = 1
+BLKIF_BE_STATUS_INTERFACE_EXISTS = 2
+BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
+BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4
+BLKIF_BE_STATUS_VBD_EXISTS = 5
+BLKIF_BE_STATUS_VBD_NOT_FOUND = 6
+BLKIF_BE_STATUS_OUT_OF_MEMORY = 7
+BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8
+BLKIF_BE_STATUS_MAPPING_ERROR = 9
+
+blkif_formats = {
+ 'blkif_be_connect_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT),
+
+ 'blkif_be_create_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE),
+
+ 'blkif_be_disconnect_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT),
+
+ 'blkif_be_destroy_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY),
+
+ 'blkif_be_vbd_create_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE),
+
+ 'blkif_be_vbd_grow_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW),
+
+ 'blkif_be_vbd_destroy_t':
+ (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY),
+
+ 'blkif_fe_interface_status_changed_t':
+ (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED),
+
+ 'blkif_fe_driver_status_changed_t':
+ (CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED),
+
+ 'blkif_fe_interface_connect_t':
+ (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT),
+}
+
+msg_formats.update(blkif_formats)
+
+#============================================================================
+# Network interface message types.
+#============================================================================
+
+CMSG_NETIF_BE = 3
+CMSG_NETIF_FE = 4
+
+CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0
+CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32
+CMSG_NETIF_FE_INTERFACE_CONNECT = 33
+CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34
+
+CMSG_NETIF_BE_CREATE = 0
+CMSG_NETIF_BE_DESTROY = 1
+CMSG_NETIF_BE_CONNECT = 2
+CMSG_NETIF_BE_DISCONNECT = 3
+CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32
+
+NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
+NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+
+NETIF_DRIVER_STATUS_DOWN = 0
+NETIF_DRIVER_STATUS_UP = 1
+
+netif_formats = {
+ 'netif_be_connect_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT),
+
+ 'netif_be_create_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE),
+
+ 'netif_be_disconnect_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT),
+
+ 'netif_be_destroy_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY),
+
+ 'netif_be_driver_status_changed_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED),
+
+ 'netif_fe_driver_status_changed_t':
+ (CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED),
+
+ 'netif_fe_interface_connect_t':
+ (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT),
+
+ 'netif_fe_interface_status_changed_t':
+ (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED),
+ }
+
+msg_formats.update(netif_formats)
+
+#============================================================================
+CMSG_SHUTDOWN = 6
+
+CMSG_SHUTDOWN_POWEROFF = 0
+CMSG_SHUTDOWN_REBOOT = 1
+CMSG_SHUTDOWN_SUSPEND = 2
+
+STOPCODE_shutdown = 0
+STOPCODE_reboot = 1
+STOPCODE_suspend = 2
+
+shutdown_formats = {
+ 'shutdown_poweroff_t':
+ (CMSG_SHUTDOWN, CMSG_SHUTDOWN_POWEROFF),
+
+ 'shutdown_reboot_t':
+ (CMSG_SHUTDOWN, CMSG_SHUTDOWN_REBOOT),
+
+ 'shutdown_suspend_t':
+ (CMSG_SHUTDOWN, CMSG_SHUTDOWN_SUSPEND),
+ }
+
+msg_formats.update(shutdown_formats)
+
+#============================================================================
+
+class Msg:
+ pass
+
+def packMsg(ty, params):
+ if DEBUG: print '>packMsg', ty, params
+ (major, minor) = msg_formats[ty]
+ args = {}
+ for (k, v) in params.items():
+ if k == 'mac':
+ for i in range(0, 6):
+ args['mac[%d]' % i] = v[i]
+ else:
+ args[k] = v
+ if DEBUG:
+ for (k, v) in args.items():
+ print 'packMsg>', k, v, type(v)
+ msgid = 0
+ msg = xu.message(major, minor, msgid, args)
+ return msg
+
+def unpackMsg(ty, msg):
+ args = msg.get_payload()
+ mac = [0, 0, 0, 0, 0, 0]
+ macs = []
+ for (k, v) in args.items():
+ if k.startswith('mac['):
+ macs += k
+ i = int(k[4:5])
+ mac[i] = v
+ else:
+ pass
+ if macs:
+ args['mac'] = mac
+ for k in macs:
+ del args[k]
+ if DEBUG: print '<unpackMsg', ty, args
+ return args
+
+def msgTypeName(ty, subty):
+ for (name, info) in msg_formats.items():
+ if info[0] == ty and info[1] == subty:
+ return name
+ return None
+
diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py
new file mode 100755
index 0000000000..2b01805be6
--- /dev/null
+++ b/tools/python/xen/xend/server/netif.py
@@ -0,0 +1,316 @@
+import random
+
+from twisted.internet import defer
+
+from xen.xend import sxp
+from xen.xend import PrettyPrint
+from xen.xend import Vifctl
+
+import channel
+import controller
+from messages import *
+
+class NetifControllerFactory(controller.ControllerFactory):
+ """Factory for creating network interface controllers.
+ Also handles the 'back-end' channel to the device driver domain.
+ """
+
+ def __init__(self):
+ controller.ControllerFactory.__init__(self)
+
+ self.majorTypes = [ CMSG_NETIF_BE ]
+
+ self.subTypes = {
+ CMSG_NETIF_BE_CREATE : self.recv_be_create,
+ CMSG_NETIF_BE_CONNECT: self.recv_be_connect,
+ CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
+ }
+ self.attached = 1
+ self.registerChannel()
+
+ def createInstance(self, dom, recreate=0):
+ """Create or find the network interface controller for a domain.
+ """
+ #print 'netif>createInstance> dom=', dom
+ netif = self.getInstanceByDom(dom)
+ if netif is None:
+ netif = NetifController(self, dom)
+ self.addInstance(netif)
+ return netif
+
+ def getDomainDevices(self, dom):
+ netif = self.getInstanceByDom(dom)
+ return (netif and netif.getDevices()) or []
+
+ def getDomainDevice(self, dom, vif):
+ netif = self.getInstanceByDom(dom)
+ return (netif and netif.getDevice(vif)) or None
+
+ def setControlDomain(self, dom, recreate=0):
+ """Set the 'back-end' device driver domain.
+ """
+ if self.dom == dom: return
+ self.deregisterChannel()
+ if not recreate:
+ self.attached = 0
+ self.dom = dom
+ self.registerChannel()
+ #
+ #if xend.netif.be_port.remote_dom != 0:
+ # xend.netif.recovery = True
+ # xend.netif.be_port = xend.main.port_from_dom(dom)
+ #
+
+ def getControlDomain(self):
+ return self.dom
+
+ def recv_be_create(self, msg, req):
+ self.callDeferred(0)
+
+ def recv_be_connect(self, msg, req):
+ val = unpackMsg('netif_be_connect_t', msg)
+ dom = val['domid']
+ vif = val['netif_handle']
+ netif = self.getInstanceByDom(dom)
+ if netif:
+ netif.send_interface_connected(vif)
+ else:
+ print "recv_be_connect> unknown vif=", vif
+ pass
+
+ def recv_be_driver_status_changed(self, msg, req):
+ val = unpackMsg('netif_be_driver_status_changed_t', msg)
+ status = val['status']
+ if status == NETIF_DRIVER_STATUS_UP and not self.attached:
+ # If we are not attached the driver domain was changed, and
+ # this signals the new driver domain is ready.
+ for netif in self.getInstances():
+ netif.reattach_devices()
+ self.attached = 1
+
+## pl = msg.get_payload()
+## status = pl['status']
+## if status == NETIF_DRIVER_STATUS_UP:
+## if xend.netif.recovery:
+## print "New netif backend now UP, notifying guests:"
+## for netif_key in interface.list.keys():
+## netif = interface.list[netif_key]
+## netif.create()
+## print " Notifying %d" % netif.dom
+## msg = xu.message(
+## CMSG_NETIF_FE,
+## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0,
+## { 'handle' : 0, 'status' : 1 })
+## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg)
+## print "Done notifying guests"
+## recovery = False
+
+class NetDev(controller.Dev):
+ """Info record for a network device.
+ """
+
+ def __init__(self, ctrl, vif, mac):
+ controller.Dev.__init__(self, ctrl)
+ self.vif = vif
+ self.mac = mac
+ self.evtchn = None
+ self.bridge = None
+ self.ipaddr = []
+
+ def sxpr(self):
+ vif = str(self.vif)
+ mac = self.get_mac()
+ val = ['netdev', ['vif', vif], ['mac', mac]]
+ if self.bridge:
+ val.append(['bridge', self.bridge])
+ if self.evtchn:
+ val.append(['evtchn',
+ self.evtchn['port1'],
+ self.evtchn['port2']])
+ return val
+
+ def get_vifname(self):
+ return "vif%d.%d" % (self.controller.dom, self.vif)
+
+ def get_mac(self):
+ return ':'.join(map(lambda x: "%x" % x, self.mac))
+
+ def vifctl_params(self):
+ return { 'mac' : self.get_mac(),
+ 'bridge': self.bridge,
+ 'ipaddr': self.ipaddr }
+
+ def up(self, bridge=None, ipaddr=[]):
+ self.bridge = bridge
+ self.ipaddr = ipaddr
+ Vifctl.up(self.get_vifname(), **self.vifctl_params())
+
+ def down(self):
+ Vifctl.down(self.get_vifname(), **self.vifctl_params())
+
+ def destroy(self):
+ def cb_destroy(val):
+ self.controller.send_be_destroy(self.vif)
+ print 'NetDev>destroy>', 'vif=', self.vif
+ PrettyPrint.prettyprint(self.sxpr())
+ self.down()
+ d = self.controller.factory.addDeferred()
+ d.addCallback(cb_destroy)
+ self.controller.send_be_disconnect(self.vif)
+ #self.controller.send_be_destroy(self.vif)
+
+
+class NetifController(controller.Controller):
+ """Network interface controller. Handles all network devices for a domain.
+ """
+
+ def __init__(self, factory, dom):
+ #print 'NetifController> dom=', dom
+ controller.Controller.__init__(self, factory, dom)
+ self.devices = {}
+
+ self.majorTypes = [ CMSG_NETIF_FE ]
+
+ self.subTypes = {
+ CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
+ self.recv_fe_driver_status_changed,
+ CMSG_NETIF_FE_INTERFACE_CONNECT :
+ self.recv_fe_interface_connect,
+ }
+ self.registerChannel()
+ #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx
+
+ def sxpr(self):
+ val = ['netif', ['dom', self.dom]]
+ return val
+
+ def randomMAC(self):
+ # VIFs get a random MAC address with a "special" vendor id.
+ #
+ # NB. The vendor is currently an "obsolete" one that used to belong
+ # to DEC (AA-00-00). Using it is probably a bit rude :-)
+ #
+ # NB2. The first bit of the first random octet is set to zero for
+ # all dynamic MAC addresses. This may allow us to manually specify
+ # MAC addresses for some VIFs with no fear of clashes.
+ mac = [ 0xaa, 0x00, 0x00,
+ random.randint(0x00, 0x7f),
+ random.randint(0x00, 0xff),
+ random.randint(0x00, 0xff) ]
+ return mac
+
+ def lostChannel(self):
+ print 'NetifController>lostChannel>', 'dom=', self.dom
+ #self.destroyDevices()
+ controller.Controller.lostChannel(self)
+
+ def getDevices(self):
+ return self.devices.values()
+
+ def getDevice(self, vif):
+ return self.devices.get(vif)
+
+ def addDevice(self, vif, vmac):
+ if vmac is None:
+ mac = self.randomMAC()
+ else:
+ mac = [ int(x, 16) for x in vmac.split(':') ]
+ if len(mac) != 6: raise ValueError("invalid mac")
+ #print "attach_device>", "vif=", vif, "mac=", mac
+ dev = NetDev(self, vif, mac)
+ self.devices[vif] = dev
+ return dev
+
+ def destroy(self):
+ print 'NetifController>destroy>', 'dom=', self.dom
+ self.destroyDevices()
+
+ def destroyDevices(self):
+ for dev in self.getDevices():
+ dev.destroy()
+
+ def attachDevice(self, vif, vmac, recreate=0):
+ """Attach a network device.
+ If vmac is None a random mac address is assigned.
+
+ @param vif interface index
+ @param vmac mac address (string)
+ """
+ self.addDevice(vif, vmac)
+ if recreate:
+ d = defer.Deferred()
+ d.callback(self)
+ else:
+ d = self.factory.addDeferred()
+ self.send_be_create(vif)
+ return d
+
+ def reattach_devices(self):
+ """Reattach all devices when the back-end control domain has changed.
+ """
+ d = self.factory.addDeferred()
+ self.send_be_create(vif)
+ self.attach_fe_devices(0)
+
+ def attach_fe_devices(self):
+ for dev in self.devices.values():
+ msg = packMsg('netif_fe_interface_status_changed_t',
+ { 'handle' : dev.vif,
+ 'status' : NETIF_INTERFACE_STATUS_DISCONNECTED,
+ 'evtchn' : 0,
+ 'mac' : dev.mac })
+ self.writeRequest(msg)
+
+ def recv_fe_driver_status_changed(self, msg, req):
+ if not req: return
+ msg = packMsg('netif_fe_driver_status_changed_t',
+ { 'status' : NETIF_DRIVER_STATUS_UP,
+ 'nr_interfaces' : len(self.devices) })
+ self.writeRequest(msg)
+ self.attach_fe_devices()
+
+ def recv_fe_interface_connect(self, msg, req):
+ val = unpackMsg('netif_fe_interface_connect_t', msg)
+ dev = self.devices[val['handle']]
+ dev.evtchn = channel.eventChannel(0, self.dom)
+ msg = packMsg('netif_be_connect_t',
+ { 'domid' : self.dom,
+ 'netif_handle' : dev.vif,
+ 'evtchn' : dev.evtchn['port1'],
+ 'tx_shmem_frame' : val['tx_shmem_frame'],
+ 'rx_shmem_frame' : val['rx_shmem_frame'] })
+ self.factory.writeRequest(msg)
+
+ def send_interface_connected(self, vif):
+ dev = self.devices[vif]
+ msg = packMsg('netif_fe_interface_status_changed_t',
+ { 'handle' : dev.vif,
+ 'status' : NETIF_INTERFACE_STATUS_CONNECTED,
+ 'evtchn' : dev.evtchn['port2'],
+ 'mac' : dev.mac })
+ self.writeRequest(msg)
+
+ def send_be_create(self, vif):
+ dev = self.devices[vif]
+ msg = packMsg('netif_be_create_t',
+ { 'domid' : self.dom,
+ 'netif_handle' : dev.vif,
+ 'mac' : dev.mac })
+ self.factory.writeRequest(msg)
+
+ def send_be_disconnect(self, vif):
+ dev = self.devices[vif]
+ msg = packMsg('netif_be_disconnect_t',
+ { 'domid' : self.dom,
+ 'netif_handle' : dev.vif })
+ self.factory.writeRequest(msg)
+
+ def send_be_destroy(self, vif):
+ print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif
+ PrettyPrint.prettyprint(self.sxpr())
+ dev = self.devices[vif]
+ del self.devices[vif]
+ msg = packMsg('netif_be_destroy_t',
+ { 'domid' : self.dom,
+ 'netif_handle' : vif })
+ self.factory.writeRequest(msg)
diff --git a/tools/python/xen/xend/server/params.py b/tools/python/xen/xend/server/params.py
new file mode 100644
index 0000000000..7949f1431f
--- /dev/null
+++ b/tools/python/xen/xend/server/params.py
@@ -0,0 +1,10 @@
+# The following parameters could be placed in a configuration file.
+PID_FILE = '/var/run/xend.pid'
+LOG_FILE = '/var/log/xend.log'
+USER = 'root'
+CONTROL_DIR = '/var/run/xend'
+MGMT_SOCK = 'xendsock' # relative to CONTROL_DIR
+EVENT_PORT = 8001
+
+CONSOLE_PORT_BASE = 9600
+
diff --git a/tools/python/xen/xend/sxp.py b/tools/python/xen/xend/sxp.py
new file mode 100644
index 0000000000..01654a2377
--- /dev/null
+++ b/tools/python/xen/xend/sxp.py
@@ -0,0 +1,580 @@
+#!/usr/bin/python2
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""
+Input-driven parsing for s-expression (sxp) format.
+Create a parser: pin = Parser();
+Then call pin.input(buf) with your input.
+Call pin.input_eof() when done.
+Use pin.read() to see if a value has been parsed, pin.get_val()
+to get a parsed value. You can call ready and get_val at any time -
+you don't have to wait until after calling input_eof.
+
+"""
+from __future__ import generators
+
+import sys
+import types
+import errno
+import string
+from StringIO import StringIO
+
+__all__ = [
+ "mime_type",
+ "ParseError",
+ "Parser",
+ "atomp",
+ "show",
+ "show_xml",
+ "elementp",
+ "name",
+ "attributes",
+ "attribute",
+ "children",
+ "child",
+ "child_at",
+ "child0",
+ "child1",
+ "child2",
+ "child3",
+ "child4",
+ "child_value",
+ "has_id",
+ "with_id",
+ "child_with_id",
+ "elements",
+ "parse",
+ ]
+
+mime_type = "application/sxp"
+
+escapes = {
+ 'a': '\a',
+ 'b': '\b',
+ 't': '\t',
+ 'n': '\n',
+ 'v': '\v',
+ 'f': '\f',
+ 'r': '\r',
+ '\\': '\\',
+ '\'': '\'',
+ '\"': '\"'}
+
+k_list_open = "("
+k_list_close = ")"
+k_attr_open = "@"
+k_eval = "!"
+
+escapes_rev = {}
+for k in escapes:
+ escapes_rev[escapes[k]] = k
+
+class ParseError(StandardError):
+
+ def __init__(self, parser, value):
+ self.parser = parser
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+class ParserState:
+
+ def __init__(self, fn, parent=None):
+ self.parent = parent
+ self.buf = ''
+ self.val = []
+ self.delim = None
+ self.fn = fn
+
+ def push(self, fn):
+ return ParserState(fn, parent=self)
+
+class Parser:
+
+ def __init__(self):
+ self.error = sys.stderr
+ self.reset()
+
+ def reset(self):
+ self.val = []
+ self.eof = 0
+ self.err = 0
+ self.line_no = 0
+ self.char_no = 0
+ self.state = None
+
+ def push_state(self, fn):
+ self.state = self.state.push(fn)
+
+ def pop_state(self):
+ val = self.state
+ self.state = self.state.parent
+ if self.state and self.state.fn == self.state_start:
+ # Return to start state - produce the value.
+ self.val += self.state.val
+ self.state.val = []
+ return val
+
+ def in_class(self, c, s):
+ return s.find(c) >= 0
+
+ def in_space_class(self, c):
+ return self.in_class(c, ' \t\n\v\f\r')
+
+ def is_separator(self, c):
+ return self.in_class(c, '{}()<>[]!;')
+
+ def in_comment_class(self, c):
+ return self.in_class(c, '#')
+
+ def in_string_quote_class(self, c):
+ return self.in_class(c, '"\'')
+
+ def in_printable_class(self, c):
+ return self.in_class(c, string.printable)
+
+ def set_error_stream(self, error):
+ self.error = error
+
+ def has_error(self):
+ return self.err > 0
+
+ def at_eof(self):
+ return self.eof
+
+ def input_eof(self):
+ self.eof = 1
+ self.input_char(-1)
+
+ def input(self, buf):
+ if not buf or len(buf) == 0:
+ self.input_eof()
+ else:
+ for c in buf:
+ self.input_char(c)
+
+ def input_char(self, c):
+ if self.at_eof():
+ pass
+ elif c == '\n':
+ self.line_no += 1
+ self.char_no = 0
+ else:
+ self.char_no += 1
+
+ if self.state is None:
+ self.begin_start(None)
+ self.state.fn(c)
+
+ def ready(self):
+ return len(self.val) > 0
+
+ def get_val(self):
+ v = self.val[0]
+ self.val = self.val[1:]
+ return v
+
+ def get_all(self):
+ return self.val
+
+ def begin_start(self, c):
+ self.state = ParserState(self.state_start)
+
+ def end_start(self):
+ self.val += self.state.val
+ self.pop_state()
+
+ def state_start(self, c):
+ if self.at_eof():
+ self.end_start()
+ elif self.in_space_class(c):
+ pass
+ elif self.in_comment_class(c):
+ self.begin_comment(c)
+ elif c == k_list_open:
+ self.begin_list(c)
+ elif c == k_list_close:
+ raise ParseError(self, "syntax error: "+c)
+ elif self.in_string_quote_class(c):
+ self.begin_string(c)
+ elif self.in_printable_class(c):
+ self.begin_atom(c)
+ elif c == chr(4):
+ # ctrl-D, EOT: end-of-text.
+ self.input_eof()
+ else:
+ raise ParseError(self, "invalid character: code %d" % ord(c))
+
+ def begin_comment(self, c):
+ self.push_state(self.state_comment)
+ self.state.buf += c
+
+ def end_comment(self):
+ self.pop_state()
+
+ def state_comment(self, c):
+ if c == '\n' or self.at_eof():
+ self.end_comment()
+ else:
+ self.state.buf += c
+
+ def begin_string(self, c):
+ self.push_state(self.state_string)
+ self.state.delim = c
+
+ def end_string(self):
+ val = self.state.buf
+ self.state.parent.val.append(val)
+ self.pop_state()
+
+ def state_string(self, c):
+ if self.at_eof():
+ raise ParseError(self, "unexpected EOF")
+ elif c == self.state.delim:
+ self.end_string()
+ elif c == '\\':
+ self.push_state(self.state_escape)
+ else:
+ self.state.buf += c
+
+ def state_escape(self, c):
+ if self.at_eof():
+ raise ParseError(self, "unexpected EOF")
+ d = escapes.get(c)
+ if d:
+ self.state.parent.buf += d
+ self.pop_state()
+ elif c == 'x':
+ self.state.fn = self.state_hex
+ self.state.val = 0
+ else:
+ self.state.fn = self.state_octal
+ self.state.val = 0
+ self.input_char(c)
+
+ def state_octal(self, c):
+ def octaldigit(c):
+ self.state.val *= 8
+ self.state.val += ord(c) - ord('0')
+ self.state.buf += c
+ if self.state.val < 0 or self.state.val > 0xff:
+ raise ParseError(self, "invalid octal escape: out of range " + self.state.buf)
+ if len(self.state.buf) == 3:
+ octaldone()
+
+ def octaldone():
+ d = chr(self.state.val)
+ self.state.parent.buf += d
+ self.pop_state()
+
+ if self.at_eof():
+ raise ParseError(self, "unexpected EOF")
+ elif '0' <= c <= '7':
+ octaldigit(c)
+ elif len(self.buf):
+ octaldone()
+ self.input_char(c)
+
+ def state_hex(self, c):
+ def hexdone():
+ d = chr(self.state.val)
+ self.state.parent.buf += d
+ self.pop_state()
+
+ def hexdigit(c, d):
+ self.state.val *= 16
+ self.state.val += ord(c) - ord(d)
+ self.state.buf += c
+ if self.state.val < 0 or self.state.val > 0xff:
+ raise ParseError(self, "invalid hex escape: out of range " + self.state.buf)
+ if len(self.state.buf) == 2:
+ hexdone()
+
+ if self.at_eof():
+ raise ParseError(self, "unexpected EOF")
+ elif '0' <= c <= '9':
+ hexdigit(c, '0')
+ elif 'A' <= c <= 'F':
+ hexdigit(c, 'A')
+ elif 'a' <= c <= 'f':
+ hexdigit(c, 'a')
+ elif len(buf):
+ hexdone()
+ self.input_char(c)
+
+ def begin_atom(self, c):
+ self.push_state(self.state_atom)
+ self.state.buf = c
+
+ def end_atom(self):
+ val = self.state.buf
+ self.state.parent.val.append(val)
+ self.pop_state()
+
+ def state_atom(self, c):
+ if self.at_eof():
+ self.end_atom()
+ elif (self.is_separator(c) or
+ self.in_space_class(c) or
+ self.in_comment_class(c)):
+ self.end_atom()
+ self.input_char(c)
+ else:
+ self.state.buf += c
+
+ def begin_list(self, c):
+ self.push_state(self.state_list)
+
+ def end_list(self):
+ val = self.state.val
+ self.state.parent.val.append(val)
+ self.pop_state()
+
+ def state_list(self, c):
+ if self.at_eof():
+ raise ParseError(self, "unexpected EOF")
+ elif c == k_list_close:
+ self.end_list()
+ else:
+ self.state_start(c)
+
+def atomp(sxpr):
+ if sxpr.isalnum() or sxpr == '@':
+ return 1
+ for c in sxpr:
+ if c in string.whitespace: return 0
+ if c in '"\'\\(){}[]<>$#&%^': return 0
+ if c in string.ascii_letters: continue
+ if c in string.digits: continue
+ if c in '.-_:/~': continue
+ return 0
+ return 1
+
+def show(sxpr, out=sys.stdout):
+ if isinstance(sxpr, types.ListType):
+ out.write(k_list_open)
+ i = 0
+ for x in sxpr:
+ if i: out.write(' ')
+ show(x, out)
+ i += 1
+ out.write(k_list_close)
+ elif isinstance(sxpr, types.StringType) and atomp(sxpr):
+ out.write(sxpr)
+ else:
+ #out.write("'" + str(sxpr) + "'")
+ out.write(repr(str(sxpr)))
+
+def show_xml(sxpr, out=sys.stdout):
+ if isinstance(sxpr, types.ListType):
+ element = name(sxpr)
+ out.write('<%s' % element)
+ for attr in attributes(sxpr):
+ out.write(' %s=%s' % (attr[0], attr[1]))
+ out.write('>')
+ i = 0
+ for x in children(sxpr):
+ if i: out.write(' ')
+ show_xml(x, out)
+ i += 1
+ out.write('</%s>' % element)
+ elif isinstance(sxpr, types.StringType) and atomp(sxpr):
+ out.write(sxpr)
+ else:
+ out.write(str(sxpr))
+
+def elementp(sxpr, elt=None):
+ return (isinstance(sxpr, types.ListType)
+ and len(sxpr)
+ and (None == elt or sxpr[0] == elt))
+
+def name(sxpr):
+ val = None
+ if isinstance(sxpr, types.StringType):
+ val = sxpr
+ elif isinstance(sxpr, types.ListType) and len(sxpr):
+ val = sxpr[0]
+ return val
+
+def attributes(sxpr):
+ val = []
+ if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
+ attr = sxpr[1]
+ if elementp(attr, k_attr_open):
+ val = attr[1:]
+ return val
+
+def attribute(sxpr, key, val=None):
+ for x in attributes(sxpr):
+ if x[0] == key:
+ val = x[1]
+ break
+ return val
+
+def children(sxpr, elt=None):
+ val = []
+ if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
+ i = 1
+ x = sxpr[i]
+ if elementp(x, k_attr_open):
+ i += 1
+ val = sxpr[i : ]
+ if elt:
+ def iselt(x):
+ return elementp(x, elt)
+ val = filter(iselt, val)
+ return val
+
+def child(sxpr, elt, val=None):
+ for x in children(sxpr):
+ if elementp(x, elt):
+ val = x
+ break
+ return val
+
+def child_at(sxpr, index, val=None):
+ kids = children(sxpr)
+ if len(kids) > index:
+ val = kids[index]
+ return val
+
+def child0(sxpr, val=None):
+ return child_at(sxpr, 0, val)
+
+def child1(sxpr, val=None):
+ return child_at(sxpr, 1, val)
+
+def child2(sxpr, val=None):
+ return child_at(sxpr, 2, val)
+
+def child3(sxpr, val=None):
+ return child_at(sxpr, 3, val)
+
+def child4(sxpr, val=None):
+ return child_at(sxpr, 4, val)
+
+def child_value(sxpr, elt, val=None):
+ kid = child(sxpr, elt)
+ if kid:
+ val = child_at(kid, 0, val)
+ return val
+
+def has_id(sxpr, id):
+ """Test if an s-expression has a given id.
+ """
+ return attribute(sxpr, 'id') == id
+
+def with_id(sxpr, id, val=None):
+ """Find the first s-expression with a given id, at any depth.
+
+ sxpr s-exp or list
+ id id
+ val value if not found (default None)
+
+ return s-exp or val
+ """
+ if isinstance(sxpr, types.ListType):
+ for n in sxpr:
+ if has_id(n, id):
+ val = n
+ break
+ v = with_id(n, id)
+ if v is None: continue
+ val = v
+ break
+ return val
+
+def child_with_id(sxpr, id, val=None):
+ """Find the first child with a given id.
+
+ sxpr s-exp or list
+ id id
+ val value if not found (default None)
+
+ return s-exp or val
+ """
+ if isinstance(sxpr, types.ListType):
+ for n in sxpr:
+ if has_id(n, id):
+ val = n
+ break
+ return val
+
+def elements(sxpr, ctxt=None):
+ """Generate elements (at any depth).
+ Visit elements in pre-order.
+ Values generated are (node, context)
+ The context is None if there is no parent, otherwise
+ (index, parent, context) where index is the node's index w.r.t its parent,
+ and context is the parent's context.
+
+ sxpr s-exp
+
+ returns generator
+ """
+ yield (sxpr, ctxt)
+ i = 0
+ for n in children(sxpr):
+ if isinstance(n, types.ListType):
+ # Calling elements() recursively does not generate recursively,
+ # it just returns a generator object. So we must iterate over it.
+ for v in elements(n, (i, sxpr, ctxt)):
+ yield v
+ i += 1
+
+def to_string(sxpr):
+ """Convert an sxpr to a string.
+
+ sxpr sxpr
+ returns string
+ """
+ io = StringIO()
+ show(sxpr, io)
+ io.seek(0)
+ val = io.getvalue()
+ io.close()
+ return val
+
+def from_string(str):
+ """Create an sxpr by parsing a string.
+
+ str string
+ returns sxpr
+ """
+ io = StringIO(str)
+ return parse(io)
+
+def parse(io):
+ """Completely parse all input from 'io'.
+
+ io input file object
+ returns list of values, None if incomplete
+ raises ParseError on parse error
+ """
+ pin = Parser()
+ while 1:
+ buf = io.readline()
+ pin.input(buf)
+ if len(buf) == 0:
+ break
+ if pin.ready():
+ val = pin.get_all()
+ else:
+ val = None
+ return val
+
+
+if __name__ == '__main__':
+ print ">main"
+ pin = Parser()
+ while 1:
+ buf = sys.stdin.read(1024)
+ #buf = sys.stdin.readline()
+ pin.input(buf)
+ while pin.ready():
+ val = pin.get_val()
+ print
+ print '****** val=', val
+ if len(buf) == 0:
+ break
+
diff --git a/tools/python/xen/xm/__init__.py b/tools/python/xen/xm/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/python/xen/xm/__init__.py
diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
new file mode 100644
index 0000000000..12e2e8010c
--- /dev/null
+++ b/tools/python/xen/xm/create.py
@@ -0,0 +1,366 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Domain creation.
+"""
+import string
+import sys
+
+from xen.xend import sxp
+from xen.xend import PrettyPrint
+from xen.xend.XendClient import server
+
+from xen.xm.opts import *
+
+gopts = Opts(use="""[options]
+
+Create a domain.
+""")
+
+gopts.opt('help', short='h',
+ fn=set_true, default=0,
+ use="Print this help.")
+
+gopts.opt('quiet', short='q',
+ fn=set_true, default=0,
+ use="Quiet.")
+
+gopts.opt('path', val='PATH',
+ fn=set_value, default='.:/etc/xen',
+ use="Search path for default scripts.")
+
+gopts.opt('defaults', short='f', val='FILE',
+ fn=set_value, default='xmdefaults',
+ use="Use the given default script.")
+
+gopts.opt('config', short='F', val='FILE',
+ fn=set_value, default=None,
+ use='Domain configuration to use (SXP).')
+
+gopts.opt('load', short='L', val='FILE',
+ fn=set_value, default=None,
+ use='Domain saved state to load.')
+
+gopts.opt('define', short='D', val='VAR=VAL',
+ fn=set_var, default=None,
+ use="""Set a variable before loading defaults, e.g. '-D vmid=3'
+ to set vmid. May be repeated to set more thanone variable.""")
+
+gopts.opt('dryrun', short='n',
+ fn=set_true, default=0,
+ use="Dry run - print the config but don't create the domain.")
+
+gopts.opt('name', short='N', val='NAME',
+ fn=set_value, default=None,
+ use="Domain name.")
+
+gopts.opt('console', short='c',
+ fn=set_true, default=0,
+ use="Connect to console after domain is created.")
+
+gopts.opt('kernel', short='k', val='FILE',
+ fn=set_value, default=None,
+ use="Path to kernel image.")
+
+gopts.opt('ramdisk', short='r', val='FILE',
+ fn=set_value, default='',
+ use="Path to ramdisk.")
+
+gopts.opt('builder', short='b', val='FUNCTION',
+ fn=set_value, default='linux',
+ use="Function to use to build the domain.")
+
+gopts.opt('memory', short='m', val='MEMORY',
+ fn=set_value, default=128,
+ use="Domain memory in MB.")
+
+gopts.opt('blkif',
+ fn=set_true, default=0,
+ use="Make the domain a block device backend.")
+
+gopts.opt('netif',
+ fn=set_true, default=0,
+ use="Make the domain a network interface backend.")
+
+gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE',
+ fn=append_value, default=[],
+ use="""Add a disk device to a domain. The physical device is DEV, which
+ is exported to the domain as VDEV. The disk is read-only if MODE
+ is 'r', read-write if MODE is 'w'.
+ The option may be repeated to add more than one disk.
+ """)
+
+gopts.opt('pci', val='BUS,DEV,FUNC',
+ fn=append_value, default=[],
+ use="""Add a PCI device to a domain, using given params (in hex).
+ For example '-pci c0,02,1a'.
+ The option may be repeated to add more than one pci device.
+ """)
+
+gopts.opt('ipaddr', short='i', val="IPADDR",
+ fn=append_value, default=[],
+ use="Add an IP address to the domain.")
+
+gopts.opt('vif', val="mac=MAC,bridge=BRIDGE",
+ fn=append_value, default=[],
+ use="""Add a network interface with the given MAC address and bridge.
+ If mac is not specified a random MAC address is used.
+ If bridge is not specified the default bridge is used.
+ This option may be repeated to add more than one vif.
+ Specifying vifs will increase the number of interfaces as needed.
+ """)
+
+gopts.opt('nics', val="NUM",
+ fn=set_int, default=1,
+ use="""Set the number of network interfaces.
+ Use the vif option to define interface parameters, otherwise
+ defaults are used. Specifying vifs will increase the
+ number of interfaces as needed.
+ """)
+
+gopts.opt('root', short='R', val='DEVICE',
+ fn=set_value, default='',
+ use="""Set the root= parameter on the kernel command line.
+ Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
+
+gopts.opt('extra', short='E', val="ARGS",
+ fn=set_value, default='',
+ use="Set extra arguments to append to the kernel command line.")
+
+gopts.opt('ip', short='I', val='IPADDR',
+ fn=set_value, default='',
+ use="Set the kernel IP interface address.")
+
+gopts.opt('gateway', val="IPADDR",
+ fn=set_value, default='',
+ use="Set the kernel IP gateway.")
+
+gopts.opt('netmask', val="MASK",
+ fn=set_value, default = '',
+ use="Set the kernel IP netmask.")
+
+gopts.opt('hostname', val="NAME",
+ fn=set_value, default='',
+ use="Set the kernel IP hostname.")
+
+gopts.opt('interface', val="INTF",
+ fn=set_value, default="eth0",
+ use="Set the kernel IP interface name.")
+
+gopts.opt('dhcp', val="off|dhcp",
+ fn=set_value, default='off',
+ use="Set the kernel dhcp option.")
+
+gopts.opt('nfs_server', val="IPADDR",
+ fn=set_value, default=None,
+ use="Set the address of the NFS server for NFS root.")
+
+gopts.opt('nfs_root', val="PATH",
+ fn=set_value, default=None,
+ use="Set the path of the root NFS directory.")
+
+def strip(pre, s):
+ """Strip prefix 'pre' if present.
+ """
+ if s.startswith(pre):
+ return s[len(pre):]
+ else:
+ return s
+
+def configure_image(config, opts):
+ """Create the image config.
+ """
+ config_image = [ opts.builder ]
+ config_image.append([ 'kernel', os.path.abspath(opts.kernel) ])
+ if opts.ramdisk:
+ config_image.append([ 'ramdisk', os.path.abspath(opts.ramdisk) ])
+ if opts.cmdline_ip:
+ cmdline_ip = strip('ip=', opts.cmdline_ip)
+ config_image.append(['ip', cmdline_ip])
+ if opts.root:
+ cmdline_root = strip('root=', opts.root)
+ config_image.append(['root', cmdline_root])
+ if opts.extra:
+ config_image.append(['args', opts.extra])
+ config.append(['image', config_image ])
+
+def configure_disks(config_devs, opts):
+ """Create the config for disks (virtual block devices).
+ """
+ for (uname, dev, mode) in opts.disk:
+ config_vbd = ['vbd',
+ ['uname', uname],
+ ['dev', dev ],
+ ['mode', mode ] ]
+ config_devs.append(['device', config_vbd])
+
+def configure_pci(config_devs, opts):
+ """Create the config for pci devices.
+ """
+ for (bus, dev, func) in opts.pci:
+ config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
+ config_devs.append(['device', config_pci])
+
+def configure_vifs(config_devs, opts):
+ """Create the config for virtual network interfaces.
+ """
+ vifs = opts.vif
+ vifs_n = max(opts.nics, len(vifs))
+
+ for idx in range(0, vifs_n):
+ if idx < len(vifs):
+ d = vifs[idx]
+ mac = d.get('mac')
+ bridge = d.get('bridge')
+ else:
+ mac = None
+ bridge = None
+ config_vif = ['vif']
+ if mac:
+ config_vif.append(['mac', mac])
+ if bridge:
+ config_vif.append(['bridge', bridge])
+ config_devs.append(['device', config_vif])
+
+def configure_vfr(config, opts):
+ if not opts.ipaddr: return
+ config_vfr = ['vfr']
+ idx = 0 # No way of saying which IP is for which vif?
+ for ip in opts.ipaddr:
+ config_vfr.append(['vif', ['id', idx], ['ip', ip]])
+ config.append(config_vfr)
+
+
+def make_config(opts):
+ """Create the domain configuration.
+ """
+
+ config = ['vm',
+ ['name', opts.name ],
+ ['memory', opts.memory ] ]
+ if opts.cpu:
+ config.append(['cpu', opts.cpu])
+ if opts.blkif:
+ config.append(['backend', ['blkif']])
+ if opts.netif:
+ config.append(['backend', ['netif']])
+
+ configure_image(config, opts)
+ config_devs = []
+ configure_disks(config_devs, opts)
+ configure_pci(config_devs, opts)
+ configure_vifs(config_devs, opts)
+ config += config_devs
+ return config
+
+def preprocess_disk(opts):
+ if not opts.disk: return
+ disk = []
+ for v in opts.disk:
+ d = v.split(',')
+ if len(d) != 3:
+ opts.err('Invalid disk specifier: ' + v)
+ disk.append(d)
+ opts.disk = disk
+
+def preprocess_pci(opts):
+ if not opts.pci: return
+ pci = []
+ for v in opts.pci:
+ d = v.split(',')
+ if len(d) != 3:
+ opts.err('Invalid pci specifier: ' + v)
+ # Components are in hex: add hex specifier.
+ hexd = map(lambda v: '0x'+v, d)
+ pci.append(hexd)
+ opts.pci = pci
+
+def preprocess_vifs(opts):
+ if not opts.vif: return
+ vifs = []
+ for vif in opts.vif:
+ d = {}
+ a = vif.split(',')
+ for b in a:
+ (k, v) = b.strip().split('=')
+ k = k.strip()
+ v = v.strip()
+ if k not in ['mac', 'bridge']:
+ opts.err('Invalid vif specifier: ' + vif)
+ d[k] = v
+ vifs.append(d)
+ opts.vif = vifs
+
+def preprocess_ip(opts):
+ setip = (opts.hostname or opts.netmask
+ or opts.gateway or opts.dhcp or opts.interface)
+ if not setip: return
+ #if not opts
+ ip = (opts.ip
+ + ':'
+ + ':' + opts.gateway
+ + ':' + opts.netmask
+ + ':' + opts.hostname
+ + ':' + opts.interface
+ + ':' + opts.dhcp)
+ opts.cmdline_ip = ip
+
+def preprocess_nfs(opts):
+ if (opts.nfs_root or opts.nfs_server):
+ if (not opts.nfs_root) or (not opts.nfs_server):
+ opts.err('Must set nfs root and nfs server')
+ else:
+ return
+ nfs = 'nfsroot=' + opts.nfs_server + ':' + opts.nfs_root
+ opts.extra = nfs + ' ' + opts.extra
+
+def preprocess(opts):
+ if not opts.kernel:
+ opts.err("No kernel specified")
+ preprocess_disk(opts)
+ preprocess_pci(opts)
+ preprocess_vifs(opts)
+ preprocess_ip(opts)
+ preprocess_nfs(opts)
+
+def make_domain(opts, config):
+ """Create, build and start a domain.
+ Returns: [int] the ID of the new domain.
+ """
+ if opts.vals.load:
+ filename = os.path.abspath(opts.vals.load)
+ dominfo = server.xend_domain_restore(filename, config)
+ else:
+ dominfo = server.xend_domain_create(config)
+
+ dom = int(sxp.child_value(dominfo, 'id'))
+ console_info = sxp.child(dominfo, 'console')
+ if console_info:
+ console_port = int(sxp.child_value(console_info, 'port'))
+ else:
+ console_port = None
+
+ if server.xend_domain_unpause(dom) < 0:
+ server.xend_domain_destroy(dom)
+ opts.err("Failed to start domain %d" % dom)
+ opts.info("Started domain %d, console on port %d"
+ % (dom, console_port))
+ return (dom, console_port)
+
+def main(argv):
+ opts = gopts
+ args = opts.parse(argv)
+ if opts.vals.help:
+ opts.usage()
+ return
+ if opts.vals.config:
+ pass
+ else:
+ opts.load_defaults()
+ preprocess(opts.vals)
+ config = make_config(opts.vals)
+ if opts.vals.dryrun:
+ PrettyPrint.prettyprint(config)
+ else:
+ make_domain(opts, config)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py
new file mode 100644
index 0000000000..3ab5d23cf7
--- /dev/null
+++ b/tools/python/xen/xm/main.py
@@ -0,0 +1,448 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Grand unified management application for Xen.
+"""
+import os
+import os.path
+import sys
+from getopt import getopt
+
+from xen.xend import PrettyPrint
+from xen.xend import sxp
+from xen.xend.XendClient import server
+from xen.xm import create, shutdown
+
+class Prog:
+ """Base class for sub-programs.
+ """
+
+ """Program group it belongs to"""
+ group = 'all'
+ """Program name."""
+ name = '??'
+ """Short program info."""
+ info = ''
+
+ def __init__(self, xm):
+ self.xm = xm
+
+ def err(self, msg):
+ self.xm.err(msg)
+
+ def help(self, args):
+ self.shortHelp(args)
+
+ def shortHelp(self, args):
+ print "%-14s %s" % (self.name, self.info)
+
+ def main(self, args):
+ """Program main entry point.
+ """
+ pass
+
+
+class ProgUnknown(Prog):
+
+ name = 'unknown'
+ info = ''
+
+ def help(self, args):
+ self.xm.err("Unknown command: %s\nTry '%s help' for more information."
+ % (args[0], self.xm.name))
+
+ main = help
+
+class Xm:
+ """Main application.
+ """
+
+ def __init__(self):
+ self.name = 'xm'
+ self.unknown = ProgUnknown(self)
+ self.progs = {}
+
+ def err(self, msg):
+ print >>sys.stderr, "Error:", msg
+ sys.exit(1)
+
+ def main(self, args):
+ """Main entry point. Dispatches to the progs.
+ """
+ self.name = args[0]
+ if len(args) < 2:
+ self.err("Missing command\nTry '%s help' for more information."
+ % self.name)
+ help = self.helparg(args)
+ p = self.getprog(args[1], self.unknown)
+ if help:
+ p.help(args[1:])
+ else:
+ p.main(args[1:])
+
+ def helparg(self, args):
+ for a in args:
+ if a in ['-h', '--help']:
+ return 1
+ return 0
+
+ def prog(self, pklass):
+ """Add a sub-program.
+
+ pklass program class (Prog subclass)
+ """
+ p = pklass(self)
+ self.progs[p.name] = p
+ return p
+
+ def getprog(self, name, val=None):
+ """Get a sub-program.
+ """
+ return self.progs.get(name, val)
+
+ def proglist(self):
+ """Get a list of sub-programs, ordered by group.
+ """
+ groups = {}
+ for p in self.progs.values():
+ l = groups.get(p.group, [])
+ l.append(p)
+ groups[p.group] = l
+ kl = groups.keys()
+ kl.sort()
+ pl = []
+ for k in kl:
+ l = groups[k]
+ l.sort()
+ pl += l
+ return pl
+
+# Create the application object, then add the sub-program classes.
+xm = Xm()
+
+class ProgHelp(Prog):
+
+ name = "help"
+ info = "Print help."
+
+ def help(self, args):
+ if len(args) == 2:
+ name = args[1]
+ p = self.xm.getprog(name)
+ if p:
+ p.help(args[1:])
+ else:
+ print '%s: Unknown command: %s' % (self.name, name)
+ else:
+ for p in self.xm.proglist():
+ p.shortHelp(args)
+ print "\nTry '%s help CMD' for help on CMD" % self.xm.name
+
+ main = help
+
+xm.prog(ProgHelp)
+
+class ProgCreate(Prog):
+
+ group = 'domain'
+ name = "create"
+ info = """Create a domain."""
+
+ def help(self, args):
+ create.main([args[0], '-h'])
+
+ def main(self, args):
+ create.main(args)
+
+xm.prog(ProgCreate)
+
+class ProgSave(Prog):
+ group = 'domain'
+ name = "save"
+ info = """Save domain state (and config) to file."""
+
+ def help(self, args):
+ print args[0], "DOM FILE"
+ print """\nSave domain with id DOM to FILE."""
+
+ def main(self, args):
+ if len(args) < 3: self.err("%s: Missing arguments" % args[0])
+ dom = args[1]
+ savefile = os.path.abspath(args[2])
+ server.xend_domain_save(dom, savefile)
+
+xm.prog(ProgSave)
+
+class ProgRestore(Prog):
+ group = 'domain'
+ name = "restore"
+ info = """Create a domain from a saved state."""
+
+ def help(self, args):
+ print args[0], "FILE [CONFIG]"
+ print "\nRestore a domain from FILE using configuration CONFIG."
+
+ def main(self, help, args):
+ if len(args) < 2: self.err("%s: Missing arguments" % args[0])
+ savefile = os.path.abspath(args[1])
+ if len(args) >= 3:
+ configfile = os.path.abspath(args[2])
+ else:
+ configfile = None
+ info = server.xend_domain_restore(savefile, configfile)
+ PrettyPrint.prettyprint(info)
+
+xm.prog(ProgRestore)
+
+class ProgList(Prog):
+ group = 'domain'
+ name = "list"
+ info = """List info about domains."""
+
+ short_options = 'l'
+ long_options = ['long']
+
+ def help(self, args):
+ if help:
+ print args[0], '[options] [DOM...]'
+ print """\nGet information about domains.
+ Either all domains or the domains given.
+
+ -l, --long Get more detailed information.
+ """
+ return
+
+ def main(self, args):
+ use_long = 0
+ (options, params) = getopt(args[1:],
+ self.short_options,
+ self.long_options)
+ n = len(params)
+ for (k, v) in options:
+ if k in ['-l', '--long']:
+ use_long = 1
+
+ if n == 0:
+ doms = map(int, server.xend_domains())
+ doms.sort()
+ else:
+ doms = map(int, params)
+
+ if use_long:
+ self.long_list(doms)
+ else:
+ self.brief_list(doms)
+
+ def brief_list(self, doms):
+ print 'Dom Name Mem(MB) CPU State Time(s)'
+ for dom in doms:
+ info = server.xend_domain(dom)
+ d = {}
+ d['dom'] = int(dom)
+ d['name'] = sxp.child_value(info, 'name', '??')
+ d['mem'] = int(sxp.child_value(info, 'memory', '0'))
+ d['cpu'] = int(sxp.child_value(info, 'cpu', '0'))
+ d['state'] = sxp.child_value(info, 'state', '??')
+ d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0'))
+ print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d)
+
+ def long_list(self, doms):
+ for dom in doms:
+ info = server.xend_domain(dom)
+ print '\nDomain %d' % dom
+ PrettyPrint.prettyprint(info)
+
+xm.prog(ProgList)
+
+class ProgDestroy(Prog):
+ group = 'domain'
+ name = "destroy"
+ info = """Terminate a domain immediately."""
+
+ def help(self, args):
+ print args[0], 'DOM'
+ print '\nTerminate domain DOM immediately.'
+
+ def main(self, args):
+ if len(args) < 2: self.err("%s: Missing domain" % args[0])
+ dom = args[1]
+ server.xend_domain_destroy(dom)
+
+xm.prog(ProgDestroy)
+
+class ProgShutdown(Prog):
+ group = 'domain'
+ name = "shutdown"
+ info = """Shutdown a domain."""
+
+ def help(self, args):
+ shutdown.main([args[0], '-h'])
+
+ def main(self, args):
+ shutdown.main(args)
+
+xm.prog(ProgShutdown)
+
+class ProgPause(Prog):
+ group = 'domain'
+ name = "pause"
+ info = """Pause execution of a domain."""
+
+ def help(self, args):
+ print args[0], 'DOM'
+ print '\nPause execution of domain DOM.'
+
+ def main(self, args):
+ if len(args) < 2: self.err("%s: Missing domain" % args[0])
+ dom = args[1]
+ server.xend_domain_pause(dom)
+
+xm.prog(ProgPause)
+
+class ProgUnpause(Prog):
+ group = 'domain'
+ name = "unpause"
+ info = """Unpause a paused domain."""
+
+ def help(self, args):
+ print args[0], 'DOM'
+ print '\nUnpause execution of domain DOM.'
+
+ def main(self, args):
+ if len(args) < 2: self.err("%s: Missing domain" % args[0])
+ dom = args[1]
+ server.xend_domain_unpause(dom)
+
+xm.prog(ProgUnpause)
+
+class ProgPincpu(Prog):
+ group = 'domain'
+ name = "pincpu"
+ info = """Pin a domain to a cpu. """
+
+ def help(self, args):
+ print args[0],'DOM CPU'
+ print '\nPin domain DOM to cpu CPU.'
+
+ def main(self, args):
+ if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
+ v = map(int, args[1:3])
+ server.xend_domain_pincpu(*v)
+
+xm.prog(ProgPincpu)
+
+class ProgBvt(Prog):
+ group = 'scheduler'
+ name = "bvt"
+ info = """Set BVT scheduler parameters."""
+
+ def help(self, args):
+ print args[0], "DOM MCUADV WARP WARPL WARPU"
+ print '\nSet Borrowed Virtual Time scheduler parameters.'
+
+ def main(self, args):
+ if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0])
+ v = map(int, args[1:6])
+ server.xend_domain_cpu_bvt_set(*v)
+
+xm.prog(ProgBvt)
+
+class ProgBvtslice(Prog):
+ group = 'scheduler'
+ name = "bvtslice"
+ info = """Set the BVT scheduler slice."""
+
+ def help(self, args):
+ print args[0], 'SLICE'
+ print '\nSet Borrowed Virtual Time scheduler slice.'
+
+ def main(self, args):
+ if len(args) < 2: self.err('%s: Missing slice' % args[0])
+ server.xend_node_cpu_bvt_slice_set(slice)
+
+xm.prog(ProgBvtslice)
+
+class ProgAtropos(Prog):
+ group = 'scheduler'
+ name= "atropos"
+ info = """Set atropos parameters."""
+
+ def help(self, args):
+ print args[0], "DOM PERIOD SLICE LATENCY XTRATIME"
+ print "\nSet atropos parameters."
+
+ def main(self, args):
+ if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0])
+ v = map(int, args[1:5])
+ server.xend_domain_cpu_atropos_set(*v)
+
+xm.prog(ProgAtropos)
+
+class ProgRrobin(Prog):
+ group = 'scheduler'
+ name = "rrobin"
+ info = """Set round robin slice."""
+
+ def help(self, args):
+ print args[0], "SLICE"
+ print "\nSet round robin scheduler slice."
+
+ def main(self, args):
+ if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0])
+ rrslice = int(args[1])
+ server.xend_node_rrobin_set(rrslice)
+
+xm.prog(ProgRrobin)
+
+class ProgInfo(Prog):
+ group = 'host'
+ name = "info"
+ info = """Get information about the xen host."""
+
+ def main(self, args):
+ info = server.xend_node()
+ for x in info[1:]:
+ print "%-23s:" % x[0], x[1]
+
+xm.prog(ProgInfo)
+
+class ProgConsoles(Prog):
+ group = 'console'
+ name = "consoles"
+ info = """Get information about domain consoles."""
+
+ def main(self, args):
+ l = server.xend_consoles()
+ print "Dom Port Id"
+ for x in l:
+ info = server.xend_console(x)
+ d = {}
+ d['dom'] = sxp.child(info, 'dst', ['dst', '?', '?'])[1]
+ d['port'] = sxp.child_value(info, 'port', '?')
+ d['id'] = sxp.child_value(info, 'id', '?')
+ print "%(dom)3s %(port)4s %(id)3s" % d
+
+xm.prog(ProgConsoles)
+
+class ProgConsole(Prog):
+ group = 'console'
+ name = "console"
+ info = """Open a console to a domain."""
+
+ def help(self, args):
+ print "console DOM"
+ print "\nOpen a console to domain DOM."
+
+ def main(self, args):
+ if len(args) < 2: self.err("%s: Missing domain" % args[0])
+ dom = args[1]
+ info = server.xend_domain(dom)
+ console = sxp.child(info, "console")
+ if not console:
+ self.err("No console information")
+ port = sxp.child_value(console, "port")
+ from xen.util import console_client
+ console_client.connect("localhost", int(port))
+
+xm.prog(ProgConsole)
+
+def main(args):
+ xm.main(args)
diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py
new file mode 100644
index 0000000000..eb07936f1e
--- /dev/null
+++ b/tools/python/xen/xm/opts.py
@@ -0,0 +1,340 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Object-oriented command-line option support.
+"""
+from getopt import getopt
+import os
+import os.path
+import sys
+import types
+
+class Opt:
+ """An individual option.
+ """
+ def __init__(self, opts, name, short=None, long=None,
+ val=None, fn=None, use=None, default=None):
+ """Create an option.
+
+ opts parent options object
+ name name of the field it controls
+ short short (1-char) command line switch (optional)
+ long long command-line switch. Defaults to option name.
+ val string used to print option args in help.
+ If val is not specified the option has no arg.
+ fn function to call when the option is specified.
+ use usage (help) string
+ default default value if not specified on command-line
+ """
+ self.opts = opts
+ self.name = name
+ self.short = short
+ if long is None:
+ long = name
+ self.long = long
+ self.val = val
+ self.use = use
+ self.default = default
+ self.optkeys = []
+ if self.short:
+ self.optkeys.append('-' + self.short)
+ if self.long:
+ self.optkeys.append('--' + self.long)
+ self.fn = fn
+ self.specified_opt = None
+ self.specified_val = None
+ self.value = None
+ self.set(default)
+
+ def __repr__(self):
+ return self.name + '=' + str(self.specified_val)
+
+ __str__ = __repr__
+
+ def set(self, value):
+ """Set the option value.
+ """
+ self.opts.setopt(self.name, value)
+
+ def get(self):
+ """Get the option value.
+ """
+ return self.opts.getopt(self.name)
+
+ def append(self, value):
+ """Append a value to the option value.
+ """
+ v = self.get() or []
+ v.append(value)
+ self.set(v)
+
+ def short_opt(self):
+ """Short option spec.
+ """
+ if self.short:
+ if self.val:
+ return self.short + ':'
+ else:
+ return self.short
+ else:
+ return None
+
+ def long_opt(self):
+ """Long option spec.
+ """
+ if self.long:
+ if self.val:
+ return self.long + '='
+ else:
+ return self.long
+ else:
+ return None
+
+ def show(self):
+ sep = ''
+ for x in self.optkeys:
+ print sep, x,
+ sep = ','
+ if self.val:
+ print self.val,
+ print
+ if self.use:
+ print '\t',
+ print self.use
+ if self.val:
+ print '\tDefault', self.default or 'None'
+
+ def specify(self, k, v):
+ """Specify the option. Called when the option is set
+ from the command line.
+
+ k option switch used
+ v optional value given (if any)
+ """
+ if k in self.optkeys:
+ if self.val is None and v:
+ self.opts.err("Option '%s' does not take a value" % k)
+ self.specified_opt = k
+ self.specified_val = v
+ if self.fn:
+ self.fn(self, k, v)
+ return 1
+ else:
+ return 0
+
+ def specified(self):
+ """Test whether the option has been specified: set
+ from the command line.
+ """
+ return self.specified_opt
+
+class OptVals:
+ """Class to hold option values.
+ """
+ pass
+
+class Opts:
+ """Container for options.
+ """
+ def __init__(self, use=None):
+ """Options constructor.
+
+ use usage string
+ """
+ self.use = use
+ # List of options.
+ self.options = []
+ # Options indexed by name.
+ self.options_map = {}
+ # Command-line arguments.
+ self.argv = []
+ # Option values.
+ self.vals = OptVals()
+ self.vals.quiet = 0
+ # Variables for default scripts.
+ self.vars = {}
+
+ def __repr__(self):
+ return '\n'.join(map(str, self.options))
+
+ __str__ = __repr__
+
+ def opt(self, name, **args):
+ """Add an option.
+
+ name option name
+ **args keyword params for option constructor
+ """
+ x = Opt(self, name, **args)
+ self.options.append(x)
+ self.options_map[name] = x
+ return x
+
+ def setvar(self, var, val):
+ """Set a default script variable.
+ """
+ self.vars[var] = val
+
+ def getvar(self, var):
+ """Get a default script variable.
+ """
+ return self.vars.get(var)
+
+ def option(self, name):
+ """Get an option (object).
+ """
+ return self.options_map.get(name)
+
+ def setopt(self, name, val):
+ """Set an option value.
+ An option can also be set using 'opts.vals.name = val'.
+ """
+ setattr(self.vals, name, val)
+
+ def getopt(self, name):
+ """Get an option value.
+ An option value can also be got using 'opts.vals.name'.
+ """
+ getattr(self.vals, name)
+
+ def specified(self, name):
+ """Test if an option has been specified.
+ """
+ opt = self.option(name)
+ return opt and opt.specified()
+
+ def err(self, msg):
+ """Print an error to stderr and exit.
+ """
+ print >>sys.stderr, "Error:", msg
+ sys.exit(1)
+
+ def info(self, msg):
+ """Print a message to stdout (unless quiet is set).
+ """
+ if self.vals.quiet: return
+ print msg
+
+ def warn(self, msg):
+ """Print a warning to stdout.
+ """
+ print >>sys.stderr, "Warning:", msg
+
+ def parse(self, argv):
+ """Parse arguments argv using the options.
+
+ return remaining arguments
+ """
+ self.argv = argv
+ (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts())
+ self.args = args
+ for (k, v) in vals:
+ for opt in self.options:
+ if opt.specify(k, v): break
+ else:
+ print >>sys.stderr, "Error: Unknown option:", k
+ self.usage()
+ return args
+
+ def short_opts(self):
+ """Get short options specifier for getopt.
+ """
+ l = []
+ for x in self.options:
+ y = x.short_opt()
+ if not y: continue
+ l.append(y)
+ return ''.join(l)
+
+ def long_opts(self):
+ """Get long options specifier for getopt.
+ """
+ l = []
+ for x in self.options:
+ y = x.long_opt()
+ if not y: continue
+ l.append(y)
+ return l
+
+ def usage(self):
+ print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
+ for opt in self.options:
+ opt.show()
+
+ def load_defaults(self):
+ """Load a defaults script. Assumes these options set:
+ 'path' search path
+ 'default' script name
+ """
+ for x in [ '' ] + self.vals.path.split(':'):
+ if x:
+ p = os.path.join(x, self.vals.defaults)
+ else:
+ p = self.vals.defaults
+ if os.path.exists(p):
+ self.load(p)
+ break
+ else:
+ self.err("Cannot open defaults file %s" % self.vals.defaults)
+
+ def load(self, defaults, help=0):
+ """Load a defaults file. Local variables in the file
+ are used to set options with the same names.
+ Variables are not used to set options that are already specified.
+ """
+ # Create global and lobal dicts for the file.
+ # Initialize locals to the vars.
+ # Use exec to do the standard imports and
+ # define variables we are passing to the script.
+ globals = {}
+ locals = {}
+ locals.update(self.vars)
+ cmd = '\n'.join(["import sys",
+ "import os",
+ "import os.path",
+ "import xen.util.ip",
+ "xm_file = '%s'" % defaults,
+ "xm_help = %d" % help ])
+ exec cmd in globals, locals
+ execfile(defaults, globals, locals)
+ if help: return
+ # Extract the values set by the script and set the corresponding
+ # options, if not set on the command line.
+ vtypes = [ types.StringType,
+ types.ListType,
+ types.IntType,
+ types.FloatType
+ ]
+ for (k, v) in locals.items():
+ if self.specified(k): continue
+ if not(type(v) in vtypes): continue
+ self.setopt(k, v)
+
+def set_true(opt, k, v):
+ """Set an option true."""
+ opt.set(1)
+
+def set_false(opt, k, v):
+ """Set an option false."""
+ opt.set(0)
+
+def set_value(opt, k, v):
+ """Set an option to a valoue."""
+ opt.set(v)
+
+def set_int(opt, k, v):
+ """Set an option to an integer value."""
+ try:
+ v = int(v)
+ except:
+ opt.opts.err('Invalid value: ' + str(v))
+ opt.set(v)
+
+def append_value(opt, k, v):
+ """Append a value to a list option."""
+ opt.append(v)
+
+def set_var(opt, k, v):
+ """Set a default script variable.
+ """
+ (var, val) = v.strip().split('=')
+ opt.opts.setvar(var.strip(), val.strip())
+
diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py
new file mode 100644
index 0000000000..aaa354554a
--- /dev/null
+++ b/tools/python/xen/xm/shutdown.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Domain shutdown.
+"""
+import string
+import sys
+import time
+
+from xen.xend.XendClient import server
+from xen.xm.opts import *
+
+gopts = Opts(use="""[options] [DOM]
+
+Shutdown one or more domains gracefully.""")
+
+gopts.opt('help', short='h',
+ fn=set_true, default=0,
+ use="Print this help.")
+
+gopts.opt('all', short='a',
+ fn=set_true, default=0,
+ use="Shutdown all domains.")
+
+gopts.opt('wait', short='w',
+ fn=set_true, default=0,
+ use='Wait for shutdown to complete.')
+
+gopts.opt('norestart', short='n',
+ fn=set_true, default=0,
+ use='Prevent domain restart.')
+
+def shutdown(opts, doms, wait):
+ def domains():
+ return [ int(a) for a in server.xend_domains() ]
+ if doms == None: doms = domains()
+ if 0 in doms:
+ doms.remove(0)
+ for d in doms:
+ server.xend_domain_shutdown(d)
+ if wait:
+ while doms:
+ alive = domains()
+ dead = []
+ for d in doms:
+ if d in alive: continue
+ dead.append(d)
+ for d in dead:
+ opts.info("Domain %d terminated" % d)
+ doms.remove(d)
+ time.sleep(1)
+ opts.info("All domains terminated")
+
+def main_all(opts, args):
+ shutdown(opts, None, opts.vals.wait)
+
+def main_dom(opts, args):
+ if len(args) < 1: opts.err('Missing domain')
+ dom = args[0]
+ try:
+ domid = int(dom)
+ except:
+ opts.err('Invalid domain: ' + dom)
+ shutdown(opts, [ domid ], opts.vals.wait)
+
+def main(argv):
+ opts = gopts
+ args = opts.parse(argv)
+ if opts.vals.help:
+ opts.usage()
+ return
+ print 'shutdown.main>', len(args), args
+ if opts.vals.all:
+ main_all(opts, args)
+ else:
+ main_dom(opts, args)
+