]> git.proxmox.com Git - mirror_lxc.git/commitdiff
lxc-ls: Implement support for nested containers
authorStéphane Graber <stgraber@ubuntu.com>
Thu, 28 Feb 2013 23:04:46 +0000 (18:04 -0500)
committerStéphane Graber <stgraber@ubuntu.com>
Fri, 1 Mar 2013 14:40:24 +0000 (09:40 -0500)
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@ubuntu.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
doc/lxc-ls.sgml.in
src/lxc/lxc-ls
src/python-lxc/lxc/__init__.py.in

index 83618e5e81858d38344a431d5a5c23b837ce01e4..877404d12cf44ff2006312b53f3394a344ec4706 100644 (file)
@@ -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>
@@ -150,6 +151,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>
+          <option><optional>--nesting</optional></option>
+        </term>
+        <listitem>
+          <para>
+            Show nested containers.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>
           <option><optional>filter</optional></option>
index 92a4e536aab909be4dc7d776d6fb7e375ad3a799..4308edea7160711c1d0fea991a72c1ea858b8c64 100644 (file)
@@ -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))
index e262c23c2e41a8367115861375ae57c0d03c34f0..f1848f24ae085edfeb4091f3b601aad45ca8ae37 100644 (file)
@@ -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):