"""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.
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}
@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:
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)
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}
@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:
(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)
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
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
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):
"<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)."
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):
"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)."
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):
"[<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)."
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
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.
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.
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:
LIST
~~~~
-| **list** [--no-headers] [--separator=*SEPARATOR*]
+| **list** [--no-headers] [--separator=*SEPARATOR*] [-v]
| [-o *[+]FIELD,...*] [group...]
Lists all existing node groups in the cluster.
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
^^^^
| **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
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
| **list**
| [--no-headers] [--separator=*SEPARATOR*]
-| [--units=*UNITS*] [-o *[+]FIELD,...*]
+| [--units=*UNITS*] [-v] [-o *[+]FIELD,...*]
| [node...]
Lists the nodes in the cluster.
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
])
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 = [
])
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 = [