3
# lxc-ls: List containers
5
# This python implementation is based on the work done in the original
6
# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
8
# (C) Copyright Canonical Ltd. 2012
11
# Stéphane Graber <stgraber@ubuntu.com>
13
# This library is free software; you can redistribute it and/or
14
# modify it under the terms of the GNU Lesser General Public
15
# License as published by the Free Software Foundation; either
16
# version 2.1 of the License, or (at your option) any later version.
18
# This library is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
# Lesser General Public License for more details.
23
# You should have received a copy of the GNU Lesser General Public
24
# License along with this library; if not, write to the Free Software
25
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
# NOTE: To remove once the API is stabilized
30
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
40
gettext.textdomain("lxc-ls")
43
# Functions used later on
44
def batch(iterable, cols=1):
47
length = len(iterable)
48
lines = math.ceil(length / cols)
50
for line in range(lines):
52
for col in range(cols):
53
index = line + (col * lines)
55
fields.append(iterable[index])
59
def getTerminalSize():
68
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
74
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
77
fd = os.open(os.ctermid(), os.O_RDONLY)
84
cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
86
return int(cr[1]), int(cr[0])
88
# Begin parsing the command line
89
parser = argparse.ArgumentParser(description=_("LXC: List containers"),
90
formatter_class=argparse.RawTextHelpFormatter)
92
parser.add_argument("-1", dest="one", action="store_true",
93
help=_("list one container per line (default when piped)"))
95
parser.add_argument("--active", action="store_true",
96
help=_("list only active containers "
97
"(same as --running --frozen)"))
99
parser.add_argument("--frozen", dest="state", action="append_const",
100
const="FROZEN", help=_("list only frozen containers"))
102
parser.add_argument("--running", dest="state", action="append_const",
103
const="RUNNING", help=_("list only running containers"))
105
parser.add_argument("--stopped", dest="state", action="append_const",
106
const="STOPPED", help=_("list only stopped containers"))
108
parser.add_argument("--fancy", action="store_true",
109
help=_("use fancy output"))
111
parser.add_argument("--fancy-format", type=str, default="name,state,ipv4,ipv6",
112
help=_("comma separated list of fields to show"))
114
parser.add_argument("filter", metavar='FILTER', type=str, nargs="?",
115
help=_("regexp to be applied on the container list"))
117
args = parser.parse_args()
119
# --active is the same as --running --frozen
123
args.state += ["RUNNING", "FROZEN"]
125
# If the output is piped, default to --one
126
if not sys.stdout.isatty():
129
# Turn args.fancy_format into a list
130
args.fancy_format = args.fancy_format.strip().split(",")
133
## The user needs to be uid 0
134
if not os.geteuid() == 0 and (args.fancy or args.state):
135
parser.error(_("You must be root to access advanced container properties. "
136
"Try running: sudo %s"
139
# List of containers, stored as dictionaries
141
for container_name in lxc.list_containers():
143
entry['name'] = container_name
146
if args.filter and not re.match(args.filter, container_name):
149
# Return before grabbing the object (non-root)
150
if not args.state and not args.fancy:
151
containers.append(entry)
154
container = lxc.Container(container_name)
157
if args.state and container.state not in args.state:
160
# Nothing more is needed if we're not printing some fancy output
162
containers.append(entry)
165
# Some extra field we may want
166
if 'state' in args.fancy_format:
167
entry['state'] = container.state
168
if 'pid' in args.fancy_format:
170
if container.init_pid != -1:
171
entry['pid'] = str(container.init_pid)
174
for protocol in ('ipv4', 'ipv6'):
175
if protocol in args.fancy_format:
176
entry[protocol] = "-"
177
ips = container.get_ips(protocol=protocol, timeout=1)
179
entry[protocol] = ", ".join(ips)
181
containers.append(entry)
185
## Standard list with one entry per line
186
if not args.fancy and args.one:
187
for container in sorted(containers,
188
key=lambda container: container['name']):
189
print(container['name'])
192
## Standard list with multiple entries per line
193
if not args.fancy and not args.one:
194
# Get the longest name and extra simple list
197
for container in containers:
198
if len(container['name']) > field_maxlength:
199
field_maxlength = len(container['name'])
200
container_names.append(container['name'])
202
# Figure out how many we can put per line
203
width = getTerminalSize()[0]
205
entries = int(width / (field_maxlength + 2))
209
for line in batch(sorted(container_names), entries):
211
for index in range(len(line)):
212
line_format += "{line[%s]:%s}" % (index, field_maxlength + 2)
214
print(line_format.format(line=line))
220
# Get the maximum length per field
221
for field in args.fancy_format:
222
field_maxlength[field] = len(field)
224
for container in containers:
225
for field in args.fancy_format:
226
if len(container[field]) > field_maxlength[field]:
227
field_maxlength[field] = len(container[field])
229
# Generate the line format string based on the maximum length and
230
# a 2 character padding
233
for field in args.fancy_format:
234
line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + 2)
237
# Get the line length minus the padding of the last field
239
for field in field_maxlength:
240
line_length += field_maxlength[field] + 2
243
print(line_format.format(fields=[header.upper()
244
for header in args.fancy_format]))
245
print("-" * line_length)
248
for container in sorted(containers,
249
key=lambda container: container['name']):
250
fields = [container[field] for field in args.fancy_format]
251
print(line_format.format(fields=fields))