[lxc-devel] [PATCH] lxc-ls: Implement support for nested containers
Serge Hallyn
serge.hallyn at ubuntu.com
Fri Mar 1 03:43:10 UTC 2013
Quoting Stéphane Graber (stgraber at ubuntu.com):
> Add initial support for showing and querying nested containers.
>
> This is done through a new --nesting argument to lxc-ls and uses
> lxc-attach to go look for sub-containers.
>
> Known limitations include the dependency on setns support for the PID
> and NETWORK namespaces and the assumption that LXCPATH for the sub-containers
> matches that of the host.
>
> Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
looks nice, thanks :)
Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>
> ---
> doc/lxc-ls.sgml.in | 12 +++++++
> src/lxc/lxc-ls | 75 ++++++++++++++++++++++++++++++++++-----
> src/python-lxc/lxc/__init__.py.in | 15 ++------
> 3 files changed, 81 insertions(+), 21 deletions(-)
>
> diff --git a/doc/lxc-ls.sgml.in b/doc/lxc-ls.sgml.in
> index 83618e5..877404d 100644
> --- a/doc/lxc-ls.sgml.in
> +++ b/doc/lxc-ls.sgml.in
> @@ -56,6 +56,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> <arg choice="opt">--stopped</arg>
> <arg choice="opt">--fancy</arg>
> <arg choice="opt">--fancy-format</arg>
> + <arg choice="opt">--nesting</arg>
> <arg choice="opt">filter</arg>
> </cmdsynopsis>
> </refsynopsisdiv>
> @@ -152,6 +153,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>
> <varlistentry>
> <term>
> + <option><optional>--nesting</optional></option>
> + </term>
> + <listitem>
> + <para>
> + Show nested containers.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry>
> + <term>
> <option><optional>filter</optional></option>
> </term>
> <listitem>
> diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls
> index 92a4e53..6468890 100644
> --- a/src/lxc/lxc-ls
> +++ b/src/lxc/lxc-ls
> @@ -31,9 +31,11 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
>
> import argparse
> import gettext
> +import json
> import lxc
> import os
> import re
> +import subprocess
> import sys
>
> _ = gettext.gettext
> @@ -85,6 +87,23 @@ def getTerminalSize():
>
> return int(cr[1]), int(cr[0])
>
> +
> +def getSubContainers(container, lxcpath):
> + attach = ['lxc-attach', '-R', '-s', 'NETWORK|PID', '-n', container,
> + '--', sys.argv[0], "--nesting"]
> +
> + with open(os.devnull, "w") as fd:
> + newenv = dict(os.environ)
> + newenv['NESTED'] = "/proc/1/root/%s" % lxcpath
> + sp = subprocess.Popen(attach, stderr=fd, stdout=subprocess.PIPE,
> + env=newenv, universal_newlines=True)
> + sp.wait()
> + out = sp.stdout.read()
> + if out:
> + return json.loads(out)
> + return None
> +
> +
> # Begin parsing the command line
> parser = argparse.ArgumentParser(description=_("LXC: List containers"),
> formatter_class=argparse.RawTextHelpFormatter)
> @@ -93,7 +112,8 @@ parser.add_argument("-1", dest="one", action="store_true",
> help=_("list one container per line (default when piped)"))
>
> parser.add_argument("-P", "--lxcpath", dest="lxcpath", metavar="PATH",
> - help=_("Use specified container path"), default=None)
> + help=_("Use specified container path"),
> + default=lxc.default_config_path)
>
> parser.add_argument("--active", action="store_true",
> help=_("list only active containers "
> @@ -114,6 +134,9 @@ parser.add_argument("--fancy", action="store_true",
> parser.add_argument("--fancy-format", type=str, default="name,state,ipv4,ipv6",
> help=_("comma separated list of fields to show"))
>
> +parser.add_argument("--nesting", dest="nesting", action="store_true",
> + help=_("show nested containers"))
> +
> parser.add_argument("filter", metavar='FILTER', type=str, nargs="?",
> help=_("regexp to be applied on the container list"))
>
> @@ -129,6 +152,9 @@ if args.active:
> if not sys.stdout.isatty():
> args.one = True
>
> +# Set the lookup path for the containers
> +lxcpath = os.environ.get('NESTED', args.lxcpath)
> +
> # Turn args.fancy_format into a list
> args.fancy_format = args.fancy_format.strip().split(",")
>
> @@ -141,7 +167,7 @@ if not os.geteuid() == 0 and (args.fancy or args.state):
>
> # List of containers, stored as dictionaries
> containers = []
> -for container_name in lxc.list_containers(config_path=args.lxcpath):
> +for container_name in lxc.list_containers(config_path=lxcpath):
> entry = {}
> entry['name'] = container_name
>
> @@ -150,7 +176,7 @@ for container_name in lxc.list_containers(config_path=args.lxcpath):
> continue
>
> # Return before grabbing the object (non-root)
> - if not args.state and not args.fancy:
> + if not args.state and not args.fancy and not args.nesting:
> containers.append(entry)
> continue
>
> @@ -161,28 +187,47 @@ for container_name in lxc.list_containers(config_path=args.lxcpath):
> continue
>
> # Nothing more is needed if we're not printing some fancy output
> - if not args.fancy:
> + if not args.fancy and not args.nesting:
> containers.append(entry)
> continue
>
> # Some extra field we may want
> - if 'state' in args.fancy_format:
> + if 'state' in args.fancy_format or args.nesting:
> entry['state'] = container.state
> - if 'pid' in args.fancy_format:
> +
> + if 'pid' in args.fancy_format or args.nesting:
> entry['pid'] = "-"
> if container.init_pid != -1:
> entry['pid'] = str(container.init_pid)
>
> # Get the IPs
> for protocol in ('ipv4', 'ipv6'):
> - if protocol in args.fancy_format:
> + if protocol in args.fancy_format or args.nesting:
> entry[protocol] = "-"
> ips = container.get_ips(protocol=protocol, timeout=1)
> if ips:
> entry[protocol] = ", ".join(ips)
>
> + # Append the container
> containers.append(entry)
>
> + # Nested containers
> + if args.nesting:
> + sub = getSubContainers(container_name, args.lxcpath)
> + if sub:
> + for entry in sub:
> + if 'nesting_parent' not in entry:
> + entry['nesting_parent'] = []
> + entry['nesting_parent'].insert(0, container_name)
> + entry['nesting_real_name'] = entry.get('nesting_real_name',
> + entry['name'])
> + entry['name'] = "%s -> %s" % (container_name, entry['name'])
> + containers += sub
> +
> +# Deal with json output:
> +if 'NESTED' in os.environ:
> + print(json.dumps(containers))
> + sys.exit(0)
>
> # Print the list
> ## Standard list with one entry per line
> @@ -226,7 +271,12 @@ if args.fancy:
>
> for container in containers:
> for field in args.fancy_format:
> - if len(container[field]) > field_maxlength[field]:
> + if field == 'name' and 'nesting_real_name' in container:
> + fieldlen = len(" " * ((len(container['nesting_parent']) - 1)
> + * 4) + " \_ " + container['nesting_real_name'])
> + if fieldlen > field_maxlength[field]:
> + field_maxlength[field] = fieldlen
> + elif len(container[field]) > field_maxlength[field]:
> field_maxlength[field] = len(container[field])
>
> # Generate the line format string based on the maximum length and
> @@ -250,5 +300,12 @@ if args.fancy:
> # Print the entries
> for container in sorted(containers,
> key=lambda container: container['name']):
> - fields = [container[field] for field in args.fancy_format]
> + fields = []
> + for field in args.fancy_format:
> + if field == 'name' and 'nesting_real_name' in container:
> + prefix = " " * ((len(container['nesting_parent']) - 1) * 4)
> + fields.append(prefix + " \_ " + container['nesting_real_name'])
> + else:
> + fields.append(container[field])
> +
> print(line_format.format(fields=fields))
> diff --git a/src/python-lxc/lxc/__init__.py.in b/src/python-lxc/lxc/__init__.py.in
> index e262c23..f1848f2 100644
> --- a/src/python-lxc/lxc/__init__.py.in
> +++ b/src/python-lxc/lxc/__init__.py.in
> @@ -337,18 +337,9 @@ class Container(_lxc.Container):
> Returns the list of IP addresses for the container.
> """
>
> - if not self.defined or not self.running:
> + if not self.running:
> return False
>
> - try:
> - os.makedirs("/run/netns")
> - except:
> - pass
> -
> - path = tempfile.mktemp(dir="/run/netns")
> -
> - os.symlink("/proc/%s/ns/net" % self.init_pid, path)
> -
> ips = []
>
> count = 0
> @@ -356,7 +347,8 @@ class Container(_lxc.Container):
> if count != 0:
> time.sleep(1)
>
> - base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
> + base_cmd = ["lxc-attach", "-s", "NETWORK", "-n", self.name, "--",
> + "ip"]
>
> # Get IPv6
> if protocol in ("ipv6", None):
> @@ -397,7 +389,6 @@ class Container(_lxc.Container):
>
> count += 1
>
> - os.remove(path)
> return ips
>
> def get_keys(self, key=None):
> --
> 1.8.1.2
>
>
> ------------------------------------------------------------------------------
> Everyone hates slow websites. So do we.
> Make your web apps faster with AppDynamics
> Download AppDynamics Lite for free today:
> http://p.sf.net/sfu/appdyn_d2d_feb
> _______________________________________________
> 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