From d18147940f8e330881681c7ef68505ac4463e652 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Fri, 21 Dec 2012 13:15:19 +0000 Subject: Introduce an LXC specific public API & library This patch introduces support for LXC specific public APIs. In common with what was done for QEMU, this creates a libvirt_lxc.so library and libvirt/libvirt-lxc.h header file. The actual APIs are int virDomainLxcOpenNamespace(virDomainPtr domain, int **fdlist, unsigned int flags); int virDomainLxcEnterNamespace(virDomainPtr domain, unsigned int nfdlist, int *fdlist, unsigned int *noldfdlist, int **oldfdlist, unsigned int flags); which provide a way to use the setns() system call to move the calling process into the container's namespace. It is not practical to write in a generically applicable manner. The nearest that we could get to such an API would be an API which allows to pass a command + argv to be executed inside a container. Even if we had such a generic API, this LXC specific API is still useful, because it allows the caller to maintain the current process context, in particular any I/O streams they have open. NB the virDomainLxcEnterNamespace() API is special in that it runs client side, so does not involve the internal driver API. Signed-off-by: Daniel P. Berrange --- Makefile.am | 35 +++++++-- generator.py | 172 ++++++++++++++++++++++++++++++++++++++++++- libvirt-lxc-override-api.xml | 19 +++++ libvirt-lxc-override.c | 141 +++++++++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+), 6 deletions(-) create mode 100644 libvirt-lxc-override-api.xml create mode 100644 libvirt-lxc-override.c diff --git a/Makefile.am b/Makefile.am index 97f21c3..dd69600 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,8 @@ EXTRA_DIST = \ libvirt-override.c \ libvirt-override.py \ libvirt-override-api.xml \ + libvirt-lxc-override.c \ + libvirt-lxc-override-api.xml \ libvirt-qemu-override.c \ libvirt-qemu-override-api.xml \ $(CLASSES_EXTRA) \ @@ -47,10 +49,13 @@ mylibs = \ myqemulibs = \ $(top_builddir)/src/libvirt-qemu.la \ $(top_builddir)/gnulib/lib/libgnu.la +mylxclibs = \ + $(top_builddir)/src/libvirt-lxc.la \ + $(top_builddir)/gnulib/lib/libgnu.la -all-local: libvirt.py libvirt_qemu.py +all-local: libvirt.py libvirt_qemu.py libvirt_lxc.py -pyexec_LTLIBRARIES = libvirtmod.la libvirtmod_qemu.la +pyexec_LTLIBRARIES = libvirtmod.la libvirtmod_qemu.la libvirtmod_lxc.la libvirtmod_la_SOURCES = libvirt-override.c typewrappers.c nodist_libvirtmod_la_SOURCES = libvirt.c libvirt.h @@ -74,6 +79,17 @@ libvirtmod_qemu_la_LDFLAGS = -module -avoid-version -shared -L$(top_builddir)/sr libvirtmod_qemu_la_LIBADD = $(myqemulibs) \ $(CYGWIN_EXTRA_LIBADD) $(CYGWIN_EXTRA_PYTHON_LIBADD) +libvirtmod_lxc_la_SOURCES = libvirt-lxc-override.c typewrappers.c +nodist_libvirtmod_lxc_la_SOURCES = libvirt-lxc.c libvirt-lxc.h +# Python <= 2.4 header files contain a redundant decl, hence we +# need extra flags here +libvirtmod_lxc_la_CFLAGS = $(WARN_PYTHON_CFLAGS) + +libvirtmod_lxc_la_LDFLAGS = -module -avoid-version -shared -L$(top_builddir)/src/.libs \ + $(CYGWIN_EXTRA_LDFLAGS) +libvirtmod_lxc_la_LIBADD = $(mylxclibs) \ + $(CYGWIN_EXTRA_LIBADD) $(CYGWIN_EXTRA_PYTHON_LIBADD) + GENERATE = generator.py API_DESC = $(top_srcdir)/docs/libvirt-api.xml $(srcdir)/libvirt-override-api.xml GENERATED= libvirt-export.c \ @@ -87,18 +103,26 @@ QEMU_GENERATED= libvirt-qemu-export.c \ libvirt-qemu.h \ libvirt_qemu.py -$(GENERATE).stamp: $(srcdir)/$(GENERATE) $(API_DESC) $(QEMU_API_DESC) +LXC_API_DESC = $(top_srcdir)/docs/libvirt-lxc-api.xml $(srcdir)/libvirt-lxc-override-api.xml +LXC_GENERATED= libvirt-lxc-export.c \ + libvirt-lxc.c \ + libvirt-lxc.h \ + libvirt_lxc.py + +$(GENERATE).stamp: $(srcdir)/$(GENERATE) $(API_DESC) $(QEMU_API_DESC) $(LXC_API_DESC) $(AM_V_GEN)$(PYTHON) $(srcdir)/$(GENERATE) $(PYTHON) && \ touch $@ -$(GENERATED) $(QEMU_GENERATED): $(GENERATE).stamp +$(GENERATED) $(QEMU_GENERATED) $(LXC_GENERATED): $(GENERATE).stamp $(libvirtmod_la_OBJECTS): $(GENERATED) $(libvirtmod_qemu_la_OBJECTS): $(QEMU_GENERATED) +$(libvirtmod_lxc_la_OBJECTS): $(LXC_GENERATED) install-data-local: $(mkinstalldirs) $(DESTDIR)$(pyexecdir) $(INSTALL) -m 0644 libvirt.py $(DESTDIR)$(pyexecdir) + $(INSTALL) -m 0644 libvirt_lxc.py $(DESTDIR)$(pyexecdir) $(INSTALL) -m 0644 libvirt_qemu.py $(DESTDIR)$(pyexecdir) $(mkinstalldirs) $(DESTDIR)$(DOCS_DIR) @(for doc in $(DOCS) ; \ @@ -106,9 +130,10 @@ install-data-local: uninstall-local: rm -f $(DESTDIR)$(pyexecdir)/libvirt.py + rm -f $(DESTDIR)$(pyexecdir)/libvirt_lxc.py rm -f $(DESTDIR)$(pyexecdir)/libvirt_qemu.py -CLEANFILES= $(GENERATED) $(QEMU_GENERATED) $(GENERATE).stamp +CLEANFILES= $(GENERATED) $(QEMU_GENERATED) $(LXC_GENERATED) $(GENERATE).stamp else all: diff --git a/generator.py b/generator.py index bae4edc..f984dfa 100755 --- a/generator.py +++ b/generator.py @@ -4,8 +4,10 @@ # functions = {} +lxc_functions = {} qemu_functions = {} enums = {} # { enumType: { enumConstant: enumValue } } +lxc_enums = {} # { enumType: { enumConstant: enumValue } } qemu_enums = {} # { enumType: { enumConstant: enumValue } } import os @@ -123,6 +125,8 @@ class docParser(xml.sax.handler.ContentHandler): if (attrs['file'] == "libvirt" or attrs['file'] == "virterror"): enum(attrs['type'],attrs['name'],attrs['value']) + elif attrs['file'] == "libvirt-lxc": + lxc_enum(attrs['type'],attrs['name'],attrs['value']) elif attrs['file'] == "libvirt-qemu": qemu_enum(attrs['type'],attrs['name'],attrs['value']) @@ -138,6 +142,11 @@ class docParser(xml.sax.handler.ContentHandler): self.function_return, self.function_args, self.function_file, self.function_module, self.function_cond) + elif self.function_module == "libvirt-lxc": + lxc_function(self.function, self.function_descr, + self.function_return, self.function_args, + self.function_file, self.function_module, + self.function_cond) elif self.function_module == "libvirt-qemu": qemu_function(self.function, self.function_descr, self.function_return, self.function_args, @@ -148,6 +157,11 @@ class docParser(xml.sax.handler.ContentHandler): self.function_return, self.function_args, self.function_file, self.function_module, self.function_cond) + elif self.function_file == "python-lxc": + lxc_function(self.function, self.function_descr, + self.function_return, self.function_args, + self.function_file, self.function_module, + self.function_cond) elif self.function_file == "python-qemu": qemu_function(self.function, self.function_descr, self.function_return, self.function_args, @@ -184,6 +198,9 @@ def function(name, desc, ret, args, file, module, cond): def qemu_function(name, desc, ret, args, file, module, cond): qemu_functions[name] = (desc, ret, args, file, module, cond) +def lxc_function(name, desc, ret, args, file, module, cond): + lxc_functions[name] = (desc, ret, args, file, module, cond) + def enum(type, name, value): if not enums.has_key(type): enums[type] = {} @@ -208,6 +225,11 @@ def enum(type, name, value): if name[-5:] != '_LAST': enums[type][name] = value +def lxc_enum(type, name, value): + if not lxc_enums.has_key(type): + lxc_enums[type] = {} + lxc_enums[type][name] = value + def qemu_enum(type, name, value): if not qemu_enums.has_key(type): qemu_enums[type] = {} @@ -222,10 +244,12 @@ def qemu_enum(type, name, value): ####################################################################### functions_failed = [] +lxc_functions_failed = [] qemu_functions_failed = [] functions_skipped = [ "virConnectListDomains", ] +lxc_functions_skipped = [] qemu_functions_skipped = [] skipped_modules = { @@ -430,6 +454,10 @@ skip_impl = ( 'virNodeGetCPUMap', ) +lxc_skip_impl = ( + 'virDomainLxcOpenNamespace', +) + qemu_skip_impl = ( 'virDomainQemuMonitorCommand', 'virDomainQemuAgentCommand', @@ -501,6 +529,8 @@ skip_function = ( "virStorageVolGetConnect", ) +lxc_skip_function = ( +) qemu_skip_function = ( #"virDomainQemuAttach", ) @@ -511,6 +541,7 @@ function_skip_python_impl = ( # be exposed in bindings ) +lxc_function_skip_python_impl = () qemu_function_skip_python_impl = () function_skip_index_one = ( @@ -521,6 +552,7 @@ def print_function_wrapper(module, name, output, export, include): global py_types global unknown_types global functions + global lxc_functions global qemu_functions global skipped_modules global function_skip_python_impl @@ -528,6 +560,8 @@ def print_function_wrapper(module, name, output, export, include): try: if module == "libvirt": (desc, ret, args, file, mod, cond) = functions[name] + if module == "libvirt-lxc": + (desc, ret, args, file, mod, cond) = lxc_functions[name] if module == "libvirt-qemu": (desc, ret, args, file, mod, cond) = qemu_functions[name] except: @@ -543,6 +577,12 @@ def print_function_wrapper(module, name, output, export, include): if name in skip_impl: # Don't delete the function entry in the caller. return 1 + elif module == "libvirt-lxc": + if name in lxc_skip_function: + return 0 + if name in lxc_skip_impl: + # Don't delete the function entry in the caller. + return 1 elif module == "libvirt-qemu": if name in qemu_skip_function: return 0 @@ -643,6 +683,10 @@ def print_function_wrapper(module, name, output, export, include): include.write("libvirt_%s(PyObject *self, PyObject *args);\n" % (name)); export.write(" { (char *)\"%s\", libvirt_%s, METH_VARARGS, NULL },\n" % (name, name)) + elif module == "libvirt-lxc": + include.write("libvirt_lxc_%s(PyObject *self, PyObject *args);\n" % (name)); + export.write(" { (char *)\"%s\", libvirt_lxc_%s, METH_VARARGS, NULL },\n" % + (name, name)) elif module == "libvirt-qemu": include.write("libvirt_qemu_%s(PyObject *self, PyObject *args);\n" % (name)); export.write(" { (char *)\"%s\", libvirt_qemu_%s, METH_VARARGS, NULL },\n" % @@ -666,6 +710,8 @@ def print_function_wrapper(module, name, output, export, include): output.write("PyObject *\n") if module == "libvirt": output.write("libvirt_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) + elif module == "libvirt-lxc": + output.write("libvirt_lxc_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) elif module == "libvirt-qemu": output.write("libvirt_qemu_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) output.write(" PyObject *args") @@ -698,6 +744,9 @@ def print_function_wrapper(module, name, output, export, include): if module == "libvirt": if name in function_skip_python_impl: return 0 + elif module == "libvirt-lxc": + if name in lxc_function_skip_python_impl: + return 0 elif module == "libvirt-qemu": if name in qemu_function_skip_python_impl: return 0 @@ -708,7 +757,7 @@ def buildStubs(module): global py_return_types global unknown_types - if module not in ["libvirt", "libvirt-qemu"]: + if module not in ["libvirt", "libvirt-qemu", "libvirt-lxc"]: print "ERROR: Unknown module type: %s" % module return None @@ -716,6 +765,10 @@ def buildStubs(module): funcs = functions funcs_failed = functions_failed funcs_skipped = functions_skipped + elif module == "libvirt-lxc": + funcs = lxc_functions + funcs_failed = lxc_functions_failed + funcs_skipped = functions_skipped elif module == "libvirt-qemu": funcs = qemu_functions funcs_failed = qemu_functions_failed @@ -1111,6 +1164,8 @@ def functionCompare(info1, info2): def writeDoc(module, name, args, indent, output): if module == "libvirt": funcs = functions + elif module == "libvirt-lxc": + funcs = lxc_functions elif module == "libvirt-qemu": funcs = qemu_functions if funcs[name][0] is None or funcs[name][0] == "": @@ -1762,11 +1817,126 @@ def qemuBuildWrappers(module): fd.close() +def lxcBuildWrappers(module): + global lxc_functions + + if not module == "libvirt-lxc": + print "ERROR: only libvirt-lxc is supported" + return None + + extra_file = os.path.join(srcPref, "%s-override.py" % module) + extra = None + + fd = open("libvirt_lxc.py", "w") + + if os.path.exists(extra_file): + extra = open(extra_file, "r") + fd.write("#! " + python + " -i\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + fd.write("# This file is automatically written by generator.py. Any changes\n") + fd.write("# made here will be lost.\n") + fd.write("#\n") + fd.write("# To change the manually written methods edit " + module + "-override.py\n") + fd.write("# To change the automatically written methods edit generator.py\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + if extra != None: + fd.writelines(extra.readlines()) + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + fd.write("# Automatically written part of python bindings for libvirt\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + if extra != None: + extra.close() + + fd.write("try:\n") + fd.write(" import libvirtmod_lxc\n") + fd.write("except ImportError, lib_e:\n") + fd.write(" try:\n") + fd.write(" import cygvirtmod_lxc as libvirtmod_lxc\n") + fd.write(" except ImportError, cyg_e:\n") + fd.write(" if str(cyg_e).count(\"No module named\"):\n") + fd.write(" raise lib_e\n\n") + + fd.write("import libvirt\n\n"); + fd.write("#\n# Functions from module %s\n#\n\n" % module) + # + # Generate functions directly, no classes + # + for name in lxc_functions.keys(): + func = nameFixup(name, 'None', None, None) + (desc, ret, args, file, mod, cond) = lxc_functions[name] + fd.write("def %s(" % func) + n = 0 + for arg in args: + if n != 0: + fd.write(", ") + fd.write("%s" % arg[0]) + n = n + 1 + fd.write("):\n") + writeDoc(module, name, args, ' ', fd); + + if ret[0] != "void": + fd.write(" ret = "); + else: + fd.write(" "); + fd.write("libvirtmod_lxc.%s(" % name) + n = 0 + + conn = None + + for arg in args: + if arg[1] == "virConnectPtr": + conn = arg[0] + + if n != 0: + fd.write(", "); + if arg[1] in ["virDomainPtr", "virConnectPtr"]: + # FIXME: This might have problem if the function + # has multiple args which are objects. + fd.write("%s.%s" % (arg[0], "_o")) + else: + fd.write("%s" % arg[0]) + n = n + 1 + fd.write(")\n"); + + if ret[0] != "void": + fd.write(" if ret is None: raise libvirt.libvirtError('" + name + "() failed')\n") + if ret[0] == "virDomainPtr": + fd.write(" __tmp = virDomain(" + conn + ",_obj=ret)\n") + fd.write(" return __tmp\n") + else: + fd.write(" return ret\n") + + fd.write("\n") + + # + # Generate enum constants + # + for type,enum in lxc_enums.items(): + fd.write("# %s\n" % type) + items = enum.items() + items.sort(lambda i1,i2: cmp(long(i1[1]),long(i2[1]))) + for name,value in items: + fd.write("%s = %s\n" % (name,value)) + fd.write("\n"); + + fd.close() + + quiet = 0 if buildStubs("libvirt") < 0: sys.exit(1) +if buildStubs("libvirt-lxc") < 0: + sys.exit(1) if buildStubs("libvirt-qemu") < 0: sys.exit(1) buildWrappers("libvirt") +lxcBuildWrappers("libvirt-lxc") qemuBuildWrappers("libvirt-qemu") sys.exit(0) diff --git a/libvirt-lxc-override-api.xml b/libvirt-lxc-override-api.xml new file mode 100644 index 0000000..db0d45d --- /dev/null +++ b/libvirt-lxc-override-api.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/libvirt-lxc-override.c b/libvirt-lxc-override.c new file mode 100644 index 0000000..c80668e --- /dev/null +++ b/libvirt-lxc-override.c @@ -0,0 +1,141 @@ +/* + * libvir.c: this modules implements the main part of the glue of the + * libvir library and the Python interpreter. It provides the + * entry points where an automatically generated stub is + * unpractical + * + * Copyright (C) 2012-2013 Red Hat, Inc. + * + * Daniel Veillard + */ + +#include + +/* Horrible kludge to work around even more horrible name-space pollution + via Python.h. That file includes /usr/include/python2.5/pyconfig*.h, + which has over 180 autoconf-style HAVE_* definitions. Shame on them. */ +#undef HAVE_PTHREAD_H + +#include +#include "libvirt/libvirt-lxc.h" +#include "libvirt/virterror.h" +#include "typewrappers.h" +#include "libvirt-lxc.h" +#include "viralloc.h" +#include "virfile.h" + +#ifndef __CYGWIN__ +extern void initlibvirtmod_lxc(void); +#else +extern void initcygvirtmod_lxc(void); +#endif + +#if 0 +# define DEBUG_ERROR 1 +#endif + +#if DEBUG_ERROR +# define DEBUG(fmt, ...) \ + printf(fmt, __VA_ARGS__) +#else +# define DEBUG(fmt, ...) \ + do {} while (0) +#endif + +/* The two-statement sequence "Py_INCREF(Py_None); return Py_None;" + is so common that we encapsulate it here. Now, each use is simply + return VIR_PY_NONE; */ +#define VIR_PY_NONE (Py_INCREF (Py_None), Py_None) +#define VIR_PY_INT_FAIL (libvirt_intWrap(-1)) +#define VIR_PY_INT_SUCCESS (libvirt_intWrap(0)) + +/************************************************************************ + * * + * Statistics * + * * + ************************************************************************/ + +static PyObject * +libvirt_lxc_virDomainLxcOpenNamespace(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int flags; + int c_retval; + int *fdlist = NULL; + int i; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virDomainLxcOpenNamespace", + &pyobj_domain, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (domain == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainLxcOpenNamespace(domain, &fdlist, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = PyList_New(c_retval); + for (i = 0 ; i < c_retval ; i++) { + PyObject *item = NULL; + + if ((item = PyInt_FromLong(fdlist[i])) == NULL) + goto error; + + if (PyList_Append(py_retval, item) < 0) { + Py_DECREF(item); + goto error; + } + } + return py_retval; + +error: + for (i = 0 ; i < c_retval ; i++) { + VIR_FORCE_CLOSE(fdlist[i]); + } + VIR_FREE(fdlist); + return VIR_PY_NONE; +} +/************************************************************************ + * * + * The registration stuff * + * * + ************************************************************************/ +static PyMethodDef libvirtLxcMethods[] = { +#include "libvirt-lxc-export.c" + {(char *) "virDomainLxcOpenNamespace", libvirt_lxc_virDomainLxcOpenNamespace, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +void +#ifndef __CYGWIN__ +initlibvirtmod_lxc +#else +initcygvirtmod_lxc +#endif + (void) +{ + static int initialized = 0; + + if (initialized != 0) + return; + + if (virInitialize() < 0) + return; + + /* initialize the python extension module */ + Py_InitModule((char *) +#ifndef __CYGWIN__ + "libvirtmod_lxc" +#else + "cygvirtmod_lxc" +#endif + , libvirtLxcMethods); + + initialized = 1; +} -- cgit