~serge-hallyn/ubuntu/saucy/lxc/lxc-dnsmasq

« back to all changes in this revision

Viewing changes to src/lxc/lxc-ls

  • Committer: Stéphane Graber
  • Date: 2013-02-18 15:20:18 UTC
  • mto: This revision was merged to the branch mainline in revision 190.
  • Revision ID: stgraber@ubuntu.com-20130218152018-ls2gi9hkqs2kuhj8
Tags: upstream-0.9.0~alpha3
Import upstream version 0.9.0~alpha3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python3
 
2
#
 
3
# lxc-ls: List containers
 
4
#
 
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)
 
7
#
 
8
# (C) Copyright Canonical Ltd. 2012
 
9
#
 
10
# Authors:
 
11
# Stéphane Graber <stgraber@ubuntu.com>
 
12
#
 
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.
 
17
#
 
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.
 
22
#
 
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
 
26
#
 
27
 
 
28
# NOTE: To remove once the API is stabilized
 
29
import warnings
 
30
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
 
31
 
 
32
import argparse
 
33
import gettext
 
34
import lxc
 
35
import os
 
36
import re
 
37
import sys
 
38
 
 
39
_ = gettext.gettext
 
40
gettext.textdomain("lxc-ls")
 
41
 
 
42
 
 
43
# Functions used later on
 
44
def batch(iterable, cols=1):
 
45
    import math
 
46
 
 
47
    length = len(iterable)
 
48
    lines = math.ceil(length / cols)
 
49
 
 
50
    for line in range(lines):
 
51
        fields = []
 
52
        for col in range(cols):
 
53
            index = line + (col * lines)
 
54
            if index < length:
 
55
                fields.append(iterable[index])
 
56
        yield fields
 
57
 
 
58
 
 
59
def getTerminalSize():
 
60
    import os
 
61
    env = os.environ
 
62
 
 
63
    def ioctl_GWINSZ(fd):
 
64
        try:
 
65
            import fcntl
 
66
            import termios
 
67
            import struct
 
68
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
 
69
                               '1234'))
 
70
            return cr
 
71
        except:
 
72
            return
 
73
 
 
74
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
 
75
    if not cr:
 
76
        try:
 
77
            fd = os.open(os.ctermid(), os.O_RDONLY)
 
78
            cr = ioctl_GWINSZ(fd)
 
79
            os.close(fd)
 
80
        except:
 
81
            pass
 
82
 
 
83
    if not cr:
 
84
        cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
 
85
 
 
86
    return int(cr[1]), int(cr[0])
 
87
 
 
88
# Begin parsing the command line
 
89
parser = argparse.ArgumentParser(description=_("LXC: List containers"),
 
90
                                 formatter_class=argparse.RawTextHelpFormatter)
 
91
 
 
92
parser.add_argument("-1", dest="one", action="store_true",
 
93
                    help=_("list one container per line (default when piped)"))
 
94
 
 
95
parser.add_argument("--active", action="store_true",
 
96
                    help=_("list only active containers "
 
97
                           "(same as --running --frozen)"))
 
98
 
 
99
parser.add_argument("--frozen", dest="state", action="append_const",
 
100
                    const="FROZEN", help=_("list only frozen containers"))
 
101
 
 
102
parser.add_argument("--running", dest="state", action="append_const",
 
103
                    const="RUNNING", help=_("list only running containers"))
 
104
 
 
105
parser.add_argument("--stopped", dest="state", action="append_const",
 
106
                    const="STOPPED", help=_("list only stopped containers"))
 
107
 
 
108
parser.add_argument("--fancy", action="store_true",
 
109
                    help=_("use fancy output"))
 
110
 
 
111
parser.add_argument("--fancy-format", type=str, default="name,state,ipv4,ipv6",
 
112
                    help=_("comma separated list of fields to show"))
 
113
 
 
114
parser.add_argument("filter", metavar='FILTER', type=str, nargs="?",
 
115
                    help=_("regexp to be applied on the container list"))
 
116
 
 
117
args = parser.parse_args()
 
118
 
 
119
# --active is the same as --running --frozen
 
120
if args.active:
 
121
    if not args.state:
 
122
        args.state = []
 
123
    args.state += ["RUNNING", "FROZEN"]
 
124
 
 
125
# If the output is piped, default to --one
 
126
if not sys.stdout.isatty():
 
127
    args.one = True
 
128
 
 
129
# Turn args.fancy_format into a list
 
130
args.fancy_format = args.fancy_format.strip().split(",")
 
131
 
 
132
# Basic checks
 
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"
 
137
                   % (sys.argv[0])))
 
138
 
 
139
# List of containers, stored as dictionaries
 
140
containers = []
 
141
for container_name in lxc.list_containers():
 
142
    entry = {}
 
143
    entry['name'] = container_name
 
144
 
 
145
    # Apply filter
 
146
    if args.filter and not re.match(args.filter, container_name):
 
147
        continue
 
148
 
 
149
    # Return before grabbing the object (non-root)
 
150
    if not args.state and not args.fancy:
 
151
        containers.append(entry)
 
152
        continue
 
153
 
 
154
    container = lxc.Container(container_name)
 
155
 
 
156
    # Filter by status
 
157
    if args.state and container.state not in args.state:
 
158
        continue
 
159
 
 
160
    # Nothing more is needed if we're not printing some fancy output
 
161
    if not args.fancy:
 
162
        containers.append(entry)
 
163
        continue
 
164
 
 
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:
 
169
        entry['pid'] = "-"
 
170
        if container.init_pid != -1:
 
171
            entry['pid'] = str(container.init_pid)
 
172
 
 
173
    # Get the IPs
 
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)
 
178
            if ips:
 
179
                entry[protocol] = ", ".join(ips)
 
180
 
 
181
    containers.append(entry)
 
182
 
 
183
 
 
184
# Print the list
 
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'])
 
190
    sys.exit(0)
 
191
 
 
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
 
195
    field_maxlength = 0
 
196
    container_names = []
 
197
    for container in containers:
 
198
        if len(container['name']) > field_maxlength:
 
199
            field_maxlength = len(container['name'])
 
200
        container_names.append(container['name'])
 
201
 
 
202
    # Figure out how many we can put per line
 
203
    width = getTerminalSize()[0]
 
204
 
 
205
    entries = int(width / (field_maxlength + 2))
 
206
    if entries == 0:
 
207
        entries = 1
 
208
 
 
209
    for line in batch(sorted(container_names), entries):
 
210
        line_format = ""
 
211
        for index in range(len(line)):
 
212
            line_format += "{line[%s]:%s}" % (index, field_maxlength + 2)
 
213
 
 
214
        print(line_format.format(line=line))
 
215
 
 
216
## Fancy listing
 
217
if args.fancy:
 
218
    field_maxlength = {}
 
219
 
 
220
    # Get the maximum length per field
 
221
    for field in args.fancy_format:
 
222
        field_maxlength[field] = len(field)
 
223
 
 
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])
 
228
 
 
229
    # Generate the line format string based on the maximum length and
 
230
    # a 2 character padding
 
231
    line_format = ""
 
232
    index = 0
 
233
    for field in args.fancy_format:
 
234
        line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + 2)
 
235
        index += 1
 
236
 
 
237
    # Get the line length minus the padding of the last field
 
238
    line_length = -2
 
239
    for field in field_maxlength:
 
240
        line_length += field_maxlength[field] + 2
 
241
 
 
242
    # Print header
 
243
    print(line_format.format(fields=[header.upper()
 
244
                                     for header in args.fancy_format]))
 
245
    print("-" * line_length)
 
246
 
 
247
    # Print the entries
 
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))