Attached patch is the first-cut of a libxl python binding. Presently
only list_domains, domid_to_name, domain_shutdown and domain_destroy are
implemented. The code takes advantage of Ian Campbells libxl IDL work to
make 178 lines of python generate 4,460 lines of boilerplate.
All that remains is:
- support marshalling of other than integer types
- call auto-generated C destructors when python objects are free''d
- flatten out unions, structs etc. for libxl-type marshalling
- manually implement a xen.lowlevel.xl.ctx method for each libxl function
Here''s an example of what current code can do:
$ sudo python>>> from xen.lowlevel import xl
>>> xl
<module ''xen.lowlevel.xl'' from
''/usr/lib/python2.6/dist-packages/xen/lowlevel/xl.so''>>>> dir(xl)
[''Error'', ''__doc__'',
''__file__'', ''__name__'',
''__package__'', ''ctx'',
''device_console'', ''device_disk'',
''device_model_info'', ''device_net2'',
''device_nic'', ''device_pci'',
''device_vfb'', ''device_vkb'',
''diskinfo'', ''domain_build_info'',
''domain_build_state'', ''domain_create_info'',
''dominfo'', ''file_reference'',
''net2info'', ''nicinfo'',
''physinfo'', ''poolinfo'',
''sched_credit'', ''vcpuinfo'',
''version_info'',
''vminfo'']>>> ctx = xl.ctx()
>>> dir(ctx)
[''domain_destroy'', ''domain_shutdown'',
''domid_to_name'',
''list_domains'']>>> ctx.list_domains()
[<xen.lowlevel.xl.dominfo object at 0x7f6765492a08>,
<xen.lowlevel.xl.dominfo object at 0x7f6765492a50>,
<xen.lowlevel.xl.dominfo object at
0x7f6765492a98>]>>> map(lambda x:x.domid, ctx.list_domains())
[0L, 133L, 136L]>>> map(ctx.domid_to_name, map(lambda x:x.domid, ctx.list_domains()))
[''Domain-0'', ''netbsd'',
''lenny'']>>> ctx.domain_destroy(136)
>>> map(ctx.domid_to_name, map(lambda x:x.domid, ctx.list_domains()))
[''Domain-0'', ''netbsd'']
-----
tools/python/genwrap.py | 178 +++++++++++++++++++++++
tools/python/xen/lowlevel/xl/xl.c | 269 ++++++++++++++++++++++++++++++++++++
tools/libxl/libxl.idl | 10 -
tools/libxl/libxltypes.py | 23 ++-
tools/python/Makefile | 1
tools/python/setup.py | 19 ++
6 files changed, 486 insertions(+), 14 deletions(-)
----8<-----------------------------------------------------------------
diff -r d11a52daace3 tools/libxl/libxl.idl
--- a/tools/libxl/libxl.idl Mon Sep 06 15:16:03 2010 +0100
+++ b/tools/libxl/libxl.idl Tue Sep 07 15:22:24 2010 +0100
@@ -6,11 +6,11 @@
libxl_ctx = Builtin("ctx")
libxl_uuid = Builtin("uuid")
libxl_mac = Builtin("mac")
-libxl_qemu_machine_type = Builtin("qemu_machine_type")
-libxl_console_consback = Builtin("console_consback")
-libxl_console_constype = Builtin("console_constype")
-libxl_disk_phystype = Builtin("disk_phystype")
-libxl_nic_type = Builtin("nic_type")
+libxl_qemu_machine_type = Number("qemu_machine_type",
namespace="libxl_")
+libxl_console_consback = Number("console_consback",
namespace="libxl_")
+libxl_console_constype = Number("console_constype",
namespace="libxl_")
+libxl_disk_phystype = Number("disk_phystype",
namespace="libxl_")
+libxl_nic_type = Number("nic_type", namespace="libxl_")
libxl_string_list = Builtin("string_list",
destructor_fn="libxl_string_list_destroy", passby=PASS_BY_REFERENCE)
libxl_key_value_list = Builtin("key_value_list",
destructor_fn="libxl_key_value_list_destroy",
passby=PASS_BY_REFERENCE)
diff -r d11a52daace3 tools/libxl/libxltypes.py
--- a/tools/libxl/libxltypes.py Mon Sep 06 15:16:03 2010 +0100
+++ b/tools/libxl/libxltypes.py Tue Sep 07 15:22:24 2010 +0100
@@ -14,10 +14,13 @@ class Type(object):
if typename is None: # Anonymous type
self.typename = None
+ self.rawname = None
elif self.namespace is None: # e.g. system provided types
self.typename = typename
+ self.rawname = typename
else:
self.typename = self.namespace + typename
+ self.rawname = typename
if self.typename is not None:
self.destructor_fn =
kwargs.setdefault(''destructor_fn'', self.typename +
"_destroy")
@@ -32,11 +35,17 @@ class Builtin(Type):
kwargs.setdefault(''destructor_fn'', None)
Type.__init__(self, typename, **kwargs)
-class UInt(Type):
+class Number(Builtin):
+ def __init__(self, ctype, **kwargs):
+ kwargs.setdefault(''namespace'', None)
+ kwargs.setdefault(''destructor_fn'', None)
+ Builtin.__init__(self, ctype, **kwargs)
+
+class UInt(Number):
def __init__(self, w, **kwargs):
kwargs.setdefault(''namespace'', None)
kwargs.setdefault(''destructor_fn'', None)
- Type.__init__(self, "uint%d_t" % w, **kwargs)
+ Number.__init__(self, "uint%d_t" % w, **kwargs)
self.width = w
@@ -128,12 +137,12 @@ class Reference(Type):
void = Builtin("void *", namespace = None)
bool = Builtin("bool", namespace = None)
-size_t = Builtin("size_t", namespace = None)
+size_t = Number("size_t", namespace = None)
-integer = Builtin("int", namespace = None)
-unsigned_integer = Builtin("unsigned int", namespace = None)
-unsigned = Builtin("unsigned int", namespace = None)
-unsigned_long = Builtin("unsigned long", namespace = None)
+integer = Number("int", namespace = None)
+unsigned_integer = Number("unsigned int", namespace = None)
+unsigned = Number("unsigned int", namespace = None)
+unsigned_long = Number("unsigned long", namespace = None)
uint8 = UInt(8)
uint16 = UInt(16)
diff -r d11a52daace3 tools/python/Makefile
--- a/tools/python/Makefile Mon Sep 06 15:16:03 2010 +0100
+++ b/tools/python/Makefile Tue Sep 07 15:22:24 2010 +0100
@@ -59,6 +59,7 @@ refresh-po: $(POTFILE)
.PHONY: install
install: install-messages install-dtd
+ PYTHONPATH=$(XEN_ROOT)/tools/libxl $(PYTHON) genwrap.py
$(XEN_ROOT)/tools/libxl/libxl.idl __pyxl_types.h
CC="$(CC)" CFLAGS="$(CFLAGS)" $(PYTHON) setup.py install \
$(PYTHON_PREFIX_ARG) --root="$(DESTDIR)" --force
diff -r d11a52daace3 tools/python/genwrap.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/genwrap.py Tue Sep 07 15:22:24 2010 +0100
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+
+import sys
+import re
+
+import libxltypes
+
+def py_wrapstruct(ty):
+ l = []
+ l.append("typedef struct {")
+ l.append(" PyObject_HEAD;")
+ l.append(" %s obj;"%ty.typename);
+ l.append("}Py_%s;"%ty.rawname)
+ l.append("")
+ return "\n".join(l) + "\n\n"
+
+def py_type(ty):
+ if ty == libxltypes.bool or isinstance(ty, libxltypes.BitField) and
ty.width == 1:
+ return "Bool"
+ if isinstance(ty, libxltypes.Number):
+ # FIXME: support unsigned properly
+ return "Int"
+ if ty == libxltypes.string:
+ return "String"
+ return None
+
+def py_attrib_get(ty, f):
+ if f.name == None:
+ return ""
+ t = py_type(f.type)
+ l = []
+ l.append("static PyObject *py_%s_%s_get(Py_%s *self, void
*priv)"%(ty.rawname, f.name, ty.rawname))
+ l.append("{")
+ if t == "Int":
+ l.append(" return
PyLong_FromUnsignedLong(self->obj.%s);"%f.name)
+ else:
+ l.append(" // %s"%t)
+ l.append(" return NULL;")
+ l.append("}")
+ return ''\n''.join(l) + "\n\n"
+
+def py_attrib_set(ty, f):
+ if f.name == None:
+ return ""
+ t = py_type(f.type)
+ l = []
+ l.append("static int py_%s_%s_set(Py_%s *self, PyObject *v, void
*priv)"%(ty.rawname, f.name, ty.rawname))
+ l.append("{")
+ if t == "Int":
+ l.append(" self->obj.%s =
PyLong_AsLongLong(v);"%f.name)
+ l.append(" return 0;")
+ else:
+ l.append(" // %s"%t)
+ l.append(" return -1;")
+ l.append("}")
+ return ''\n''.join(l) + "\n\n"
+
+def py_object_def(ty):
+ l = []
+ funcs="""
+static void Py%s_dealloc(Py_%s *self)
+{
+ self->ob_type->tp_free((PyObject *)self);
+}
+
+static int Py%s_init(Py_%s *self, PyObject *args, PyObject *kwds)
+{
+ return 0;
+}
+
+static PyObject *Py%s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ Py_%s *self = (Py_%s *)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+ memset(&self->obj, 0, sizeof(self->obj));
+ return (PyObject *)self;
+}
+"""%tuple(ty.rawname for x in range(7))
+
+ l.append("static PyGetSetDef Py%s_getset[] = {"%ty.rawname)
+ for f in ty.fields:
+ if f.name == None:
+ continue
+ l.append(" { .name = \"%s\", "%f.name)
+ l.append(" .get = (getter)py_%s_%s_get, "%(ty.rawname,
f.name))
+ l.append(" .set = (setter)py_%s_%s_set },"%(ty.rawname,
f.name))
+ l.append(" { .name = NULL }")
+ l.append("};")
+ struct="""
+static PyTypeObject Py%s_Type= {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ PKG ".%s",
+ sizeof(Py_%s),
+ 0,
+ (destructor)Py%s_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ NULL, /* 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 */
+ NULL, /* tp_call */
+ NULL, /* tp_str */
+ NULL, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "%s", /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ NULL, /* tp_methods */
+ NULL, /* tp_members */
+ Py%s_getset, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Py%s_init, /* tp_init */
+ NULL, /* tp_alloc */
+ Py%s_new, /* tp_new */
+};
+
+"""%tuple(ty.rawname for x in range(8))
+ return funcs + ''\n''.join(l) + "\n\n" + struct
+
+def py_initfuncs(types):
+ l = []
+ l.append("static void gentypes__init(PyObject *m)")
+ l.append("{")
+ for ty in types:
+ l.append(" if (PyType_Ready(&Py%s_Type) >= 0)
{"%ty.rawname)
+ l.append(" Py_INCREF(&Py%s_Type);"%ty.rawname)
+ l.append(" PyModule_AddObject(m, \"%s\",
(PyObject *)&Py%s_Type);"%(ty.rawname, ty.rawname))
+ l.append(" }")
+ l.append("}")
+ return ''\n''.join(l) + "\n\n"
+
+if __name__ == ''__main__'':
+ if len(sys.argv) < 3:
+ print >>sys.stderr, "Usage: genwrap.py <idl>
<pythonwrapper>"
+ sys.exit(1)
+
+ idl = sys.argv[1]
+ (_,types) = libxltypes.parse(idl)
+
+ wrapper = sys.argv[2]
+ f = open(wrapper, "w")
+ f.write("""#ifndef __PYXL_TYPES_H
+#define __PYXL_TYPES_H
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % " ".join(sys.argv))
+ for ty in types:
+ f.write("/* Attribute get/set functions for %s
*/\n"%ty.typename)
+ f.write(py_wrapstruct(ty))
+ for a in ty.fields:
+ f.write(py_attrib_get(ty,a))
+ f.write(py_attrib_set(ty,a))
+ f.write(py_object_def(ty))
+ f.write(py_initfuncs(types))
+ f.write("""#endif /* __PYXL_TYPES_H */\n""")
+ f.close()
diff -r d11a52daace3 tools/python/setup.py
--- a/tools/python/setup.py Mon Sep 06 15:16:03 2010 +0100
+++ b/tools/python/setup.py Tue Sep 07 15:22:24 2010 +0100
@@ -9,14 +9,23 @@ extra_compile_args = [ "-fno-strict-ali
include_dirs = [ XEN_ROOT + "/tools/libxc",
XEN_ROOT + "/tools/xenstore",
XEN_ROOT + "/tools/include",
+ XEN_ROOT + "/tools/libxl",
]
library_dirs = [ XEN_ROOT + "/tools/libxc",
XEN_ROOT + "/tools/xenstore",
+ XEN_ROOT + "/tools/libxl",
+ XEN_ROOT + "/tools/blktap2/control",
]
libraries = [ "xenctrl", "xenguest", "xenstore" ]
+plat = os.uname()[0]
+if plat == ''Linux'':
+ uuid_libs = ["uuid"]
+else:
+ uuid_libs = []
+
xc = Extension("xc",
extra_compile_args = extra_compile_args,
include_dirs = include_dirs + [
"xen/lowlevel/xc" ],
@@ -83,8 +92,14 @@ netlink = Extension("netlink",
sources = [
"xen/lowlevel/netlink/netlink.c",
"xen/lowlevel/netlink/libnetlink.c"])
-modules = [ xc, xs, ptsname, acm, flask ]
-plat = os.uname()[0]
+xl = Extension("xl",
+ extra_compile_args = extra_compile_args,
+ include_dirs = include_dirs + [
"xen/lowlevel/xl" ],
+ library_dirs = library_dirs,
+ libraries = libraries + ["xenlight",
"blktapctl" ] + uuid_libs,
+ sources = [ "xen/lowlevel/xl/xl.c" ])
+
+modules = [ xc, xs, ptsname, acm, flask, xl ]
if plat == ''SunOS'':
modules.extend([ scf, process ])
if plat == ''Linux'':
diff -r d11a52daace3 tools/python/xen/lowlevel/xl/xl.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/lowlevel/xl/xl.c Tue Sep 07 15:22:24 2010 +0100
@@ -0,0 +1,269 @@
+/******************************************************************************
+ * xl.c
+ *
+ * Copyright (c) 2010 Citrix Ltd.
+ * Author: Gianni Tedesco
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <Python.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <xenctrl.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+
+#include <libxl.h>
+#include <libxl_utils.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* Needed for Python versions earlier than 2.3. */
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC DL_EXPORT(void)
+#endif
+
+#define PKG "xen.lowlevel.xl"
+#define CLS "ctx"
+
+static PyObject *xl_error_obj;
+
+#include "_pyxl_types.h"
+
+typedef struct {
+ PyObject_HEAD;
+ libxl_ctx ctx;
+ xentoollog_logger_stdiostream *logger;
+ xentoollog_level minmsglevel;
+} XlObject;
+
+static PyObject *pyxl_list_domains(XlObject *self)
+{
+ libxl_dominfo *cur, *info;
+ PyObject *list;
+ int nr_dom, i;
+
+ info = libxl_list_domain(&self->ctx, &nr_dom);
+ if ( NULL == info )
+ return PyList_New(0);
+
+ list = PyList_New(nr_dom);
+ if ( NULL == list )
+ goto err_mem;
+
+ for(i = 0, cur = info; i < nr_dom; i++, cur++) {
+ Py_dominfo *di;
+ di = (Py_dominfo *)Pydominfo_new(&Pydominfo_Type, NULL, NULL);
+ if ( NULL == di )
+ goto err_mem;
+ memcpy(&di->obj, cur, sizeof(di->obj));
+ PyList_SetItem(list, i, (PyObject *)di);
+ }
+
+ free(info);
+ return list;
+err_mem:
+ Py_DECREF(list);
+ PyErr_SetString(PyExc_MemoryError, "Allocating domain list");
+ return NULL;
+}
+
+static PyObject *pyxl_domid_to_name(XlObject *self, PyObject *args)
+{
+ char *domname;
+ int domid;
+ PyObject *ret;
+
+ if ( !PyArg_ParseTuple(args, "i", &domid) )
+ return NULL;
+
+ domname = libxl_domid_to_name(&self->ctx, domid);
+ ret = PyString_FromString(domname);
+ free(domname);
+
+ return ret;
+}
+
+static PyObject *pyxl_domain_shutdown(XlObject *self, PyObject *args)
+{
+ int domid, req = 0;
+ if ( !PyArg_ParseTuple(args, "i|i", &domid, &req) )
+ return NULL;
+ if ( libxl_domain_shutdown(&self->ctx, domid, req) ) {
+ PyErr_SetString(xl_error_obj, "cannot shutdown domain");
+ return NULL;
+ }
+ return Py_None;
+}
+
+static PyObject *pyxl_domain_destroy(XlObject *self, PyObject *args)
+{
+ int domid, force = 1;
+ if ( !PyArg_ParseTuple(args, "i|i", &domid, &force) )
+ return NULL;
+ if ( libxl_domain_destroy(&self->ctx, domid, force) ) {
+ PyErr_SetString(xl_error_obj, "cannot destroy domain");
+ return NULL;
+ }
+ return Py_None;
+}
+
+static PyMethodDef pyxl_methods[] = {
+ {"list_domains", (PyCFunction)pyxl_list_domains, METH_NOARGS,
+ "List domains"},
+ {"domid_to_name", (PyCFunction)pyxl_domid_to_name, METH_VARARGS,
+ "Retrieve name from domain-id"},
+ {"domain_shutdown", (PyCFunction)pyxl_domain_shutdown,
METH_VARARGS,
+ "Shutdown a domain"},
+ {"domain_destroy", (PyCFunction)pyxl_domain_destroy,
METH_VARARGS,
+ "Destroy a domain"},
+ { NULL, NULL, 0, NULL }
+};
+
+static PyObject *PyXl_getattr(PyObject *obj, char *name)
+{
+ return Py_FindMethod(pyxl_methods, obj, name);
+}
+
+static PyObject *PyXl_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ XlObject *self = (XlObject *)type->tp_alloc(type, 0);
+
+ if (self == NULL)
+ return NULL;
+
+ memset(&self->ctx, 0, sizeof(self->ctx));
+ self->logger = NULL;
+ self->minmsglevel = XTL_PROGRESS;
+
+ return (PyObject *)self;
+}
+
+static int
+PyXl_init(XlObject *self, PyObject *args, PyObject *kwds)
+{
+ self->logger = xtl_createlogger_stdiostream(stderr,
self->minmsglevel, 0);
+ if (!self->logger) {
+ PyErr_SetString(xl_error_obj, "cannot init xl logger");
+ return -1;
+ }
+
+ if ( libxl_ctx_init(&self->ctx, LIBXL_VERSION,
+ (xentoollog_logger*)self->logger) ) {
+ PyErr_SetString(xl_error_obj, "cannot init xl context");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void PyXl_dealloc(XlObject *self)
+{
+ libxl_ctx_free(&self->ctx);
+ if ( self->logger )
+ xtl_logger_destroy((xentoollog_logger*)self->logger);
+
+ self->ob_type->tp_free((PyObject *)self);
+}
+
+static PyTypeObject PyXlType = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ PKG "." CLS,
+ sizeof(XlObject),
+ 0,
+ (destructor)PyXl_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ PyXl_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 */
+ NULL, /* tp_call */
+ NULL, /* tp_str */
+ NULL, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "libxenlight connection", /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ pyxl_methods, /* tp_methods */
+ NULL, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyXl_init, /* tp_init */
+ NULL, /* tp_alloc */
+ PyXl_new, /* tp_new */
+};
+
+static PyMethodDef xl_methods[] = { { NULL } };
+
+PyMODINIT_FUNC initxl(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyXlType) < 0)
+ return;
+
+ m = Py_InitModule(PKG, xl_methods);
+
+ if (m == NULL)
+ return;
+
+ xl_error_obj = PyErr_NewException(PKG ".Error",
PyExc_RuntimeError, NULL);
+
+ Py_INCREF(&PyXlType);
+ PyModule_AddObject(m, CLS, (PyObject *)&PyXlType);
+
+ Py_INCREF(xl_error_obj);
+ PyModule_AddObject(m, "Error", xl_error_obj);
+
+ gentypes__init(m);
+}
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * End:
+ */
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel