Revision f0b1bafe
b/lib/cli.py | ||
---|---|---|
2382 | 2382 |
"""Callable class for formatting fields of a query. |
2383 | 2383 |
|
2384 | 2384 |
""" |
2385 |
def __init__(self, fn, status_fn): |
|
2385 |
def __init__(self, fn, status_fn, verbose):
|
|
2386 | 2386 |
"""Initializes this class. |
2387 | 2387 |
|
2388 | 2388 |
@type fn: callable |
2389 | 2389 |
@param fn: Formatting function |
2390 | 2390 |
@type status_fn: callable |
2391 | 2391 |
@param status_fn: Function to report fields' status |
2392 |
@type verbose: boolean |
|
2393 |
@param verbose: whether to use verbose field descriptions or not |
|
2392 | 2394 |
|
2393 | 2395 |
""" |
2394 | 2396 |
self._fn = fn |
2395 | 2397 |
self._status_fn = status_fn |
2398 |
if verbose: |
|
2399 |
self._desc_index = 0 |
|
2400 |
else: |
|
2401 |
self._desc_index = 1 |
|
2396 | 2402 |
|
2397 | 2403 |
def __call__(self, data): |
2398 | 2404 |
"""Returns a field's string representation. |
... | ... | |
2409 | 2415 |
assert value is None, \ |
2410 | 2416 |
"Found value %r for abnormal status %s" % (value, status) |
2411 | 2417 |
|
2412 |
if status == constants.RS_UNKNOWN: |
|
2413 |
return "(unknown)" |
|
2414 |
|
|
2415 |
if status == constants.RS_NODATA: |
|
2416 |
return "(nodata)" |
|
2417 |
|
|
2418 |
if status == constants.RS_UNAVAIL: |
|
2419 |
return "(unavail)" |
|
2420 |
|
|
2421 |
if status == constants.RS_OFFLINE: |
|
2422 |
return "(offline)" |
|
2418 |
if status in constants.RSS_DESCRIPTION: |
|
2419 |
return constants.RSS_DESCRIPTION[status][self._desc_index] |
|
2423 | 2420 |
|
2424 | 2421 |
raise NotImplementedError("Unknown status %s" % status) |
2425 | 2422 |
|
2426 | 2423 |
|
2427 | 2424 |
def FormatQueryResult(result, unit=None, format_override=None, separator=None, |
2428 |
header=False): |
|
2425 |
header=False, verbose=False):
|
|
2429 | 2426 |
"""Formats data in L{objects.QueryResponse}. |
2430 | 2427 |
|
2431 | 2428 |
@type result: L{objects.QueryResponse} |
... | ... | |
2440 | 2437 |
@param separator: String used to separate fields |
2441 | 2438 |
@type header: bool |
2442 | 2439 |
@param header: Whether to output header row |
2440 |
@type verbose: boolean |
|
2441 |
@param verbose: whether to use verbose field descriptions or not |
|
2443 | 2442 |
|
2444 | 2443 |
""" |
2445 | 2444 |
if unit is None: |
... | ... | |
2462 | 2461 |
assert fdef.title and fdef.name |
2463 | 2462 |
(fn, align_right) = _GetColumnFormatter(fdef, format_override, unit) |
2464 | 2463 |
columns.append(TableColumn(fdef.title, |
2465 |
_QueryColumnFormatter(fn, _RecordStatus), |
|
2464 |
_QueryColumnFormatter(fn, _RecordStatus, |
|
2465 |
verbose), |
|
2466 | 2466 |
align_right)) |
2467 | 2467 |
|
2468 | 2468 |
table = FormatTable(result.data, columns, header, separator) |
... | ... | |
2511 | 2511 |
|
2512 | 2512 |
|
2513 | 2513 |
def GenericList(resource, fields, names, unit, separator, header, cl=None, |
2514 |
format_override=None): |
|
2514 |
format_override=None, verbose=False):
|
|
2515 | 2515 |
"""Generic implementation for listing all items of a resource. |
2516 | 2516 |
|
2517 | 2517 |
@param resource: One of L{constants.QR_OP_LUXI} |
... | ... | |
2530 | 2530 |
@type format_override: dict |
2531 | 2531 |
@param format_override: Dictionary for overriding field formatting functions, |
2532 | 2532 |
indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY} |
2533 |
@type verbose: boolean |
|
2534 |
@param verbose: whether to use verbose field descriptions or not |
|
2533 | 2535 |
|
2534 | 2536 |
""" |
2535 | 2537 |
if cl is None: |
... | ... | |
2544 | 2546 |
|
2545 | 2547 |
(status, data) = FormatQueryResult(response, unit=unit, separator=separator, |
2546 | 2548 |
header=header, |
2547 |
format_override=format_override) |
|
2549 |
format_override=format_override, |
|
2550 |
verbose=verbose) |
|
2548 | 2551 |
|
2549 | 2552 |
for line in data: |
2550 | 2553 |
ToStdout(line) |
b/lib/client/gnt_debug.py | ||
---|---|---|
503 | 503 |
while True: |
504 | 504 |
ret = GenericList(constants.QR_LOCK, selected_fields, None, None, |
505 | 505 |
opts.separator, not opts.no_headers, |
506 |
format_override=fmtoverride) |
|
506 |
format_override=fmtoverride, verbose=opts.verbose)
|
|
507 | 507 |
|
508 | 508 |
if ret != constants.EXIT_SUCCESS: |
509 | 509 |
return ret |
... | ... | |
575 | 575 |
TestJobqueue, ARGS_NONE, [PRIORITY_OPT], |
576 | 576 |
"", "Test a few aspects of the job queue"), |
577 | 577 |
"locks": ( |
578 |
ListLocks, ARGS_NONE, [NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT], |
|
578 |
ListLocks, ARGS_NONE, |
|
579 |
[NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT, VERBOSE_OPT], |
|
579 | 580 |
"[--interval N]", "Show a list of locks in the master daemon"), |
580 | 581 |
} |
581 | 582 |
|
b/lib/client/gnt_group.py | ||
---|---|---|
1 | 1 |
# |
2 | 2 |
# |
3 | 3 |
|
4 |
# Copyright (C) 2010 Google Inc. |
|
4 |
# Copyright (C) 2010, 2011 Google Inc.
|
|
5 | 5 |
# |
6 | 6 |
# This program is free software; you can redistribute it and/or modify |
7 | 7 |
# it under the terms of the GNU General Public License as published by |
... | ... | |
101 | 101 |
|
102 | 102 |
return GenericList(constants.QR_GROUP, desired_fields, args, None, |
103 | 103 |
opts.separator, not opts.no_headers, |
104 |
format_override=fmtoverride) |
|
104 |
format_override=fmtoverride, verbose=opts.verbose)
|
|
105 | 105 |
|
106 | 106 |
|
107 | 107 |
def ListGroupFields(opts, args): |
... | ... | |
189 | 189 |
"<group_name> <node>...", "Assign nodes to a group"), |
190 | 190 |
"list": ( |
191 | 191 |
ListGroups, ARGS_MANY_GROUPS, |
192 |
[NOHDR_OPT, SEP_OPT, FIELDS_OPT], |
|
192 |
[NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT],
|
|
193 | 193 |
"[<group_name>...]", |
194 | 194 |
"Lists the node groups in the cluster. The available fields can be shown" |
195 | 195 |
" using the \"list-fields\" command (see the man page for details)." |
b/lib/client/gnt_instance.py | ||
---|---|---|
257 | 257 |
|
258 | 258 |
return GenericList(constants.QR_INSTANCE, selected_fields, args, opts.units, |
259 | 259 |
opts.separator, not opts.no_headers, |
260 |
format_override=fmtoverride) |
|
260 |
format_override=fmtoverride, verbose=opts.verbose)
|
|
261 | 261 |
|
262 | 262 |
|
263 | 263 |
def ListInstanceFields(opts, args): |
... | ... | |
1398 | 1398 |
"Show information on the specified instance(s)"), |
1399 | 1399 |
'list': ( |
1400 | 1400 |
ListInstances, ARGS_MANY_INSTANCES, |
1401 |
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], |
|
1401 |
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT],
|
|
1402 | 1402 |
"[<instance>...]", |
1403 | 1403 |
"Lists the instances and their status. The available fields can be shown" |
1404 | 1404 |
" using the \"list-fields\" command (see the man page for details)." |
b/lib/client/gnt_node.py | ||
---|---|---|
223 | 223 |
|
224 | 224 |
return GenericList(constants.QR_NODE, selected_fields, args, opts.units, |
225 | 225 |
opts.separator, not opts.no_headers, |
226 |
format_override=fmtoverride) |
|
226 |
format_override=fmtoverride, verbose=opts.verbose)
|
|
227 | 227 |
|
228 | 228 |
|
229 | 229 |
def ListNodeFields(opts, args): |
... | ... | |
747 | 747 |
"[<node_name>...]", "Show information about the node(s)"), |
748 | 748 |
'list': ( |
749 | 749 |
ListNodes, ARGS_MANY_NODES, |
750 |
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], |
|
750 |
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT],
|
|
751 | 751 |
"[nodes...]", |
752 | 752 |
"Lists the nodes in the cluster. The available fields can be shown using" |
753 | 753 |
" the \"list-fields\" command (see the man page for details)." |
b/lib/constants.py | ||
---|---|---|
1029 | 1029 |
RS_OFFLINE, |
1030 | 1030 |
]) |
1031 | 1031 |
|
1032 |
#: Dictionary with special field cases and their verbose/terse formatting |
|
1033 |
RSS_DESCRIPTION = { |
|
1034 |
RS_UNKNOWN: ("(unknown)", "??"), |
|
1035 |
RS_NODATA: ("(nodata)", "?"), |
|
1036 |
RS_OFFLINE: ("(offline)", "*"), |
|
1037 |
RS_UNAVAIL: ("(unavail)", "-"), |
|
1038 |
} |
|
1039 |
|
|
1032 | 1040 |
# max dynamic devices |
1033 | 1041 |
MAX_NICS = 8 |
1034 | 1042 |
MAX_DISKS = 16 |
b/man/ganeti.rst | ||
---|---|---|
179 | 179 |
|
180 | 180 |
All Ganeti daemons re-open the log file(s) when sent a SIGHUP signal. |
181 | 181 |
**logrotate**(8) can be used to rotate Ganeti's log files. |
182 |
|
|
183 |
Common field formatting |
|
184 |
----------------------- |
|
185 |
|
|
186 |
Multiple ganeti commands use the same framework for tabular listing of |
|
187 |
resources (e.g. **gnt-instance list**, **gnt-node list**, **gnt-group |
|
188 |
list**, **gnt-debug locks**, etc.). For these commands, special states |
|
189 |
are denoted via a special symbol (in terse mode) or a string (in |
|
190 |
verbose mode): |
|
191 |
|
|
192 |
*, (offline) |
|
193 |
The node in question is marked offline, and thus it cannot be |
|
194 |
queried for data. This result is persistent until the node is |
|
195 |
de-offlined. |
|
196 |
|
|
197 |
?, (nodata) |
|
198 |
Ganeti expected to receive an answer from this entity, but the |
|
199 |
cluster RPC call failed and/or we didn't receive a valid answer; |
|
200 |
usually more information is available in the node daemon log (if |
|
201 |
the node is alive) or the master daemon log. This result is |
|
202 |
transient, and re-running command might return a different result. |
|
203 |
|
|
204 |
-, (unavail) |
|
205 |
The respective field doesn't make sense for this entity; |
|
206 |
e.g. querying a down instance for its current memory 'live' usage, |
|
207 |
or querying a non-vm_capable node for disk/memory data. This |
|
208 |
result is persistent, and until the entity state is changed via |
|
209 |
ganeti commands, the result won't change. |
|
210 |
|
|
211 |
??, (unknown) |
|
212 |
This field is not known (note that this is different from entity |
|
213 |
being unknown). Either you have mis-typed the field name, or you |
|
214 |
are using a field that the running Ganeti master daemon doesn't |
|
215 |
know. This result is persistent, re-running the command won't |
|
216 |
change it. |
b/man/gnt-debug.rst | ||
---|---|---|
93 | 93 |
LOCKS |
94 | 94 |
~~~~~ |
95 | 95 |
|
96 |
| **locks** [--no-headers] [--separator=*SEPARATOR*] |
|
96 |
| **locks** [--no-headers] [--separator=*SEPARATOR*] [-v]
|
|
97 | 97 |
| [-o *[+]FIELD,...*] [--interval=*SECONDS*] |
98 | 98 |
|
99 | 99 |
Shows a list of locks in the master daemon. |
... | ... | |
103 | 103 |
used between the output fields. Both these options are to help |
104 | 104 |
scripting. |
105 | 105 |
|
106 |
The ``-v`` option activates verbose mode, which changes the display of |
|
107 |
special field states (see **ganeti(7)**). |
|
108 |
|
|
106 | 109 |
The ``-o`` option takes a comma-separated list of output fields. |
107 | 110 |
The available fields and their meaning are: |
108 | 111 |
|
b/man/gnt-group.rst | ||
---|---|---|
92 | 92 |
LIST |
93 | 93 |
~~~~ |
94 | 94 |
|
95 |
| **list** [--no-headers] [--separator=*SEPARATOR*] |
|
95 |
| **list** [--no-headers] [--separator=*SEPARATOR*] [-v]
|
|
96 | 96 |
| [-o *[+]FIELD,...*] [group...] |
97 | 97 |
|
98 | 98 |
Lists all existing node groups in the cluster. |
... | ... | |
102 | 102 |
used between the output fields. Both these options are to help |
103 | 103 |
scripting. |
104 | 104 |
|
105 |
The ``-v`` option activates verbose mode, which changes the display of |
|
106 |
special field states (see **ganeti(7)**). |
|
107 |
|
|
105 | 108 |
The ``-o`` option takes a comma-separated list of output fields. |
106 | 109 |
If the value of the option starts with the character ``+``, the new |
107 | 110 |
fields will be added to the default list. This allows to quickly |
b/man/gnt-instance.rst | ||
---|---|---|
624 | 624 |
^^^^ |
625 | 625 |
|
626 | 626 |
| **list** |
627 |
| [--no-headers] [--separator=*SEPARATOR*] [--units=*UNITS*] |
|
627 |
| [--no-headers] [--separator=*SEPARATOR*] [--units=*UNITS*] [-v]
|
|
628 | 628 |
| [-o *[+]FIELD,...*] [instance...] |
629 | 629 |
|
630 | 630 |
Shows the currently configured instances with memory usage, disk |
... | ... | |
642 | 642 |
parsing by scripts. In both cases, the ``--units`` option can be |
643 | 643 |
used to enforce a given output unit. |
644 | 644 |
|
645 |
The ``-v`` option activates verbose mode, which changes the display of |
|
646 |
special field states (see **ganeti(7)**). |
|
647 |
|
|
645 | 648 |
The ``-o`` option takes a comma-separated list of output fields. |
646 | 649 |
The available fields and their meaning are: |
647 | 650 |
|
648 | 651 |
|
649 |
|
|
650 | 652 |
name |
651 | 653 |
the instance name |
652 | 654 |
|
b/man/gnt-node.rst | ||
---|---|---|
149 | 149 |
|
150 | 150 |
| **list** |
151 | 151 |
| [--no-headers] [--separator=*SEPARATOR*] |
152 |
| [--units=*UNITS*] [-o *[+]FIELD,...*] |
|
152 |
| [--units=*UNITS*] [-v] [-o *[+]FIELD,...*]
|
|
153 | 153 |
| [node...] |
154 | 154 |
|
155 | 155 |
Lists the nodes in the cluster. |
... | ... | |
169 | 169 |
Queries of nodes will be done in parallel with any running jobs. This might |
170 | 170 |
give inconsistent results for the free disk/memory. |
171 | 171 |
|
172 |
The ``-v`` option activates verbose mode, which changes the display of |
|
173 |
special field states (see **ganeti(7)**). |
|
174 |
|
|
172 | 175 |
The ``-o`` option takes a comma-separated list of output fields. |
173 | 176 |
The available fields and their meaning are: |
174 | 177 |
|
175 | 178 |
|
176 |
|
|
177 | 179 |
name |
178 | 180 |
the node name |
179 | 181 |
|
b/test/ganeti.cli_unittest.py | ||
---|---|---|
1 | 1 |
#!/usr/bin/python |
2 | 2 |
# |
3 | 3 |
|
4 |
# Copyright (C) 2008 Google Inc. |
|
4 |
# Copyright (C) 2008, 2011 Google Inc.
|
|
5 | 5 |
# |
6 | 6 |
# This program is free software; you can redistribute it and/or modify |
7 | 7 |
# it under the terms of the GNU General Public License as published by |
... | ... | |
396 | 396 |
]) |
397 | 397 |
|
398 | 398 |
self.assertEqual(cli.FormatQueryResult(response, header=True, |
399 |
separator="|"), |
|
399 |
separator="|", verbose=True),
|
|
400 | 400 |
(cli.QR_UNKNOWN, [ |
401 | 401 |
"ID|unk|Unavail|NoData|OffLine", |
402 | 402 |
"1|(unknown)|N||(offline)", |
403 | 403 |
"2|(unknown)|(nodata)|x|(offline)", |
404 | 404 |
"3|(unknown)|N|(unavail)|(offline)", |
405 | 405 |
])) |
406 |
self.assertEqual(cli.FormatQueryResult(response, header=True, |
|
407 |
separator="|", verbose=False), |
|
408 |
(cli.QR_UNKNOWN, [ |
|
409 |
"ID|unk|Unavail|NoData|OffLine", |
|
410 |
"1|??|N||*", |
|
411 |
"2|??|?|x|*", |
|
412 |
"3|??|N|-|*", |
|
413 |
])) |
|
406 | 414 |
|
407 | 415 |
def testNoData(self): |
408 | 416 |
fields = [ |
... | ... | |
452 | 460 |
]) |
453 | 461 |
|
454 | 462 |
self.assertEqual(cli.FormatQueryResult(response, header=False, |
455 |
separator="|"), |
|
463 |
separator="|", verbose=True),
|
|
456 | 464 |
(cli.QR_INCOMPLETE, [ |
457 | 465 |
"1|N||(offline)", |
458 | 466 |
"2|(nodata)|x|abc", |
459 | 467 |
"3|N|(unavail)|(offline)", |
460 | 468 |
])) |
469 |
self.assertEqual(cli.FormatQueryResult(response, header=False, |
|
470 |
separator="|", verbose=False), |
|
471 |
(cli.QR_INCOMPLETE, [ |
|
472 |
"1|N||*", |
|
473 |
"2|?|x|abc", |
|
474 |
"3|N|-|*", |
|
475 |
])) |
|
461 | 476 |
|
462 | 477 |
def testInvalidFieldType(self): |
463 | 478 |
fields = [ |
Also available in: Unified diff