python-lxc-0.1/0000775000175000017500000000000012375151316012441 5ustar chuckchuckpython-lxc-0.1/lxc.c0000664000175000017500000014426012375151311013375 0ustar chuckchuck/* * python-lxc: Python bindings for LXC * * (C) Copyright Canonical Ltd. 2012-2013 * * Authors: * Stéphane Graber * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include "structmember.h" #include #include #include #include #define INIT_MODULE(m) init ## m static int PyUnicode_FSConverter(PyObject *unicode, void *result) { PyObject *bytes; char **str = result; if (unicode == NULL) return 1; if (!PyUnicode_FSConverter(unicode, &bytes)) return 0; *str = strdup(PyBytes_AS_STRING(bytes)); Py_XDECREF(bytes); return 1; } /* Helper functions */ int lxc_wait_for_pid_status(pid_t pid) { int status, ret; again: ret = waitpid(pid, &status, 0); if (ret == -1) { if (errno == EINTR) goto again; return -1; } if (ret != pid) goto again; return status; } char** convert_tuple_to_char_pointer_array(PyObject *argv) { int argc; int i, j; /* not a list or tuple */ if (!PyList_Check(argv) && !PyTuple_Check(argv)) { PyErr_SetString(PyExc_TypeError, "Expected list or tuple."); return NULL; } argc = PySequence_Fast_GET_SIZE(argv); char **result = (char**) calloc(argc + 1, sizeof(char*)); if (result == NULL) { PyErr_SetNone(PyExc_MemoryError); return NULL; } for (i = 0; i < argc; i++) { PyObject *pyobj = PySequence_Fast_GET_ITEM(argv, i); assert(pyobj != NULL); char *str = NULL; // PyObject *pystr = NULL; // pystr = PyUnicode_AsUTF8String(pyobj); // if (!pystr) { /* Maybe it wasn't UTF-8 encoded. An exception is already set. */ // goto error; // } str = PyBytes_AsString(pyobj); if (!str) { /* Maybe pystr wasn't a valid object. An exception is already set. */ // Py_DECREF(pystr); goto error; } /* We must make a copy of str, because it points into internal memory * which we do not own. Assume it's NULL terminated, otherwise we'd * have to use PyUnicode_AsUTF8AndSize() and be explicit about copying * the memory. */ result[i] = strdup(str); /* Do not decref pyobj since we stole a reference by using * PyTuple_GET_ITEM(). */ // Py_DECREF(pystr); if (result[i] == NULL) { PyErr_SetNone(PyExc_MemoryError); goto error; } } result[argc] = NULL; return result; error: /* We can only iterate up to but not including i because malloc() does not * initialize its memory. Thus if we got here, i points to the index * after the last strdup'd entry in result. */ for (j = 0; j < i; j++) free(result[j]); free(result); return NULL; } struct lxc_attach_python_payload { PyObject *fn; PyObject *arg; }; static int lxc_attach_python_exec(void* _payload) { struct lxc_attach_python_payload *payload = (struct lxc_attach_python_payload *)_payload; PyObject *result = PyObject_CallFunctionObjArgs(payload->fn, payload->arg, NULL); if (!result) { PyErr_Print(); return -1; } if (PyLong_Check(result)) return (int)PyLong_AsLong(result); else return -1; } static void lxc_attach_free_options(lxc_attach_options_t *options); static lxc_attach_options_t *lxc_attach_parse_options(PyObject *kwds) { static char *kwlist[] = {"attach_flags", "namespaces", "personality", "initial_cwd", "uid", "gid", "env_policy", "extra_env_vars", "extra_keep_env", "stdin", "stdout", "stderr", NULL}; long temp_uid, temp_gid; int temp_env_policy; PyObject *extra_env_vars_obj = NULL; PyObject *extra_keep_env_obj = NULL; PyObject *stdin_obj = NULL; PyObject *stdout_obj = NULL; PyObject *stderr_obj = NULL; PyObject *initial_cwd_obj = NULL; PyObject *dummy = NULL; bool parse_result; lxc_attach_options_t default_options = LXC_ATTACH_OPTIONS_DEFAULT; lxc_attach_options_t *options = malloc(sizeof(*options)); if (!options) { PyErr_SetNone(PyExc_MemoryError); return NULL; } memcpy(options, &default_options, sizeof(*options)); /* we need some dummy variables because we can't be sure * the data types match completely */ temp_uid = -1; temp_gid = -1; temp_env_policy = options->env_policy; /* we need a dummy tuple */ dummy = PyTuple_New(0); parse_result = PyArg_ParseTupleAndKeywords(dummy, kwds, "|iilO&lliOOOOO", kwlist, &options->attach_flags, &options->namespaces, &options->personality, PyUnicode_FSConverter, &initial_cwd_obj, &temp_uid, &temp_gid, &temp_env_policy, &extra_env_vars_obj, &extra_keep_env_obj, &stdin_obj, &stdout_obj, &stderr_obj); /* immediately get rid of the dummy tuple */ Py_DECREF(dummy); if (!parse_result) { lxc_attach_free_options(options); return NULL; } /* duplicate the string, so we don't depend on some random Python object */ if (initial_cwd_obj != NULL) { options->initial_cwd = strndup(PyBytes_AsString(initial_cwd_obj), PyBytes_Size(initial_cwd_obj)); Py_DECREF(initial_cwd_obj); } /* do the type conversion from the types that match the parse string */ if (temp_uid != -1) options->uid = (uid_t)temp_uid; if (temp_gid != -1) options->gid = (gid_t)temp_gid; options->env_policy = (lxc_attach_env_policy_t)temp_env_policy; if (extra_env_vars_obj) options->extra_env_vars = convert_tuple_to_char_pointer_array(extra_env_vars_obj); if (extra_keep_env_obj) options->extra_keep_env = convert_tuple_to_char_pointer_array(extra_keep_env_obj); if (stdin_obj) { options->stdin_fd = PyObject_AsFileDescriptor(stdin_obj); if (options->stdin_fd < 0) { lxc_attach_free_options(options); return NULL; } } if (stdout_obj) { options->stdout_fd = PyObject_AsFileDescriptor(stdout_obj); if (options->stdout_fd < 0) { lxc_attach_free_options(options); return NULL; } } if (stderr_obj) { options->stderr_fd = PyObject_AsFileDescriptor(stderr_obj); if (options->stderr_fd < 0) { lxc_attach_free_options(options); return NULL; } } return options; } void lxc_attach_free_options(lxc_attach_options_t *options) { int i; if (!options) return; if (options->initial_cwd) free(options->initial_cwd); if (options->extra_env_vars) { for (i = 0; options->extra_env_vars[i]; i++) free(options->extra_env_vars[i]); free(options->extra_env_vars); } if (options->extra_keep_env) { for (i = 0; options->extra_keep_env[i]; i++) free(options->extra_keep_env[i]); free(options->extra_keep_env); } free(options); } /* Module functions */ static PyObject * LXC_attach_run_command(PyObject *self, PyObject *arg) { PyObject *args_obj = NULL; int i, rv; lxc_attach_command_t cmd = { NULL, /* program */ NULL /* argv[] */ }; if (!PyArg_ParseTuple(arg, "sO", (const char**)&cmd.program, &args_obj)) return NULL; if (args_obj && PyList_Check(args_obj)) { cmd.argv = convert_tuple_to_char_pointer_array(args_obj); } else { PyErr_Format(PyExc_TypeError, "Second part of tuple passed to " "attach_run_command must be a list."); return NULL; } if (!cmd.argv) return NULL; rv = lxc_attach_run_command(&cmd); for (i = 0; cmd.argv[i]; i++) free(cmd.argv[i]); free(cmd.argv); return PyLong_FromLong(rv); } static PyObject * LXC_attach_run_shell(PyObject *self, PyObject *arg) { int rv; rv = lxc_attach_run_shell(NULL); return PyLong_FromLong(rv); } static PyObject * LXC_get_global_config_item(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char* key = NULL; const char* value = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) return NULL; value = lxc_get_global_config_item(key); if (!value) { PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); return NULL; } return PyUnicode_FromString(value); } static PyObject * LXC_get_version(PyObject *self, PyObject *args) { return PyUnicode_FromString(lxc_get_version()); } static PyObject * LXC_list_containers(PyObject *self, PyObject *args, PyObject *kwds) { char **names = NULL; PyObject *list = NULL; int list_count = 0; int list_active = 1; int list_defined = 1; PyObject *py_list_active = NULL; PyObject *py_list_defined = NULL; char* config_path = NULL; int i = 0; PyObject *vargs = NULL; static char *kwlist[] = {"active", "defined", "config_path", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOs", kwlist, &py_list_active, &py_list_defined, &config_path, &vargs)) return NULL; /* We default to listing everything */ if (py_list_active && py_list_active != Py_True) { list_active = 0; } if (py_list_defined && py_list_defined != Py_True) { list_defined = 0; } /* Call the right API function based on filters */ if (list_active == 1 && list_defined == 1) list_count = list_all_containers(config_path, &names, NULL); else if (list_active == 1) list_count = list_active_containers(config_path, &names, NULL); else if (list_defined == 1) list_count = list_defined_containers(config_path, &names, NULL); /* Handle failure */ if (list_count < 0) { PyErr_SetString(PyExc_ValueError, "failure to list containers"); return NULL; } /* Generate the tuple */ list = PyTuple_New(list_count); for (i = 0; i < list_count; i++) { PyTuple_SET_ITEM(list, i, PyUnicode_FromString(names[i])); free(names[i]); } free(names); return list; } /* Base type and functions for Container */ typedef struct { PyObject_HEAD struct lxc_container *container; } Container; static void Container_dealloc(Container* self) { lxc_container_put(self->container); Py_TYPE(self)->tp_free((PyObject*)self); } static int Container_init(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"name", "config_path", NULL}; char *name = NULL; PyObject *fs_config_path = NULL; char *config_path = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O&", kwlist, &name, PyUnicode_FSConverter, &fs_config_path)) return -1; if (fs_config_path != NULL) { config_path = PyBytes_AS_STRING(fs_config_path); assert(config_path != NULL); } self->container = lxc_container_new(name, config_path); if (!self->container) { Py_XDECREF(fs_config_path); fprintf(stderr, "%d: error creating container %s\n", __LINE__, name); return -1; } Py_XDECREF(fs_config_path); return 0; } static PyObject * Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { Container *self; self = (Container *)type->tp_alloc(type, 0); return (PyObject *)self; } /* Container properties */ static PyObject * Container_config_file_name(Container *self, void *closure) { return PyUnicode_FromString( self->container->config_file_name(self->container)); } static PyObject * Container_controllable(Container *self, void *closure) { if (self->container->may_control(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_defined(Container *self, void *closure) { if (self->container->is_defined(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_init_pid(Container *self, void *closure) { return PyLong_FromLong(self->container->init_pid(self->container)); } static PyObject * Container_name(Container *self, void *closure) { return PyUnicode_FromString(self->container->name); } static PyObject * Container_running(Container *self, void *closure) { if (self->container->is_running(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_state(Container *self, void *closure) { return PyUnicode_FromString(self->container->state(self->container)); } /* Container Functions */ static PyObject * Container_add_device_node(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"src_path", "dest_path", NULL}; char *src_path = NULL; char *dst_path = NULL; PyObject *py_src_path = NULL; PyObject *py_dst_path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, PyUnicode_FSConverter, &py_src_path, PyUnicode_FSConverter, &py_dst_path)) return NULL; if (py_src_path != NULL) { src_path = PyBytes_AS_STRING(py_src_path); assert(src_path != NULL); } if (py_dst_path != NULL) { dst_path = PyBytes_AS_STRING(py_dst_path); assert(dst_path != NULL); } if (self->container->add_device_node(self->container, src_path, dst_path)) { Py_XDECREF(py_src_path); Py_XDECREF(py_dst_path); Py_RETURN_TRUE; } Py_XDECREF(py_src_path); Py_XDECREF(py_dst_path); Py_RETURN_FALSE; } static PyObject * Container_attach_and_possibly_wait(Container *self, PyObject *args, PyObject *kwds, int wait) { struct lxc_attach_python_payload payload = { NULL, NULL }; lxc_attach_options_t *options = NULL; long ret; pid_t pid; if (!PyArg_ParseTuple(args, "O|O", &payload.fn, &payload.arg)) return NULL; if (!PyCallable_Check(payload.fn)) { PyErr_Format(PyExc_TypeError, "attach: object not callable"); return NULL; } options = lxc_attach_parse_options(kwds); if (!options) return NULL; ret = self->container->attach(self->container, lxc_attach_python_exec, &payload, options, &pid); if (ret < 0) goto out; if (wait) { ret = lxc_wait_for_pid_status(pid); /* handle case where attach fails */ if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) ret = -1; } else { ret = (long)pid; } out: lxc_attach_free_options(options); return PyLong_FromLong(ret); } static PyObject * Container_attach(Container *self, PyObject *args, PyObject *kwds) { return Container_attach_and_possibly_wait(self, args, kwds, 0); } static PyObject * Container_attach_wait(Container *self, PyObject *args, PyObject *kwds) { return Container_attach_and_possibly_wait(self, args, kwds, 1); } static PyObject * Container_clear_config(Container *self, PyObject *args, PyObject *kwds) { self->container->clear_config(self->container); Py_RETURN_NONE; } static PyObject * Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char *key = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &key)) return NULL; if (self->container->clear_config_item(self->container, key)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_clone(Container *self, PyObject *args, PyObject *kwds) { char *newname = NULL; char *config_path = NULL; int flags = 0; char *bdevtype = NULL; char *bdevdata = NULL; unsigned long newsize = 0; char **hookargs = NULL; PyObject *py_hookargs = NULL; PyObject *py_config_path = NULL; struct lxc_container *new_container = NULL; int i = 0; static char *kwlist[] = {"newname", "config_path", "flags", "bdevtype", "bdevdata", "newsize", "hookargs", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O&isskO", kwlist, &newname, PyUnicode_FSConverter, &py_config_path, &flags, &bdevtype, &bdevdata, &newsize, &py_hookargs)) return NULL; if (py_hookargs) { if (PyTuple_Check(py_hookargs)) { hookargs = convert_tuple_to_char_pointer_array(py_hookargs); if (!hookargs) { return NULL; } } else { PyErr_SetString(PyExc_ValueError, "hookargs needs to be a tuple"); return NULL; } } if (py_config_path != NULL) { config_path = PyBytes_AS_STRING(py_config_path); assert(config_path != NULL); } new_container = self->container->clone(self->container, newname, config_path, flags, bdevtype, bdevdata, newsize, hookargs); Py_XDECREF(py_config_path); if (hookargs) { for (i = 0; i < PyTuple_GET_SIZE(py_hookargs); i++) free(hookargs[i]); free(hookargs); } if (new_container == NULL) { Py_RETURN_FALSE; } lxc_container_put(new_container); Py_RETURN_TRUE; } static PyObject * Container_console(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"ttynum", "stdinfd", "stdoutfd", "stderrfd", "escape", NULL}; int ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 1; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", kwlist, &ttynum, &stdinfd, &stdoutfd, &stderrfd, &escape)) return NULL; if (self->container->console(self->container, ttynum, stdinfd, stdoutfd, stderrfd, escape) == 0) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_console_getfd(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"ttynum", NULL}; int ttynum = -1, masterfd; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &ttynum)) return NULL; if (self->container->console_getfd(self->container, &ttynum, &masterfd) < 0) { PyErr_SetString(PyExc_ValueError, "Unable to allocate tty"); return NULL; } return PyLong_FromLong(masterfd); } static PyObject * Container_create(Container *self, PyObject *args, PyObject *kwds) { char* template_name = NULL; int flags = 0; char** create_args = {NULL}; PyObject *retval = NULL; PyObject *vargs = NULL; int i = 0; static char *kwlist[] = {"template", "flags", "args", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|siO", kwlist, &template_name, &flags, &vargs)) return NULL; if (vargs) { if (PyTuple_Check(vargs)) { create_args = convert_tuple_to_char_pointer_array(vargs); if (!create_args) { return NULL; } } else { PyErr_SetString(PyExc_ValueError, "args needs to be a tuple"); return NULL; } } if (self->container->create(self->container, template_name, NULL, NULL, flags, create_args)) retval = Py_True; else retval = Py_False; if (vargs) { /* We cannot have gotten here unless vargs was given and create_args * was successfully allocated. */ for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) free(create_args[i]); free(create_args); } Py_INCREF(retval); return retval; } static PyObject * Container_destroy(Container *self, PyObject *args, PyObject *kwds) { if (self->container->destroy(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_freeze(Container *self, PyObject *args, PyObject *kwds) { if (self->container->freeze(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &key)) return NULL; len = self->container->get_cgroup_item(self->container, key, NULL, 0); if (len < 0) { PyErr_SetString(PyExc_KeyError, "Invalid cgroup entry"); return NULL; } char* value = (char*) malloc(sizeof(char)*len + 1); if (value == NULL) return PyErr_NoMemory(); if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) { PyErr_SetString(PyExc_ValueError, "Unable to read config value"); free(value); return NULL; } ret = PyUnicode_FromString(value); free(value); return ret; } static PyObject * Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) return NULL; len = self->container->get_config_item(self->container, key, NULL, 0); if (len < 0) { PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); return NULL; } if (len == 0) { return PyUnicode_FromString(""); } char* value = (char*) malloc(sizeof(char)*len + 1); if (value == NULL) return PyErr_NoMemory(); if (self->container->get_config_item(self->container, key, value, len + 1) != len) { PyErr_SetString(PyExc_ValueError, "Unable to read config value"); free(value); return NULL; } ret = PyUnicode_FromString(value); free(value); return ret; } static PyObject * Container_get_config_path(Container *self, PyObject *args, PyObject *kwds) { return PyUnicode_FromString( self->container->get_config_path(self->container)); } static PyObject * Container_get_keys(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &key)) return NULL; len = self->container->get_keys(self->container, key, NULL, 0); if (len < 0) { PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); return NULL; } char* value = (char*) malloc(sizeof(char)*len + 1); if (value == NULL) return PyErr_NoMemory(); if (self->container->get_keys(self->container, key, value, len + 1) != len) { PyErr_SetString(PyExc_ValueError, "Unable to read config keys"); free(value); return NULL; } ret = PyUnicode_FromString(value); free(value); return ret; } static PyObject * Container_get_interfaces(Container *self) { int i = 0; char** interfaces = NULL; PyObject* ret; /* Get the interfaces */ interfaces = self->container->get_interfaces(self->container); if (!interfaces) return PyTuple_New(0); /* Count the entries */ while (interfaces[i]) i++; /* Create the new tuple */ ret = PyTuple_New(i); if (!ret) return NULL; /* Add the entries to the tuple and free the memory */ i = 0; while (interfaces[i]) { PyObject *unicode = PyUnicode_FromString(interfaces[i]); if (!unicode) { Py_DECREF(ret); ret = NULL; break; } PyTuple_SET_ITEM(ret, i, unicode); i++; } /* Free the list of IPs */ i = 0; while (interfaces[i]) { free(interfaces[i]); i++; } free(interfaces); return ret; } static PyObject * Container_get_ips(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"interface", "family", "scope", NULL}; char* interface = NULL; char* family = NULL; int scope = 0; int i = 0; char** ips = NULL; PyObject* ret; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ssi", kwlist, &interface, &family, &scope)) return NULL; /* Get the IPs */ ips = self->container->get_ips(self->container, interface, family, scope); if (!ips) return PyTuple_New(0); /* Count the entries */ while (ips[i]) i++; /* Create the new tuple */ ret = PyTuple_New(i); if (!ret) return NULL; /* Add the entries to the tuple and free the memory */ i = 0; while (ips[i]) { PyObject *unicode = PyUnicode_FromString(ips[i]); if (!unicode) { Py_DECREF(ret); ret = NULL; break; } PyTuple_SET_ITEM(ret, i, unicode); i++; } /* Free the list of IPs */ i = 0; while (ips[i]) { free(ips[i]); i++; } free(ips); return ret; } static PyObject * Container_get_running_config_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", NULL}; char* key = NULL; char* value = NULL; PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) return NULL; value = self->container->get_running_config_item(self->container, key); if (!value) Py_RETURN_NONE; ret = PyUnicode_FromString(value); free(value); return ret; } static PyObject * Container_load_config(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"path", NULL}; PyObject *fs_path = NULL; char* path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, PyUnicode_FSConverter, &fs_path)) return NULL; if (fs_path != NULL) { path = PyBytes_AS_STRING(fs_path); assert(path != NULL); } if (self->container->load_config(self->container, path)) { Py_XDECREF(fs_path); Py_RETURN_TRUE; } Py_XDECREF(fs_path); Py_RETURN_FALSE; } static PyObject * Container_reboot(Container *self, PyObject *args, PyObject *kwds) { if (self->container->reboot(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_rename(Container *self, PyObject *args, PyObject *kwds) { char *new_name = NULL; static char *kwlist[] = {"new_name", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &new_name)) return NULL; if (self->container->rename(self->container, new_name)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_remove_device_node(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"src_path", "dest_path", NULL}; char *src_path = NULL; char *dst_path = NULL; PyObject *py_src_path = NULL; PyObject *py_dst_path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, PyUnicode_FSConverter, &py_src_path, PyUnicode_FSConverter, &py_dst_path)) return NULL; if (py_src_path != NULL) { src_path = PyBytes_AS_STRING(py_src_path); assert(src_path != NULL); } if (py_dst_path != NULL) { dst_path = PyBytes_AS_STRING(py_dst_path); assert(dst_path != NULL); } if (self->container->remove_device_node(self->container, src_path, dst_path)) { Py_XDECREF(py_src_path); Py_XDECREF(py_dst_path); Py_RETURN_TRUE; } Py_XDECREF(py_src_path); Py_XDECREF(py_dst_path); Py_RETURN_FALSE; } static PyObject * Container_save_config(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"path", NULL}; PyObject *fs_path = NULL; char* path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, PyUnicode_FSConverter, &fs_path)) return NULL; if (fs_path != NULL) { path = PyBytes_AS_STRING(fs_path); assert(path != NULL); } if (self->container->save_config(self->container, path)) { Py_XDECREF(fs_path); Py_RETURN_TRUE; } Py_XDECREF(fs_path); Py_RETURN_FALSE; } static PyObject * Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", "value", NULL}; char *key = NULL; char *value = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &key, &value)) return NULL; if (self->container->set_cgroup_item(self->container, key, value)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"key", "value", NULL}; char *key = NULL; char *value = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &key, &value)) return NULL; if (self->container->set_config_item(self->container, key, value)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_set_config_path(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"path", NULL}; char *path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &path)) return NULL; if (self->container->set_config_path(self->container, path)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_shutdown(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"timeout", NULL}; int timeout = -1; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &timeout)) return NULL; if (self->container->shutdown(self->container, timeout)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_snapshot(Container *self, PyObject *args, PyObject *kwds) { char *comment_path = NULL; static char *kwlist[] = {"comment_path", NULL}; int retval = 0; int ret = 0; char newname[20]; PyObject *py_comment_path = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, PyUnicode_FSConverter, &py_comment_path)) return NULL; if (py_comment_path != NULL) { comment_path = PyBytes_AS_STRING(py_comment_path); assert(comment_path != NULL); } retval = self->container->snapshot(self->container, comment_path); Py_XDECREF(py_comment_path); if (retval < 0) { Py_RETURN_FALSE; } ret = snprintf(newname, 20, "snap%d", retval); if (ret < 0 || ret >= 20) return NULL; return PyUnicode_FromString(newname); } static PyObject * Container_snapshot_destroy(Container *self, PyObject *args, PyObject *kwds) { char *name = NULL; static char *kwlist[] = {"name", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &name)) return NULL; if (self->container->snapshot_destroy(self->container, name)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_snapshot_list(Container *self, PyObject *args, PyObject *kwds) { struct lxc_snapshot *snap; int snap_count = 0; PyObject *list = NULL; int i = 0; snap_count = self->container->snapshot_list(self->container, &snap); if (snap_count < 0) { PyErr_SetString(PyExc_KeyError, "Unable to list snapshots"); return NULL; } list = PyTuple_New(snap_count); for (i = 0; i < snap_count; i++) { PyObject *list_entry = NULL; list_entry = PyTuple_New(4); PyTuple_SET_ITEM(list_entry, 0, PyUnicode_FromString(snap[i].name)); PyTuple_SET_ITEM(list_entry, 1, PyUnicode_FromString(snap[i].comment_pathname)); PyTuple_SET_ITEM(list_entry, 2, PyUnicode_FromString(snap[i].timestamp)); PyTuple_SET_ITEM(list_entry, 3, PyUnicode_FromString(snap[i].lxcpath)); snap[i].free(&snap[i]); PyTuple_SET_ITEM(list, i, list_entry); } return list; } static PyObject * Container_snapshot_restore(Container *self, PyObject *args, PyObject *kwds) { char *name = NULL; char *newname = NULL; static char *kwlist[] = {"name", "newname", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &name, &newname)) return NULL; if (self->container->snapshot_restore(self->container, name, newname)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_start(Container *self, PyObject *args, PyObject *kwds) { PyObject *useinit = NULL; PyObject *daemonize = NULL; PyObject *close_fds = NULL; PyObject *vargs = NULL; char** init_args = {NULL}; PyObject *retval = NULL; int init_useinit = 0, i = 0; static char *kwlist[] = {"useinit", "daemonize", "close_fds", "cmd", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &useinit, &daemonize, &close_fds, &vargs)) return NULL; if (useinit && useinit == Py_True) { init_useinit = 1; } if (vargs && PyTuple_Check(vargs)) { init_args = convert_tuple_to_char_pointer_array(vargs); if (!init_args) { return NULL; } } if (close_fds && close_fds == Py_True) { self->container->want_close_all_fds(self->container, true); } else { self->container->want_close_all_fds(self->container, false); } if (!daemonize || daemonize == Py_True) { self->container->want_daemonize(self->container, true); } else { self->container->want_daemonize(self->container, false); } if (self->container->start(self->container, init_useinit, init_args)) retval = Py_True; else retval = Py_False; if (vargs) { /* We cannot have gotten here unless vargs was given and create_args * was successfully allocated. */ for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) free(init_args[i]); free(init_args); } Py_INCREF(retval); return retval; } static PyObject * Container_stop(Container *self, PyObject *args, PyObject *kwds) { if (self->container->stop(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_unfreeze(Container *self, PyObject *args, PyObject *kwds) { if (self->container->unfreeze(self->container)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * Container_wait(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"state", "timeout", NULL}; char *state = NULL; int timeout = -1; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &state, &timeout)) return NULL; if (self->container->wait(self->container, state, timeout)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } /* Function/Properties list */ static PyGetSetDef Container_getseters[] = { {"config_file_name", (getter)Container_config_file_name, NULL, "Path to the container configuration", NULL}, {"controllable", (getter)Container_controllable, NULL, "Boolean indicating whether the container may be controlled", NULL}, {"defined", (getter)Container_defined, NULL, "Boolean indicating whether the container configuration exists", NULL}, {"init_pid", (getter)Container_init_pid, NULL, "PID of the container's init process in the host's PID namespace", NULL}, {"name", (getter)Container_name, NULL, "Container name", NULL}, {"running", (getter)Container_running, NULL, "Boolean indicating whether the container is running or not", NULL}, {"state", (getter)Container_state, NULL, "Container state", NULL}, {NULL, NULL, NULL, NULL, NULL} }; static PyMethodDef Container_methods[] = { {"add_device_node", (PyCFunction)Container_add_device_node, METH_VARARGS|METH_KEYWORDS, "add_device_node(src_path, dest_path) -> boolean\n" "\n" "Pass a new device to the container." }, {"attach", (PyCFunction)Container_attach, METH_VARARGS|METH_KEYWORDS, "attach(run, payload) -> int\n" "\n" "Attach to the container. Returns the pid of the attached process." }, {"attach_wait", (PyCFunction)Container_attach_wait, METH_VARARGS|METH_KEYWORDS, "attach(run, payload) -> int\n" "\n" "Attach to the container. Returns the exit code of the process." }, {"clear_config", (PyCFunction)Container_clear_config, METH_NOARGS, "clear_config()\n" "\n" "Clear any container configuration." }, {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS|METH_KEYWORDS, "clear_config_item(key) -> boolean\n" "\n" "Clear the current value of a config key." }, {"console", (PyCFunction)Container_console, METH_VARARGS|METH_KEYWORDS, "console(ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, " "escape = 0) -> boolean\n" "\n" "Attach to container's console." }, {"console_getfd", (PyCFunction)Container_console_getfd, METH_VARARGS|METH_KEYWORDS, "console(ttynum = -1) -> boolean\n" "\n" "Attach to container's console." }, {"clone", (PyCFunction)Container_clone, METH_VARARGS|METH_KEYWORDS, "clone(newname, config_path, flags, bdevtype, bdevdata, newsize, " "hookargs) -> boolean\n" "\n" "Create a new container based on the current one." }, {"create", (PyCFunction)Container_create, METH_VARARGS|METH_KEYWORDS, "create(template, args = (,)) -> boolean\n" "\n" "Create a new rootfs for the container, using the given template " "and passing some optional arguments to it." }, {"destroy", (PyCFunction)Container_destroy, METH_NOARGS, "destroy() -> boolean\n" "\n" "Destroys the container." }, {"freeze", (PyCFunction)Container_freeze, METH_NOARGS, "freeze() -> boolean\n" "\n" "Freezes the container and returns its return code." }, {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS|METH_KEYWORDS, "get_cgroup_item(key) -> string\n" "\n" "Get the current value of a cgroup entry." }, {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS|METH_KEYWORDS, "get_config_item(key) -> string\n" "\n" "Get the current value of a config key." }, {"get_config_path", (PyCFunction)Container_get_config_path, METH_NOARGS, "get_config_path() -> string\n" "\n" "Return the LXC config path (where the containers are stored)." }, {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS|METH_KEYWORDS, "get_keys(key) -> string\n" "\n" "Get a list of valid sub-keys for a key." }, {"get_interfaces", (PyCFunction)Container_get_interfaces, METH_NOARGS, "get_interface() -> tuple\n" "\n" "Get a tuple of interfaces for the container." }, {"get_ips", (PyCFunction)Container_get_ips, METH_VARARGS|METH_KEYWORDS, "get_ips(interface, family, scope) -> tuple\n" "\n" "Get a tuple of IPs for the container." }, {"get_running_config_item", (PyCFunction)Container_get_running_config_item, METH_VARARGS|METH_KEYWORDS, "get_running_config_item(key) -> string\n" "\n" "Get the runtime value of a config key." }, {"load_config", (PyCFunction)Container_load_config, METH_VARARGS|METH_KEYWORDS, "load_config(path = DEFAULT) -> boolean\n" "\n" "Read the container configuration from its default " "location or from an alternative location if provided." }, {"reboot", (PyCFunction)Container_reboot, METH_NOARGS, "reboot() -> boolean\n" "\n" "Ask the container to reboot." }, {"rename", (PyCFunction)Container_rename, METH_VARARGS|METH_KEYWORDS, "rename(new_name) -> boolean\n" "\n" "Rename the container." }, {"remove_device_node", (PyCFunction)Container_remove_device_node, METH_VARARGS|METH_KEYWORDS, "remove_device_node(src_path, dest_path) -> boolean\n" "\n" "Remove a device from the container." }, {"save_config", (PyCFunction)Container_save_config, METH_VARARGS|METH_KEYWORDS, "save_config(path = DEFAULT) -> boolean\n" "\n" "Save the container configuration to its default " "location or to an alternative location if provided." }, {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS|METH_KEYWORDS, "set_cgroup_item(key, value) -> boolean\n" "\n" "Set a cgroup entry to the provided value." }, {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS|METH_KEYWORDS, "set_config_item(key, value) -> boolean\n" "\n" "Set a config key to the provided value." }, {"set_config_path", (PyCFunction)Container_set_config_path, METH_VARARGS|METH_KEYWORDS, "set_config_path(path) -> boolean\n" "\n" "Set the LXC config path (where the containers are stored)." }, {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS|METH_KEYWORDS, "shutdown(timeout = -1) -> boolean\n" "\n" "Sends SIGPWR to the container and wait for it to shutdown." "-1 means wait forever, 0 means skip waiting." }, {"snapshot", (PyCFunction)Container_snapshot, METH_VARARGS|METH_KEYWORDS, "snapshot(comment_path = None) -> string\n" "\n" "Snapshot the container and return the snapshot name " "(or False on error)." }, {"snapshot_destroy", (PyCFunction)Container_snapshot_destroy, METH_VARARGS|METH_KEYWORDS, "snapshot_destroy(name) -> boolean\n" "\n" "Destroy a snapshot." }, {"snapshot_list", (PyCFunction)Container_snapshot_list, METH_NOARGS, "snapshot_list() -> tuple of snapshot tuples\n" "\n" "List all snapshots for a container." }, {"snapshot_restore", (PyCFunction)Container_snapshot_restore, METH_VARARGS|METH_KEYWORDS, "snapshot_restore(name, newname = None) -> boolean\n" "\n" "Restore a container snapshot. If newname is provided a new " "container will be created from the snapshot, otherwise an in-place " "restore will be attempted." }, {"start", (PyCFunction)Container_start, METH_VARARGS|METH_KEYWORDS, "start(useinit = False, daemonize=True, close_fds=False, " "cmd = (,)) -> boolean\n" "\n" "Start the container, return True on success.\n" "When set useinit will make LXC use lxc-init to start the container.\n" "The container can be started in the foreground with daemonize=False.\n" "All fds may also be closed by passing close_fds=True." }, {"stop", (PyCFunction)Container_stop, METH_NOARGS, "stop() -> boolean\n" "\n" "Stop the container and returns its return code." }, {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS, "unfreeze() -> boolean\n" "\n" "Unfreezes the container and returns its return code." }, {"wait", (PyCFunction)Container_wait, METH_VARARGS|METH_KEYWORDS, "wait(state, timeout = -1) -> boolean\n" "\n" "Wait for the container to reach a given state or timeout." }, {NULL, NULL, 0, NULL} }; static PyTypeObject _lxc_ContainerType = { PyVarObject_HEAD_INIT(NULL, 0) "lxc.Container", /* tp_name */ sizeof(Container), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Container_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Container objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Container_methods, /* tp_methods */ 0, /* tp_members */ Container_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Container_init, /* tp_init */ 0, /* tp_alloc */ Container_new, /* tp_new */ }; static PyMethodDef LXC_methods[] = { {"attach_run_command", (PyCFunction)LXC_attach_run_command, METH_O, "Runs a command when attaching, to use as the run parameter for attach " "or attach_wait"}, {"attach_run_shell", (PyCFunction)LXC_attach_run_shell, METH_O, "Starts up a shell when attaching, to use as the run parameter for " "attach or attach_wait"}, {"get_global_config_item", (PyCFunction)LXC_get_global_config_item, METH_VARARGS|METH_KEYWORDS, "Returns the current LXC config path"}, {"get_version", (PyCFunction)LXC_get_version, METH_NOARGS, "Returns the current LXC library version"}, {"list_containers", (PyCFunction)LXC_list_containers, METH_VARARGS|METH_KEYWORDS, "Returns a list of container names or objects"}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC INIT_MODULE(_lxc)(void) { PyObject* m; PyObject* d; if (PyType_Ready(&_lxc_ContainerType) < 0) return; m = Py_InitModule("_lxc", LXC_methods); if (m == NULL) return; Py_INCREF(&_lxc_ContainerType); PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType); /* add constants */ d = PyModule_GetDict(m); #define PYLXC_EXPORT_CONST(c) \ PyDict_SetItemString(d, #c, PyLong_FromLong(c)) /* namespace flags (no other python lib exports this) */ PYLXC_EXPORT_CONST(CLONE_NEWUTS); PYLXC_EXPORT_CONST(CLONE_NEWIPC); PYLXC_EXPORT_CONST(CLONE_NEWUSER); PYLXC_EXPORT_CONST(CLONE_NEWPID); PYLXC_EXPORT_CONST(CLONE_NEWNET); PYLXC_EXPORT_CONST(CLONE_NEWNS); /* attach: environment variable handling */ PYLXC_EXPORT_CONST(LXC_ATTACH_CLEAR_ENV); PYLXC_EXPORT_CONST(LXC_ATTACH_KEEP_ENV); /* attach: attach options */ PYLXC_EXPORT_CONST(LXC_ATTACH_DEFAULT); PYLXC_EXPORT_CONST(LXC_ATTACH_DROP_CAPABILITIES); PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_EXEC); PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_NOW); PYLXC_EXPORT_CONST(LXC_ATTACH_MOVE_TO_CGROUP); PYLXC_EXPORT_CONST(LXC_ATTACH_REMOUNT_PROC_SYS); PYLXC_EXPORT_CONST(LXC_ATTACH_SET_PERSONALITY); /* clone: clone flags */ PYLXC_EXPORT_CONST(LXC_CLONE_KEEPBDEVTYPE); PYLXC_EXPORT_CONST(LXC_CLONE_KEEPMACADDR); PYLXC_EXPORT_CONST(LXC_CLONE_KEEPNAME); PYLXC_EXPORT_CONST(LXC_CLONE_MAYBE_SNAPSHOT); PYLXC_EXPORT_CONST(LXC_CLONE_SNAPSHOT); /* create: create flags */ PYLXC_EXPORT_CONST(LXC_CREATE_QUIET); #undef PYLXC_EXPORT_CONST return; } /* * kate: space-indent on; indent-width 4; mixedindent off; indent-mode cstyle; */ python-lxc-0.1/setup.cfg0000664000175000017500000000010112375151311014245 0ustar chuckchuck[bdist_rpm] group=System Environment/Libraries requires=lxc-libs python-lxc-0.1/PKG-INFO0000664000175000017500000000045312375151316013540 0ustar chuckchuckMetadata-Version: 1.0 Name: lxc-python2 Version: 0.1 Summary: Python2 bindings for LXC Home-page: git://github.com/lxc/python2-lxc Author: lxc Author-email: lxc-devel@lists.linuxcontainers.org License: LGPLv2+ Description: The lxc-python2 package contains lxc bindings for python2 Platform: UNKNOWN python-lxc-0.1/lxc/0000775000175000017500000000000012375151316013227 5ustar chuckchuckpython-lxc-0.1/lxc/__init__.py0000664000175000017500000003647712375151311015354 0ustar chuckchuck# # -*- coding: utf-8 -*- # python-lxc: Python bindings for LXC # # (C) Copyright Canonical Ltd. 2012 # # Authors: # Stéphane Graber # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # import _lxc import os import subprocess import time default_config_path = _lxc.get_global_config_item("lxc.lxcpath") get_global_config_item = _lxc.get_global_config_item version = _lxc.get_version() class ContainerNetwork(object): props = {} def __init__(self, container, index): self.container = container self.index = index for key in self.container.get_keys("lxc.network.%s" % self.index): if "." in key: self.props[key.replace(".", "_")] = key else: self.props[key] = key if not self.props: return False def __delattr__(self, key): if key in ["container", "index", "props"]: return object.__delattr__(self, key) if key not in self.props: raise AttributeError("'%s' network has no attribute '%s'" % ( self.__get_network_item("type"), key)) return self.__clear_network_item(self.props[key]) def __dir__(self): return sorted(self.props.keys()) def __getattr__(self, key): if key in ["container", "index", "props"]: return object.__getattribute__(self, key) if key not in self.props: raise AttributeError("'%s' network has no attribute '%s'" % ( self.__get_network_item("type"), key)) return self.__get_network_item(self.props[key]) def __hasattr__(self, key): if key in ["container", "index", "props"]: return object.__hasattr__(self, key) if key not in self.props: raise AttributeError("'%s' network has no attribute '%s'" % ( self.__get_network_item("type"), key)) return True def __repr__(self): return "'%s' network at index '%s'" % ( self.__get_network_item("type"), self.index) def __setattr__(self, key, value): if key in ["container", "index", "props"]: return object.__setattr__(self, key, value) if key not in self.props: raise AttributeError("'%s' network has no attribute '%s'" % ( self.__get_network_item("type"), key)) return self.__set_network_item(self.props[key], value) def __clear_network_item(self, key): return self.container.clear_config_item("lxc.network.%s.%s" % ( self.index, key)) def __get_network_item(self, key): return self.container.get_config_item("lxc.network.%s.%s" % ( self.index, key)) def __set_network_item(self, key, value): return self.container.set_config_item("lxc.network.%s.%s" % ( self.index, key), value) class ContainerNetworkList(): def __init__(self, container): self.container = container def __getitem__(self, index): if index >= len(self): raise IndexError("list index out of range") return ContainerNetwork(self.container, index) def __len__(self): values = self.container.get_config_item("lxc.network") if values: return len(values) else: return 0 def add(self, network_type): index = len(self) return self.container.set_config_item("lxc.network.%s.type" % index, network_type) def remove(self, index): count = len(self) if index >= count: raise IndexError("list index out of range") return self.container.clear_config_item("lxc.network.%s" % index) class Container(_lxc.Container): def __init__(self, name, config_path=None): """ Creates a new Container instance. """ if config_path: _lxc.Container.__init__(self, name, config_path) else: _lxc.Container.__init__(self, name) self.network = ContainerNetworkList(self) def add_device_net(self, name, destname=None): """ Add network device to running container. """ if not self.running: return False if os.path.exists("/sys/class/net/%s/phy80211/name" % name): with open("/sys/class/net/%s/phy80211/name" % name) as fd: phy = fd.read().strip() if subprocess.call(['iw', 'phy', phy, 'set', 'netns', str(self.init_pid)]) != 0: return False if destname: def rename_interface(args): old, new = args return subprocess.call(['ip', 'link', 'set', 'dev', old, 'name', new]) return self.attach_wait(rename_interface, (name, destname), namespaces=(CLONE_NEWNET)) == 0 return True if not destname: destname = name if not os.path.exists("/sys/class/net/%s/" % name): return False return subprocess.call(['ip', 'link', 'set', 'dev', name, 'netns', str(self.init_pid), 'name', destname]) == 0 def append_config_item(self, key, value): """ Append 'value' to 'key', assuming 'key' is a list. If 'key' isn't a list, 'value' will be set as the value of 'key'. """ return _lxc.Container.set_config_item(self, key, value) def create(self, template=None, flags=0, args=()): """ Create a new rootfs for the container. "template" if passed must be a valid template name. "flags" (optional) is an integer representing the optional create flags to be passed. "args" (optional) is a tuple of arguments to pass to the template. It can also be provided as a dict. """ if isinstance(args, dict): template_args = [] for item in args.items(): template_args.append("--%s" % item[0]) template_args.append("%s" % item[1]) else: template_args = args if template: return _lxc.Container.create(self, template=template, flags=flags, args=tuple(template_args)) else: return _lxc.Container.create(self, flags=flags, args=tuple(template_args)) def clone(self, newname, config_path=None, flags=0, bdevtype=None, bdevdata=None, newsize=0, hookargs=()): """ Clone the current container. """ args = {} args['newname'] = newname args['flags'] = flags args['newsize'] = newsize args['hookargs'] = hookargs if config_path: args['config_path'] = config_path if bdevtype: args['bdevtype'] = bdevtype if bdevdata: args['bdevdata'] = bdevdata if _lxc.Container.clone(self, **args): return Container(newname, config_path=config_path) else: return False def console(self, ttynum=-1, stdinfd=0, stdoutfd=1, stderrfd=2, escape=1): """ Attach to console of running container. """ if not self.running: return False return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, stderrfd, escape) def console_getfd(self, ttynum=-1): """ Attach to console of running container. """ if not self.running: return False return _lxc.Container.console_getfd(self, ttynum) def get_cgroup_item(self, key): """ Returns the value for a given cgroup entry. A list is returned when multiple values are set. """ value = _lxc.Container.get_cgroup_item(self, key) if value is False: return False else: return value.rstrip("\n") def get_config_item(self, key): """ Returns the value for a given config key. A list is returned when multiple values are set. """ value = _lxc.Container.get_config_item(self, key) if value is False: return False elif value.endswith("\n"): return value.rstrip("\n").split("\n") else: return value def get_keys(self, key=None): """ Returns a list of valid sub-keys. """ if key: value = _lxc.Container.get_keys(self, key) else: value = _lxc.Container.get_keys(self) if value is False: return False elif value.endswith("\n"): return value.rstrip("\n").split("\n") else: return value def get_interfaces(self): """ Get a tuple of interfaces for the container. """ return _lxc.Container.get_interfaces(self) def get_ips(self, interface=None, family=None, scope=None, timeout=0): """ Get a tuple of IPs for the container. """ kwargs = {} if interface: kwargs['interface'] = interface if family: kwargs['family'] = family if scope: kwargs['scope'] = scope ips = None timeout = int(os.environ.get('LXC_GETIP_TIMEOUT', timeout)) while not ips: ips = _lxc.Container.get_ips(self, **kwargs) if timeout == 0: break timeout -= 1 time.sleep(1) return ips def rename(self, new_name): """ Rename the container. On success, returns the new Container object. On failure, returns False. """ if _lxc.Container.rename(self, new_name): return Container(new_name) return False def set_config_item(self, key, value): """ Set a config key to a provided value. The value can be a list for the keys supporting multiple values. """ try: old_value = self.get_config_item(key) except KeyError: old_value = None # Check if it's a list def set_key(key, value): self.clear_config_item(key) if isinstance(value, list): for entry in value: if not _lxc.Container.set_config_item(self, key, entry): return False else: _lxc.Container.set_config_item(self, key, value) set_key(key, value) new_value = self.get_config_item(key) # loglevel is special and won't match the string we set if key == "lxc.loglevel": new_value = value if (isinstance(value, str) and isinstance(new_value, str) and value == new_value): return True elif (isinstance(value, list) and isinstance(new_value, list) and set(value) == set(new_value)): return True elif (isinstance(value, str) and isinstance(new_value, list) and set([value]) == set(new_value)): return True elif old_value: set_key(key, old_value) return False else: self.clear_config_item(key) return False def wait(self, state, timeout=-1): """ Wait for the container to reach a given state or timeout. """ if isinstance(state, str): state = state.upper() return _lxc.Container.wait(self, state, timeout) def list_containers(active=True, defined=True, as_object=False, config_path=None): """ List the containers on the system. """ if config_path: if not os.path.exists(config_path): return tuple() try: entries = _lxc.list_containers(active=active, defined=defined, config_path=config_path) except ValueError: return tuple() else: try: entries = _lxc.list_containers(active=active, defined=defined) except ValueError: return tuple() if as_object: return tuple([Container(name, config_path) for name in entries]) else: return entries def attach_run_command(cmd): """ Run a command when attaching Please do not call directly, this will execvp the command. This is to be used in conjunction with the attach method of a container. """ if isinstance(cmd, tuple): return _lxc.attach_run_command(cmd) elif isinstance(cmd, list): return _lxc.attach_run_command((cmd[0], cmd)) else: return _lxc.attach_run_command((cmd, [cmd])) def attach_run_shell(): """ Run a shell when attaching Please do not call directly, this will execvp the shell. This is to be used in conjunction with the attach method of a container. """ return _lxc.attach_run_shell(None) def arch_to_personality(arch): """ Determine the process personality corresponding to the architecture """ if isinstance(arch, bytes): arch = str(arch, 'utf-8') return _lxc.arch_to_personality(arch) # namespace flags (no other python lib exports this) CLONE_NEWIPC = _lxc.CLONE_NEWIPC CLONE_NEWNET = _lxc.CLONE_NEWNET CLONE_NEWNS = _lxc.CLONE_NEWNS CLONE_NEWPID = _lxc.CLONE_NEWPID CLONE_NEWUSER = _lxc.CLONE_NEWUSER CLONE_NEWUTS = _lxc.CLONE_NEWUTS # attach: environment variable handling LXC_ATTACH_CLEAR_ENV = _lxc.LXC_ATTACH_CLEAR_ENV LXC_ATTACH_KEEP_ENV = _lxc.LXC_ATTACH_KEEP_ENV # attach: attach options LXC_ATTACH_DEFAULT = _lxc.LXC_ATTACH_DEFAULT LXC_ATTACH_DROP_CAPABILITIES = _lxc.LXC_ATTACH_DROP_CAPABILITIES LXC_ATTACH_LSM_EXEC = _lxc.LXC_ATTACH_LSM_EXEC LXC_ATTACH_LSM_NOW = _lxc.LXC_ATTACH_LSM_NOW LXC_ATTACH_MOVE_TO_CGROUP = _lxc.LXC_ATTACH_MOVE_TO_CGROUP LXC_ATTACH_REMOUNT_PROC_SYS = _lxc.LXC_ATTACH_REMOUNT_PROC_SYS LXC_ATTACH_SET_PERSONALITY = _lxc.LXC_ATTACH_SET_PERSONALITY # clone: clone flags LXC_CLONE_KEEPBDEVTYPE = _lxc.LXC_CLONE_KEEPBDEVTYPE LXC_CLONE_KEEPMACADDR = _lxc.LXC_CLONE_KEEPMACADDR LXC_CLONE_KEEPNAME = _lxc.LXC_CLONE_KEEPNAME LXC_CLONE_MAYBE_SNAPSHOT = _lxc.LXC_CLONE_MAYBE_SNAPSHOT LXC_CLONE_SNAPSHOT = _lxc.LXC_CLONE_SNAPSHOT # create: create flags LXC_CREATE_QUIET = _lxc.LXC_CREATE_QUIET python-lxc-0.1/setup.py0000664000175000017500000000260312375151311014147 0ustar chuckchuck#!/usr/bin/python # # python-lxc: Python bindings for LXC # # (C) Copyright Canonical Ltd. 2012 # # Authors: # Stephane Graber # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from distutils.core import setup, Extension module = Extension('_lxc', sources=['lxc.c'], libraries=['lxc']) setup(name='lxc-python2', version='0.1', description='Python2 bindings for LXC', long_description='The lxc-python2 package contains lxc bindings for python2', license='LGPLv2+', maintainer='lxc', maintainer_email='lxc-devel@lists.linuxcontainers.org', url='git://github.com/lxc/python2-lxc', packages=['lxc'], package_dir={'lxc': 'lxc'}, ext_modules=[module]) python-lxc-0.1/README0000664000175000017500000000022112375151311013307 0ustar chuckchuckThese are lxc bindings for python2. Build: python setup.py build Install: python setup.py install Build rpm: python setup.py bdist_rpm