[lxc-devel] [PATCH] lxcapi: Add new get_ips() call

Stéphane Graber stgraber at ubuntu.com
Mon May 20 21:57:05 UTC 2013


This adds a new get_ips call which takes a family (inet, inet6 or NULL),
a network interface (or NULL for all) and a scope (0 for global) and returns
a char** of all the IPs in the container.

This also adds a matching python3 binding (function result is a tuple) and
deprecates the previous pure-python get_ips() implementation.

WARNING: The python get_ips() call is quite different from the previous
implementation. The timeout argument has been removed, the family names are
slightly different (inet/inet6 vs ipv4/ipv6) and an extra scope parameter
has been added.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 src/lxc/lxccontainer.c              | 113 ++++++++++++++++++++++++++++++++++++
 src/lxc/lxccontainer.h              |   1 +
 src/python-lxc/examples/api_test.py |   9 ++-
 src/python-lxc/lxc.c                |  61 +++++++++++++++++++
 src/python-lxc/lxc/__init__.py      |  61 -------------------
 5 files changed, 183 insertions(+), 62 deletions(-)

diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 23db6f2..0e0c029 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -37,6 +37,10 @@
 #include "bdev.h"
 #include <lxc/utils.h>
 #include <lxc/monitor.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
 
 static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -768,6 +772,114 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
 	return ret == 0;
 }
 
+char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, int scope)
+{
+	int count = 0;
+	struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+	char addressOutputBuffer[INET6_ADDRSTRLEN];
+	void *tempAddrPtr = NULL;
+	char **addresses = NULL, **temp;
+	char *address = NULL;
+	char new_netns_path[MAXPATHLEN];
+	int old_netns = -1, new_netns = -1, ret = 0;
+
+	if (!c->is_running(c))
+		goto out;
+
+	/* Save reference to old netns */
+	old_netns = open("/proc/self/ns/net", O_RDONLY);
+	if (old_netns < 0) {
+		SYSERROR("failed to open /proc/self/ns/net");
+		goto out;
+	}
+
+	/* Switch to new netns */
+	ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", c->init_pid(c));
+	if (ret < 0 || ret >= MAXPATHLEN)
+		goto out;
+
+	new_netns = open(new_netns_path, O_RDONLY);
+	if (new_netns < 0) {
+		SYSERROR("failed to open %s", new_netns_path);
+		goto out;
+	}
+
+	if (setns(new_netns, CLONE_NEWNET)) {
+		SYSERROR("failed to setns");
+		goto out;
+	}
+
+	/* Grab the list of interfaces */
+	if (getifaddrs(&interfaceArray)) {
+		SYSERROR("failed to get interfaces list");
+		goto out;
+	}
+
+	/* Iterate through the interfaces */
+	for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+		if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
+			if (family && strcmp(family, "inet"))
+				continue;
+			tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
+		}
+		else {
+			if (family && strcmp(family, "inet6"))
+				continue;
+
+			if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
+				continue;
+
+			tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
+		}
+
+		if (interface && strcmp(interface, tempIfAddr->ifa_name))
+			continue;
+		else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
+			continue;
+
+		address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
+					   tempAddrPtr,
+					   addressOutputBuffer,
+					   sizeof(addressOutputBuffer));
+		if (!address)
+			continue;
+
+		count += 1;
+		temp = realloc(addresses, count * sizeof(*addresses));
+		if (!temp) {
+			count--;
+			goto out;
+		}
+		addresses = temp;
+		addresses[count - 1] = strdup(address);
+	}
+
+out:
+	if(interfaceArray)
+		freeifaddrs(interfaceArray);
+
+	/* Append NULL to the array */
+	if (count) {
+		count++;
+		temp = realloc(addresses, count * sizeof(*addresses));
+		if (!temp) {
+			return NULL;
+		}
+		addresses = temp;
+		addresses[count - 1] = NULL;
+	}
+
+	/* Switch back to original netns */
+	if (old_netns >= 0 && setns(old_netns, CLONE_NEWNET))
+		SYSERROR("failed to setns");
+	if (new_netns >= 0)
+		close(new_netns);
+	if (old_netns >= 0)
+		close(old_netns);
+
+	return addresses;
+}
+
 static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
 {
 	int ret;
@@ -1642,6 +1754,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 	c->get_config_path = lxcapi_get_config_path;
 	c->set_config_path = lxcapi_set_config_path;
 	c->clone = lxcapi_clone;
+	c->get_ips = lxcapi_get_ips;
 
 	/* we'll allow the caller to update these later */
 	if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 67fbed4..bf3168c 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -58,6 +58,7 @@ struct lxc_container {
 	 * the length which was our would be printed. */
 	int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen);
 	int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen);
+	char** (*get_ips)(struct lxc_container *c, char* interface, char* family, int scope);
 	/*
 	 * get_cgroup_item returns the number of bytes read, or an error (<0).
 	 * If retv NULL or inlen 0 is passed in, then the length of the cgroup
diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py
index 367bb7a..45a1150 100644
--- a/src/python-lxc/examples/api_test.py
+++ b/src/python-lxc/examples/api_test.py
@@ -28,6 +28,7 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
 import lxc
 import uuid
 import sys
+import time
 
 # Some constants
 LXC_TEMPLATE = "ubuntu"
@@ -90,7 +91,13 @@ assert(container.state == "RUNNING")
 
 ## Checking IP address
 print("Getting the IP addresses")
-ips = container.get_ips(timeout=10)
+
+count = 0
+ips = []
+while not ips or count == 10:
+    ips = container.get_ips()
+    time.sleep(1)
+    count += 1
 container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0")
 
 # A few basic checks of the current state
diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c
index 4e9fde7..cdc1e8b 100644
--- a/src/python-lxc/lxc.c
+++ b/src/python-lxc/lxc.c
@@ -400,6 +400,61 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
 }
 
 static PyObject *
+Container_get_ips(Container *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"interface", "family", "scope", NULL};
+    char* interface = NULL;
+    char* family = NULL;
+    int scope = 0;
+
+    int i = 0;
+    char** ips = NULL;
+
+    PyObject* ret;
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ssi", kwlist,
+                                      &interface, &family, &scope))
+        return NULL;
+
+    /* Get the IPs */
+    ips = self->container->get_ips(self->container, interface, family, scope);
+    if (!ips)
+        return PyTuple_New(0);
+
+    /* Count the entries */
+    while (ips[i])
+        i++;
+
+    /* Create the new tuple */
+    ret = PyTuple_New(i);
+    if (!ret)
+        return NULL;
+
+    /* Add the entries to the tuple and free the memory */
+    i = 0;
+    while (ips[i]) {
+        PyObject *unicode = PyUnicode_FromString(ips[i]);
+        if (!unicode) {
+            Py_DECREF(ret);
+            ret = NULL;
+            break;
+        }
+        PyTuple_SET_ITEM(ret, i, unicode);
+        i++;
+    }
+
+    /* Free the list of IPs */
+    i = 0;
+    while (ips[i]) {
+        free(ips[i]);
+        i++;
+    }
+    free(ips);
+
+    return ret;
+}
+
+static PyObject *
 Container_load_config(Container *self, PyObject *args, PyObject *kwds)
 {
     static char *kwlist[] = {"path", NULL};
@@ -678,6 +733,12 @@ static PyMethodDef Container_methods[] = {
      "\n"
      "Get a list of valid sub-keys for a key."
     },
+    {"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."
+    },
     {"load_config", (PyCFunction)Container_load_config,
      METH_VARARGS|METH_KEYWORDS,
      "load_config(path = DEFAULT) -> boolean\n"
diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py
index c235d18..2fc907c 100644
--- a/src/python-lxc/lxc/__init__.py
+++ b/src/python-lxc/lxc/__init__.py
@@ -26,7 +26,6 @@ import glob
 import os
 import subprocess
 import stat
-import time
 import warnings
 
 warnings.warn("The python-lxc API isn't yet stable "
@@ -332,66 +331,6 @@ class Container(_lxc.Container):
         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.running:
-            return False
-
-        ips = []
-
-        count = 0
-        while count < timeout:
-            if count != 0:
-                time.sleep(1)
-
-            base_cmd = ["lxc-attach", "-s", "NETWORK",
-                        "-P", self.get_config_path(), "-n", self.name, "--",
-                        "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
-
-        return ips
-
     def get_keys(self, key=None):
         """
             Returns a list of valid sub-keys.
-- 
1.8.1.2





More information about the lxc-devel mailing list