[libvirt] [PATCH v4] lxc: Inherit namespace feature

This patch adds feature for lxc containers to inherit namespaces. This is very similar to what lxc-tools or docker provides. Look for "man lxc-start" and you will find that you can pass command args as [ --share-[net|ipc|uts] name|pid ]. Or check out docker networking option in which you can give --net=container:NAME_or_ID as an option for sharing +namespace.
From this patch you can add extra libvirt option to share namespace in following way.
<lxc:namespace> <lxc:sharenet type='netns' value='red'/> <lxc:shareipc type='pid' value='12345'/> <lxc:shareuts type='name' value='container1'/> </lxc:namespace> The netns option is specific to sharenet. It can be used to inherit from existing network namespace. --- docs/drvlxc.html.in | 21 +++++ docs/schemas/domaincommon.rng | 42 +++++++++ po/POTFILES.in | 1 + src/Makefile.am | 7 +- src/lxc/lxc_conf.c | 2 +- src/lxc/lxc_container.c | 71 +++++++++++++-- src/lxc/lxc_container.h | 2 + src/lxc/lxc_controller.c | 57 +++++++++++- src/lxc/lxc_domain.c | 149 ++++++++++++++++++++++++++++++++ src/lxc/lxc_domain.h | 26 ++++++ src/lxc/lxc_process.c | 157 ++++++++++++++++++++++++++++++++++ tests/lxcxml2xmldata/lxc-sharenet.xml | 33 +++++++ tests/lxcxml2xmltest.c | 1 + 13 files changed, 560 insertions(+), 9 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-sharenet.xml diff --git a/docs/drvlxc.html.in b/docs/drvlxc.html.in index a094bd9..d6c57c4 100644 --- a/docs/drvlxc.html.in +++ b/docs/drvlxc.html.in @@ -590,6 +590,27 @@ Note that allowing capabilities that are normally dropped by default can serious affect the security of the container and the host. </p> +<h2><a name="share">Inherit namespaces</a></h2> + +<p> +Libvirt allows you to inherit the namespace from container/process just like lxc tools +or docker provides to share the network namespace. The following can be used to share +required namespaces. If we want to share only one then the other namespaces can be ignored. +The netns option is specific to sharenet. It can be used in cases we want to use existing network namespace +rather than creating new network namespace for the container. In this case privnet option will be +ignored. +</p> +<pre> +<domain type='lxc' xmlns:lxc='http://libvirt.org/schemas/domain/lxc/1.0'> +... +<lxc:namespace> + <lxc:sharenet type='netns' value='red'/> + <lxc:shareuts type='name' value='container1'/> + <lxc:shareipc type='pid' value='12345'/> +</lxc:namespace> +</domain> +</pre> + <h2><a name="usage">Container usage / management</a></h2> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 043c975..fa026cd 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -68,6 +68,9 @@ <ref name='qemucmdline'/> </optional> <optional> + <ref name='lxcsharens'/> + </optional> + <optional> <ref name='keywrap'/> </optional> </interleave> @@ -5057,6 +5060,45 @@ </element> </define> + <!-- + Optional hypervisor extensions in their own namespace: + LXC + --> + <define name="lxcsharens"> + <element name="namespace" ns="http://libvirt.org/schemas/domain/lxc/1.0"> + <zeroOrMore> + <element name="sharenet"> + <attribute name="type"> + <choice> + <value>netns</value> + <value>name</value> + <value>pid</value> + </choice> + </attribute> + <attribute name='value'/> + </element> + <element name="shareipc"> + <attribute name="type"> + <choice> + <value>name</value> + <value>pid</value> + </choice> + </attribute> + <attribute name='value'/> + </element> + <element name="shareuts"> + <attribute name="type"> + <choice> + <value>name</value> + <value>pid</value> + </choice> + </attribute> + <attribute name='value'/> + </element> + </zeroOrMore> + </element> + </define> + <define name="metadata"> <element name="metadata"> <zeroOrMore> diff --git a/po/POTFILES.in b/po/POTFILES.in index 1e52e6a..46220f7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -85,6 +85,7 @@ src/lxc/lxc_native.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c +src/lxc/lxc_domain.c src/lxc/lxc_driver.c src/lxc/lxc_process.c src/libxl/libxl_domain.c diff --git a/src/Makefile.am b/src/Makefile.am index c4d49a5..24d31e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1320,7 +1320,12 @@ libvirt_driver_lxc_impl_la_CFLAGS = \ -I$(srcdir)/access \ -I$(srcdir)/conf \ $(AM_CFLAGS) -libvirt_driver_lxc_impl_la_LIBADD = $(CAPNG_LIBS) $(LIBNL_LIBS) $(FUSE_LIBS) +libvirt_driver_lxc_impl_la_LIBADD = \ + $(CAPNG_LIBS) \ + $(LIBNL_LIBS) \ + $(LIBXML_LIBS) \ + $(FUSE_LIBS) + if WITH_BLKID libvirt_driver_lxc_impl_la_CFLAGS += $(BLKID_CFLAGS) libvirt_driver_lxc_impl_la_LIBADD += $(BLKID_LIBS) diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c index b689b92..8ada531 100644 --- a/src/lxc/lxc_conf.c +++ b/src/lxc/lxc_conf.c @@ -213,7 +213,7 @@ lxcDomainXMLConfInit(void) { return virDomainXMLOptionNew(&virLXCDriverDomainDefParserConfig, &virLXCDriverPrivateDataCallbacks, - NULL); + &virLXCDriverDomainXMLNamespace); } diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 11e9514..8011ed0 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -27,6 +27,7 @@ #include <config.h> #include <fcntl.h> +#include <sched.h> #include <limits.h> #include <stdlib.h> #include <stdio.h> @@ -38,7 +39,6 @@ #include <mntent.h> #include <sys/reboot.h> #include <linux/reboot.h> - /* Yes, we want linux private one, for _syscall2() macro */ #include <linux/unistd.h> @@ -111,6 +111,7 @@ struct __lxc_child_argv { size_t nttyPaths; char **ttyPaths; int handshakefd; + int *nsInheritFDs; }; static int lxcContainerMountFSBlock(virDomainFSDefPtr fs, @@ -2144,6 +2145,35 @@ static int lxcContainerDropCapabilities(virDomainDefPtr def ATTRIBUTE_UNUSED, /** + * lxcAttach_ns: + * @ns_fd: array of namespaces to attach + */ +static int lxcAttachNS(int *ns_fd) +{ + size_t i; + if (ns_fd) + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) { + if (ns_fd[i] < 0) + continue; + VIR_DEBUG("Setting into namespace\n"); + /* We get EINVAL if new NS is same as the current + * NS, or if the fd namespace doesn't match the + * type passed to setns()'s second param. Since we + * pass 0, we know the EINVAL is harmless + */ + if (setns(ns_fd[i], 0) < 0 && + errno != EINVAL) { + virReportSystemError(errno, _("failed to set namespace '%s'"), + virLXCDomainNamespaceTypeToString(i)); + return -1; + } + VIR_FORCE_CLOSE(ns_fd[i]); + } + return 0; +} + + +/** * lxcContainerChild: * @data: pointer to container arguments * @@ -2172,6 +2202,12 @@ static int lxcContainerChild(void *data) goto cleanup; } + if (lxcAttachNS(argv->nsInheritFDs) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("failed to attach the namespace")); + return -1; + } + /* Wait for controller to finish setup tasks, including * things like move of network interfaces, uid/gid mapping */ @@ -2342,6 +2378,7 @@ int lxcContainerStart(virDomainDefPtr def, int *passFDs, int control, int handshakefd, + int *nsInheritFDs, size_t nttyPaths, char **ttyPaths) { @@ -2359,7 +2396,8 @@ int lxcContainerStart(virDomainDefPtr def, .monitor = control, .nttyPaths = nttyPaths, .ttyPaths = ttyPaths, - .handshakefd = handshakefd + .handshakefd = handshakefd, + .nsInheritFDs = nsInheritFDs, }; /* allocate a stack for the container */ @@ -2368,7 +2406,7 @@ int lxcContainerStart(virDomainDefPtr def, stacktop = stack + stacksize; - cflags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|SIGCHLD; + cflags = CLONE_NEWPID|CLONE_NEWNS|SIGCHLD; if (userns_required(def)) { if (userns_supported()) { @@ -2381,10 +2419,31 @@ int lxcContainerStart(virDomainDefPtr def, return -1; } } + if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHARENET] == -1) { + if (lxcNeedNetworkNamespace(def)) { + VIR_DEBUG("Enable network namespaces"); + cflags |= CLONE_NEWNET; + } + } else { + if (lxcNeedNetworkNamespace(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Config askes for inherit net namespace " + "as well as private network interfaces")); + return -1; + } + VIR_DEBUG("Inheriting a net namespace"); + } + + if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC] == -1) { + cflags |= CLONE_NEWIPC; + } else { + VIR_DEBUG("Inheriting an IPC namespace"); + } - if (lxcNeedNetworkNamespace(def)) { - VIR_DEBUG("Enable network namespaces"); - cflags |= CLONE_NEWNET; + if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS] == -1) { + cflags |= CLONE_NEWUTS; + } else { + VIR_DEBUG("Inheriting a UTS namespace"); } VIR_DEBUG("Cloning container init process"); diff --git a/src/lxc/lxc_container.h b/src/lxc/lxc_container.h index 67292ab..33eaab4 100644 --- a/src/lxc/lxc_container.h +++ b/src/lxc/lxc_container.h @@ -25,6 +25,7 @@ # define LXC_CONTAINER_H # include "lxc_conf.h" +# include "lxc_domain.h" # include "security/security_manager.h" enum { @@ -60,6 +61,7 @@ int lxcContainerStart(virDomainDefPtr def, int *passFDs, int control, int handshakefd, + int *nsInheritFDs, size_t nttyPaths, char **ttyPaths); diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index 48a3597..a94e819 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -119,6 +119,8 @@ struct _virLXCController { size_t npassFDs; int *passFDs; + int *nsFDs; + size_t nconsoles; virLXCControllerConsolePtr consoles; char *devptmx; @@ -287,6 +289,7 @@ static void virLXCControllerFree(virLXCControllerPtr ctrl) VIR_FREE(ctrl->nbdpids); + VIR_FREE(ctrl->nsFDs); virCgroupFree(&ctrl->cgroup); /* This must always be the last thing to be closed */ @@ -2391,6 +2394,7 @@ virLXCControllerRun(virLXCControllerPtr ctrl) ctrl->passFDs, control[1], containerhandshake[1], + ctrl->nsFDs, ctrl->nconsoles, containerTTYPaths)) < 0) goto cleanup; @@ -2400,6 +2404,10 @@ virLXCControllerRun(virLXCControllerPtr ctrl) for (i = 0; i < ctrl->npassFDs; i++) VIR_FORCE_CLOSE(ctrl->passFDs[i]); + if (ctrl->nsFDs) + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) + VIR_FORCE_CLOSE(ctrl->nsFDs[i]); + if (virLXCControllerSetupCgroupLimits(ctrl) < 0) goto cleanup; @@ -2468,6 +2476,7 @@ int main(int argc, char *argv[]) const char *name = NULL; size_t nveths = 0; char **veths = NULL; + int ns_fd[VIR_LXC_DOMAIN_NAMESPACE_LAST]; int handshakeFd = -1; bool bg = false; const struct option options[] = { @@ -2478,6 +2487,9 @@ int main(int argc, char *argv[]) { "passfd", 1, NULL, 'p' }, { "handshakefd", 1, NULL, 's' }, { "security", 1, NULL, 'S' }, + { "share-net", 1, NULL, 'N' }, + { "share-ipc", 1, NULL, 'I' }, + { "share-uts", 1, NULL, 'U' }, { "help", 0, NULL, 'h' }, { 0, 0, 0, 0 }, }; @@ -2489,6 +2501,9 @@ int main(int argc, char *argv[]) size_t i; const char *securityDriver = "none"; + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) + ns_fd[i] = -1; + if (setlocale(LC_ALL, "") == NULL || bindtextdomain(PACKAGE, LOCALEDIR) == NULL || textdomain(PACKAGE) == NULL || @@ -2504,7 +2519,7 @@ int main(int argc, char *argv[]) while (1) { int c; - c = getopt_long(argc, argv, "dn:v:p:m:c:s:h:S:", + c = getopt_long(argc, argv, "dn:v:p:m:c:s:h:S:N:I:U:", options, NULL); if (c == -1) @@ -2552,6 +2567,30 @@ int main(int argc, char *argv[]) } break; + case 'N': + if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHARENET]) < 0) { + fprintf(stderr, "malformed --share-net argument '%s'", + optarg); + goto cleanup; + } + break; + + case 'I': + if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC]) < 0) { + fprintf(stderr, "malformed --share-ipc argument '%s'", + optarg); + goto cleanup; + } + break; + + case 'U': + if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS]) < 0) { + fprintf(stderr, "malformed --share-uts argument '%s'", + optarg); + goto cleanup; + } + break; + case 'S': securityDriver = optarg; break; @@ -2569,6 +2608,9 @@ int main(int argc, char *argv[]) fprintf(stderr, " -v VETH, --veth VETH\n"); fprintf(stderr, " -s FD, --handshakefd FD\n"); fprintf(stderr, " -S NAME, --security NAME\n"); + fprintf(stderr, " -N FD, --share-net FD\n"); + fprintf(stderr, " -I FD, --share-ipc FD\n"); + fprintf(stderr, " -U FD, --share-uts FD\n"); fprintf(stderr, " -h, --help\n"); fprintf(stderr, "\n"); goto cleanup; @@ -2621,6 +2663,19 @@ int main(int argc, char *argv[]) ctrl->passFDs = passFDs; ctrl->npassFDs = npassFDs; + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) { + if (ns_fd[i] != -1) { + if (!ctrl->nsFDs) {/*allocate only once */ + size_t j = 0; + if (VIR_ALLOC_N(ctrl->nsFDs, VIR_LXC_DOMAIN_NAMESPACE_LAST) < 0) + goto cleanup; + for (j = 0; j < VIR_LXC_DOMAIN_NAMESPACE_LAST; j++) + ctrl->nsFDs[j] = -1; + } + ctrl->nsFDs[i] = ns_fd[i]; + } + } + for (i = 0; i < nttyFDs; i++) { if (virLXCControllerAddConsole(ctrl, ttyFDs[i]) < 0) goto cleanup; diff --git a/src/lxc/lxc_domain.c b/src/lxc/lxc_domain.c index 2f377d8..e3da9f0 100644 --- a/src/lxc/lxc_domain.c +++ b/src/lxc/lxc_domain.c @@ -26,8 +26,13 @@ #include "viralloc.h" #include "virlog.h" #include "virerror.h" +#include <libxml/xpathInternals.h> +#include "virstring.h" +#include "virutil.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_LXC +#define LXC_NAMESPACE_HREF "http://libvirt.org/schemas/domain/lxc/1.0" VIR_LOG_INIT("lxc.lxc_domain"); @@ -41,6 +46,150 @@ static void *virLXCDomainObjPrivateAlloc(void) return priv; } +VIR_ENUM_IMPL(virLXCDomainNamespace, + VIR_LXC_DOMAIN_NAMESPACE_LAST, + "sharenet", + "shareipc", + "shareuts") + +VIR_ENUM_IMPL(virLXCDomainNamespaceSource, + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_LAST, + "none", + "name", + "pid", + "netns") + +static void +lxcDomainDefNamespaceFree(void *nsdata) +{ + size_t i; + lxcDomainDefPtr lxcDef = nsdata; + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) + VIR_FREE(lxcDef->ns_val[i]); + VIR_FREE(nsdata); +} + +static int +lxcDomainDefNamespaceParse(xmlDocPtr xml ATTRIBUTE_UNUSED, + xmlNodePtr root ATTRIBUTE_UNUSED, + xmlXPathContextPtr ctxt, + void **data) +{ + lxcDomainDefPtr lxcDef = NULL; + xmlNodePtr *nodes = NULL; + bool uses_lxc_ns = false; + xmlNodePtr node; + int feature; + int n; + char *tmp = NULL; + size_t i; + + if (xmlXPathRegisterNs(ctxt, BAD_CAST "lxc", BAD_CAST LXC_NAMESPACE_HREF) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to register xml namespace '%s'"), + LXC_NAMESPACE_HREF); + return -1; + } + + if (VIR_ALLOC(lxcDef) < 0) + return -1; + + node = ctxt->node; + if ((n = virXPathNodeSet("./lxc:namespace/*", ctxt, &nodes)) < 0) + goto error; + uses_lxc_ns |= n > 0; + + for (i = 0; i < n; i++) { + if ((feature = virLXCDomainNamespaceTypeFromString( + (const char *) nodes[i]->name)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported Namespace feature: %s"), + nodes[i]->name); + goto error; + } + + ctxt->node = nodes[i]; + + if (!(tmp = virXMLPropString(nodes[i], "type"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No lxc environment type specified")); + goto error; + } + if ((lxcDef->ns_source[feature] = + virLXCDomainNamespaceSourceTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown LXC namespace source '%s'"), + tmp); + VIR_FREE(tmp); + goto error; + } + VIR_FREE(tmp); + + if (!(lxcDef->ns_val[feature] = + virXMLPropString(nodes[i], "value"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No lxc environment type specified")); + goto error; + } + } + VIR_FREE(nodes); + ctxt->node = node; + if (uses_lxc_ns) + *data = lxcDef; + else + VIR_FREE(lxcDef); + return 0; + error: + VIR_FREE(nodes); + lxcDomainDefNamespaceFree(lxcDef); + return -1; +} + + +static int +lxcDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + lxcDomainDefPtr lxcDef = nsdata; + size_t i; + + if (!lxcDef) + return 0; + + virBufferAddLit(buf, "<lxc:namespace>\n"); + virBufferAdjustIndent(buf, 2); + + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) { + if (lxcDef->ns_source[i] == VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE) + continue; + + virBufferAsprintf(buf, "<lxc:%s type='%s' value='%s'/>\n", + virLXCDomainNamespaceTypeToString(i), + virLXCDomainNamespaceSourceTypeToString( + lxcDef->ns_source[i]), + lxcDef->ns_val[i]); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</lxc:namespace>\n"); + return 0; +} + +static const char * +lxcDomainDefNamespaceHref(void) +{ + return "xmlns:lxc='" LXC_NAMESPACE_HREF "'"; +} + + +virDomainXMLNamespace virLXCDriverDomainXMLNamespace = { + .parse = lxcDomainDefNamespaceParse, + .free = lxcDomainDefNamespaceFree, + .format = lxcDomainDefNamespaceFormatXML, + .href = lxcDomainDefNamespaceHref, +}; + + static void virLXCDomainObjPrivateFree(void *data) { virLXCDomainObjPrivatePtr priv = data; diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h index 751aece..2119c78 100644 --- a/src/lxc/lxc_domain.h +++ b/src/lxc/lxc_domain.h @@ -27,6 +27,31 @@ # include "lxc_conf.h" # include "lxc_monitor.h" +typedef enum { + VIR_LXC_DOMAIN_NAMESPACE_SHARENET = 0, + VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC, + VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS, + VIR_LXC_DOMAIN_NAMESPACE_LAST, +} virLXCDomainNamespace; + +typedef enum { + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE, + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NAME, + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_PID, + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NETNS, + + VIR_LXC_DOMAIN_NAMESPACE_SOURCE_LAST, +} virLXCDomainNamespaceSource; + +VIR_ENUM_DECL(virLXCDomainNamespace) +VIR_ENUM_DECL(virLXCDomainNamespaceSource) + +typedef struct _lxcDomainDef lxcDomainDef; +typedef lxcDomainDef *lxcDomainDefPtr; +struct _lxcDomainDef { + int ns_source[VIR_LXC_DOMAIN_NAMESPACE_LAST]; /* virLXCDomainNamespaceSource */ + char *ns_val[VIR_LXC_DOMAIN_NAMESPACE_LAST]; +}; typedef struct _virLXCDomainObjPrivate virLXCDomainObjPrivate; typedef virLXCDomainObjPrivate *virLXCDomainObjPrivatePtr; @@ -41,6 +66,7 @@ struct _virLXCDomainObjPrivate { virCgroupPtr cgroup; }; +extern virDomainXMLNamespace virLXCDriverDomainXMLNamespace; extern virDomainXMLPrivateDataCallbacks virLXCDriverPrivateDataCallbacks; extern virDomainDefParserConfig virLXCDriverDomainDefParserConfig; diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index e99b039..ade0ed7 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -359,6 +359,143 @@ char *virLXCProcessSetupInterfaceDirect(virConnectPtr conn, return ret; } +static const char *nsInfoLocal[VIR_LXC_DOMAIN_NAMESPACE_LAST] = { + [VIR_LXC_DOMAIN_NAMESPACE_SHARENET] = "net", + [VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC] = "ipc", + [VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS] = "uts", +}; + +static int virLXCProcessSetupNamespaceName(virConnectPtr conn, int ns_type, const char *name) +{ + virLXCDriverPtr driver = conn->privateData; + int fd = -1; + virDomainObjPtr vm; + virLXCDomainObjPrivatePtr priv; + char *path; + + vm = virDomainObjListFindByName(driver->domains, name); + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("No domain with matching name '%s'"), name); + return -1; + } + + priv = vm->privateData; + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Init pid is not yet available")); + goto cleanup; + } + + if (virAsprintf(&path, "/proc/%lld/ns/%s", + (long long int)priv->initpid, + nsInfoLocal[ns_type]) < 0) + goto cleanup; + + if ((fd = open(path, O_RDONLY)) < 0) { + virReportSystemError(errno, + _("failed to open ns %s"), + virLXCDomainNamespaceTypeToString(ns_type)); + goto cleanup; + } + + cleanup: + VIR_FREE(path); + virObjectUnlock(vm); + virObjectUnref(vm); + return fd; +} + + +static int virLXCProcessSetupNamespacePID(int ns_type, const char *name) +{ + int fd; + char *path; + + if (virAsprintf(&path, "/proc/%s/ns/%s", + name, + nsInfoLocal[ns_type]) < 0) + return -1; + fd = open(path, O_RDONLY); + VIR_FREE(path); + if (fd < 0) { + virReportSystemError(errno, + _("failed to open ns %s"), + virLXCDomainNamespaceTypeToString(ns_type)); + return -1; + } + return fd; +} + + +static int virLXCProcessSetupNamespaceNet(int ns_type, const char *name) +{ + char *path; + int fd; + if (ns_type != VIR_LXC_DOMAIN_NAMESPACE_SHARENET) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'netns' namespace source can only be " + "used with sharenet")); + return -1; + } + + if (virAsprintf(&path, "/var/run/netns/%s", name) < 0) + return -1; + fd = open(path, O_RDONLY); + VIR_FREE(path); + if (fd < 0) { + virReportSystemError(errno, + _("failed to open netns %s"), name); + return -1; + } + return fd; +} + + +/** + * virLXCProcessSetupNamespaces: + * @conn: pointer to connection + * @def: pointer to virtual machines namespaceData + * @nsFDs: out parameter to store the namespace FD + * + * Opens the specified namespace that needs to be shared and + * will moved into the container namespace later after clone has been called. + * + * Returns 0 on success or -1 in case of error + */ +static int virLXCProcessSetupNamespaces(virConnectPtr conn, + lxcDomainDefPtr lxcDef, + int *nsFDs) +{ + size_t i; + + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) + nsFDs[i] = -1; + /*If there are no namespace to be opened just return success*/ + if (lxcDef == NULL) + return 0; + + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) { + switch (lxcDef->ns_source[i]) { + case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE: + continue; + case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NAME: + if ((nsFDs[i] = virLXCProcessSetupNamespaceName(conn, i, lxcDef->ns_val[i])) < 0) + return -1; + break; + case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_PID: + if ((nsFDs[i] = virLXCProcessSetupNamespacePID(i, lxcDef->ns_val[i])) < 0) + return -1; + break; + case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NETNS: + if ((nsFDs[i] = virLXCProcessSetupNamespaceNet(i, lxcDef->ns_val[i])) < 0) + return -1; + break; + } + } + + return 0; +} /** * virLXCProcessSetupInterfaces: @@ -764,6 +901,7 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver, char **veths, int *ttyFDs, size_t nttyFDs, + int *nsInheritFDs, int *files, size_t nfiles, int handshakefd, @@ -825,6 +963,19 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver, virCommandPassFD(cmd, files[i], 0); } + for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) { + if (nsInheritFDs[i] > 0) { + char *tmp = NULL; + if (virAsprintf(&tmp, "--share-%s", + nsInfoLocal[i]) < 0) + goto cleanup; + virCommandAddArg(cmd, tmp); + virCommandAddArgFormat(cmd, "%d", nsInheritFDs[i]); + virCommandPassFD(cmd, nsInheritFDs[i], 0); + VIR_FREE(tmp); + } + } + virCommandAddArgPair(cmd, "--security", virSecurityManagerGetModel(driver->securityManager)); @@ -1032,6 +1183,7 @@ int virLXCProcessStart(virConnectPtr conn, off_t pos = -1; char ebuf[1024]; char *timestamp; + int nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_LAST]; virCommandPtr cmd = NULL; virLXCDomainObjPrivatePtr priv = vm->privateData; virCapsPtr caps = NULL; @@ -1204,6 +1356,10 @@ int virLXCProcessStart(virConnectPtr conn, if (virLXCProcessSetupInterfaces(conn, vm->def, &nveths, &veths) < 0) goto cleanup; + VIR_DEBUG("Setting up namespaces if any"); + if (virLXCProcessSetupNamespaces(conn, vm->def->namespaceData, nsInheritFDs) < 0) + goto cleanup; + VIR_DEBUG("Preparing to launch"); if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR|S_IWUSR)) < 0) { @@ -1223,6 +1379,7 @@ int virLXCProcessStart(virConnectPtr conn, vm, nveths, veths, ttyFDs, nttyFDs, + nsInheritFDs, files, nfiles, handshakefds[1], &logfd, diff --git a/tests/lxcxml2xmldata/lxc-sharenet.xml b/tests/lxcxml2xmldata/lxc-sharenet.xml new file mode 100644 index 0000000..a2b8d1b --- /dev/null +++ b/tests/lxcxml2xmldata/lxc-sharenet.xml @@ -0,0 +1,33 @@ +<domain type='lxc' xmlns:lxc='http://libvirt.org/schemas/domain/lxc/1.0'> + <name>jessie</name> + <uuid>e21987a5-e98e-9c99-0e35-803e4d9ad1fe</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>1048576</currentMemory> + <vcpu placement='static'>1</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64'>exe</type> + <init>/sbin/init</init> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/libexec/libvirt_lxc</emulator> + <filesystem type='mount' accessmode='passthrough'> + <source dir='/mach/jessie'/> + <target dir='/'/> + </filesystem> + <console type='pty'> + <target type='lxc' port='0'/> + </console> + </devices> + <lxc:namespace> + <lxc:sharenet type='netns' value='red'/> + <lxc:shareipc type='pid' value='12345'/> + <lxc:shareuts type='name' value='container1'/> + </lxc:namespace> +</domain> diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c index 3e00347..8d824b9 100644 --- a/tests/lxcxml2xmltest.c +++ b/tests/lxcxml2xmltest.c @@ -133,6 +133,7 @@ mymain(void) DO_TEST("filesystem-root"); DO_TEST("idmap"); DO_TEST("capabilities"); + DO_TEST("sharenet"); virObjectUnref(caps); virObjectUnref(xmlopt); -- 1.9.1

On Thu, Aug 20, 2015 at 07:16:17PM +0530, ik.nitk wrote:
This patch adds feature for lxc containers to inherit namespaces. This is very similar to what lxc-tools or docker provides. Look for "man lxc-start" and you will find that you can pass command args as [ --share-[net|ipc|uts] name|pid ]. Or check out docker networking option in which you can give --net=container:NAME_or_ID as an option for sharing +namespace.
From this patch you can add extra libvirt option to share namespace in following way.
<lxc:namespace> <lxc:sharenet type='netns' value='red'/> <lxc:shareipc type='pid' value='12345'/> <lxc:shareuts type='name' value='container1'/> </lxc:namespace>
The netns option is specific to sharenet. It can be used to inherit from existing network namespace.
ACK and pushed to GIT master. Thanks for taking the time to work on this feature ! Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
ik.nitk