[lxc-devel] [PATCH 4/4] python: Remove hardcoded LXCPATH

Stéphane Graber stgraber at ubuntu.com
Wed Dec 5 23:51:10 UTC 2012


Switch the python scripts to using @LXCPATH at .

According to grep, this was the last occurence of a /var/*/lxc
path in the code.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 configure.ac                           |   2 +
 src/python-lxc/examples/api_test.py    | 151 -----------
 src/python-lxc/examples/api_test.py.in | 151 +++++++++++
 src/python-lxc/lxc/__init__.py         | 473 ---------------------------------
 src/python-lxc/lxc/__init__.py.in      | 473 +++++++++++++++++++++++++++++++++
 5 files changed, 626 insertions(+), 624 deletions(-)
 delete mode 100644 src/python-lxc/examples/api_test.py
 create mode 100644 src/python-lxc/examples/api_test.py.in
 delete mode 100644 src/python-lxc/lxc/__init__.py
 create mode 100644 src/python-lxc/lxc/__init__.py.in

diff --git a/configure.ac b/configure.ac
index 893e179..ef321ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -271,6 +271,8 @@ AC_CONFIG_FILES([
 	src/lxc/legacy/lxc-ls
 
 	src/python-lxc/Makefile
+	src/python-lxc/lxc/__init__.py
+	src/python-lxc/examples/api_test.py
 
 	src/tests/Makefile
 
diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py
deleted file mode 100644
index 3ca0267..0000000
--- a/src/python-lxc/examples/api_test.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/python3
-#
-# api_test.py: Test/demo of the python3-lxc API
-#
-# (C) Copyright Canonical Ltd. 2012
-#
-# Authors:
-# Stéphane Graber <stgraber at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-import warnings
-warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
-
-import lxc
-import uuid
-import sys
-
-# Some constants
-LXC_PATH_LIB = "/var/lib/lxc"
-LXC_TEMPLATE = "ubuntu"
-
-# Let's pick a random name, avoiding clashes
-CONTAINER_NAME = str(uuid.uuid1())
-CLONE_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_PATH_LIB, CONTAINER_NAME))
-assert(not container.defined)
-assert(container.init_pid == -1)
-assert(container.name == CONTAINER_NAME)
-assert(not container.running)
-assert(container.state == "STOPPED")
-
-## Create a rootfs
-print("Creating rootfs using '%s'" % LXC_TEMPLATE)
-container.create(LXC_TEMPLATE)
-
-assert(container.defined)
-assert(container.name == CONTAINER_NAME
-       == container.get_config_item("lxc.utsname"))
-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.network.0"))
-assert(len(container.network) == 1)
-assert(container.network[0].hwaddr.startswith("00:16:3e"))
-
-## 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 IP addresses")
-ips = container.get_ips(timeout=10)
-container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0")
-
-# A few basic checks of the current state
-assert(len(ips) > 0)
-
-## 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")
-container.shutdown(3)
-
-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")
-
-## Cloning the container
-print("Cloning the container")
-clone = lxc.Container(CLONE_NAME)
-clone.clone(container)
-clone.start()
-clone.stop()
-clone.destroy()
-
-## Destroy the container
-print("Destroying the container")
-container.destroy()
-
-assert(not container.defined)
diff --git a/src/python-lxc/examples/api_test.py.in b/src/python-lxc/examples/api_test.py.in
new file mode 100644
index 0000000..0b17bd6
--- /dev/null
+++ b/src/python-lxc/examples/api_test.py.in
@@ -0,0 +1,151 @@
+#!/usr/bin/python3
+#
+# api_test.py: Test/demo of the python3-lxc API
+#
+# (C) Copyright Canonical Ltd. 2012
+#
+# Authors:
+# Stéphane Graber <stgraber at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import warnings
+warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
+
+import lxc
+import uuid
+import sys
+
+# Some constants
+LXC_PATH_LIB = "@LXCPATH@"
+LXC_TEMPLATE = "ubuntu"
+
+# Let's pick a random name, avoiding clashes
+CONTAINER_NAME = str(uuid.uuid1())
+CLONE_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_PATH_LIB, CONTAINER_NAME))
+assert(not container.defined)
+assert(container.init_pid == -1)
+assert(container.name == CONTAINER_NAME)
+assert(not container.running)
+assert(container.state == "STOPPED")
+
+## Create a rootfs
+print("Creating rootfs using '%s'" % LXC_TEMPLATE)
+container.create(LXC_TEMPLATE)
+
+assert(container.defined)
+assert(container.name == CONTAINER_NAME
+       == container.get_config_item("lxc.utsname"))
+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.network.0"))
+assert(len(container.network) == 1)
+assert(container.network[0].hwaddr.startswith("00:16:3e"))
+
+## 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 IP addresses")
+ips = container.get_ips(timeout=10)
+container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0")
+
+# A few basic checks of the current state
+assert(len(ips) > 0)
+
+## 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")
+container.shutdown(3)
+
+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")
+
+## Cloning the container
+print("Cloning the container")
+clone = lxc.Container(CLONE_NAME)
+clone.clone(container)
+clone.start()
+clone.stop()
+clone.destroy()
+
+## Destroy the container
+print("Destroying the container")
+container.destroy()
+
+assert(not container.defined)
diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py
deleted file mode 100644
index cde4fd1..0000000
--- a/src/python-lxc/lxc/__init__.py
+++ /dev/null
@@ -1,473 +0,0 @@
-#
-# python-lxc: Python bindings for LXC
-#
-# (C) Copyright Canonical Ltd. 2012
-#
-# Authors:
-# Stéphane Graber <stgraber at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-import _lxc
-import glob
-import os
-import subprocess
-import stat
-import tempfile
-import time
-import warnings
-
-warnings.warn("The python-lxc API isn't yet stable "
-              "and may change at any point in the future.", Warning, 2)
-
-
-class ContainerNetwork():
-    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):
-        """
-            Creates a new Container instance.
-        """
-
-        if os.geteuid() != 0:
-            raise Exception("Running as non-root.")
-
-        _lxc.Container.__init__(self, name)
-        self.network = ContainerNetworkList(self)
-
-    def add_device_node(self, path, destpath=None):
-        """
-            Add block/char device to running container.
-        """
-
-        if not self.running:
-            return False
-
-        if not destpath:
-            destpath = path
-
-        if not os.path.exists(path):
-            return False
-
-        # Lookup the source
-        path_stat = os.stat(path)
-        mode = stat.S_IMODE(path_stat.st_mode)
-
-        # Lookup the cgroup
-        cgroup_path = None
-        with open("/proc/%s/cgroup" % self.init_pid, "r") as fd:
-            for line in fd:
-                if ":devices:" in line:
-                    cgroup_path = line.split(":")[-1].strip()
-                    break
-            else:
-                return False
-
-        # Lookup the cgroup mount point
-        cgroup = None
-        with open("/proc/mounts", "r") as fd:
-            for line in fd:
-                mount = line.split()
-                if (mount[2] == "cgroup" and "devices" in mount[3]
-                        and os.path.exists("%s/%s" % (mount[1], cgroup_path))):
-                    cgroup = "%s/%s" % (mount[1], cgroup_path)
-                    break
-
-        if not os.path.exists(cgroup):
-            return False
-
-        # Allow the target
-        with open("%s/devices.allow" % cgroup, "a") as fd:
-            if stat.S_ISBLK(path_stat.st_mode):
-                fd.write("b %s:%s rwm" % (int(path_stat.st_rdev / 256),
-                                          int(path_stat.st_rdev % 256)))
-            elif stat.S_ISCHR(path_stat.st_mode):
-                fd.write("c %s:%s rwm" % (int(path_stat.st_rdev / 256),
-                                          int(path_stat.st_rdev % 256)))
-
-        # Create the target
-        rootfs = "/proc/%s/root/" % self.init_pid
-        container_path = "%s/%s" % (rootfs, destpath)
-
-        if os.path.exists(container_path):
-            os.remove(container_path)
-
-        os.mknod(container_path, path_stat.st_mode, path_stat.st_rdev)
-        os.chmod(container_path, mode)
-        os.chown(container_path, 0, 0)
-
-        return True
-
-    def add_device_net(self, name, destname=None):
-        """
-            Add network device to running container.
-        """
-
-        if not self.running:
-            return False
-
-        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 attach(self, namespace="ALL", *cmd):
-        """
-            Attach to a running container.
-        """
-
-        if not self.running:
-            return False
-
-        attach = ["lxc-attach", "-n", self.name]
-        if namespace != "ALL":
-            attach += ["-s", namespace]
-
-        if cmd:
-            attach += ["--"] + list(cmd)
-
-        if subprocess.call(
-                attach,
-                universal_newlines=True) != 0:
-            return False
-        return True
-
-    def create(self, template, args={}):
-        """
-            Create a new rootfs for the container.
-
-            "template" must be a valid template name.
-
-            "args" (optional) is a dictionary of parameters and values to pass
-            to the template.
-        """
-
-        template_args = []
-        for item in args.items():
-            template_args.append("--%s" % item[0])
-            template_args.append("%s" % item[1])
-
-        return _lxc.Container.create(self, template, tuple(template_args))
-
-    def clone(self, container):
-        """
-            Clone an existing container into a new one.
-        """
-
-        if self.defined:
-            return False
-
-        if isinstance(container, Container):
-            source = container
-        else:
-            source = Container(container)
-
-        if not source.defined:
-            return False
-
-        if subprocess.call(["lxc-clone", "-o", source.name, "-n", self.name],
-                           universal_newlines=True) != 0:
-            return False
-
-        self.load_config()
-        return True
-
-    def console(self, tty="1"):
-        """
-            Access the console of a container.
-        """
-
-        if not self.running:
-            return False
-
-        if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty],
-                           universal_newlines=True) != 0:
-            return False
-        return True
-
-    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_ips(self, timeout=60, interface=None, protocol=None):
-        """
-            Returns the list of IP addresses for the container.
-        """
-
-        if not self.defined or not self.running:
-            return False
-
-        try:
-            os.makedirs("/run/netns")
-        except:
-            pass
-
-        path = tempfile.mktemp(dir="/run/netns")
-
-        os.symlink("/proc/%s/ns/net" % self.init_pid, path)
-
-        ips = []
-
-        count = 0
-        while count < timeout:
-            if count != 0:
-                time.sleep(1)
-
-            base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
-
-            # Get IPv6
-            if protocol in ("ipv6", None):
-                ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"]
-                if interface:
-                    ip = subprocess.Popen(ip6_cmd + ["dev", interface],
-                                          stdout=subprocess.PIPE,
-                                          universal_newlines=True)
-                else:
-                    ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE,
-                                          universal_newlines=True)
-
-                ip.wait()
-                for line in ip.stdout.read().split("\n"):
-                    fields = line.split()
-                    if len(fields) > 2 and fields[0] == "inet6":
-                        ips.append(fields[1].split('/')[0])
-
-            # Get IPv4
-            if protocol in ("ipv4", None):
-                ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"]
-                if interface:
-                    ip = subprocess.Popen(ip4_cmd + ["dev", interface],
-                                          stdout=subprocess.PIPE,
-                                          universal_newlines=True)
-                else:
-                    ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE,
-                                          universal_newlines=True)
-
-                ip.wait()
-                for line in ip.stdout.read().split("\n"):
-                    fields = line.split()
-                    if len(fields) > 2 and fields[0] == "inet":
-                        ips.append(fields[1].split('/')[0])
-
-            if ips:
-                break
-
-            count += 1
-
-        os.remove(path)
-        return ips
-
-    def get_keys(self, key):
-        """
-            Returns a list of valid sub-keys.
-        """
-        value = _lxc.Container.get_keys(self, key)
-
-        if value is False:
-            return False
-        elif value.endswith("\n"):
-            return value.rstrip("\n").split("\n")
-        else:
-            return value
-
-    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.
-        """
-        old_value = self.get_config_item(key)
-
-        # 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)
-
-        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(as_object=False):
-    """
-        List the containers on the system.
-    """
-    containers = []
-    for entry in glob.glob("/var/lib/lxc/*/config"):
-        if as_object:
-            containers.append(Container(entry.split("/")[-2]))
-        else:
-            containers.append(entry.split("/")[-2])
-    return containers
diff --git a/src/python-lxc/lxc/__init__.py.in b/src/python-lxc/lxc/__init__.py.in
new file mode 100644
index 0000000..f42b944
--- /dev/null
+++ b/src/python-lxc/lxc/__init__.py.in
@@ -0,0 +1,473 @@
+#
+# python-lxc: Python bindings for LXC
+#
+# (C) Copyright Canonical Ltd. 2012
+#
+# Authors:
+# Stéphane Graber <stgraber at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import _lxc
+import glob
+import os
+import subprocess
+import stat
+import tempfile
+import time
+import warnings
+
+warnings.warn("The python-lxc API isn't yet stable "
+              "and may change at any point in the future.", Warning, 2)
+
+
+class ContainerNetwork():
+    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):
+        """
+            Creates a new Container instance.
+        """
+
+        if os.geteuid() != 0:
+            raise Exception("Running as non-root.")
+
+        _lxc.Container.__init__(self, name)
+        self.network = ContainerNetworkList(self)
+
+    def add_device_node(self, path, destpath=None):
+        """
+            Add block/char device to running container.
+        """
+
+        if not self.running:
+            return False
+
+        if not destpath:
+            destpath = path
+
+        if not os.path.exists(path):
+            return False
+
+        # Lookup the source
+        path_stat = os.stat(path)
+        mode = stat.S_IMODE(path_stat.st_mode)
+
+        # Lookup the cgroup
+        cgroup_path = None
+        with open("/proc/%s/cgroup" % self.init_pid, "r") as fd:
+            for line in fd:
+                if ":devices:" in line:
+                    cgroup_path = line.split(":")[-1].strip()
+                    break
+            else:
+                return False
+
+        # Lookup the cgroup mount point
+        cgroup = None
+        with open("/proc/mounts", "r") as fd:
+            for line in fd:
+                mount = line.split()
+                if (mount[2] == "cgroup" and "devices" in mount[3]
+                        and os.path.exists("%s/%s" % (mount[1], cgroup_path))):
+                    cgroup = "%s/%s" % (mount[1], cgroup_path)
+                    break
+
+        if not os.path.exists(cgroup):
+            return False
+
+        # Allow the target
+        with open("%s/devices.allow" % cgroup, "a") as fd:
+            if stat.S_ISBLK(path_stat.st_mode):
+                fd.write("b %s:%s rwm" % (int(path_stat.st_rdev / 256),
+                                          int(path_stat.st_rdev % 256)))
+            elif stat.S_ISCHR(path_stat.st_mode):
+                fd.write("c %s:%s rwm" % (int(path_stat.st_rdev / 256),
+                                          int(path_stat.st_rdev % 256)))
+
+        # Create the target
+        rootfs = "/proc/%s/root/" % self.init_pid
+        container_path = "%s/%s" % (rootfs, destpath)
+
+        if os.path.exists(container_path):
+            os.remove(container_path)
+
+        os.mknod(container_path, path_stat.st_mode, path_stat.st_rdev)
+        os.chmod(container_path, mode)
+        os.chown(container_path, 0, 0)
+
+        return True
+
+    def add_device_net(self, name, destname=None):
+        """
+            Add network device to running container.
+        """
+
+        if not self.running:
+            return False
+
+        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 attach(self, namespace="ALL", *cmd):
+        """
+            Attach to a running container.
+        """
+
+        if not self.running:
+            return False
+
+        attach = ["lxc-attach", "-n", self.name]
+        if namespace != "ALL":
+            attach += ["-s", namespace]
+
+        if cmd:
+            attach += ["--"] + list(cmd)
+
+        if subprocess.call(
+                attach,
+                universal_newlines=True) != 0:
+            return False
+        return True
+
+    def create(self, template, args={}):
+        """
+            Create a new rootfs for the container.
+
+            "template" must be a valid template name.
+
+            "args" (optional) is a dictionary of parameters and values to pass
+            to the template.
+        """
+
+        template_args = []
+        for item in args.items():
+            template_args.append("--%s" % item[0])
+            template_args.append("%s" % item[1])
+
+        return _lxc.Container.create(self, template, tuple(template_args))
+
+    def clone(self, container):
+        """
+            Clone an existing container into a new one.
+        """
+
+        if self.defined:
+            return False
+
+        if isinstance(container, Container):
+            source = container
+        else:
+            source = Container(container)
+
+        if not source.defined:
+            return False
+
+        if subprocess.call(["lxc-clone", "-o", source.name, "-n", self.name],
+                           universal_newlines=True) != 0:
+            return False
+
+        self.load_config()
+        return True
+
+    def console(self, tty="1"):
+        """
+            Access the console of a container.
+        """
+
+        if not self.running:
+            return False
+
+        if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty],
+                           universal_newlines=True) != 0:
+            return False
+        return True
+
+    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_ips(self, timeout=60, interface=None, protocol=None):
+        """
+            Returns the list of IP addresses for the container.
+        """
+
+        if not self.defined or not self.running:
+            return False
+
+        try:
+            os.makedirs("/run/netns")
+        except:
+            pass
+
+        path = tempfile.mktemp(dir="/run/netns")
+
+        os.symlink("/proc/%s/ns/net" % self.init_pid, path)
+
+        ips = []
+
+        count = 0
+        while count < timeout:
+            if count != 0:
+                time.sleep(1)
+
+            base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
+
+            # Get IPv6
+            if protocol in ("ipv6", None):
+                ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"]
+                if interface:
+                    ip = subprocess.Popen(ip6_cmd + ["dev", interface],
+                                          stdout=subprocess.PIPE,
+                                          universal_newlines=True)
+                else:
+                    ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE,
+                                          universal_newlines=True)
+
+                ip.wait()
+                for line in ip.stdout.read().split("\n"):
+                    fields = line.split()
+                    if len(fields) > 2 and fields[0] == "inet6":
+                        ips.append(fields[1].split('/')[0])
+
+            # Get IPv4
+            if protocol in ("ipv4", None):
+                ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"]
+                if interface:
+                    ip = subprocess.Popen(ip4_cmd + ["dev", interface],
+                                          stdout=subprocess.PIPE,
+                                          universal_newlines=True)
+                else:
+                    ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE,
+                                          universal_newlines=True)
+
+                ip.wait()
+                for line in ip.stdout.read().split("\n"):
+                    fields = line.split()
+                    if len(fields) > 2 and fields[0] == "inet":
+                        ips.append(fields[1].split('/')[0])
+
+            if ips:
+                break
+
+            count += 1
+
+        os.remove(path)
+        return ips
+
+    def get_keys(self, key):
+        """
+            Returns a list of valid sub-keys.
+        """
+        value = _lxc.Container.get_keys(self, key)
+
+        if value is False:
+            return False
+        elif value.endswith("\n"):
+            return value.rstrip("\n").split("\n")
+        else:
+            return value
+
+    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.
+        """
+        old_value = self.get_config_item(key)
+
+        # 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)
+
+        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(as_object=False):
+    """
+        List the containers on the system.
+    """
+    containers = []
+    for entry in glob.glob("@LXCPATH@/*/config"):
+        if as_object:
+            containers.append(Container(entry.split("/")[-2]))
+        else:
+            containers.append(entry.split("/")[-2])
+    return containers
-- 
1.8.0





More information about the lxc-devel mailing list