Commit f0b1bafe authored by Iustin Pop's avatar Iustin Pop

Change the list formatting to a 'special' chars

And also enable verbose display via the, well, verbose option. Man
page and tests are updated, and the formatting is moved from 4 if
statements to a data structure.
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarRené Nussbaumer <rn@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent 3a969900
......@@ -2382,17 +2382,23 @@ class _QueryColumnFormatter:
"""Callable class for formatting fields of a query.
"""
def __init__(self, fn, status_fn):
def __init__(self, fn, status_fn, verbose):
"""Initializes this class.
@type fn: callable
@param fn: Formatting function
@type status_fn: callable
@param status_fn: Function to report fields' status
@type verbose: boolean
@param verbose: whether to use verbose field descriptions or not
"""
self._fn = fn
self._status_fn = status_fn
if verbose:
self._desc_index = 0
else:
self._desc_index = 1
def __call__(self, data):
"""Returns a field's string representation.
......@@ -2409,23 +2415,14 @@ class _QueryColumnFormatter:
assert value is None, \
"Found value %r for abnormal status %s" % (value, status)
if status == constants.RS_UNKNOWN:
return "(unknown)"
if status == constants.RS_NODATA:
return "(nodata)"
if status == constants.RS_UNAVAIL:
return "(unavail)"
if status == constants.RS_OFFLINE:
return "(offline)"
if status in constants.RSS_DESCRIPTION:
return constants.RSS_DESCRIPTION[status][self._desc_index]
raise NotImplementedError("Unknown status %s" % status)
def FormatQueryResult(result, unit=None, format_override=None, separator=None,
header=False):
header=False, verbose=False):
"""Formats data in L{objects.QueryResponse}.
@type result: L{objects.QueryResponse}
......@@ -2440,6 +2437,8 @@ def FormatQueryResult(result, unit=None, format_override=None, separator=None,
@param separator: String used to separate fields
@type header: bool
@param header: Whether to output header row
@type verbose: boolean
@param verbose: whether to use verbose field descriptions or not
"""
if unit is None:
......@@ -2462,7 +2461,8 @@ def FormatQueryResult(result, unit=None, format_override=None, separator=None,
assert fdef.title and fdef.name
(fn, align_right) = _GetColumnFormatter(fdef, format_override, unit)
columns.append(TableColumn(fdef.title,
_QueryColumnFormatter(fn, _RecordStatus),
_QueryColumnFormatter(fn, _RecordStatus,
verbose),
align_right))
table = FormatTable(result.data, columns, header, separator)
......@@ -2511,7 +2511,7 @@ def _WarnUnknownFields(fdefs):
def GenericList(resource, fields, names, unit, separator, header, cl=None,
format_override=None):
format_override=None, verbose=False):
"""Generic implementation for listing all items of a resource.
@param resource: One of L{constants.QR_OP_LUXI}
......@@ -2530,6 +2530,8 @@ def GenericList(resource, fields, names, unit, separator, header, cl=None,
@type format_override: dict
@param format_override: Dictionary for overriding field formatting functions,
indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
@type verbose: boolean
@param verbose: whether to use verbose field descriptions or not
"""
if cl is None:
......@@ -2544,7 +2546,8 @@ def GenericList(resource, fields, names, unit, separator, header, cl=None,
(status, data) = FormatQueryResult(response, unit=unit, separator=separator,
header=header,
format_override=format_override)
format_override=format_override,
verbose=verbose)
for line in data:
ToStdout(line)
......
......@@ -503,7 +503,7 @@ def ListLocks(opts, args): # pylint: disable-msg=W0613
while True:
ret = GenericList(constants.QR_LOCK, selected_fields, None, None,
opts.separator, not opts.no_headers,
format_override=fmtoverride)
format_override=fmtoverride, verbose=opts.verbose)
if ret != constants.EXIT_SUCCESS:
return ret
......@@ -575,7 +575,8 @@ commands = {
TestJobqueue, ARGS_NONE, [PRIORITY_OPT],
"", "Test a few aspects of the job queue"),
"locks": (
ListLocks, ARGS_NONE, [NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT],
ListLocks, ARGS_NONE,
[NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT, VERBOSE_OPT],
"[--interval N]", "Show a list of locks in the master daemon"),
}
......
#
#
# Copyright (C) 2010 Google Inc.
# Copyright (C) 2010, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -101,7 +101,7 @@ def ListGroups(opts, args):
return GenericList(constants.QR_GROUP, desired_fields, args, None,
opts.separator, not opts.no_headers,
format_override=fmtoverride)
format_override=fmtoverride, verbose=opts.verbose)
def ListGroupFields(opts, args):
......@@ -189,7 +189,7 @@ commands = {
"<group_name> <node>...", "Assign nodes to a group"),
"list": (
ListGroups, ARGS_MANY_GROUPS,
[NOHDR_OPT, SEP_OPT, FIELDS_OPT],
[NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT],
"[<group_name>...]",
"Lists the node groups in the cluster. The available fields can be shown"
" using the \"list-fields\" command (see the man page for details)."
......
......@@ -257,7 +257,7 @@ def ListInstances(opts, args):
return GenericList(constants.QR_INSTANCE, selected_fields, args, opts.units,
opts.separator, not opts.no_headers,
format_override=fmtoverride)
format_override=fmtoverride, verbose=opts.verbose)
def ListInstanceFields(opts, args):
......@@ -1398,7 +1398,7 @@ commands = {
"Show information on the specified instance(s)"),
'list': (
ListInstances, ARGS_MANY_INSTANCES,
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT],
"[<instance>...]",
"Lists the instances and their status. The available fields can be shown"
" using the \"list-fields\" command (see the man page for details)."
......
......@@ -223,7 +223,7 @@ def ListNodes(opts, args):
return GenericList(constants.QR_NODE, selected_fields, args, opts.units,
opts.separator, not opts.no_headers,
format_override=fmtoverride)
format_override=fmtoverride, verbose=opts.verbose)
def ListNodeFields(opts, args):
......@@ -747,7 +747,7 @@ commands = {
"[<node_name>...]", "Show information about the node(s)"),
'list': (
ListNodes, ARGS_MANY_NODES,
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT],
"[nodes...]",
"Lists the nodes in the cluster. The available fields can be shown using"
" the \"list-fields\" command (see the man page for details)."
......
......@@ -1029,6 +1029,14 @@ RS_ALL = frozenset([
RS_OFFLINE,
])
#: Dictionary with special field cases and their verbose/terse formatting
RSS_DESCRIPTION = {
RS_UNKNOWN: ("(unknown)", "??"),
RS_NODATA: ("(nodata)", "?"),
RS_OFFLINE: ("(offline)", "*"),
RS_UNAVAIL: ("(unavail)", "-"),
}
# max dynamic devices
MAX_NICS = 8
MAX_DISKS = 16
......
......@@ -179,3 +179,38 @@ Common daemon functionality
All Ganeti daemons re-open the log file(s) when sent a SIGHUP signal.
**logrotate**(8) can be used to rotate Ganeti's log files.
Common field formatting
-----------------------
Multiple ganeti commands use the same framework for tabular listing of
resources (e.g. **gnt-instance list**, **gnt-node list**, **gnt-group
list**, **gnt-debug locks**, etc.). For these commands, special states
are denoted via a special symbol (in terse mode) or a string (in
verbose mode):
*, (offline)
The node in question is marked offline, and thus it cannot be
queried for data. This result is persistent until the node is
de-offlined.
?, (nodata)
Ganeti expected to receive an answer from this entity, but the
cluster RPC call failed and/or we didn't receive a valid answer;
usually more information is available in the node daemon log (if
the node is alive) or the master daemon log. This result is
transient, and re-running command might return a different result.
-, (unavail)
The respective field doesn't make sense for this entity;
e.g. querying a down instance for its current memory 'live' usage,
or querying a non-vm_capable node for disk/memory data. This
result is persistent, and until the entity state is changed via
ganeti commands, the result won't change.
??, (unknown)
This field is not known (note that this is different from entity
being unknown). Either you have mis-typed the field name, or you
are using a field that the running Ganeti master daemon doesn't
know. This result is persistent, re-running the command won't
change it.
......@@ -93,7 +93,7 @@ failed jobs deliberately.
LOCKS
~~~~~
| **locks** [--no-headers] [--separator=*SEPARATOR*]
| **locks** [--no-headers] [--separator=*SEPARATOR*] [-v]
| [-o *[+]FIELD,...*] [--interval=*SECONDS*]
Shows a list of locks in the master daemon.
......@@ -103,6 +103,9 @@ The ``--no-headers`` option will skip the initial header line. The
used between the output fields. Both these options are to help
scripting.
The ``-v`` option activates verbose mode, which changes the display of
special field states (see **ganeti(7)**).
The ``-o`` option takes a comma-separated list of output fields.
The available fields and their meaning are:
......
......@@ -92,7 +92,7 @@ least one group, so the last group cannot be removed.
LIST
~~~~
| **list** [--no-headers] [--separator=*SEPARATOR*]
| **list** [--no-headers] [--separator=*SEPARATOR*] [-v]
| [-o *[+]FIELD,...*] [group...]
Lists all existing node groups in the cluster.
......@@ -102,6 +102,9 @@ The ``--no-headers`` option will skip the initial header line. The
used between the output fields. Both these options are to help
scripting.
The ``-v`` option activates verbose mode, which changes the display of
special field states (see **ganeti(7)**).
The ``-o`` option takes a comma-separated list of output fields.
If the value of the option starts with the character ``+``, the new
fields will be added to the default list. This allows to quickly
......
......@@ -624,7 +624,7 @@ LIST
^^^^
| **list**
| [--no-headers] [--separator=*SEPARATOR*] [--units=*UNITS*]
| [--no-headers] [--separator=*SEPARATOR*] [--units=*UNITS*] [-v]
| [-o *[+]FIELD,...*] [instance...]
Shows the currently configured instances with memory usage, disk
......@@ -642,11 +642,13 @@ option is given, then the values are shown in mebibytes to allow
parsing by scripts. In both cases, the ``--units`` option can be
used to enforce a given output unit.
The ``-v`` option activates verbose mode, which changes the display of
special field states (see **ganeti(7)**).
The ``-o`` option takes a comma-separated list of output fields.
The available fields and their meaning are:
name
the instance name
......
......@@ -149,7 +149,7 @@ LIST
| **list**
| [--no-headers] [--separator=*SEPARATOR*]
| [--units=*UNITS*] [-o *[+]FIELD,...*]
| [--units=*UNITS*] [-v] [-o *[+]FIELD,...*]
| [node...]
Lists the nodes in the cluster.
......@@ -169,11 +169,13 @@ used to enforce a given output unit.
Queries of nodes will be done in parallel with any running jobs. This might
give inconsistent results for the free disk/memory.
The ``-v`` option activates verbose mode, which changes the display of
special field states (see **ganeti(7)**).
The ``-o`` option takes a comma-separated list of output fields.
The available fields and their meaning are:
name
the node name
......
#!/usr/bin/python
#
# Copyright (C) 2008 Google Inc.
# Copyright (C) 2008, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -396,13 +396,21 @@ class TestFormatQueryResult(unittest.TestCase):
])
self.assertEqual(cli.FormatQueryResult(response, header=True,
separator="|"),
separator="|", verbose=True),
(cli.QR_UNKNOWN, [
"ID|unk|Unavail|NoData|OffLine",
"1|(unknown)|N||(offline)",
"2|(unknown)|(nodata)|x|(offline)",
"3|(unknown)|N|(unavail)|(offline)",
]))
self.assertEqual(cli.FormatQueryResult(response, header=True,
separator="|", verbose=False),
(cli.QR_UNKNOWN, [
"ID|unk|Unavail|NoData|OffLine",
"1|??|N||*",
"2|??|?|x|*",
"3|??|N|-|*",
]))
def testNoData(self):
fields = [
......@@ -452,12 +460,19 @@ class TestFormatQueryResult(unittest.TestCase):
])
self.assertEqual(cli.FormatQueryResult(response, header=False,
separator="|"),
separator="|", verbose=True),
(cli.QR_INCOMPLETE, [
"1|N||(offline)",
"2|(nodata)|x|abc",
"3|N|(unavail)|(offline)",
]))
self.assertEqual(cli.FormatQueryResult(response, header=False,
separator="|", verbose=False),
(cli.QR_INCOMPLETE, [
"1|N||*",
"2|?|x|abc",
"3|N|-|*",
]))
def testInvalidFieldType(self):
fields = [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment