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

Serge Hallyn serge.hallyn at ubuntu.com
Tue May 21 15:04:04 UTC 2013


Quoting Stéphane Graber (stgraber at 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 <stgraber at ubuntu.com>

Acked-by: Serge E. Hallyn <serge.hallyn at 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 at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel




More information about the lxc-devel mailing list