On Tue, Mar 04, 2014 at 04:41:14PM -0500, Stéphane Graber wrote: > This reworks the way lxc-ls works in nesting mode. In the past it'd use > attach_wait's subprocess function to call itself in the container's > namespace, carefully only attaching to the namespaces it needed. > > This works great for system containers but not so much as soon as you > also need to attach to userns. Instead this fix moves all of the > container listing code into a get_containers function (hence the massive > diff, sorry), this function is then called recursively. > > For running containers, the function is called through attach_wait > inside the container's namespace, for stopped container, the function is > simply called recursively with a base path (container's rootfs) in an > attempt to find containers that way. > Communication between the parent lxc-ls and the child lxc-ls is done > through a temporary fd and serialized state using json (similar to what > was done using stdout in the previous implementation). > > As get_global_config_item unfortunately caches the values, there's no > easy way to figure out what the lxcpath should be for a root container > when running as non-root, so just use @LXCPATH@ for now and have > python do the parsing itself. > > As a result, the following things now work as expected: > - listing nested unprivileged containers (root containers inside unpriv) > - listing nested containers when they're not running > - filtering containers in nesting mode (only the first level is filtered) > - copy with invalid config (used to traceback) >
I tested the above in the following scenarios: nested as root without running containers on 3.13 => works nested as root with running containers on 3.13 => works nested as root without running containers on 3.2 => fails with clear error nested as root with running containers on 3.2 => fails with clear error nested as root without running containers using custom lxcpath => works nested as root with running containers using custom lxcpath => works nested as user without running containers => works nested as user with running containers => works nested as user without running containers using custom lxcpath => works nested as user with running containers using custom lxcpath => works Again, sorry for the massive diff, between shifting most of the code in a function and renaming the file to lxc-ls.in, that was enough to confuse git enough to have it think it's a whole new piece of code... > Signed-off-by: Stéphane Graber <[email protected]> > --- > configure.ac | 1 + > src/lxc/lxc-ls | 411 ------------------------------------------------ > src/lxc/lxc-ls.in | 463 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 464 insertions(+), 411 deletions(-) > delete mode 100755 src/lxc/lxc-ls > create mode 100755 src/lxc/lxc-ls.in > > diff --git a/configure.ac b/configure.ac > index 47e3869..e352840 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -696,6 +696,7 @@ AC_CONFIG_FILES([ > src/Makefile > src/lxc/Makefile > src/lxc/lxc-checkconfig > + src/lxc/lxc-ls > src/lxc/lxc-start-ephemeral > src/lxc/legacy/lxc-ls > src/lxc/lxc.functions > diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls > deleted file mode 100755 > index 065b3e8..0000000 > --- a/src/lxc/lxc-ls > +++ /dev/null > @@ -1,411 +0,0 @@ > -#!/usr/bin/python3 > -# > -# lxc-ls: List containers > -# > -# This python implementation is based on the work done in the original > -# shell implementation done by Serge Hallyn in Ubuntu (and other > contributors) > -# > -# (C) Copyright Canonical Ltd. 2012 > -# > -# Authors: > -# Stéphane Graber <[email protected]> > -# > -# This library is free software; you can redistribute it and/or > -# modify it under the terms of the GNU Lesser General Public > -# License as published by the Free Software Foundation; either > -# version 2.1 of the License, or (at your option) any later version. > -# > -# This library is distributed in the hope that it will be useful, > -# but WITHOUT ANY WARRANTY; without even the implied warranty of > -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > -# Lesser General Public License for more details. > -# > -# You should have received a copy of the GNU Lesser General Public > -# License along with this library; if not, write to the Free Software > -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > -# > - > -import argparse > -import gettext > -import json > -import lxc > -import os > -import re > -import tempfile > -import sys > - > -_ = gettext.gettext > -gettext.textdomain("lxc-ls") > - > - > -# Functions used later on > -def batch(iterable, cols=1): > - import math > - > - length = len(iterable) > - lines = math.ceil(length / cols) > - > - for line in range(lines): > - fields = [] > - for col in range(cols): > - index = line + (col * lines) > - if index < length: > - fields.append(iterable[index]) > - yield fields > - > - > -def getTerminalSize(): > - import os > - env = os.environ > - > - def ioctl_GWINSZ(fd): > - try: > - import fcntl > - import termios > - import struct > - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, > - '1234')) > - return cr > - except: > - return > - > - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) > - if not cr: > - try: > - fd = os.open(os.ctermid(), os.O_RDONLY) > - cr = ioctl_GWINSZ(fd) > - os.close(fd) > - except: > - pass > - > - if not cr: > - cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) > - > - return int(cr[1]), int(cr[0]) > - > - > -def getSubContainers(container): > - with open(os.devnull, "w") as fd: > - fdnum, path = tempfile.mkstemp() > - os.remove(path) > - > - fd = os.fdopen(fdnum) > - > - container.attach_wait( > - lxc.attach_run_command, [sys.argv[0], "--nesting"], > - attach_flags=(lxc.LXC_ATTACH_REMOUNT_PROC_SYS), > - namespaces=(lxc.CLONE_NEWNET + lxc.CLONE_NEWPID), > - extra_env_vars=["NESTED=/proc/1/root/%s" % > - lxc.default_config_path], > - stdout=fd) > - > - fd.seek(0) > - out = fd.read() > - fd.close() > - if out: > - return json.loads(out) > - return None > - > -# Constants > -FIELDS = ("name", "state", "ipv4", "ipv6", "autostart", "pid", > - "memory", "ram", "swap") > - > -# Begin parsing the command line > -parser = argparse.ArgumentParser(description=_("LXC: List containers"), > - > formatter_class=argparse.RawTextHelpFormatter) > - > -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=lxc.default_config_path) > - > -parser.add_argument("--active", action="store_true", > - help=_("list only active containers")) > - > -parser.add_argument("--frozen", dest="state", action="append_const", > - const="FROZEN", help=_("list only frozen containers")) > - > -parser.add_argument("--running", dest="state", action="append_const", > - const="RUNNING", help=_("list only running containers")) > - > -parser.add_argument("--stopped", dest="state", action="append_const", > - const="STOPPED", help=_("list only stopped containers")) > - > -parser.add_argument("-f", "--fancy", action="store_true", > - help=_("use fancy output")) > - > -parser.add_argument("-F", "--fancy-format", type=str, > - default="name,state,ipv4,ipv6,autostart", > - 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")) > - > -args = parser.parse_args() > - > -# --active is the same as --running --frozen > -if args.active: > - if not args.state: > - args.state = [] > - args.state += ["RUNNING", "FROZEN", "UNKNOWN"] > - > -# If the output is piped, default to --one > -if not sys.stdout.isatty(): > - args.one = True > - > -# Set the lookup path for the containers > -# This value will contain the full path for a nested containers > -# use args.lxcpath if you need the value relative to the container > -nest_lxcpath = os.environ.get('NESTED', args.lxcpath) > - > -# Turn args.fancy_format into a list > -args.fancy_format = args.fancy_format.strip().split(",") > - > -if set(args.fancy_format) - set(FIELDS): > - parser.error(_("Invalid field(s): %s" % > - ", ".join(list(set(args.fancy_format) - set(FIELDS))))) > - > -# Basic checks > -## Check for setns > -SUPPORT_SETNS = os.path.exists("/proc/self/ns") > -SUPPORT_SETNS_NET = False > -SUPPORT_SETNS_PID = False > -if SUPPORT_SETNS: > - SUPPORT_SETNS_NET = os.path.exists("/proc/self/ns/net") > - SUPPORT_SETNS_PID = os.path.exists("/proc/self/ns/pid") > - > -## Nesting requires setns to pid and net ns > -if args.nesting: > - if not SUPPORT_SETNS: > - parser.error(_("Showing nested containers requires setns support " > - "which your kernel doesn't support.")) > - > - if not SUPPORT_SETNS_NET: > - parser.error(_("Showing nested containers requires setns to the " > - "network namespace which your kernel doesn't > support.")) > - > - if not SUPPORT_SETNS_PID: > - parser.error(_("Showing nested containers requires setns to the " > - "PID namespace which your kernel doesn't support.")) > - > -# List of containers, stored as dictionaries > -containers = [] > -for container_name in lxc.list_containers(config_path=nest_lxcpath): > - entry = {} > - entry['name'] = container_name > - > - # Apply filter > - if args.filter and not re.match(args.filter, container_name): > - continue > - > - # Return before grabbing the object (non-root) > - if not args.state and not args.fancy and not args.nesting: > - containers.append(entry) > - continue > - > - container = lxc.Container(container_name, args.lxcpath) > - > - if 'NESTED' in os.environ: > - container.load_config(os.path.join(nest_lxcpath, container_name, > - "config")) > - > - if container.controllable: > - state = container.state > - else: > - state = 'UNKNOWN' > - > - # Filter by status > - if args.state and state not in args.state: > - continue > - > - # Nothing more is needed if we're not printing some fancy output > - if not args.fancy and not args.nesting: > - containers.append(entry) > - continue > - > - # Some extra field we may want > - if 'state' in args.fancy_format or args.nesting: > - entry['state'] = state > - > - if 'pid' in args.fancy_format or args.nesting: > - entry['pid'] = "-" > - if state == 'UNKNOWN': > - entry['pid'] = state > - elif container.init_pid != -1: > - entry['pid'] = str(container.init_pid) > - > - if 'autostart' in args.fancy_format or args.nesting: > - entry['autostart'] = "NO" > - try: > - if container.get_config_item("lxc.start.auto") == "1": > - entry['autostart'] = "YES" > - > - groups = container.get_config_item("lxc.group") > - if len(groups) > 0: > - entry['autostart'] = "YES (%s)" % ", ".join(groups) > - except KeyError: > - pass > - > - if 'memory' in args.fancy_format or \ > - 'ram' in args.fancy_format or \ > - 'swap' in args.fancy_format: > - > - if container.running: > - try: > - memory_total = int(container.get_cgroup_item( > - "memory.usage_in_bytes")) > - except: > - memory_total = 0 > - > - try: > - memory_swap = int(container.get_cgroup_item( > - "memory.memsw.usage_in_bytes")) > - except: > - memory_swap = 0 > - else: > - memory_total = 0 > - memory_swap = 0 > - > - if 'memory' in args.fancy_format: > - if container.running: > - entry['memory'] = "%sMB" % round(memory_total / 1048576, 2) > - else: > - entry['memory'] = "-" > - > - if 'ram' in args.fancy_format: > - if container.running: > - entry['ram'] = "%sMB" % round( > - (memory_total - memory_swap) / 1048576, 2) > - else: > - entry['ram'] = "-" > - > - if 'swap' in args.fancy_format: > - if container.running: > - entry['swap'] = "%sMB" % round(memory_swap / 1048576, 2) > - else: > - entry['swap'] = "-" > - > - # Get the IPs > - for family, protocol in {'inet': 'ipv4', 'inet6': 'ipv6'}.items(): > - if protocol in args.fancy_format or args.nesting: > - entry[protocol] = "-" > - > - if state == 'UNKNOWN': > - entry[protocol] = state > - continue > - > - if container.running: > - if not SUPPORT_SETNS_NET: > - entry[protocol] = 'UNKNOWN' > - continue > - > - ips = container.get_ips(family=family) > - if ips: > - entry[protocol] = ", ".join(ips) > - > - # Append the container > - containers.append(entry) > - > - # Nested containers > - if args.nesting and container.state == "RUNNING": > - sub = getSubContainers(container) > - 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 > -if not args.fancy and args.one: > - for container in sorted(containers, > - key=lambda container: container['name']): > - print(container['name']) > - sys.exit(0) > - > -## Standard list with multiple entries per line > -if not args.fancy and not args.one: > - # Get the longest name and extra simple list > - field_maxlength = 0 > - container_names = [] > - for container in containers: > - if len(container['name']) > field_maxlength: > - field_maxlength = len(container['name']) > - container_names.append(container['name']) > - > - # Figure out how many we can put per line > - width = getTerminalSize()[0] > - > - entries = int(width / (field_maxlength + 2)) > - if entries == 0: > - entries = 1 > - > - for line in batch(sorted(container_names), entries): > - line_format = "" > - for index in range(len(line)): > - line_format += "{line[%s]:%s}" % (index, field_maxlength + 2) > - > - print(line_format.format(line=line)) > - > -## Fancy listing > -if args.fancy: > - field_maxlength = {} > - > - # Get the maximum length per field > - for field in args.fancy_format: > - field_maxlength[field] = len(field) > - > - for container in containers: > - for field in args.fancy_format: > - 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 > - # a 2 character padding > - line_format = "" > - index = 0 > - for field in args.fancy_format: > - line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + > 2) > - index += 1 > - > - # Get the line length minus the padding of the last field > - line_length = -2 > - for field in field_maxlength: > - line_length += field_maxlength[field] + 2 > - > - # Print header > - print(line_format.format(fields=[header.upper() > - for header in args.fancy_format])) > - print("-" * line_length) > - > - # Print the entries > - for container in sorted(containers, > - key=lambda container: container['name']): > - 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/lxc/lxc-ls.in b/src/lxc/lxc-ls.in > new file mode 100755 > index 0000000..8c33f50 > --- /dev/null > +++ b/src/lxc/lxc-ls.in > @@ -0,0 +1,463 @@ > +#!/usr/bin/python3 > +# > +# lxc-ls: List containers > +# > +# This python implementation is based on the work done in the original > +# shell implementation done by Serge Hallyn in Ubuntu (and other > contributors) > +# > +# (C) Copyright Canonical Ltd. 2012 > +# > +# Authors: > +# Stéphane Graber <[email protected]> > +# > +# This library is free software; you can redistribute it and/or > +# modify it under the terms of the GNU Lesser General Public > +# License as published by the Free Software Foundation; either > +# version 2.1 of the License, or (at your option) any later version. > +# > +# This library is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +# Lesser General Public License for more details. > +# > +# You should have received a copy of the GNU Lesser General Public > +# License along with this library; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > +# > + > +import argparse > +import gettext > +import json > +import lxc > +import os > +import re > +import shutil > +import tempfile > +import sys > + > +_ = gettext.gettext > +gettext.textdomain("lxc-ls") > + > +# Constants > +LXCPATH = "@LXCPATH@" > +RUNTIME_PATH = "@RUNTIME_PATH@" > + > + > +# Functions used later on > +def batch(iterable, cols=1): > + import math > + > + length = len(iterable) > + lines = math.ceil(length / cols) > + > + for line in range(lines): > + fields = [] > + for col in range(cols): > + index = line + (col * lines) > + if index < length: > + fields.append(iterable[index]) > + yield fields > + > + > +def get_terminal_size(): > + import os > + env = os.environ > + > + def ioctl_GWINSZ(fd): > + try: > + import fcntl > + import termios > + import struct > + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, > + '1234')) > + return cr > + except: > + return > + > + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) > + if not cr: > + try: > + fd = os.open(os.ctermid(), os.O_RDONLY) > + cr = ioctl_GWINSZ(fd) > + os.close(fd) > + except: > + pass > + > + if not cr: > + cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) > + > + return int(cr[1]), int(cr[0]) > + > + > +def get_root_path(path): > + lxc_path = LXCPATH > + global_conf = "%s/etc/lxc/lxc.conf" % path > + if os.path.exists(global_conf): > + with open(global_conf, "r") as fd: > + for line in fd: > + if line.startswith("lxc.lxcpath"): > + lxc_path = line.split("=")[-1].strip() > + break > + return lxc_path > + > + > +# Constants > +FIELDS = ("name", "state", "ipv4", "ipv6", "autostart", "pid", > + "memory", "ram", "swap") > + > +# Begin parsing the command line > +parser = argparse.ArgumentParser(description=_("LXC: List containers"), > + > formatter_class=argparse.RawTextHelpFormatter) > + > +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=lxc.default_config_path) > + > +parser.add_argument("--active", action="store_true", > + help=_("list only active containers")) > + > +parser.add_argument("--frozen", dest="state", action="append_const", > + const="FROZEN", help=_("list only frozen containers")) > + > +parser.add_argument("--running", dest="state", action="append_const", > + const="RUNNING", help=_("list only running containers")) > + > +parser.add_argument("--stopped", dest="state", action="append_const", > + const="STOPPED", help=_("list only stopped containers")) > + > +parser.add_argument("-f", "--fancy", action="store_true", > + help=_("use fancy output")) > + > +parser.add_argument("-F", "--fancy-format", type=str, > + default="name,state,ipv4,ipv6,autostart", > + 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")) > + > +args = parser.parse_args() > + > +# --active is the same as --running --frozen > +if args.active: > + if not args.state: > + args.state = [] > + args.state += ["RUNNING", "FROZEN", "UNKNOWN"] > + > +# If the output is piped, default to --one > +if not sys.stdout.isatty(): > + args.one = True > + > +# Turn args.fancy_format into a list > +args.fancy_format = args.fancy_format.strip().split(",") > + > +if set(args.fancy_format) - set(FIELDS): > + parser.error(_("Invalid field(s): %s" % > + ", ".join(list(set(args.fancy_format) - set(FIELDS))))) > + > +# Basic checks > +## Check for setns > +SUPPORT_SETNS = os.path.exists("/proc/self/ns") > +SUPPORT_SETNS_NET = False > +SUPPORT_SETNS_PID = False > +if SUPPORT_SETNS: > + SUPPORT_SETNS_NET = os.path.exists("/proc/self/ns/net") > + SUPPORT_SETNS_PID = os.path.exists("/proc/self/ns/pid") > + > +## Nesting requires setns to pid and net ns > +if args.nesting: > + if not SUPPORT_SETNS: > + parser.error(_("Showing nested containers requires setns support " > + "which your kernel doesn't support.")) > + > + if not SUPPORT_SETNS_NET: > + parser.error(_("Showing nested containers requires setns to the " > + "network namespace which your kernel doesn't > support.")) > + > + if not SUPPORT_SETNS_PID: > + parser.error(_("Showing nested containers requires setns to the " > + "PID namespace which your kernel doesn't support.")) > + > +# Set the actual lxcpath value > +if not args.lxcpath: > + args.lxcpath = lxc.default_config_path > + > + > +# List of containers, stored as dictionaries > +def get_containers(fd=None, base="/", root=False): > + containers = [] > + > + paths = [args.lxcpath] > + > + if not root: > + paths.append(get_root_path(base)) > + > + # Generate a unique list of valid paths > + paths = set([os.path.normpath("%s/%s" % (base, path)) for path in paths]) > + > + for path in paths: > + if not os.access(path, os.R_OK): > + continue > + > + for container_name in lxc.list_containers(config_path=path): > + entry = {} > + entry['name'] = container_name > + > + # Apply filter > + if root and args.filter and \ > + not re.match(args.filter, container_name): > + continue > + > + # Return before grabbing the object (non-root) > + if not args.state and not args.fancy and not args.nesting: > + containers.append(entry) > + continue > + > + try: > + container = lxc.Container(container_name, path) > + except: > + print("Invalid container: %s" % conainer_name, > file=sys.stderr) > + pass > + > + if container.controllable: > + state = container.state > + else: > + state = 'UNKNOWN' > + > + # Filter by status > + if args.state and state not in args.state: > + continue > + > + # Nothing more is needed if we're not printing some fancy output > + if not args.fancy and not args.nesting: > + containers.append(entry) > + continue > + > + # Some extra field we may want > + if 'state' in args.fancy_format or args.nesting: > + entry['state'] = state > + > + if 'pid' in args.fancy_format or args.nesting: > + entry['pid'] = "-" > + if state == 'UNKNOWN': > + entry['pid'] = state > + elif container.init_pid != -1: > + entry['pid'] = str(container.init_pid) > + > + if 'autostart' in args.fancy_format or args.nesting: > + entry['autostart'] = "NO" > + try: > + if container.get_config_item("lxc.start.auto") == "1": > + entry['autostart'] = "YES" > + > + groups = container.get_config_item("lxc.group") > + if len(groups) > 0: > + entry['autostart'] = "YES (%s)" % ", > ".join(groups) > + except KeyError: > + pass > + > + if 'memory' in args.fancy_format or \ > + 'ram' in args.fancy_format or \ > + 'swap' in args.fancy_format: > + > + if container.running: > + try: > + memory_total = int(container.get_cgroup_item( > + "memory.usage_in_bytes")) > + except: > + memory_total = 0 > + > + try: > + memory_swap = int(container.get_cgroup_item( > + "memory.memsw.usage_in_bytes")) > + except: > + memory_swap = 0 > + else: > + memory_total = 0 > + memory_swap = 0 > + > + if 'memory' in args.fancy_format: > + if container.running: > + entry['memory'] = "%sMB" % round(memory_total / 1048576, > 2) > + else: > + entry['memory'] = "-" > + > + if 'ram' in args.fancy_format: > + if container.running: > + entry['ram'] = "%sMB" % round( > + (memory_total - memory_swap) / 1048576, 2) > + else: > + entry['ram'] = "-" > + > + if 'swap' in args.fancy_format: > + if container.running: > + entry['swap'] = "%sMB" % round(memory_swap / 1048576, 2) > + else: > + entry['swap'] = "-" > + > + # Get the IPs > + for family, protocol in {'inet': 'ipv4', 'inet6': > 'ipv6'}.items(): > + if protocol in args.fancy_format or args.nesting: > + entry[protocol] = "-" > + > + if state == 'UNKNOWN': > + entry[protocol] = state > + continue > + > + if container.running: > + if not SUPPORT_SETNS_NET: > + entry[protocol] = 'UNKNOWN' > + continue > + > + ips = container.get_ips(family=family) > + if ips: > + entry[protocol] = ", ".join(ips) > + > + # Nested containers > + if args.nesting: > + if container.running: > + # Recursive call in container namespace > + temp_fd, temp_file = tempfile.mkstemp() > + os.remove(temp_file) > + > + container.attach_wait(get_containers, temp_fd, > + attach_flags=0) > + > + json_file = os.fdopen(temp_fd, "r") > + json_file.seek(0) > + > + try: > + sub_containers = json.loads(json_file.read()) > + except: > + sub_containers = [] > + > + json_file.close() > + else: > + def clear_lock(): > + try: > + lock_path = "%s/lock/lxc/%s/%s" % (RUNTIME_PATH, > + path, > + entry['name']) > + if os.path.exists(lock_path): > + if os.path.isdir(lock_path): > + shutil.rmtree(lock_path) > + else: > + os.remove(lock_path) > + except: > + pass > + > + clear_lock() > + > + # Recursive call using container rootfs > + sub_containers = get_containers( > + base="%s/%s" % ( > + base, container.get_config_item("lxc.rootfs"))) > + > + clear_lock() > + > + for sub in sub_containers: > + if 'nesting_parent' not in sub: > + sub['nesting_parent'] = [] > + sub['nesting_parent'].insert(0, entry['name']) > + sub['nesting_real_name'] = sub.get('nesting_real_name', > + sub['name']) > + sub['name'] = "%s/%s" % (entry['name'], sub['name']) > + containers.append(sub) > + > + # Append the container > + containers.append(entry) > + > + if fd: > + json_file = os.fdopen(fd, "w+") > + json_file.write(json.dumps(containers)) > + return > + > + return containers > + > +containers = get_containers(root=True) > + > +# Print the list > +## Standard list with one entry per line > +if not args.fancy and args.one: > + for container in sorted(containers, > + key=lambda container: container['name']): > + print(container['name']) > + sys.exit(0) > + > +## Standard list with multiple entries per line > +if not args.fancy and not args.one: > + # Get the longest name and extra simple list > + field_maxlength = 0 > + container_names = [] > + for container in containers: > + if len(container['name']) > field_maxlength: > + field_maxlength = len(container['name']) > + container_names.append(container['name']) > + > + # Figure out how many we can put per line > + width = get_terminal_size()[0] > + > + entries = int(width / (field_maxlength + 2)) > + if entries == 0: > + entries = 1 > + > + for line in batch(sorted(container_names), entries): > + line_format = "" > + for index in range(len(line)): > + line_format += "{line[%s]:%s}" % (index, field_maxlength + 2) > + > + print(line_format.format(line=line)) > + > +## Fancy listing > +if args.fancy: > + field_maxlength = {} > + > + # Get the maximum length per field > + for field in args.fancy_format: > + field_maxlength[field] = len(field) > + > + for container in containers: > + for field in args.fancy_format: > + 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 > + # a 2 character padding > + line_format = "" > + index = 0 > + for field in args.fancy_format: > + line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + > 2) > + index += 1 > + > + # Get the line length minus the padding of the last field > + line_length = -2 > + for field in field_maxlength: > + line_length += field_maxlength[field] + 2 > + > + # Print header > + print(line_format.format(fields=[header.upper() > + for header in args.fancy_format])) > + print("-" * line_length) > + > + # Print the entries > + for container in sorted(containers, > + key=lambda container: container['name']): > + 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)) > -- > 1.9.0 > -- Stéphane Graber Ubuntu developer http://www.ubuntu.com
signature.asc
Description: Digital signature
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
