4 # Copyright (C) 2010, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Module for query operations
26 - Add field definitions
27 - See how L{NODE_FIELDS} is built
29 - Query field definition (L{objects.QueryFieldDefinition}, use
30 L{_MakeField} for creating), containing:
31 - Name, must be lowercase and match L{FIELD_NAME_RE}
32 - Title for tables, must not contain whitespace and match
34 - Value data type, e.g. L{constants.QFT_NUMBER}
35 - Human-readable description, must not end with punctuation or
37 - Data request type, see e.g. C{NQ_*}
38 - OR-ed flags, see C{QFF_*}
39 - A retrieval function, see L{Query.__init__} for description
40 - Pass list of fields through L{_PrepareFieldList} for preparation and
42 - Instantiate L{Query} with prepared field list definition and selected fields
43 - Call L{Query.RequestedData} to determine what data to collect/compute
44 - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use
46 - Data container must support iteration using C{__iter__}
47 - Items are passed to retrieval functions and can have any format
48 - Call L{Query.GetFields} to get list of definitions for selected fields
50 @attention: Retrieval functions must be idempotent. They can be called multiple
51 times, in any order and any number of times.
59 from ganeti import constants
60 from ganeti import errors
61 from ganeti import utils
62 from ganeti import compat
63 from ganeti import objects
65 from ganeti import runtime
66 from ganeti import qlang
68 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
69 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
70 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
71 RS_UNAVAIL, RS_OFFLINE)
74 # Constants for requesting data from the caller/data provider. Each property
75 # collected/computed separately by the data provider should have its own to
76 # only collect the requested data and not more.
88 IQ_NODES) = range(100, 105)
92 LQ_PENDING) = range(10, 13)
97 GQ_DISKPARAMS) = range(200, 204)
101 CQ_WATCHER_PAUSE) = range(300, 303)
105 QFF_IP_ADDRESS = 0x02
106 # Next values: 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
107 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS)
109 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
110 TITLE_RE = re.compile(r"^[^\s]+$")
111 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
113 #: Verification function for each field type
115 QFT_UNKNOWN: ht.TNone,
116 QFT_TEXT: ht.TString,
120 QFT_TIMESTAMP: ht.TNumber,
121 QFT_OTHER: lambda _: True,
124 # Unique objects for special field statuses
125 _FS_UNKNOWN = object()
126 _FS_NODATA = object()
127 _FS_UNAVAIL = object()
128 _FS_OFFLINE = object()
130 #: List of all special status
131 _FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE])
133 #: VType to QFT mapping
135 # TODO: fix validation of empty strings
136 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
137 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
138 constants.VTYPE_BOOL: QFT_BOOL,
139 constants.VTYPE_SIZE: QFT_UNIT,
140 constants.VTYPE_INT: QFT_NUMBER,
143 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
145 # TODO: Consider moving titles closer to constants
147 constants.ND_OOB_PROGRAM: "OutOfBandProgram",
148 constants.ND_SPINDLE_COUNT: "SpindleCount",
152 def _GetUnknownField(ctx, item): # pylint: disable=W0613
153 """Gets the contents of an unknown field.
159 def _GetQueryFields(fielddefs, selected):
160 """Calculates the internal list of selected fields.
162 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
164 @type fielddefs: dict
165 @param fielddefs: Field definitions
166 @type selected: list of strings
167 @param selected: List of selected fields
172 for name in selected:
174 fdef = fielddefs[name]
176 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
177 None, 0, _GetUnknownField)
179 assert len(fdef) == 4
186 def GetAllFields(fielddefs):
187 """Extract L{objects.QueryFieldDefinition} from field definitions.
189 @rtype: list of L{objects.QueryFieldDefinition}
192 return [fdef for (fdef, _, _, _) in fielddefs]
196 """Class for filter analytics.
198 When filters are used, the user of the L{Query} class usually doesn't know
199 exactly which items will be necessary for building the result. It therefore
200 has to prepare and compute the input data for potentially returning
203 There are two ways to optimize this. The first, and simpler, is to assign
204 each field a group of data, so that the caller can determine which
205 computations are necessary depending on the data groups requested. The list
206 of referenced groups must also be computed for fields referenced in the
209 The second is restricting the items based on a primary key. The primary key
210 is usually a unique name (e.g. a node name). This class extracts all
211 referenced names from a filter. If it encounters any filter condition which
212 disallows such a list to be determined (e.g. a non-equality filter), all
213 names will be requested.
215 The end-effect is that any operation other than L{qlang.OP_OR} and
216 L{qlang.OP_EQUAL} will make the query more expensive.
219 def __init__(self, namefield):
220 """Initializes this class.
222 @type namefield: string
223 @param namefield: Field caller is interested in
226 self._namefield = namefield
228 #: Whether all names need to be requested (e.g. if a non-equality operator
230 self._allnames = False
232 #: Which names to request
235 #: Data kinds referenced by the filter (used by L{Query.RequestedData})
236 self._datakinds = set()
238 def RequestedNames(self):
239 """Returns all requested values.
241 Returns C{None} if list of values can't be determined (e.g. encountered
242 non-equality operators).
247 if self._allnames or self._names is None:
250 return utils.UniqueSequence(self._names)
252 def ReferencedData(self):
253 """Returns all kinds of data referenced by the filter.
256 return frozenset(self._datakinds)
258 def _NeedAllNames(self):
259 """Changes internal state to request all names.
262 self._allnames = True
265 def NoteLogicOp(self, op):
266 """Called when handling a logic operation.
272 if op != qlang.OP_OR:
275 def NoteUnaryOp(self, op): # pylint: disable=W0613
276 """Called when handling an unary operation.
284 def NoteBinaryOp(self, op, datakind, name, value):
285 """Called when handling a binary operation.
290 @param name: Left-hand side of operator (field name)
291 @param value: Right-hand side of operator
294 if datakind is not None:
295 self._datakinds.add(datakind)
300 # If any operator other than equality was used, all names need to be
302 if op == qlang.OP_EQUAL and name == self._namefield:
303 if self._names is None:
305 self._names.append(value)
310 def _WrapLogicOp(op_fn, sentences, ctx, item):
311 """Wrapper for logic operator functions.
314 return op_fn(fn(ctx, item) for fn in sentences)
317 def _WrapUnaryOp(op_fn, inner, ctx, item):
318 """Wrapper for unary operator functions.
321 return op_fn(inner(ctx, item))
324 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
325 """Wrapper for binary operator functions.
328 return op_fn(retrieval_fn(ctx, item), value)
331 def _WrapNot(fn, lhs, rhs):
332 """Negates the result of a wrapped function.
335 return not fn(lhs, rhs)
338 def _PrepareRegex(pattern):
339 """Compiles a regular expression.
343 return re.compile(pattern)
344 except re.error, err:
345 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
348 class _FilterCompilerHelper:
349 """Converts a query filter to a callable usable for filtering.
352 # String statement has no effect, pylint: disable=W0105
354 #: How deep filters can be nested
357 # Unique identifiers for operator groups
360 _OPTYPE_BINARY) = range(1, 4)
362 """Functions for equality checks depending on field flags.
364 List of tuples containing flags and a callable receiving the left- and
365 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
366 (e.g. L{QFF_HOSTNAME}).
368 Order matters. The first item with flags will be used. Flags are checked
374 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
375 case_sensitive=False),
377 (None, operator.eq, None),
382 Operator as key (C{qlang.OP_*}), value a tuple of operator group
383 (C{_OPTYPE_*}) and a group-specific value:
385 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
387 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
388 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
389 right-hand side of the operator, used by L{_HandleBinaryOp}
394 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
395 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
398 qlang.OP_NOT: (_OPTYPE_UNARY, None),
399 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
402 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
404 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
405 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
406 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
407 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
409 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
410 (None, operator.contains, None),
414 def __init__(self, fields):
415 """Initializes this class.
417 @param fields: Field definitions (return value of L{_PrepareFieldList})
420 self._fields = fields
422 self._op_handler = None
424 def __call__(self, hints, qfilter):
425 """Converts a query filter into a callable function.
427 @type hints: L{_FilterHints} or None
428 @param hints: Callbacks doing analysis on filter
430 @param qfilter: Filter structure
432 @return: Function receiving context and item as parameters, returning
433 boolean as to whether item matches filter
438 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
440 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
442 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
446 filter_fn = self._Compile(qfilter, 0)
448 self._op_handler = None
452 def _Compile(self, qfilter, level):
453 """Inner function for converting filters.
455 Calls the correct handler functions for the top-level operator. This
456 function is called recursively (e.g. for logic operators).
459 if not (isinstance(qfilter, (list, tuple)) and qfilter):
460 raise errors.ParameterError("Invalid filter on level %s" % level)
463 if level >= self._LEVELS_MAX:
464 raise errors.ParameterError("Only up to %s levels are allowed (filter"
465 " nested too deep)" % self._LEVELS_MAX)
467 # Create copy to be modified
468 operands = qfilter[:]
472 (kind, op_data) = self._OPS[op]
474 raise errors.ParameterError("Unknown operator '%s'" % op)
476 (handler, hints_cb) = self._op_handler[kind]
478 return handler(hints_cb, level, op, op_data, operands)
480 def _LookupField(self, name):
481 """Returns a field definition by name.
485 return self._fields[name]
487 raise errors.ParameterError("Unknown field '%s'" % name)
489 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
490 """Handles logic operators.
492 @type hints_fn: callable
493 @param hints_fn: Callback doing some analysis on the filter
495 @param level: Current depth
498 @type op_fn: callable
499 @param op_fn: Function implementing operator
501 @param operands: List of operands
507 return compat.partial(_WrapLogicOp, op_fn,
508 [self._Compile(op, level + 1) for op in operands])
510 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
511 """Handles unary operators.
513 @type hints_fn: callable
514 @param hints_fn: Callback doing some analysis on the filter
516 @param level: Current depth
519 @type op_fn: callable
520 @param op_fn: Function implementing operator
522 @param operands: List of operands
530 if len(operands) != 1:
531 raise errors.ParameterError("Unary operator '%s' expects exactly one"
534 if op == qlang.OP_TRUE:
535 (_, _, _, retrieval_fn) = self._LookupField(operands[0])
537 op_fn = operator.truth
539 elif op == qlang.OP_NOT:
540 op_fn = operator.not_
541 arg = self._Compile(operands[0], level + 1)
543 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
545 return compat.partial(_WrapUnaryOp, op_fn, arg)
547 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
548 """Handles binary operators.
550 @type hints_fn: callable
551 @param hints_fn: Callback doing some analysis on the filter
553 @param level: Current depth
556 @param op_data: Functions implementing operators
558 @param operands: List of operands
561 # Unused arguments, pylint: disable=W0613
563 (name, value) = operands
564 except (ValueError, TypeError):
565 raise errors.ParameterError("Invalid binary operator, expected exactly"
568 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
570 assert fdef.kind != QFT_UNKNOWN
572 # TODO: Type conversions?
574 verify_fn = _VERIFY_FN[fdef.kind]
575 if not verify_fn(value):
576 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
577 " with '%s', expected %s" %
578 (name, fdef.kind, value.__class__.__name__,
582 hints_fn(op, datakind, name, value)
584 for (fn_flags, fn, valprepfn) in op_data:
585 if fn_flags is None or fn_flags & field_flags:
586 # Prepare value if necessary (e.g. compile regular expression)
588 value = valprepfn(value)
590 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
592 raise errors.ProgrammerError("Unable to find operator implementation"
593 " (op '%s', flags %s)" % (op, field_flags))
596 def _CompileFilter(fields, hints, qfilter):
597 """Converts a query filter into a callable function.
599 See L{_FilterCompilerHelper} for details.
604 return _FilterCompilerHelper(fields)(hints, qfilter)
608 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
609 """Initializes this class.
611 The field definition is a dictionary with the field's name as a key and a
612 tuple containing, in order, the field definition object
613 (L{objects.QueryFieldDefinition}, the data kind to help calling code
614 collect data and a retrieval function. The retrieval function is called
615 with two parameters, in order, the data container and the item in container
616 (see L{Query.Query}).
618 Users of this class can call L{RequestedData} before preparing the data
619 container to determine what data is needed.
621 @type fieldlist: dictionary
622 @param fieldlist: Field definitions
623 @type selected: list of strings
624 @param selected: List of selected fields
627 assert namefield is None or namefield in fieldlist
629 self._fields = _GetQueryFields(fieldlist, selected)
631 self._filter_fn = None
632 self._requested_names = None
633 self._filter_datakinds = frozenset()
635 if qfilter is not None:
636 # Collect requested names if wanted
638 hints = _FilterHints(namefield)
642 # Build filter function
643 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
645 self._requested_names = hints.RequestedNames()
646 self._filter_datakinds = hints.ReferencedData()
648 if namefield is None:
651 (_, _, _, self._name_fn) = fieldlist[namefield]
653 def RequestedNames(self):
654 """Returns all names referenced in the filter.
656 If there is no filter or operators are preventing determining the exact
657 names, C{None} is returned.
660 return self._requested_names
662 def RequestedData(self):
663 """Gets requested kinds of data.
668 return (self._filter_datakinds |
669 frozenset(datakind for (_, datakind, _, _) in self._fields
670 if datakind is not None))
673 """Returns the list of fields for this query.
675 Includes unknown fields.
677 @rtype: List of L{objects.QueryFieldDefinition}
680 return GetAllFields(self._fields)
682 def Query(self, ctx, sort_by_name=True):
685 @param ctx: Data container passed to field retrieval functions, must
686 support iteration using C{__iter__}
687 @type sort_by_name: boolean
688 @param sort_by_name: Whether to sort by name or keep the input data's
692 sort = (self._name_fn and sort_by_name)
696 for idx, item in enumerate(ctx):
697 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
700 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
704 _VerifyResultRow(self._fields, row)
707 (status, name) = _ProcessResult(self._name_fn(ctx, item))
708 assert status == constants.RS_NORMAL
709 # TODO: Are there cases where we wouldn't want to use NiceSort?
710 result.append((utils.NiceSortKey(name), idx, row))
717 # TODO: Would "heapq" be more efficient than sorting?
719 # Sorting in-place instead of using "sorted()"
722 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
724 return map(operator.itemgetter(2), result)
726 def OldStyleQuery(self, ctx, sort_by_name=True):
727 """Query with "old" query result format.
729 See L{Query.Query} for arguments.
732 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
733 if fdef.kind == QFT_UNKNOWN)
735 raise errors.OpPrereqError("Unknown output fields selected: %s" %
736 (utils.CommaJoin(unknown), ),
739 return [[value for (_, value) in row]
740 for row in self.Query(ctx, sort_by_name=sort_by_name)]
743 def _ProcessResult(value):
744 """Converts result values into externally-visible ones.
747 if value is _FS_UNKNOWN:
748 return (RS_UNKNOWN, None)
749 elif value is _FS_NODATA:
750 return (RS_NODATA, None)
751 elif value is _FS_UNAVAIL:
752 return (RS_UNAVAIL, None)
753 elif value is _FS_OFFLINE:
754 return (RS_OFFLINE, None)
756 return (RS_NORMAL, value)
759 def _VerifyResultRow(fields, row):
760 """Verifies the contents of a query result row.
763 @param fields: Field definitions for result
764 @type row: list of tuples
768 assert len(row) == len(fields)
770 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
771 if status == RS_NORMAL:
772 if not _VERIFY_FN[fdef.kind](value):
773 errs.append("normal field %s fails validation (value is %s)" %
775 elif value is not None:
776 errs.append("abnormal field %s has a non-None value" % fdef.name)
777 assert not errs, ("Failed validation: %s in row %s" %
778 (utils.CommaJoin(errs), row))
781 def _FieldDictKey((fdef, _, flags, fn)):
782 """Generates key for field dictionary.
785 assert fdef.name and fdef.title, "Name and title are required"
786 assert FIELD_NAME_RE.match(fdef.name)
787 assert TITLE_RE.match(fdef.title)
788 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
789 fdef.doc.strip() == fdef.doc), \
790 "Invalid description for field '%s'" % fdef.name
792 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
797 def _PrepareFieldList(fields, aliases):
798 """Prepares field list for use by L{Query}.
800 Converts the list to a dictionary and does some verification.
802 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
803 kind, retrieval function)
804 @param fields: List of fields, see L{Query.__init__} for a better
806 @type aliases: list of tuples; (alias, target)
807 @param aliases: list of tuples containing aliases; for each
808 alias/target pair, a duplicate will be created in the field list
810 @return: Field dictionary for L{Query}
814 duplicates = utils.FindDuplicates(fdef.title.lower()
815 for (fdef, _, _, _) in fields)
816 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
818 result = utils.SequenceToDict(fields, key=_FieldDictKey)
820 for alias, target in aliases:
821 assert alias not in result, "Alias %s overrides an existing field" % alias
822 assert target in result, "Missing target %s for alias %s" % (target, alias)
823 (fdef, k, flags, fn) = result[target]
826 result[alias] = (fdef, k, flags, fn)
828 assert len(result) == len(fields) + len(aliases)
829 assert compat.all(name == fdef.name
830 for (name, (fdef, _, _, _)) in result.items())
835 def GetQueryResponse(query, ctx, sort_by_name=True):
836 """Prepares the response for a query.
838 @type query: L{Query}
839 @param ctx: Data container, see L{Query.Query}
840 @type sort_by_name: boolean
841 @param sort_by_name: Whether to sort by name or keep the input data's
845 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
846 fields=query.GetFields()).ToDict()
849 def QueryFields(fielddefs, selected):
850 """Returns list of available fields.
852 @type fielddefs: dict
853 @param fielddefs: Field definitions
854 @type selected: list of strings
855 @param selected: List of selected fields
856 @return: List of L{objects.QueryFieldDefinition}
860 # Client requests all fields, sort by name
861 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
862 key=operator.attrgetter("name"))
864 # Keep order as requested by client
865 fdefs = Query(fielddefs, selected).GetFields()
867 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
870 def _MakeField(name, title, kind, doc):
871 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
873 @param name: Field name as a regular expression
874 @param title: Human-readable title
875 @param kind: Field type
876 @param doc: Human-readable description
879 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
883 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
884 """Returns a static value.
890 def _StaticValue(value):
891 """Prepares a function to return a static value.
894 return compat.partial(_StaticValueInner, value)
897 def _GetNodeRole(node, master_name):
898 """Determine node role.
900 @type node: L{objects.Node}
901 @param node: Node object
902 @type master_name: string
903 @param master_name: Master node name
906 if node.name == master_name:
907 return constants.NR_MASTER
908 elif node.master_candidate:
909 return constants.NR_MCANDIDATE
911 return constants.NR_DRAINED
913 return constants.NR_OFFLINE
915 return constants.NR_REGULAR
918 def _GetItemAttr(attr):
919 """Returns a field function to return an attribute of the item.
921 @param attr: Attribute name
924 getter = operator.attrgetter(attr)
925 return lambda _, item: getter(item)
928 def _GetNDParam(name):
929 """Return a field function to return an ND parameter out of the context.
933 if ctx.ndparams is None:
936 return ctx.ndparams.get(name, None)
940 def _BuildNDFields(is_group):
941 """Builds all the ndparam fields.
943 @param is_group: whether this is called at group or node level
947 field_kind = GQ_CONFIG
949 field_kind = NQ_GROUP
950 return [(_MakeField("ndp/%s" % name, NDP_TITLE.get(name, "ndp/%s" % name),
951 _VTToQFT[kind], "The \"%s\" node parameter" % name),
952 field_kind, 0, _GetNDParam(name))
953 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
956 def _ConvWrapInner(convert, fn, ctx, item):
957 """Wrapper for converting values.
959 @param convert: Conversion function receiving value as single parameter
960 @param fn: Retrieval function
963 value = fn(ctx, item)
965 # Is the value an abnormal status?
966 if compat.any(value is fs for fs in _FS_ALL):
970 # TODO: Should conversion function also receive context, item or both?
971 return convert(value)
974 def _ConvWrap(convert, fn):
975 """Convenience wrapper for L{_ConvWrapInner}.
977 @param convert: Conversion function receiving value as single parameter
978 @param fn: Retrieval function
981 return compat.partial(_ConvWrapInner, convert, fn)
984 def _GetItemTimestamp(getter):
985 """Returns function for getting timestamp of item.
987 @type getter: callable
988 @param getter: Function to retrieve timestamp attribute
992 """Returns a timestamp of item.
995 timestamp = getter(item)
996 if timestamp is None:
997 # Old configs might not have all timestamps
1005 def _GetItemTimestampFields(datatype):
1006 """Returns common timestamp fields.
1008 @param datatype: Field data type for use by L{Query.RequestedData}
1012 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1013 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1014 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1015 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1019 class NodeQueryData:
1020 """Data container for node data queries.
1023 def __init__(self, nodes, live_data, master_name, node_to_primary,
1024 node_to_secondary, groups, oob_support, cluster):
1025 """Initializes this class.
1029 self.live_data = live_data
1030 self.master_name = master_name
1031 self.node_to_primary = node_to_primary
1032 self.node_to_secondary = node_to_secondary
1033 self.groups = groups
1034 self.oob_support = oob_support
1035 self.cluster = cluster
1037 # Used for individual rows
1038 self.curlive_data = None
1039 self.ndparams = None
1042 """Iterate over all nodes.
1044 This function has side-effects and only one instance of the resulting
1045 generator should be used at a time.
1048 for node in self.nodes:
1049 group = self.groups.get(node.group, None)
1051 self.ndparams = None
1053 self.ndparams = self.cluster.FillND(node, group)
1055 self.curlive_data = self.live_data.get(node.name, None)
1057 self.curlive_data = None
1061 #: Fields that are direct attributes of an L{objects.Node} object
1062 _NODE_SIMPLE_FIELDS = {
1063 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1064 "master_candidate": ("MasterC", QFT_BOOL, 0,
1065 "Whether node is a master candidate"),
1066 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1067 "Whether node can become a master candidate"),
1068 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1069 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1070 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1071 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1072 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1076 #: Fields requiring talking to the node
1077 # Note that none of these are available for non-vm_capable nodes
1078 _NODE_LIVE_FIELDS = {
1079 "bootid": ("BootID", QFT_TEXT, "bootid",
1080 "Random UUID renewed for each system reboot, can be used"
1081 " for detecting reboots by tracking changes"),
1082 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1083 "Number of NUMA domains on node (if exported by hypervisor)"),
1084 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1085 "Number of physical CPU sockets (if exported by hypervisor)"),
1086 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1087 "dfree": ("DFree", QFT_UNIT, "vg_free",
1088 "Available disk space in volume group"),
1089 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1090 "Total disk space in volume group used for instance disk"
1092 "mfree": ("MFree", QFT_UNIT, "memory_free",
1093 "Memory available for instance allocations"),
1094 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1095 "Amount of memory used by node (dom0 for Xen)"),
1096 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1097 "Total amount of memory of physical machine"),
1102 """Build function for calling another function with an node group.
1104 @param cb: The callback to be called with the nodegroup
1108 """Get group data for a node.
1110 @type ctx: L{NodeQueryData}
1111 @type inst: L{objects.Node}
1112 @param inst: Node object
1115 ng = ctx.groups.get(node.group, None)
1117 # Nodes always have a group, or the configuration is corrupt
1120 return cb(ctx, node, ng)
1125 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1126 """Returns the name of a node's group.
1128 @type ctx: L{NodeQueryData}
1129 @type node: L{objects.Node}
1130 @param node: Node object
1131 @type ng: L{objects.NodeGroup}
1132 @param ng: The node group this node belongs to
1138 def _GetNodePower(ctx, node):
1139 """Returns the node powered state
1141 @type ctx: L{NodeQueryData}
1142 @type node: L{objects.Node}
1143 @param node: Node object
1146 if ctx.oob_support[node.name]:
1152 def _GetNdParams(ctx, node, ng):
1153 """Returns the ndparams for this node.
1155 @type ctx: L{NodeQueryData}
1156 @type node: L{objects.Node}
1157 @param node: Node object
1158 @type ng: L{objects.NodeGroup}
1159 @param ng: The node group this node belongs to
1162 return ctx.cluster.SimpleFillND(ng.FillND(node))
1165 def _GetLiveNodeField(field, kind, ctx, node):
1166 """Gets the value of a "live" field from L{NodeQueryData}.
1168 @param field: Live field name
1169 @param kind: Data kind, one of L{constants.QFT_ALL}
1170 @type ctx: L{NodeQueryData}
1171 @type node: L{objects.Node}
1172 @param node: Node object
1178 if not node.vm_capable:
1181 if not ctx.curlive_data:
1185 value = ctx.curlive_data[field]
1189 if kind == QFT_TEXT:
1192 assert kind in (QFT_NUMBER, QFT_UNIT)
1194 # Try to convert into number
1197 except (ValueError, TypeError):
1198 logging.exception("Failed to convert node field '%s' (value %r) to int",
1203 def _GetNodeHvState(_, node):
1204 """Converts node's hypervisor state for query result.
1207 hv_state = node.hv_state
1209 if hv_state is None:
1212 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1215 def _GetNodeDiskState(_, node):
1216 """Converts node's disk state for query result.
1219 disk_state = node.disk_state
1221 if disk_state is None:
1224 return dict((disk_kind, dict((name, value.ToDict())
1225 for (name, value) in kind_state.items()))
1226 for (disk_kind, kind_state) in disk_state.items())
1229 def _BuildNodeFields():
1230 """Builds list of fields for node queries.
1234 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1235 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1236 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1237 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1238 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1239 lambda ctx, node: list(node.GetTags())),
1240 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1241 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1242 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1243 _GetGroup(_GetNodeGroup)),
1244 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1245 NQ_CONFIG, 0, _GetItemAttr("group")),
1246 (_MakeField("powered", "Powered", QFT_BOOL,
1247 "Whether node is thought to be powered on"),
1248 NQ_OOB, 0, _GetNodePower),
1249 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1250 "Merged node parameters"),
1251 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1252 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1253 "Custom node parameters"),
1254 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1255 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1256 NQ_CONFIG, 0, _GetNodeHvState),
1257 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1258 NQ_CONFIG, 0, _GetNodeDiskState),
1261 fields.extend(_BuildNDFields(False))
1264 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1265 constants.NR_REGULAR, constants.NR_DRAINED,
1266 constants.NR_OFFLINE)
1267 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1268 " \"%s\" for regular, \"%s\" for a drained, \"%s\" for offline" %
1270 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1271 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1272 assert set(role_values) == constants.NR_ALL
1274 def _GetLength(getter):
1275 return lambda ctx, node: len(getter(ctx)[node.name])
1277 def _GetList(getter):
1278 return lambda ctx, node: list(getter(ctx)[node.name])
1280 # Add fields operating on instance lists
1281 for prefix, titleprefix, docword, getter in \
1282 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1283 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1284 # TODO: Allow filterting by hostname in list
1286 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1287 "Number of instances with this node as %s" % docword),
1288 NQ_INST, 0, _GetLength(getter)),
1289 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1291 "List of instances with this node as %s" % docword),
1292 NQ_INST, 0, _GetList(getter)),
1297 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1298 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1301 # Add fields requiring live data
1303 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1304 compat.partial(_GetLiveNodeField, nfield, kind))
1305 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1309 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1311 return _PrepareFieldList(fields, [])
1314 class InstanceQueryData:
1315 """Data container for instance data queries.
1318 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1319 live_data, wrongnode_inst, console, nodes, groups):
1320 """Initializes this class.
1322 @param instances: List of instance objects
1323 @param cluster: Cluster object
1324 @type disk_usage: dict; instance name as key
1325 @param disk_usage: Per-instance disk usage
1326 @type offline_nodes: list of strings
1327 @param offline_nodes: List of offline nodes
1328 @type bad_nodes: list of strings
1329 @param bad_nodes: List of faulty nodes
1330 @type live_data: dict; instance name as key
1331 @param live_data: Per-instance live data
1332 @type wrongnode_inst: set
1333 @param wrongnode_inst: Set of instances running on wrong node(s)
1334 @type console: dict; instance name as key
1335 @param console: Per-instance console information
1336 @type nodes: dict; node name as key
1337 @param nodes: Node objects
1340 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1341 "Offline nodes not included in bad nodes"
1342 assert not (set(live_data.keys()) & set(bad_nodes)), \
1343 "Found live data for bad or offline nodes"
1345 self.instances = instances
1346 self.cluster = cluster
1347 self.disk_usage = disk_usage
1348 self.offline_nodes = offline_nodes
1349 self.bad_nodes = bad_nodes
1350 self.live_data = live_data
1351 self.wrongnode_inst = wrongnode_inst
1352 self.console = console
1354 self.groups = groups
1356 # Used for individual rows
1357 self.inst_hvparams = None
1358 self.inst_beparams = None
1359 self.inst_osparams = None
1360 self.inst_nicparams = None
1363 """Iterate over all instances.
1365 This function has side-effects and only one instance of the resulting
1366 generator should be used at a time.
1369 for inst in self.instances:
1370 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1371 self.inst_beparams = self.cluster.FillBE(inst)
1372 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1373 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1374 for nic in inst.nics]
1379 def _GetInstOperState(ctx, inst):
1380 """Get instance's operational status.
1382 @type ctx: L{InstanceQueryData}
1383 @type inst: L{objects.Instance}
1384 @param inst: Instance object
1387 # Can't use RS_OFFLINE here as it would describe the instance to
1388 # be offline when we actually don't know due to missing data
1389 if inst.primary_node in ctx.bad_nodes:
1392 return bool(ctx.live_data.get(inst.name))
1395 def _GetInstLiveData(name):
1396 """Build function for retrieving live data.
1399 @param name: Live data field name
1403 """Get live data for an instance.
1405 @type ctx: L{InstanceQueryData}
1406 @type inst: L{objects.Instance}
1407 @param inst: Instance object
1410 if (inst.primary_node in ctx.bad_nodes or
1411 inst.primary_node in ctx.offline_nodes):
1412 # Can't use RS_OFFLINE here as it would describe the instance to be
1413 # offline when we actually don't know due to missing data
1416 if inst.name in ctx.live_data:
1417 data = ctx.live_data[inst.name]
1426 def _GetInstStatus(ctx, inst):
1427 """Get instance status.
1429 @type ctx: L{InstanceQueryData}
1430 @type inst: L{objects.Instance}
1431 @param inst: Instance object
1434 if inst.primary_node in ctx.offline_nodes:
1435 return constants.INSTST_NODEOFFLINE
1437 if inst.primary_node in ctx.bad_nodes:
1438 return constants.INSTST_NODEDOWN
1440 if bool(ctx.live_data.get(inst.name)):
1441 if inst.name in ctx.wrongnode_inst:
1442 return constants.INSTST_WRONGNODE
1443 elif inst.admin_state == constants.ADMINST_UP:
1444 return constants.INSTST_RUNNING
1446 return constants.INSTST_ERRORUP
1448 if inst.admin_state == constants.ADMINST_UP:
1449 return constants.INSTST_ERRORDOWN
1450 elif inst.admin_state == constants.ADMINST_DOWN:
1451 return constants.INSTST_ADMINDOWN
1453 return constants.INSTST_ADMINOFFLINE
1456 def _GetInstDiskSize(index):
1457 """Build function for retrieving disk size.
1460 @param index: Disk index
1464 """Get size of a disk.
1466 @type inst: L{objects.Instance}
1467 @param inst: Instance object
1471 return inst.disks[index].size
1478 def _GetInstNic(index, cb):
1479 """Build function for calling another function with an instance NIC.
1482 @param index: NIC index
1488 """Call helper function with instance NIC.
1490 @type ctx: L{InstanceQueryData}
1491 @type inst: L{objects.Instance}
1492 @param inst: Instance object
1496 nic = inst.nics[index]
1500 return cb(ctx, index, nic)
1505 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1506 """Get a NIC's IP address.
1508 @type ctx: L{InstanceQueryData}
1509 @type nic: L{objects.NIC}
1510 @param nic: NIC object
1519 def _GetInstNicBridge(ctx, index, _):
1520 """Get a NIC's bridge.
1522 @type ctx: L{InstanceQueryData}
1524 @param index: NIC index
1527 assert len(ctx.inst_nicparams) >= index
1529 nicparams = ctx.inst_nicparams[index]
1531 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1532 return nicparams[constants.NIC_LINK]
1537 def _GetInstAllNicBridges(ctx, inst):
1538 """Get all network bridges for an instance.
1540 @type ctx: L{InstanceQueryData}
1541 @type inst: L{objects.Instance}
1542 @param inst: Instance object
1545 assert len(ctx.inst_nicparams) == len(inst.nics)
1549 for nicp in ctx.inst_nicparams:
1550 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1551 result.append(nicp[constants.NIC_LINK])
1555 assert len(result) == len(inst.nics)
1560 def _GetInstNicParam(name):
1561 """Build function for retrieving a NIC parameter.
1564 @param name: Parameter name
1567 def fn(ctx, index, _):
1568 """Get a NIC's bridge.
1570 @type ctx: L{InstanceQueryData}
1571 @type inst: L{objects.Instance}
1572 @param inst: Instance object
1573 @type nic: L{objects.NIC}
1574 @param nic: NIC object
1577 assert len(ctx.inst_nicparams) >= index
1578 return ctx.inst_nicparams[index][name]
1583 def _GetInstanceNetworkFields():
1584 """Get instance fields involving network interfaces.
1586 @return: Tuple containing list of field definitions used as input for
1587 L{_PrepareFieldList} and a list of aliases
1590 nic_mac_fn = lambda ctx, _, nic: nic.mac
1591 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1592 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1596 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1597 "Number of network interfaces"),
1598 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1599 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1600 "List containing each network interface's MAC address"),
1601 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1602 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1603 "List containing each network interface's IP address"),
1604 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1605 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1606 "List containing each network interface's mode"), IQ_CONFIG, 0,
1607 lambda ctx, inst: [nicp[constants.NIC_MODE]
1608 for nicp in ctx.inst_nicparams]),
1609 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1610 "List containing each network interface's link"), IQ_CONFIG, 0,
1611 lambda ctx, inst: [nicp[constants.NIC_LINK]
1612 for nicp in ctx.inst_nicparams]),
1613 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1614 "List containing each network interface's bridge"),
1615 IQ_CONFIG, 0, _GetInstAllNicBridges),
1619 for i in range(constants.MAX_NICS):
1620 numtext = utils.FormatOrdinal(i + 1)
1622 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1623 "IP address of %s network interface" % numtext),
1624 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1625 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1626 "MAC address of %s network interface" % numtext),
1627 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1628 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1629 "Mode of %s network interface" % numtext),
1630 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1631 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1632 "Link of %s network interface" % numtext),
1633 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1634 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1635 "Bridge of %s network interface" % numtext),
1636 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1640 # Legacy fields for first NIC
1642 ("mac", "nic.mac/0"),
1643 ("bridge", "nic.bridge/0"),
1644 ("nic_mode", "nic.mode/0"),
1645 ("nic_link", "nic.link/0"),
1648 return (fields, aliases)
1651 def _GetInstDiskUsage(ctx, inst):
1652 """Get disk usage for an instance.
1654 @type ctx: L{InstanceQueryData}
1655 @type inst: L{objects.Instance}
1656 @param inst: Instance object
1659 usage = ctx.disk_usage[inst.name]
1667 def _GetInstanceConsole(ctx, inst):
1668 """Get console information for instance.
1670 @type ctx: L{InstanceQueryData}
1671 @type inst: L{objects.Instance}
1672 @param inst: Instance object
1675 consinfo = ctx.console[inst.name]
1677 if consinfo is None:
1683 def _GetInstanceDiskFields():
1684 """Get instance fields involving disks.
1686 @return: List of field definitions used as input for L{_PrepareFieldList}
1690 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1691 "Total disk space used by instance on each of its nodes;"
1692 " this is not the disk size visible to the instance, but"
1693 " the usage on the node"),
1694 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1695 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1696 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1697 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1698 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1703 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1704 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1705 IQ_CONFIG, 0, _GetInstDiskSize(i))
1706 for i in range(constants.MAX_DISKS)
1712 def _GetInstanceParameterFields():
1713 """Get instance fields involving parameters.
1715 @return: List of field definitions used as input for L{_PrepareFieldList}
1718 # TODO: Consider moving titles closer to constants
1720 constants.BE_AUTO_BALANCE: "Auto_balance",
1721 constants.BE_MAXMEM: "ConfigMaxMem",
1722 constants.BE_MINMEM: "ConfigMinMem",
1723 constants.BE_VCPUS: "ConfigVCPUs",
1727 constants.HV_ACPI: "ACPI",
1728 constants.HV_BOOT_ORDER: "Boot_order",
1729 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1730 constants.HV_DISK_TYPE: "Disk_type",
1731 constants.HV_INITRD_PATH: "Initrd_path",
1732 constants.HV_KERNEL_PATH: "Kernel_path",
1733 constants.HV_NIC_TYPE: "NIC_type",
1734 constants.HV_PAE: "PAE",
1735 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1740 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1741 "Hypervisor parameters (merged)"),
1742 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1743 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1744 "Backend parameters (merged)"),
1745 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1746 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1747 "Operating system parameters (merged)"),
1748 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1750 # Unfilled parameters
1751 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1752 "Custom hypervisor parameters"),
1753 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1754 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1755 "Custom backend parameters",),
1756 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1757 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1758 "Custom operating system parameters",),
1759 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1760 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1761 "Custom network interface parameters"),
1762 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1766 def _GetInstHvParam(name):
1767 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1770 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1771 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1772 IQ_CONFIG, 0, _GetInstHvParam(name))
1773 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1774 if name not in constants.HVC_GLOBALS
1778 def _GetInstBeParam(name):
1779 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1782 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1783 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1784 IQ_CONFIG, 0, _GetInstBeParam(name))
1785 for name, kind in constants.BES_PARAMETER_TYPES.items()
1791 _INST_SIMPLE_FIELDS = {
1792 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1793 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1794 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1795 # Depending on the hypervisor, the port can be None
1796 "network_port": ("Network_port", QFT_OTHER, 0,
1797 "Instance network port if available (e.g. for VNC console)"),
1798 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1799 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1800 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1804 def _GetInstNodeGroup(ctx, default, node_name):
1805 """Gets group UUID of an instance node.
1807 @type ctx: L{InstanceQueryData}
1808 @param default: Default value
1809 @type node_name: string
1810 @param node_name: Node name
1814 node = ctx.nodes[node_name]
1821 def _GetInstNodeGroupName(ctx, default, node_name):
1822 """Gets group name of an instance node.
1824 @type ctx: L{InstanceQueryData}
1825 @param default: Default value
1826 @type node_name: string
1827 @param node_name: Node name
1831 node = ctx.nodes[node_name]
1836 group = ctx.groups[node.group]
1843 def _BuildInstanceFields():
1844 """Builds list of fields for instance queries.
1848 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1849 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1850 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1851 "Primary node's group"),
1853 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1854 inst.primary_node)),
1855 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1856 "Primary node's group UUID"),
1858 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1859 # TODO: Allow filtering by secondary node as hostname
1860 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1861 "Secondary nodes; usually this will just be one node"),
1862 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1863 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1864 "Node groups of secondary nodes"),
1866 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1867 inst.secondary_nodes)),
1868 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1869 "Node group UUIDs of secondary nodes"),
1871 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1872 inst.secondary_nodes)),
1873 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1874 "Desired state of instance"),
1875 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1876 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1877 "Desired state of instance"),
1878 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1879 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1880 lambda ctx, inst: list(inst.GetTags())),
1881 (_MakeField("console", "Console", QFT_OTHER,
1882 "Instance console information"), IQ_CONSOLE, 0,
1883 _GetInstanceConsole),
1888 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1889 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1892 # Fields requiring talking to the node
1894 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1895 IQ_LIVE, 0, _GetInstOperState),
1896 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1897 "Actual memory usage as seen by hypervisor"),
1898 IQ_LIVE, 0, _GetInstLiveData("memory")),
1899 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1900 "Actual number of VCPUs as seen by hypervisor"),
1901 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1905 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1906 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1907 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1908 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1909 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1910 " and actually is, \"%s\" if instance is stopped and"
1911 " is not running, \"%s\" if instance running, but not on its"
1912 " designated primary node, \"%s\" if instance should be"
1913 " stopped, but is actually running, \"%s\" if instance should"
1914 " run, but doesn't, \"%s\" if instance's primary node is down,"
1915 " \"%s\" if instance's primary node is marked offline,"
1916 " \"%s\" if instance is offline and does not use dynamic"
1917 " resources" % status_values)
1918 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1919 IQ_LIVE, 0, _GetInstStatus))
1920 assert set(status_values) == constants.INSTST_ALL, \
1921 "Status documentation mismatch"
1923 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1925 fields.extend(network_fields)
1926 fields.extend(_GetInstanceParameterFields())
1927 fields.extend(_GetInstanceDiskFields())
1928 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1931 ("vcpus", "be/vcpus"),
1932 ("be/memory", "be/maxmem"),
1933 ("sda_size", "disk.size/0"),
1934 ("sdb_size", "disk.size/1"),
1937 return _PrepareFieldList(fields, aliases)
1940 class LockQueryData:
1941 """Data container for lock data queries.
1944 def __init__(self, lockdata):
1945 """Initializes this class.
1948 self.lockdata = lockdata
1951 """Iterate over all locks.
1954 return iter(self.lockdata)
1957 def _GetLockOwners(_, data):
1958 """Returns a sorted list of a lock's current owners.
1961 (_, _, owners, _) = data
1964 owners = utils.NiceSort(owners)
1969 def _GetLockPending(_, data):
1970 """Returns a sorted list of a lock's pending acquires.
1973 (_, _, _, pending) = data
1976 pending = [(mode, utils.NiceSort(names))
1977 for (mode, names) in pending]
1982 def _BuildLockFields():
1983 """Builds list of fields for lock queries.
1986 return _PrepareFieldList([
1987 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
1988 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
1989 lambda ctx, (name, mode, owners, pending): name),
1990 (_MakeField("mode", "Mode", QFT_OTHER,
1991 "Mode in which the lock is currently acquired"
1992 " (exclusive or shared)"),
1993 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
1994 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
1995 LQ_OWNER, 0, _GetLockOwners),
1996 (_MakeField("pending", "Pending", QFT_OTHER,
1997 "Threads waiting for the lock"),
1998 LQ_PENDING, 0, _GetLockPending),
2002 class GroupQueryData:
2003 """Data container for node group data queries.
2006 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2008 """Initializes this class.
2010 @param cluster: Cluster object
2011 @param groups: List of node group objects
2012 @type group_to_nodes: dict; group UUID as key
2013 @param group_to_nodes: Per-group list of nodes
2014 @type group_to_instances: dict; group UUID as key
2015 @param group_to_instances: Per-group list of (primary) instances
2016 @type want_diskparams: bool
2017 @param want_diskparams: Whether diskparamters should be calculated
2020 self.groups = groups
2021 self.group_to_nodes = group_to_nodes
2022 self.group_to_instances = group_to_instances
2023 self.cluster = cluster
2024 self.want_diskparams = want_diskparams
2026 # Used for individual rows
2027 self.group_ipolicy = None
2028 self.ndparams = None
2029 self.group_dp = None
2032 """Iterate over all node groups.
2034 This function has side-effects and only one instance of the resulting
2035 generator should be used at a time.
2038 for group in self.groups:
2039 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2040 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2041 if self.want_diskparams:
2042 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2044 self.group_dp = None
2048 _GROUP_SIMPLE_FIELDS = {
2049 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2050 "name": ("Group", QFT_TEXT, "Group name"),
2051 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2052 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2056 def _BuildGroupFields():
2057 """Builds list of fields for node group queries.
2061 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2063 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2065 def _GetLength(getter):
2066 return lambda ctx, group: len(getter(ctx)[group.uuid])
2068 def _GetSortedList(getter):
2069 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2071 group_to_nodes = operator.attrgetter("group_to_nodes")
2072 group_to_instances = operator.attrgetter("group_to_instances")
2074 # Add fields for nodes
2076 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2077 GQ_NODE, 0, _GetLength(group_to_nodes)),
2078 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2079 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2082 # Add fields for instances
2084 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2085 "Number of primary instances"),
2086 GQ_INST, 0, _GetLength(group_to_instances)),
2087 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2088 "List of primary instances"),
2089 GQ_INST, 0, _GetSortedList(group_to_instances)),
2094 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2095 lambda ctx, group: list(group.GetTags())),
2096 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2097 "Instance policy limitations (merged)"),
2098 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2099 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2100 "Custom instance policy limitations"),
2101 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2102 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2103 "Custom node parameters"),
2104 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2105 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2107 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2108 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2109 "Disk parameters (merged)"),
2110 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2111 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2112 "Custom disk parameters"),
2113 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2117 fields.extend(_BuildNDFields(True))
2119 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2121 return _PrepareFieldList(fields, [])
2124 class OsInfo(objects.ConfigObject):
2137 def _BuildOsFields():
2138 """Builds list of fields for operating system queries.
2142 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2143 None, 0, _GetItemAttr("name")),
2144 (_MakeField("valid", "Valid", QFT_BOOL,
2145 "Whether operating system definition is valid"),
2146 None, 0, _GetItemAttr("valid")),
2147 (_MakeField("hidden", "Hidden", QFT_BOOL,
2148 "Whether operating system is hidden"),
2149 None, 0, _GetItemAttr("hidden")),
2150 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2151 "Whether operating system is blacklisted"),
2152 None, 0, _GetItemAttr("blacklisted")),
2153 (_MakeField("variants", "Variants", QFT_OTHER,
2154 "Operating system variants"),
2155 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2156 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2157 "Operating system API versions"),
2158 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2159 (_MakeField("parameters", "Parameters", QFT_OTHER,
2160 "Operating system parameters"),
2161 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2162 _GetItemAttr("parameters"))),
2163 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2164 "Status from node"),
2165 None, 0, _GetItemAttr("node_status")),
2168 return _PrepareFieldList(fields, [])
2171 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2172 """Return L{_FS_UNAVAIL} if job is None.
2174 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2175 found, in which case this function converts it to L{_FS_UNAVAIL}.
2184 def _JobUnavail(inner):
2185 """Wrapper for L{_JobUnavailInner}.
2188 return compat.partial(_JobUnavailInner, inner)
2191 def _PerJobOpInner(fn, job):
2192 """Executes a function per opcode in a job.
2195 return map(fn, job.ops)
2199 """Wrapper for L{_PerJobOpInner}.
2202 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2205 def _JobTimestampInner(fn, job):
2206 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2211 if timestamp is None:
2217 def _JobTimestamp(fn):
2218 """Wrapper for L{_JobTimestampInner}.
2221 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2224 def _BuildJobFields():
2225 """Builds list of fields for job queries.
2229 (_MakeField("id", "ID", QFT_TEXT, "Job ID"),
2230 None, 0, lambda _, (job_id, job): job_id),
2231 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2232 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2233 (_MakeField("priority", "Priority", QFT_NUMBER,
2234 ("Current job priority (%s to %s)" %
2235 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2236 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2237 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2238 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2239 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2240 "List of opcodes results"),
2241 None, 0, _PerJobOp(operator.attrgetter("result"))),
2242 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2243 "List of opcodes status"),
2244 None, 0, _PerJobOp(operator.attrgetter("status"))),
2245 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2246 "List of opcode output logs"),
2247 None, 0, _PerJobOp(operator.attrgetter("log"))),
2248 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2249 "List of opcode start timestamps (before acquiring locks)"),
2250 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2251 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2252 "List of opcode execution start timestamps (after acquiring"
2254 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2255 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2256 "List of opcode execution end timestamps"),
2257 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2258 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2259 "List of opcode priorities"),
2260 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2261 (_MakeField("received_ts", "Received", QFT_OTHER,
2262 "Timestamp of when job was received"),
2263 None, 0, _JobTimestamp(operator.attrgetter("received_timestamp"))),
2264 (_MakeField("start_ts", "Start", QFT_OTHER,
2265 "Timestamp of job start"),
2266 None, 0, _JobTimestamp(operator.attrgetter("start_timestamp"))),
2267 (_MakeField("end_ts", "End", QFT_OTHER,
2268 "Timestamp of job end"),
2269 None, 0, _JobTimestamp(operator.attrgetter("end_timestamp"))),
2270 (_MakeField("summary", "Summary", QFT_OTHER,
2271 "List of per-opcode summaries"),
2272 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2275 return _PrepareFieldList(fields, [])
2278 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2279 """Returns an export name if available.
2288 def _BuildExportFields():
2289 """Builds list of fields for exports.
2293 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2294 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2295 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2296 None, 0, _GetExportName),
2299 return _PrepareFieldList(fields, [])
2302 _CLUSTER_VERSION_FIELDS = {
2303 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2304 "Software version"),
2305 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2306 constants.PROTOCOL_VERSION,
2307 "RPC protocol version"),
2308 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2309 "Configuration format version"),
2310 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2311 "API version for OS template scripts"),
2312 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2313 "Import/export file format version"),
2317 _CLUSTER_SIMPLE_FIELDS = {
2318 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2319 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2320 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2324 class ClusterQueryData:
2325 def __init__(self, cluster, drain_flag, watcher_pause):
2326 """Initializes this class.
2328 @type cluster: L{objects.Cluster}
2329 @param cluster: Instance of cluster object
2330 @type drain_flag: bool
2331 @param drain_flag: Whether job queue is drained
2332 @type watcher_pause: number
2333 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2336 self._cluster = cluster
2337 self.drain_flag = drain_flag
2338 self.watcher_pause = watcher_pause
2341 return iter([self._cluster])
2344 def _ClusterWatcherPause(ctx, _):
2345 """Returns until when watcher is paused (if available).
2348 if ctx.watcher_pause is None:
2351 return ctx.watcher_pause
2354 def _BuildClusterFields():
2355 """Builds list of fields for cluster information.
2359 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2360 lambda ctx, cluster: list(cluster.GetTags())),
2361 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2362 "Architecture information"), None, 0,
2363 lambda ctx, _: runtime.GetArchInfo()),
2364 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2365 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2366 lambda ctx, _: ctx.drain_flag),
2367 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2368 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2369 _ClusterWatcherPause),
2374 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2375 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2380 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2381 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
2385 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2387 return _PrepareFieldList(fields, [
2388 ("name", "cluster_name"),
2392 #: Fields for cluster information
2393 CLUSTER_FIELDS = _BuildClusterFields()
2395 #: Fields available for node queries
2396 NODE_FIELDS = _BuildNodeFields()
2398 #: Fields available for instance queries
2399 INSTANCE_FIELDS = _BuildInstanceFields()
2401 #: Fields available for lock queries
2402 LOCK_FIELDS = _BuildLockFields()
2404 #: Fields available for node group queries
2405 GROUP_FIELDS = _BuildGroupFields()
2407 #: Fields available for operating system queries
2408 OS_FIELDS = _BuildOsFields()
2410 #: Fields available for job queries
2411 JOB_FIELDS = _BuildJobFields()
2413 #: Fields available for exports
2414 EXPORT_FIELDS = _BuildExportFields()
2416 #: All available resources
2418 constants.QR_CLUSTER: CLUSTER_FIELDS,
2419 constants.QR_INSTANCE: INSTANCE_FIELDS,
2420 constants.QR_NODE: NODE_FIELDS,
2421 constants.QR_LOCK: LOCK_FIELDS,
2422 constants.QR_GROUP: GROUP_FIELDS,
2423 constants.QR_OS: OS_FIELDS,
2424 constants.QR_JOB: JOB_FIELDS,
2425 constants.QR_EXPORT: EXPORT_FIELDS,
2428 #: All available field lists
2429 ALL_FIELD_LISTS = ALL_FIELDS.values()