tree-wide: remove python3 bindings

parent b52a5bef
...@@ -67,10 +67,6 @@ src/lxc/version.h ...@@ -67,10 +67,6 @@ src/lxc/version.h
src/lxc/cmd/lxc-checkconfig src/lxc/cmd/lxc-checkconfig
src/lxc/cmd/lxc-update-config src/lxc/cmd/lxc-update-config
src/python-lxc/build/
src/python-lxc/lxc/__pycache__/
src/python-lxc/lxc.egg-info/
src/tests/lxc-test-device-add-remove src/tests/lxc-test-device-add-remove
src/tests/lxc-test-attach src/tests/lxc-test-attach
src/tests/lxc-test-apparmor src/tests/lxc-test-apparmor
......
...@@ -13,10 +13,6 @@ EXTRA_DIST = \ ...@@ -13,10 +13,6 @@ EXTRA_DIST = \
RPMARGS = RPMARGS =
if ENABLE_PYTHON
RPMARGS += --with python
endif
pcdatadir = $(libdir)/pkgconfig pcdatadir = $(libdir)/pkgconfig
pcdata_DATA = lxc.pc pcdata_DATA = lxc.pc
......
...@@ -363,29 +363,6 @@ AC_ARG_ENABLE([examples], ...@@ -363,29 +363,6 @@ AC_ARG_ENABLE([examples],
[], [enable_examples=yes]) [], [enable_examples=yes])
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"]) AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"])
# Python3 module and scripts
AC_ARG_ENABLE([python],
[AC_HELP_STRING([--enable-python], [enable python binding [default=auto]])],
[], [enable_python=auto])
if test "x$enable_python" = "xauto"; then
PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[enable_python=yes],[enable_python=no])
if test "$CC" = "clang"; then
enable_python=no
fi
fi
if test "x$enable_python" = "xyes" && test "$CC" = "clang"; then
AC_MSG_ERROR([Python3 is incompatible with the clang compiler])
fi
AM_CONDITIONAL([ENABLE_PYTHON], [test "x$enable_python" = "xyes"])
AM_COND_IF([ENABLE_PYTHON],
[AM_PATH_PYTHON([3.2], [], [AC_MSG_ERROR([You must install python3])])
PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[],[AC_MSG_ERROR([You must install python3-dev])])
AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])])
# Enable dumping stack traces # Enable dumping stack traces
AC_ARG_ENABLE([mutex-debugging], AC_ARG_ENABLE([mutex-debugging],
[AC_HELP_STRING([--enable-mutex-debugging], [Makes mutexes to report error and provide stack trace [default=no]])], [AC_HELP_STRING([--enable-mutex-debugging], [Makes mutexes to report error and provide stack trace [default=no]])],
...@@ -894,7 +871,6 @@ AC_CONFIG_FILES([ ...@@ -894,7 +871,6 @@ AC_CONFIG_FILES([
src/lxc/cmd/lxc-checkconfig src/lxc/cmd/lxc-checkconfig
src/lxc/cmd/lxc-update-config src/lxc/cmd/lxc-update-config
src/lxc/version.h src/lxc/version.h
src/python-lxc/Makefile
src/tests/Makefile src/tests/Makefile
src/tests/lxc-test-usernic src/tests/lxc-test-usernic
...@@ -957,9 +933,6 @@ PAM: ...@@ -957,9 +933,6 @@ PAM:
- PAM module: $enable_pam - PAM module: $enable_pam
- cgroup PAM module: $pamdir - cgroup PAM module: $pamdir
Bindings:
- python3: $enable_python
Documentation: Documentation:
- examples: $enable_examples - examples: $enable_examples
- API documentation: $enable_api_docs - API documentation: $enable_api_docs
......
...@@ -41,13 +41,6 @@ man_MANS = \ ...@@ -41,13 +41,6 @@ man_MANS = \
\ \
lxc.7 lxc.7
if ENABLE_DEPRECATED
man_MANS += lxc-clone.1
if ENABLE_PYTHON
man_MANS += lxc-start-ephemeral.1
endif
endif
%.1 : %.sgml %.1 : %.sgml
$(db2xman) --encoding=UTF-8 $< $(db2xman) --encoding=UTF-8 $<
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
......
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
# License along with this library; if not, write to the Free Software # License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
%global with_python %{?_with_python: 1} %{?!_with_python: 0}
# Set with_systemd on distros that use it, so we can install the service # Set with_systemd on distros that use it, so we can install the service
# file, otherwise the sysvinit script will be installed # file, otherwise the sysvinit script will be installed
%if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210 %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210
...@@ -93,12 +91,6 @@ BuildRequires: libseccomp-devel ...@@ -93,12 +91,6 @@ BuildRequires: libseccomp-devel
%endif %endif
%endif %endif
%if %{with_python}
Requires: python3
BuildRequires: python3-devel
BuildRequires: python3-setuptools
%endif
%description %description
Containers are insulated areas inside a system, which have their own namespace Containers are insulated areas inside a system, which have their own namespace
for filesystem, network, PID, IPC, CPU and memory allocation and which can be for filesystem, network, PID, IPC, CPU and memory allocation and which can be
...@@ -127,9 +119,6 @@ development of the Linux containers. ...@@ -127,9 +119,6 @@ development of the Linux containers.
%setup -q -n %{name}-%{version}%{?beta_dot} %setup -q -n %{name}-%{version}%{?beta_dot}
%build %build
PATH=$PATH:/usr/sbin:/sbin %configure $args \ PATH=$PATH:/usr/sbin:/sbin %configure $args \
%if %{with_python}
--enable-python \
%endif
%if "x%{_unitdir}" != "x" %if "x%{_unitdir}" != "x"
--with-systemdsystemunitdir=%{_unitdir} \ --with-systemdsystemunitdir=%{_unitdir} \
%endif %endif
...@@ -266,10 +255,6 @@ fi ...@@ -266,10 +255,6 @@ fi
%attr(555,root,root) %{_libexecdir}/%{name}/lxc-containers %attr(555,root,root) %{_libexecdir}/%{name}/lxc-containers
%endif %endif
%if %{with_python}
%{python3_sitearch}/*
%endif
%files devel %files devel
%defattr(-,root,root) %defattr(-,root,root)
%{_includedir}/%{name}/* %{_includedir}/%{name}/*
......
SUBDIRS = lxc tests python-lxc SUBDIRS = lxc tests
if ENABLE_PYTHON
if HAVE_DEBIAN
DISTSETUPOPTS=--install-layout=deb
else
DISTSETUPOPTS=
endif
if ENABLE_RPATH
RPATHOPTS=-R $(libdir)
else
RPATHOPTS=
endif
CALL_SETUP_PY := cd @srcdir@ && $(PYTHON) setup.py build -b @abs_builddir@/build egg_info -e @abs_builddir@
all:
$(CALL_SETUP_PY) build_ext -I @abs_top_srcdir@/src -L @abs_top_builddir@/src/lxc/.libs/ $(RPATHOPTS) --no-pkg-config
DESTDIR = / # default
install:
$(CALL_SETUP_PY) install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS) --root=$(DESTDIR)
clean-local:
rm -rf @builddir@/build
endif
EXTRA_DIST = \
setup.py \
lxc.c \
lxc/__init__.py \
examples/api_test.py \
examples/pyconsole.py \
examples/pyconsole-vte.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# api_test.py: Test/demo of the python3-lxc API
#
# (C) Copyright Canonical Ltd. 2012
#
# Authors:
# Stéphane Graber <stgraber@ubuntu.com>
#
# 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 uuid
import os
import subprocess
import sys
import time
# Let's pick a random name, avoiding clashes
CONTAINER_NAME = str(uuid.uuid1())
CLONE_NAME = str(uuid.uuid1())
RENAME_NAME = str(uuid.uuid1())
## Instantiate the container instance
print("Getting instance for '%s'" % CONTAINER_NAME)
container = lxc.Container(CONTAINER_NAME)
# A few basic checks of the current state
assert(container.config_file_name == "%s/%s/config" %
(lxc.default_config_path, CONTAINER_NAME))
assert(not container.defined)
assert(container.init_pid == -1)
assert(container.name == CONTAINER_NAME)
assert(not container.running)
assert(container.state == "STOPPED")
# Try to get the host architecture for dpkg systems
arch = "i386"
try:
with open(os.path.devnull, "w") as devnull:
dpkg = subprocess.Popen(['dpkg', '--print-architecture'],
stderr=devnull, stdout=subprocess.PIPE,
universal_newlines=True)
if dpkg.wait() == 0:
arch = dpkg.stdout.read().strip()
except:
pass
## Create a rootfs
print("Creating rootfs using 'download', arch=%s" % arch)
container.create("download", 0,
{"dist": "ubuntu",
"release": "xenial",
"arch": arch})
assert(container.defined)
assert(container.name == CONTAINER_NAME
== container.get_config_item("lxc.uts.name"))
assert(container.name in lxc.list_containers())
## Test the config
print("Testing the configuration")
capdrop = container.get_config_item("lxc.cap.drop")
container.clear_config_item("lxc.cap.drop")
container.set_config_item("lxc.cap.drop", capdrop[:-1])
container.append_config_item("lxc.cap.drop", capdrop[-1])
container.save_config()
# A few basic checks of the current state
assert(isinstance(capdrop, list))
assert(capdrop == container.get_config_item("lxc.cap.drop"))
## Test the networking
print("Testing the networking")
# A few basic checks of the current state
assert("name" in container.get_keys("lxc.net.0"))
assert(len(container.network) == 1)
## Starting the container
print("Starting the container")
container.start()
container.wait("RUNNING", 3)
# A few basic checks of the current state
assert(container.init_pid > 1)
assert(container.running)
assert(container.state == "RUNNING")
## Checking IP address
print("Getting the interface names")
assert(set(container.get_interfaces()) == set(('lo', 'eth0')))
## Checking IP address
print("Getting the IP addresses")
count = 0
ips = []
while not ips or count == 10:
ips = container.get_ips()
time.sleep(1)
count += 1
if os.geteuid():
container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"],
namespaces=(lxc.CLONE_NEWUSER + lxc.CLONE_NEWNET
+ lxc.CLONE_NEWUTS))
else:
container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"],
namespaces=(lxc.CLONE_NEWNET + lxc.CLONE_NEWUTS))
# A few basic checks of the current state
assert(len(ips) > 0)
## Test running config
assert(container.name == CONTAINER_NAME
== container.get_config_item("lxc.uts.name")
== container.get_running_config_item("lxc.uts.name"))
## Testing cgroups a bit
print("Testing cgroup API")
max_mem = container.get_cgroup_item("memory.max_usage_in_bytes")
current_limit = container.get_cgroup_item("memory.limit_in_bytes")
assert(container.set_cgroup_item("memory.limit_in_bytes", max_mem))
assert(container.get_cgroup_item("memory.limit_in_bytes") != current_limit)
## Freezing the container
print("Freezing the container")
container.freeze()
container.wait("FROZEN", 3)
# A few basic checks of the current state
assert(container.init_pid > 1)
assert(container.running)
assert(container.state == "FROZEN")
## Unfreezing the container
print("Unfreezing the container")
container.unfreeze()
container.wait("RUNNING", 3)
# A few basic checks of the current state
assert(container.init_pid > 1)
assert(container.running)
assert(container.state == "RUNNING")
if len(sys.argv) > 1 and sys.argv[1] == "--with-console":
## Attaching to tty1
print("Attaching to tty1")
container.console(tty=1)
## Shutting down the container
print("Shutting down the container")
if not container.shutdown(3):
container.stop()
if container.running:
print("Stopping the container")
container.stop()
container.wait("STOPPED", 3)
# A few basic checks of the current state
assert(container.init_pid == -1)
assert(not container.running)
assert(container.state == "STOPPED")
## Snapshotting the container
print("Snapshotting the container")
assert(not container.snapshot_list())
assert(container.snapshot() == "snap0")
assert(len(container.snapshot_list()) == 1)
assert(container.snapshot_restore("snap0") is True)
assert(container.snapshot_destroy("snap0") is True)
## Cloning the container
print("Cloning the container as '%s'" % CLONE_NAME)
clone = container.clone(CLONE_NAME)
assert(clone is not False)
print ("Renaming the clone to '%s'" % RENAME_NAME)
rename = clone.rename(RENAME_NAME)
rename.start()
rename.stop()
rename.destroy()
## Destroy the container
print("Destroying the container")
container.destroy()
assert(not container.defined)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# pyconsole-vte: Example program showing use of console functions
# in the lxc python binding
#
# (C) Copyright Oracle. 2013
#
# Authors:
# Dwight Engen <dwight.engen@oracle.com>
#
# 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 gtk
import vte
import lxc
import sys
def gtk_exit_cb(terminal):
gtk.main_quit()
def vte_con(ct, ttynum):
print("Doing console in a VTE widget...")
masterfd = ct.console_getfd(ttynum)
term = vte.Terminal()
term.set_cursor_blinks(True)
term.set_scrollback_lines(1000)
term.connect('eof', gtk_exit_cb)
term.set_pty(masterfd)
term.feed_child('\n')
#term.feed_child('ps aux\n')
vscrollbar = gtk.VScrollbar()
vscrollbar.set_adjustment(term.get_adjustment())
hbox = gtk.HBox()
hbox.pack_start(term)
hbox.pack_start(vscrollbar)
window = gtk.Window()
window.add(hbox)
window.connect('delete-event', lambda window, event: gtk.main_quit())
window.show_all()
gtk.main()
print("Console done")
if __name__ == '__main__':
ttynum = -1
if len(sys.argv) < 2:
sys.exit("Usage: %s container-name [ttynum]" % sys.argv[0])
if len(sys.argv) > 2:
ttynum = int(sys.argv[2])
ct = lxc.Container(sys.argv[1])
print("Container:%s tty:%d" % (ct.name, ttynum))
if not ct.defined:
sys.exit("Container %s not defined" % ct.name)
if not ct.running:
sys.exit("Container %s not running" % ct.name)
vte_con(ct, ttynum)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# pyconsole: Example program showing use of console functions
# in the lxc python binding
#
# (C) Copyright Oracle. 2013
#
# Authors:
# Dwight Engen <dwight.engen@oracle.com>
#
# 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 sys
import time
if __name__ == '__main__':
ttynum = -1
escape = 1
if len(sys.argv) < 2:
sys.exit("Usage: %s container-name [ttynum [escape]]" % sys.argv[0])
if len(sys.argv) > 2:
ttynum = int(sys.argv[2])
if len(sys.argv) > 3:
escape = ord(sys.argv[3]) - ord('a') + 1
ct = lxc.Container(sys.argv[1])
print("Container:%s tty:%d Ctrl-%c q to quit" %
(ct.name, ttynum, ord('a') + escape-1))
time.sleep(1)
if not ct.defined:
sys.exit("Container %s not defined" % ct.name)
if not ct.running:
sys.exit("Container %s not running" % ct.name)
ct.console(ttynum, 0, 1, 2, escape)
print("Console done")
/*
* python-lxc: Python bindings for LXC
*
* (C) Copyright Canonical Ltd. 2012-2013
*
* Authors:
* Stéphane Graber <stgraber@ubuntu.com>
*
* 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 <Python.h>
#include "structmember.h"
#include <lxc/lxccontainer.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sched.h>
/*
* CLONE_* definitions copied from lxc/namespace.h
*/
#ifndef CLONE_FS
# define CLONE_FS 0x00000200
#endif
#ifndef CLONE_NEWNS
# define CLONE_NEWNS 0x00020000
#endif
#ifndef CLONE_NEWCGROUP
# define CLONE_NEWCGROUP 0x02000000
#endif
#ifndef CLONE_NEWUTS
# define CLONE_NEWUTS 0x04000000
#endif
#ifndef CLONE_NEWIPC
# define CLONE_NEWIPC 0x08000000
#endif
#ifndef CLONE_NEWUSER
# define CLONE_NEWUSER 0x10000000
#endif
#ifndef CLONE_NEWPID
# define CLONE_NEWPID 0x20000000
#endif
#ifndef CLONE_NEWNET
# define CLONE_NEWNET 0x40000000
#endif
/* From sys/personality.h */
#define PER_LINUX 0x0000
#define PER_LINUX32 0x0008
/* Helper functions */
/* Copied from lxc/utils.c */
static 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;
}
/* Copied from lxc/confile.c, with HAVE_SYS_PERSONALITY_H check removed */
signed long lxc_config_parse_arch(const char *arch)
{
struct per_name {
char *name;
unsigned long per;
} pername[] = {
{ "x86", PER_LINUX32 },
{ "linux32", PER_LINUX32 },
{ "i386", PER_LINUX32 },
{ "i486", PER_LINUX32 },
{ "i586", PER_LINUX32 },
{ "i686", PER_LINUX32 },
{ "athlon", PER_LINUX32 },
{ "linux64", PER_LINUX },
{ "x86_64", PER_LINUX },
{ "amd64", PER_LINUX },
};
size_t len = sizeof(pername) / sizeof(pername[0]);
size_t i;
for (i = 0; i < len; i++) {
if (!strcmp(pername[i].name, arch))
return pername[i].per;
}
return -1;
}
char**
convert_tuple_to_char_pointer_array(PyObject *argv) {
int argc;
int i, j;
char **result;
/* 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);
result = (char**) calloc(argc + 1, sizeof(char*));
if (result == NULL) {
PyErr_SetNone(PyExc_MemoryError);
return NULL;
}
for (i = 0; i < argc; i++) {
char *str = NULL;
PyObject *pystr = NULL;
PyObject *pyobj = PySequence_Fast_GET_ITEM(argv, i);
assert(pyobj != NULL);
if (!PyUnicode_Check(pyobj)) {
PyErr_SetString(PyExc_ValueError, "Expected a string");
goto error;
}
pystr = PyUnicode_AsUTF8String(pyobj);
if (!pystr) {
/* Maybe it wasn't UTF-8 encoded. An exception is already set. */
goto error;
}
str = PyBytes_AsString(pystr);
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)
{
/* This function is the first one to be called after attaching to a
* container. As lxc_attach() calls fork() PyOS_AfterFork should be called
* in the new process if the Python interpreter will continue to be used.
*/
PyOS_AfterFork();
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;
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_arch_to_personality(PyObject *self, PyObject *arg)
{
long rv = -1;
PyObject *pystr = NULL;
char *str;
if (!PyUnicode_Check(arg)) {
PyErr_SetString(PyExc_ValueError, "Expected a string");
return NULL;
}
pystr = PyUnicode_AsUTF8String(arg);
if (!pystr)
return NULL;
str = PyBytes_AsString(pystr);
if (!str)
goto out;
rv = lxc_config_parse_arch(str);
if (rv == -1)
PyErr_SetString(PyExc_KeyError, "Failed to lookup architecture.");
out:
Py_DECREF(pystr);
return rv == -1 ? NULL : PyLong_FromLong(rv);
}
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)
{
const char *rv = NULL;
rv = lxc_get_version();
if (!rv) {
return PyUnicode_FromString("");
}
return PyUnicode_FromString(rv);
}
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++) {
if (!names[i]) {
continue;
}
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);
PyErr_Format(PyExc_RuntimeError, "%s:%s:%d: error during init for container '%s'.",
__FUNCTION__, __FILE__, __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)
{
char *rv = NULL;
rv = self->container->config_file_name(self->container);
if (!rv) {
return PyUnicode_FromString("");
}
return PyUnicode_FromString(rv);
}
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)
{
if (!self->container->name) {
return PyUnicode_FromString("");
}
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)
{
const char *rv = NULL;
rv = self->container->state(self->container);
if (!rv) {
return PyUnicode_FromString("");
}
return PyUnicode_FromString(rv);
}
/* Container Functions */
static PyObject *
Container_attach_interface(Container *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"src_ifname", "dst_ifname", NULL};
char *src_name = NULL;
char *dst_name = NULL;
PyObject *py_src_name = NULL;
PyObject *py_dst_name = NULL;
if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist,
PyUnicode_FSConverter, &py_src_name,
PyUnicode_FSConverter, &py_dst_name))
return NULL;
if (py_src_name != NULL) {
src_name = PyBytes_AS_STRING(py_src_name);
assert(src_name != NULL);
}
if (py_dst_name != NULL) {
dst_name = PyBytes_AS_STRING(py_dst_name);
assert(dst_name != NULL);
}
if (self->container->attach_interface(self->container, src_name, dst_name)) {
Py_XDECREF(py_src_name);
Py_XDECREF(py_dst_name);
Py_RETURN_TRUE;
}
Py_XDECREF(py_src_name);
Py_XDECREF(py_dst_name);
Py_RETURN_FALSE;
}
static PyObject *
Container_detach_interface(Container *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"ifname", NULL};
char *ifname = NULL;
PyObject *py_ifname = NULL;
if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist,
PyUnicode_FSConverter, &py_ifname))
return NULL;
if (py_ifname != NULL) {
ifname = PyBytes_AS_STRING(py_ifname);
assert(ifname != NULL);
}
if (self->container->detach_interface(self->container, ifname, NULL)) {
Py_XDECREF(py_ifname);
Py_RETURN_TRUE;
}
Py_XDECREF(py_ifname);
Py_RETURN_FALSE;
}
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) {
Py_BEGIN_ALLOW_THREADS
ret = lxc_wait_for_pid_status(pid);
Py_END_ALLOW_THREADS
/* 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;
char *bdevtype = NULL;
int i = 0;
static char *kwlist[] = {"template", "flags", "bdevtype", "args", NULL};
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|sisO", kwlist,
&template_name, &flags, &bdevtype, &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, bdevtype, 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;
char* value;
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;
}
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;
char* value;
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("");
}
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)
{
const char *rv = NULL;
rv = self->container->get_config_path(self->container);
if (!rv) {
return PyUnicode_FromString("");
}
return PyUnicode_FromString(rv);
}
static PyObject *
Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"key", NULL};
char* key = NULL;
int len = 0;
char* value;
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;
}
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]) {
if (!interfaces[i]) {
i++;
continue;
}
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]) {
if (!ips[i]) {
i++;
continue;
}
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[] = {
{"attach_interface", (PyCFunction)Container_attach_interface,
METH_VARARGS|METH_KEYWORDS,
"attach_interface(src_ifname, dest_ifname) -> boolean\n"
"\n"
"Pass a new network device to the container."
},
{"detach_interface", (PyCFunction)Container_detach_interface,
METH_VARARGS|METH_KEYWORDS,
"detach_interface(ifname) -> boolean\n"
"\n"
"detach a network device from the container."
},
{"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[] = {
{"arch_to_personality", (PyCFunction)LXC_arch_to_personality, METH_O,
"Returns the process personality of the corresponding architecture"},
{"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}
};
static PyModuleDef _lxcmodule = {
PyModuleDef_HEAD_INIT,
"_lxc",
"Binding for liblxc in python",
-1,
LXC_methods
};
PyMODINIT_FUNC
PyInit__lxc(void)
{
PyObject* m;
PyObject* d;
if (PyType_Ready(&_lxc_ContainerType) < 0)
return NULL;
m = PyModule_Create(&_lxcmodule);
if (m == NULL)
return NULL;
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 m;
}
/*
* kate: space-indent on; indent-width 4; mixedindent off; indent-mode cstyle;
*/
#
# -*- coding: utf-8 -*-
# python-lxc: Python bindings for LXC
#
# (C) Copyright Canonical Ltd. 2012
#
# Authors:
# Stéphane Graber <stgraber@ubuntu.com>
#
# 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.net.%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):
if key in ("ipv4", "ipv6"):
return self.container.clear_config_item("lxc.net.%s.%s" % (
self.index, key))
else:
return self.container.set_config_item("lxc.net.%s.%s" % (
self.index, key), "")
def __get_network_item(self, key):
return self.container.get_config_item("lxc.net.%s.%s" % (
self.index, key))
def __set_network_item(self, key, value):
return self.container.set_config_item("lxc.net.%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.net")
if values:
return len(values)
else:
return 0
def add(self, network_type):
index = len(self)
return self.container.set_config_item("lxc.net.%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.net.%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=(), bdevtype=None):
"""
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):
tmp_args = []
for item in args.items():
tmp_args.append("--%s" % item[0])
tmp_args.append("%s" % item[1])
args = tmp_args
template_args = {}
if template:
template_args['template'] = template
template_args['flags'] = flags
template_args['args'] = tuple(args)
if bdevtype:
template_args['bdevtype'] = bdevtype
return _lxc.Container.create(self, **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.log.level":
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
#!/usr/bin/env python3
#
# python-lxc: Python bindings for LXC
#
# (C) Copyright Canonical Ltd. 2012
#
# Authors:
# Stéphane Graber <stgraber@ubuntu.com>
#
# 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 os
import subprocess
# Fix build when PIE is enabled (must run before setuptools import)
for var in ("LDFLAGS", "CFLAGS"):
current = os.environ.get(var, None)
if not current:
continue
new = []
for flag in current.split(" "):
if flag.lower() in ("-pie", "-fpie"):
if "-fPIC" not in new:
new.append("-fPIC")
continue
new.append(flag)
os.environ[var] = " ".join(new)
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext as BuildExtCommand
class LxcBuildExtCommand(BuildExtCommand):
user_options = BuildExtCommand.user_options + [
('no-pkg-config', None,
"don't use pkg-config to detect include/library paths")
]
def initialize_options(self):
super(LxcBuildExtCommand, self).initialize_options()
self.no_pkg_config = False
def build_extensions(self):
if not self.no_pkg_config:
pkg_config_executable = os.environ.get('PKG_CONFIG_EXECUTABLE',
'pkg-config')
def get_pkg_config_var(name):
args = [pkg_config_executable, '--variable', name, 'lxc']
output = subprocess.check_output(args,
universal_newlines=True)
return output.rstrip('\n')
try:
includedir = get_pkg_config_var('includedir')
libdir = get_pkg_config_var('libdir')
self.compiler.add_include_dir(includedir)
self.compiler.add_library_dir(libdir)
except subprocess.CalledProcessError:
pass
super(LxcBuildExtCommand, self).build_extensions()
setup(name='lxc',
version='0.1',
description='LXC',
packages=['lxc'],
package_dir={'lxc': 'lxc'},
ext_modules=[Extension('_lxc', sources=['lxc.c'], libraries=['lxc'])],
cmdclass={'build_ext': LxcBuildExtCommand},
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment