[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