mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-09 14:40:07 +00:00
lxc-ls: Implement support for nested containers
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>
This commit is contained in:
parent
36368228d2
commit
0e21ea4b15
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user