]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxc-ls
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 # NOTE: To remove once the API is stabilized
30 warnings
.filterwarnings("ignore", "The python-lxc API isn't yet stable")
42 gettext
.textdomain("lxc-ls")
45 # Functions used later on
46 def batch(iterable
, cols
=1):
49 length
= len(iterable
)
50 lines
= math
.ceil(length
/ cols
)
52 for line
in range(lines
):
54 for col
in range(cols
):
55 index
= line
+ (col
* lines
)
57 fields
.append(iterable
[index
])
61 def getTerminalSize():
70 cr
= struct
.unpack('hh', fcntl
.ioctl(fd
, termios
.TIOCGWINSZ
,
76 cr
= ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
79 fd
= os
.open(os
.ctermid(), os
.O_RDONLY
)
86 cr
= (env
.get('LINES', 25), env
.get('COLUMNS', 80))
88 return int(cr
[1]), int(cr
[0])
91 def getSubContainers(container
, lxcpath
):
92 attach
= ['lxc-attach', '-P', lxcpath
, '-R', '-s', 'NETWORK|PID',
94 '--', sys
.argv
[0], "--nesting"]
96 with
open(os
.devnull
, "w") as fd
:
97 newenv
= dict(os
.environ
)
98 newenv
['NESTED'] = "/proc/1/root/%s" % lxc
.default_config_path
99 sp
= subprocess
.Popen(attach
, stderr
=fd
, stdout
=subprocess
.PIPE
,
100 env
=newenv
, universal_newlines
=True)
102 out
= sp
.stdout
.read()
104 return json
.loads(out
)
108 # Begin parsing the command line
109 parser
= argparse
.ArgumentParser(description
=_("LXC: List containers"),
110 formatter_class
=argparse
.RawTextHelpFormatter
)
112 parser
.add_argument("-1", dest
="one", action
="store_true",
113 help=_("list one container per line (default when piped)"))
115 parser
.add_argument("-P", "--lxcpath", dest
="lxcpath", metavar
="PATH",
116 help=_("Use specified container path"),
117 default
=lxc
.default_config_path
)
119 parser
.add_argument("--active", action
="store_true",
120 help=_("list only active containers "
121 "(same as --running --frozen)"))
123 parser
.add_argument("--frozen", dest
="state", action
="append_const",
124 const
="FROZEN", help=_("list only frozen containers"))
126 parser
.add_argument("--running", dest
="state", action
="append_const",
127 const
="RUNNING", help=_("list only running containers"))
129 parser
.add_argument("--stopped", dest
="state", action
="append_const",
130 const
="STOPPED", help=_("list only stopped containers"))
132 parser
.add_argument("--fancy", action
="store_true",
133 help=_("use fancy output"))
135 parser
.add_argument("--fancy-format", type=str, default
="name,state,ipv4,ipv6",
136 help=_("comma separated list of fields to show"))
138 parser
.add_argument("--nesting", dest
="nesting", action
="store_true",
139 help=_("show nested containers"))
141 parser
.add_argument("filter", metavar
='FILTER', type=str, nargs
="?",
142 help=_("regexp to be applied on the container list"))
144 args
= parser
.parse_args()
146 # --active is the same as --running --frozen
150 args
.state
+= ["RUNNING", "FROZEN"]
152 # If the output is piped, default to --one
153 if not sys
.stdout
.isatty():
156 # Set the lookup path for the containers
157 lxcpath
= os
.environ
.get('NESTED', args
.lxcpath
)
159 # Turn args.fancy_format into a list
160 args
.fancy_format
= args
.fancy_format
.strip().split(",")
163 ## The user needs to be uid 0
164 if not os
.geteuid() == 0 and (args
.fancy
or args
.state
):
165 parser
.error(_("You must be root to access advanced container properties. "
166 "Try running: sudo %s"
169 # List of containers, stored as dictionaries
171 for container_name
in lxc
.list_containers(config_path
=lxcpath
):
173 entry
['name'] = container_name
176 if args
.filter and not re
.match(args
.filter, container_name
):
179 # Return before grabbing the object (non-root)
180 if not args
.state
and not args
.fancy
and not args
.nesting
:
181 containers
.append(entry
)
184 container
= lxc
.Container(container_name
, args
.lxcpath
)
187 if args
.state
and container
.state
not in args
.state
:
190 # Nothing more is needed if we're not printing some fancy output
191 if not args
.fancy
and not args
.nesting
:
192 containers
.append(entry
)
195 # Some extra field we may want
196 if 'state' in args
.fancy_format
or args
.nesting
:
197 entry
['state'] = container
.state
199 if 'pid' in args
.fancy_format
or args
.nesting
:
201 if container
.init_pid
!= -1:
202 entry
['pid'] = str(container
.init_pid
)
205 for family
, protocol
in {'inet': 'ipv4', 'inet6': 'ipv6'}.items():
206 if protocol
in args
.fancy_format
or args
.nesting
:
207 entry
[protocol
] = "-"
208 ips
= container
.get_ips(family
=family
)
210 entry
[protocol
] = ", ".join(ips
)
212 # Append the container
213 containers
.append(entry
)
217 sub
= getSubContainers(container_name
, args
.lxcpath
)
220 if 'nesting_parent' not in entry
:
221 entry
['nesting_parent'] = []
222 entry
['nesting_parent'].insert(0, container_name
)
223 entry
['nesting_real_name'] = entry
.get('nesting_real_name',
225 entry
['name'] = "%s/%s" % (container_name
, entry
['name'])
228 # Deal with json output:
229 if 'NESTED' in os
.environ
:
230 print(json
.dumps(containers
))
234 ## Standard list with one entry per line
235 if not args
.fancy
and args
.one
:
236 for container
in sorted(containers
,
237 key
=lambda container
: container
['name']):
238 print(container
['name'])
241 ## Standard list with multiple entries per line
242 if not args
.fancy
and not args
.one
:
243 # Get the longest name and extra simple list
246 for container
in containers
:
247 if len(container
['name']) > field_maxlength
:
248 field_maxlength
= len(container
['name'])
249 container_names
.append(container
['name'])
251 # Figure out how many we can put per line
252 width
= getTerminalSize()[0]
254 entries
= int(width
/ (field_maxlength
+ 2))
258 for line
in batch(sorted(container_names
), entries
):
260 for index
in range(len(line
)):
261 line_format
+= "{line[%s]:%s}" % (index
, field_maxlength
+ 2)
263 print(line_format
.format(line
=line
))
269 # Get the maximum length per field
270 for field
in args
.fancy_format
:
271 field_maxlength
[field
] = len(field
)
273 for container
in containers
:
274 for field
in args
.fancy_format
:
275 if field
== 'name' and 'nesting_real_name' in container
:
276 fieldlen
= len(" " * ((len(container
['nesting_parent']) - 1)
277 * 4) + " \_ " + container
['nesting_real_name'])
278 if fieldlen
> field_maxlength
[field
]:
279 field_maxlength
[field
] = fieldlen
280 elif len(container
[field
]) > field_maxlength
[field
]:
281 field_maxlength
[field
] = len(container
[field
])
283 # Generate the line format string based on the maximum length and
284 # a 2 character padding
287 for field
in args
.fancy_format
:
288 line_format
+= "{fields[%s]:%s}" % (index
, field_maxlength
[field
] + 2)
291 # Get the line length minus the padding of the last field
293 for field
in field_maxlength
:
294 line_length
+= field_maxlength
[field
] + 2
297 print(line_format
.format(fields
=[header
.upper()
298 for header
in args
.fancy_format
]))
299 print("-" * line_length
)
302 for container
in sorted(containers
,
303 key
=lambda container
: container
['name']):
305 for field
in args
.fancy_format
:
306 if field
== 'name' and 'nesting_real_name' in container
:
307 prefix
= " " * ((len(container
['nesting_parent']) - 1) * 4)
308 fields
.append(prefix
+ " \_ " + container
['nesting_real_name'])
310 fields
.append(container
[field
])
312 print(line_format
.format(fields
=fields
))