Quoting Stéphane Graber (stgra...@ubuntu.com): > 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>
Acked-by: Serge E. Hallyn <serge.hal...@ubuntu.com> > --- > src/lxc/lxccontainer.c | 116 > ++++++++++++++++++++++++++++++++++++ > 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, 186 insertions(+), 62 deletions(-) > > diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c > index 23db6f2..4a6f774 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,117 @@ 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); > + > + /* 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); > + > + /* Append NULL to the array */ > + if (count) { > + count++; > + temp = realloc(addresses, count * sizeof(*addresses)); > + if (!temp) { > + for (int i = 0; i < count-1; i++) > + free(addresses[i]); > + free(addresses); > + return NULL; > + } > + addresses = temp; > + addresses[count - 1] = NULL; > + } > + > + return addresses; > +} > + > static int lxcapi_get_config_item(struct lxc_container *c, const char *key, > char *retv, int inlen) > { > int ret; > @@ -1642,6 +1757,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 > > > ------------------------------------------------------------------------------ > Try New Relic Now & We'll Send You this Cool Shirt > New Relic is the only SaaS-based application performance monitoring service > that delivers powerful full stack analytics. Optimize and monitor your > browser, app, & servers with just a few lines of code. Try New Relic > and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may > _______________________________________________ > Lxc-devel mailing list > Lxc-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/lxc-devel ------------------------------------------------------------------------------ Try New Relic Now & We'll Send You this Cool Shirt New Relic is the only SaaS-based application performance monitoring service that delivers powerful full stack analytics. Optimize and monitor your browser, app, & servers with just a few lines of code. Try New Relic and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel