[lxc-devel] [PATCH] Rewrite lxc-ls in python
Stéphane Graber
stgraber at ubuntu.com
Wed Nov 21 23:04:03 UTC 2012
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: Stéphane Graber <stgraber at ubuntu.com>
---
.gitignore | 1 -
configure.ac | 1 -
doc/lxc-ls.sgml.in | 150 ++++++++++++++++++++++-----------
src/lxc/Makefile.am | 2 +-
src/lxc/lxc-ls | 236
++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lxc/lxc-ls.in | 94 ---------------------
6 files changed, 340 insertions(+), 144 deletions(-)
create mode 100644 src/lxc/lxc-ls
delete mode 100644 src/lxc/lxc-ls.in
diff --git a/.gitignore b/.gitignore
index d5b07e3..398e4fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,7 +50,6 @@ src/lxc/lxc-freeze
src/lxc/lxc-info
src/lxc/lxc-init
src/lxc/lxc-kill
-src/lxc/lxc-ls
src/lxc/lxc-monitor
src/lxc/lxc-netstat
src/lxc/lxc-ps
diff --git a/configure.ac b/configure.ac
index b6fa365..5f0cc0b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -257,7 +257,6 @@ AC_CONFIG_FILES([
src/Makefile
src/lxc/Makefile
src/lxc/lxc-ps
- src/lxc/lxc-ls
src/lxc/lxc-netstat
src/lxc/lxc-checkconfig
src/lxc/lxc-setcap
diff --git a/doc/lxc-ls.sgml.in b/doc/lxc-ls.sgml.in
index f5f6573..138908d 100644
--- a/doc/lxc-ls.sgml.in
+++ b/doc/lxc-ls.sgml.in
@@ -49,8 +49,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
<refsynopsisdiv>
<cmdsynopsis>
<command>lxc-ls</command>
+ <arg choice="opt">-1</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>
</refsynopsisdiv>
@@ -65,77 +71,127 @@ Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
<refsect1>
<title>Options</title>
<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>
- <term>
- <option><optional>--active</optional></option>
- </term>
- <listitem>
- <para>
- List active containers.
- </para>
- </listitem>
+ <term>
+ <option><optional>--active</optional></option>
+ </term>
+ <listitem>
+ <para>
+ List only active containers (same as --frozen --running).
+ </para>
+ </listitem>
</varlistentry>
<varlistentry>
- <term>
- <option><optional>ls options</optional></option>
- </term>
- <listitem>
- <para>
- The option passed to <command>lxc-ls</command> are the
- same as the <command>ls</command> command.
- </para>
- </listitem>
+ <term>
+ <option><optional>--frozen</optional></option>
+ </term>
+ <listitem>
+ <para>
+ List only frozen containers.
+ </para>
+ </listitem>
</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>
- <term>lxc-ls -l</term>
- <listitem>
- <para>
- list all the container and their permissions.
- </para>
- </listitem>
+ <term>
+ <option><optional>--fancy</optional></option>
+ </term>
+ <listitem>
+ <para>
+ Use a fancy, column-based output.
+ </para>
+ </listitem>
</varlistentry>
<varlistentry>
- <term>lxc-ls --active -1</term>
- <listitem>
- <para>
- list active containers and display the list in one column.
- </para>
- </listitem>
+ <term>
+ <option><optional>--fancy-format</optional></option>
+ </term>
+ <listitem>
+ <para>
+ 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>
+ <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>
</refsect1>
<refsect1>
- <title>See Also</title>
-
- <simpara> - <citerefentry>
- <refentrytitle>ls</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>,
- </simpara>
+ <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>
+ <varlistentry>
+ <term>lxc-ls --active -1</term>
+ <listitem>
+ <para>
+ list active containers and display the list in one column.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
- &seealso;
-
<refsect1>
<title>Author</title>
- <para>Daniel Lezcano <email>daniel.lezcano at free.fr</email></para>
+ <para>Stéphane Graber <email>stgraber at ubuntu.com</email></para>
</refsect1>
-
</refentry>
<!-- Keep this comment at the end of the file
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 7d86ad6..9219ba6 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -87,7 +87,6 @@ liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS)
$(SECCOMP_LIBS) -lrt
bin_SCRIPTS = \
lxc-ps \
lxc-netstat \
- lxc-ls \
lxc-checkconfig \
lxc-setcap \
lxc-setuid \
@@ -98,6 +97,7 @@ bin_SCRIPTS = \
lxc-destroy
if ENABLE_PYTHON
+ bin_SCRIPTS += lxc-ls
bin_SCRIPTS += lxc-start-ephemeral
endif
diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls
new file mode 100644
index 0000000..8a1d1ed
--- /dev/null
+++ b/src/lxc/lxc-ls
@@ -0,0 +1,236 @@
+#!/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 at 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))
diff --git a/src/lxc/lxc-ls.in b/src/lxc/lxc-ls.in
deleted file mode 100644
index f26572d..0000000
--- a/src/lxc/lxc-ls.in
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/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
--
1.8.0
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 899 bytes
Desc: OpenPGP digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20121121/9dc509a7/attachment.pgp>
More information about the lxc-devel
mailing list