Commit 4e7186c5 by Stéphane Graber

Rewrite lxc-ls in python

This rewrite is mostly compatible with the shell version. --active and -1 still work and behave as they used to. This adds --running, --stopped and --frozen as state filters. A new "fancy" view is also implemented (can be used with --fancy) and will show containers in a column-based interface with the following fields: - name - state - ipv4 - ipv6 - pid of init Signed-off-by: 's avatarStéphane Graber <stgraber@ubuntu.com> Acked-by: 's avatarSerge E. Hallyn <serge.hallyn@ubuntu.com>
parent 4a85ce2a
...@@ -50,7 +50,6 @@ src/lxc/lxc-freeze ...@@ -50,7 +50,6 @@ src/lxc/lxc-freeze
src/lxc/lxc-info src/lxc/lxc-info
src/lxc/lxc-init src/lxc/lxc-init
src/lxc/lxc-kill src/lxc/lxc-kill
src/lxc/lxc-ls
src/lxc/lxc-monitor src/lxc/lxc-monitor
src/lxc/lxc-netstat src/lxc/lxc-netstat
src/lxc/lxc-ps src/lxc/lxc-ps
......
...@@ -256,7 +256,6 @@ AC_CONFIG_FILES([ ...@@ -256,7 +256,6 @@ AC_CONFIG_FILES([
src/Makefile src/Makefile
src/lxc/Makefile src/lxc/Makefile
src/lxc/lxc-ps src/lxc/lxc-ps
src/lxc/lxc-ls
src/lxc/lxc-netstat src/lxc/lxc-netstat
src/lxc/lxc-checkconfig src/lxc/lxc-checkconfig
src/lxc/lxc-setcap src/lxc/lxc-setcap
......
...@@ -49,8 +49,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ...@@ -49,8 +49,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
<refsynopsisdiv> <refsynopsisdiv>
<cmdsynopsis> <cmdsynopsis>
<command>lxc-ls</command> <command>lxc-ls</command>
<arg choice="opt">-1</arg>
<arg choice="opt">--active</arg> <arg choice="opt">--active</arg>
<arg choice="opt">ls option</arg> <arg choice="opt">--frozen</arg>
<arg choice="opt">--running</arg>
<arg choice="opt">--stopped</arg>
<arg choice="opt">--fancy</arg>
<arg choice="opt">--fancy-format</arg>
<arg choice="opt">filter</arg>
</cmdsynopsis> </cmdsynopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -65,55 +71,96 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ...@@ -65,55 +71,96 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
<refsect1> <refsect1>
<title>Options</title> <title>Options</title>
<variablelist> <variablelist>
<varlistentry>
<term>
<option><optional>-1</optional></option>
</term>
<listitem>
<para>
Show one entry per line. (default when /dev/stdout isn't a tty)
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<option><optional>--active</optional></option> <option><optional>--active</optional></option>
</term> </term>
<listitem> <listitem>
<para> <para>
List active containers. List only active containers (same as --frozen --running).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<option><optional>ls options</optional></option> <option><optional>--frozen</optional></option>
</term> </term>
<listitem> <listitem>
<para> <para>
The option passed to <command>lxc-ls</command> are the List only frozen containers.
same as the <command>ls</command> command. </para>
</para> </listitem>
</listitem>
</varlistentry> </varlistentry>
</variablelist> <varlistentry>
<term>
<option><optional>--running</optional></option>
</term>
<listitem>
<para>
List only running containers.
</para>
</listitem>
</varlistentry>
</refsect1> <varlistentry>
<term>
<option><optional>--stopped</optional></option>
</term>
<listitem>
<para>
List only stopped containers.
</para>
</listitem>
</varlistentry>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry> <varlistentry>
<term>lxc-ls -l</term> <term>
<listitem> <option><optional>--fancy</optional></option>
<para> </term>
list all the container and their permissions. <listitem>
</para> <para>
</listitem> Use a fancy, column-based output.
</para>
</listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term>lxc-ls --active -1</term> <term>
<listitem> <option><optional>--fancy-format</optional></option>
<para> </term>
list active containers and display the list in one column. <listitem>
</para> <para>
</listitem> Comma separate list of column to show in the fancy output.
Valid values are: name, state, ipv4, ipv6 and pid
Default is: name,state,ipv4,ipv6
</para>
</listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<option><optional>filter</optional></option>
</term>
<listitem>
<para>
The filter passed to <command>lxc-ls</command> will be
applied to the container name. The format is a regular expression.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -122,20 +169,38 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ...@@ -122,20 +169,38 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
<simpara> <simpara>
<citerefentry> <citerefentry>
<refentrytitle>ls</refentrytitle> <refentrytitle>ls</refentrytitle>
<manvolnum>1</manvolnum> <manvolnum>1</manvolnum>
</citerefentry>, </citerefentry>,
</simpara> </simpara>
</refsect1> <title>Examples</title>
<variablelist>
<varlistentry>
<term>lxc-ls --fancy</term>
<listitem>
<para>
list all the containers, listing one per line along with its
name, state, ipv4 and ipv6 addresses.
</para>
</listitem>
</varlistentry>
&seealso; <varlistentry>
<term>lxc-ls --active -1</term>
<listitem>
<para>
list active containers and display the list in one column.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1> <refsect1>
<title>Author</title> <title>Author</title>
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para> <para>Stéphane Graber <email>stgraber@ubuntu.com</email></para>
</refsect1> </refsect1>
</refentry> </refentry>
<!-- Keep this comment at the end of the file <!-- Keep this comment at the end of the file
......
...@@ -87,7 +87,6 @@ liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) -lrt ...@@ -87,7 +87,6 @@ liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) -lrt
bin_SCRIPTS = \ bin_SCRIPTS = \
lxc-ps \ lxc-ps \
lxc-netstat \ lxc-netstat \
lxc-ls \
lxc-checkconfig \ lxc-checkconfig \
lxc-setcap \ lxc-setcap \
lxc-setuid \ lxc-setuid \
...@@ -99,6 +98,7 @@ bin_SCRIPTS = \ ...@@ -99,6 +98,7 @@ bin_SCRIPTS = \
if ENABLE_PYTHON if ENABLE_PYTHON
bin_SCRIPTS += lxc-device bin_SCRIPTS += lxc-device
bin_SCRIPTS += lxc-ls
bin_SCRIPTS += lxc-start-ephemeral bin_SCRIPTS += lxc-start-ephemeral
endif endif
......
#!/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 <stgraber@ubuntu.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# NOTE: To remove once the API is stabilized
import warnings
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
import argparse
import gettext
import lxc
import re
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])
# 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("--active", action="store_true",
help=_("list only active containers "
"(same as --running --frozen)"))
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("--fancy", action="store_true",
help=_("use fancy output"))
parser.add_argument("--fancy-format", type=str, default="name,state,ipv4,ipv6",
help=_("comma separated list of fields to show"))
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"]
# 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(",")
# List of containers, stored as dictionaries
containers = []
for container in lxc.list_containers(as_object=True):
# Filter by status
if args.state and container.state not in args.state:
continue
# Apply filter
if args.filter and not re.match(args.filter, container.name):
continue
entry = {}
entry['name'] = container.name
# Nothing more is needed if we're not printing some fancy output
if not args.fancy:
containers.append(entry)
continue
# Some extra field we may want
if 'state' in args.fancy_format:
entry['state'] = container.state
if 'pid' in args.fancy_format:
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:
entry[protocol] = "-"
ips = container.get_ips(protocol=protocol, timeout=1)
if ips:
entry[protocol] = ", ".join(ips)
containers.append(entry)
# 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 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 = [container[field] for field in args.fancy_format]
print(line_format.format(fields=fields))
#!/bin/bash
#
# lxc: linux Container library
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
lxc_path=@LXCPATH@
usage()
{
echo "usage: $(basename $0) [--active] [--] [LS_OPTIONS...]" >&2
}
help() {
usage
echo >&2
echo "List containers existing on the system." >&2
echo >&2
echo " --active list active containers" >&2
echo " LS_OPTIONS ls command options (see \`ls --help')" >&2
}
get_parent_cgroup()
{
local hierarchies hierarchy fields subsystems init_cgroup mountpoint
parent_cgroup=""
# Obtain a list of hierarchies that contain one or more subsystems
hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2)
# Iterate through the list until a suitable hierarchy is found
for hierarchy in $hierarchies; do
# Obtain information about the init process in the hierarchy
fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1)
if [ -z "$fields" ]; then continue; fi
fields=${fields#*:}
# Get a comma-separated list of the hierarchy's subsystems
subsystems=${fields%:*}
# Get the cgroup of the init process in the hierarchy
init_cgroup=${fields#*:}
# Get the filesystem mountpoint of the hierarchy
mountpoint=$(grep -E "^[^ ]+ [^ ]+ cgroup ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2)
if [ -z "$mountpoint" ]; then continue; fi
# Return the absolute path to the containers' parent cgroup
# (do not append '/lxc' if the hierarchy contains the 'ns' subsystem)
if [[ ",$subsystems," == *,ns,* ]]; then
parent_cgroup="${mountpoint}${init_cgroup%/}"
else
parent_cgroup="${mountpoint}${init_cgroup%/}/lxc"
fi
break
done
}
directory=$(readlink -f "$lxc_path")
for i in "$@"; do
case $i in
--help)
help; exit;;
--active)
get_parent_cgroup; directory="$parent_cgroup"; shift;;
--)
shift; break;;
*)
break;;
esac
done
containers=""
if [ ! -z "$directory" ]; then
containers=$(find $directory -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null)
fi
cd "$directory"
ls -d $@ -- $containers
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment