mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-27 14:30:32 +00:00
lxcapi: Add new get_ips() call
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@ubuntu.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
92f023dccc
commit
9c83a66139
@ -37,6 +37,10 @@
|
|||||||
#include "bdev.h"
|
#include "bdev.h"
|
||||||
#include <lxc/utils.h>
|
#include <lxc/utils.h>
|
||||||
#include <lxc/monitor.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;
|
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;
|
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)
|
static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
|
||||||
{
|
{
|
||||||
int ret;
|
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->get_config_path = lxcapi_get_config_path;
|
||||||
c->set_config_path = lxcapi_set_config_path;
|
c->set_config_path = lxcapi_set_config_path;
|
||||||
c->clone = lxcapi_clone;
|
c->clone = lxcapi_clone;
|
||||||
|
c->get_ips = lxcapi_get_ips;
|
||||||
|
|
||||||
/* we'll allow the caller to update these later */
|
/* we'll allow the caller to update these later */
|
||||||
if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
|
if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
|
||||||
|
@ -58,6 +58,7 @@ struct lxc_container {
|
|||||||
* the length which was our would be printed. */
|
* 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_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);
|
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).
|
* 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
|
* If retv NULL or inlen 0 is passed in, then the length of the cgroup
|
||||||
|
@ -28,6 +28,7 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
|
|||||||
import lxc
|
import lxc
|
||||||
import uuid
|
import uuid
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
# Some constants
|
# Some constants
|
||||||
LXC_TEMPLATE = "ubuntu"
|
LXC_TEMPLATE = "ubuntu"
|
||||||
@ -90,7 +91,13 @@ assert(container.state == "RUNNING")
|
|||||||
|
|
||||||
## Checking IP address
|
## Checking IP address
|
||||||
print("Getting the IP addresses")
|
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")
|
container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0")
|
||||||
|
|
||||||
# A few basic checks of the current state
|
# A few basic checks of the current state
|
||||||
|
@ -399,6 +399,61 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 *
|
static PyObject *
|
||||||
Container_load_config(Container *self, PyObject *args, PyObject *kwds)
|
Container_load_config(Container *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
@ -678,6 +733,12 @@ static PyMethodDef Container_methods[] = {
|
|||||||
"\n"
|
"\n"
|
||||||
"Get a list of valid sub-keys for a key."
|
"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,
|
{"load_config", (PyCFunction)Container_load_config,
|
||||||
METH_VARARGS|METH_KEYWORDS,
|
METH_VARARGS|METH_KEYWORDS,
|
||||||
"load_config(path = DEFAULT) -> boolean\n"
|
"load_config(path = DEFAULT) -> boolean\n"
|
||||||
|
@ -26,7 +26,6 @@ import glob
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import stat
|
import stat
|
||||||
import time
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
warnings.warn("The python-lxc API isn't yet stable "
|
warnings.warn("The python-lxc API isn't yet stable "
|
||||||
@ -332,66 +331,6 @@ class Container(_lxc.Container):
|
|||||||
else:
|
else:
|
||||||
return value
|
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):
|
def get_keys(self, key=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of valid sub-keys.
|
Returns a list of valid sub-keys.
|
||||||
|
Loading…
Reference in New Issue
Block a user