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 <stgra...@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 ------------------------------------------------------------------------------ AlienVault Unified Security Management (USM) platform delivers complete security visibility with the essential security capabilities. Easily and efficiently configure, manage, and operate all of your security controls from a single console and one unified framework. Download a free trial. http://p.sf.net/sfu/alienvault_d2d _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel