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_LT: (_OPTYPE_BINARY, [
407 (None, operator.lt, None),
409 qlang.OP_GT: (_OPTYPE_BINARY, [
410 (None, operator.gt, None),
412 qlang.OP_LE: (_OPTYPE_BINARY, [
413 (None, operator.le, None),
415 qlang.OP_GE: (_OPTYPE_BINARY, [
416 (None, operator.ge, None),
418 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
419 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
421 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
422 (None, operator.contains, None),
426 def __init__(self, fields):
427 """Initializes this class.
429 @param fields: Field definitions (return value of L{_PrepareFieldList})
432 self._fields = fields
434 self._op_handler = None
436 def __call__(self, hints, qfilter):
437 """Converts a query filter into a callable function.
439 @type hints: L{_FilterHints} or None
440 @param hints: Callbacks doing analysis on filter
442 @param qfilter: Filter structure
444 @return: Function receiving context and item as parameters, returning
445 boolean as to whether item matches filter
450 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
452 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
454 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
458 filter_fn = self._Compile(qfilter, 0)
460 self._op_handler = None
464 def _Compile(self, qfilter, level):
465 """Inner function for converting filters.
467 Calls the correct handler functions for the top-level operator. This
468 function is called recursively (e.g. for logic operators).
471 if not (isinstance(qfilter, (list, tuple)) and qfilter):
472 raise errors.ParameterError("Invalid filter on level %s" % level)
475 if level >= self._LEVELS_MAX:
476 raise errors.ParameterError("Only up to %s levels are allowed (filter"
477 " nested too deep)" % self._LEVELS_MAX)
479 # Create copy to be modified
480 operands = qfilter[:]
484 (kind, op_data) = self._OPS[op]
486 raise errors.ParameterError("Unknown operator '%s'" % op)
488 (handler, hints_cb) = self._op_handler[kind]
490 return handler(hints_cb, level, op, op_data, operands)
492 def _LookupField(self, name):
493 """Returns a field definition by name.
497 return self._fields[name]
499 raise errors.ParameterError("Unknown field '%s'" % name)
501 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
502 """Handles logic operators.
504 @type hints_fn: callable
505 @param hints_fn: Callback doing some analysis on the filter
507 @param level: Current depth
510 @type op_fn: callable
511 @param op_fn: Function implementing operator
513 @param operands: List of operands
519 return compat.partial(_WrapLogicOp, op_fn,
520 [self._Compile(op, level + 1) for op in operands])
522 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
523 """Handles unary operators.
525 @type hints_fn: callable
526 @param hints_fn: Callback doing some analysis on the filter
528 @param level: Current depth
531 @type op_fn: callable
532 @param op_fn: Function implementing operator
534 @param operands: List of operands
542 if len(operands) != 1:
543 raise errors.ParameterError("Unary operator '%s' expects exactly one"
546 if op == qlang.OP_TRUE:
547 (_, _, _, retrieval_fn) = self._LookupField(operands[0])
549 op_fn = operator.truth
551 elif op == qlang.OP_NOT:
552 op_fn = operator.not_
553 arg = self._Compile(operands[0], level + 1)
555 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
557 return compat.partial(_WrapUnaryOp, op_fn, arg)
559 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
560 """Handles binary operators.
562 @type hints_fn: callable
563 @param hints_fn: Callback doing some analysis on the filter
565 @param level: Current depth
568 @param op_data: Functions implementing operators
570 @param operands: List of operands
573 # Unused arguments, pylint: disable=W0613
575 (name, value) = operands
576 except (ValueError, TypeError):
577 raise errors.ParameterError("Invalid binary operator, expected exactly"
580 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
582 assert fdef.kind != QFT_UNKNOWN
584 # TODO: Type conversions?
586 verify_fn = _VERIFY_FN[fdef.kind]
587 if not verify_fn(value):
588 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
589 " with '%s', expected %s" %
590 (name, fdef.kind, value.__class__.__name__,
594 hints_fn(op, datakind, name, value)
596 for (fn_flags, fn, valprepfn) in op_data:
597 if fn_flags is None or fn_flags & field_flags:
598 # Prepare value if necessary (e.g. compile regular expression)
600 value = valprepfn(value)
602 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
604 raise errors.ProgrammerError("Unable to find operator implementation"
605 " (op '%s', flags %s)" % (op, field_flags))
608 def _CompileFilter(fields, hints, qfilter):
609 """Converts a query filter into a callable function.
611 See L{_FilterCompilerHelper} for details.
616 return _FilterCompilerHelper(fields)(hints, qfilter)
620 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
621 """Initializes this class.
623 The field definition is a dictionary with the field's name as a key and a
624 tuple containing, in order, the field definition object
625 (L{objects.QueryFieldDefinition}, the data kind to help calling code
626 collect data and a retrieval function. The retrieval function is called
627 with two parameters, in order, the data container and the item in container
628 (see L{Query.Query}).
630 Users of this class can call L{RequestedData} before preparing the data
631 container to determine what data is needed.
633 @type fieldlist: dictionary
634 @param fieldlist: Field definitions
635 @type selected: list of strings
636 @param selected: List of selected fields
639 assert namefield is None or namefield in fieldlist
641 self._fields = _GetQueryFields(fieldlist, selected)
643 self._filter_fn = None
644 self._requested_names = None
645 self._filter_datakinds = frozenset()
647 if qfilter is not None:
648 # Collect requested names if wanted
650 hints = _FilterHints(namefield)
654 # Build filter function
655 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
657 self._requested_names = hints.RequestedNames()
658 self._filter_datakinds = hints.ReferencedData()
660 if namefield is None:
663 (_, _, _, self._name_fn) = fieldlist[namefield]
665 def RequestedNames(self):
666 """Returns all names referenced in the filter.
668 If there is no filter or operators are preventing determining the exact
669 names, C{None} is returned.
672 return self._requested_names
674 def RequestedData(self):
675 """Gets requested kinds of data.
680 return (self._filter_datakinds |
681 frozenset(datakind for (_, datakind, _, _) in self._fields
682 if datakind is not None))
685 """Returns the list of fields for this query.
687 Includes unknown fields.
689 @rtype: List of L{objects.QueryFieldDefinition}
692 return GetAllFields(self._fields)
694 def Query(self, ctx, sort_by_name=True):
697 @param ctx: Data container passed to field retrieval functions, must
698 support iteration using C{__iter__}
699 @type sort_by_name: boolean
700 @param sort_by_name: Whether to sort by name or keep the input data's
704 sort = (self._name_fn and sort_by_name)
708 for idx, item in enumerate(ctx):
709 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
712 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
716 _VerifyResultRow(self._fields, row)
719 (status, name) = _ProcessResult(self._name_fn(ctx, item))
720 assert status == constants.RS_NORMAL
721 # TODO: Are there cases where we wouldn't want to use NiceSort?
722 result.append((utils.NiceSortKey(name), idx, row))
729 # TODO: Would "heapq" be more efficient than sorting?
731 # Sorting in-place instead of using "sorted()"
734 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
736 return map(operator.itemgetter(2), result)
738 def OldStyleQuery(self, ctx, sort_by_name=True):
739 """Query with "old" query result format.
741 See L{Query.Query} for arguments.
744 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
745 if fdef.kind == QFT_UNKNOWN)
747 raise errors.OpPrereqError("Unknown output fields selected: %s" %
748 (utils.CommaJoin(unknown), ),
751 return [[value for (_, value) in row]
752 for row in self.Query(ctx, sort_by_name=sort_by_name)]
755 def _ProcessResult(value):
756 """Converts result values into externally-visible ones.
759 if value is _FS_UNKNOWN:
760 return (RS_UNKNOWN, None)
761 elif value is _FS_NODATA:
762 return (RS_NODATA, None)
763 elif value is _FS_UNAVAIL:
764 return (RS_UNAVAIL, None)
765 elif value is _FS_OFFLINE:
766 return (RS_OFFLINE, None)
768 return (RS_NORMAL, value)
771 def _VerifyResultRow(fields, row):
772 """Verifies the contents of a query result row.
775 @param fields: Field definitions for result
776 @type row: list of tuples
780 assert len(row) == len(fields)
782 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
783 if status == RS_NORMAL:
784 if not _VERIFY_FN[fdef.kind](value):
785 errs.append("normal field %s fails validation (value is %s)" %
787 elif value is not None:
788 errs.append("abnormal field %s has a non-None value" % fdef.name)
789 assert not errs, ("Failed validation: %s in row %s" %
790 (utils.CommaJoin(errs), row))
793 def _FieldDictKey((fdef, _, flags, fn)):
794 """Generates key for field dictionary.
797 assert fdef.name and fdef.title, "Name and title are required"
798 assert FIELD_NAME_RE.match(fdef.name)
799 assert TITLE_RE.match(fdef.title)
800 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
801 fdef.doc.strip() == fdef.doc), \
802 "Invalid description for field '%s'" % fdef.name
804 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
809 def _PrepareFieldList(fields, aliases):
810 """Prepares field list for use by L{Query}.
812 Converts the list to a dictionary and does some verification.
814 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
815 kind, retrieval function)
816 @param fields: List of fields, see L{Query.__init__} for a better
818 @type aliases: list of tuples; (alias, target)
819 @param aliases: list of tuples containing aliases; for each
820 alias/target pair, a duplicate will be created in the field list
822 @return: Field dictionary for L{Query}
826 duplicates = utils.FindDuplicates(fdef.title.lower()
827 for (fdef, _, _, _) in fields)
828 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
830 result = utils.SequenceToDict(fields, key=_FieldDictKey)
832 for alias, target in aliases:
833 assert alias not in result, "Alias %s overrides an existing field" % alias
834 assert target in result, "Missing target %s for alias %s" % (target, alias)
835 (fdef, k, flags, fn) = result[target]
838 result[alias] = (fdef, k, flags, fn)
840 assert len(result) == len(fields) + len(aliases)
841 assert compat.all(name == fdef.name
842 for (name, (fdef, _, _, _)) in result.items())
847 def GetQueryResponse(query, ctx, sort_by_name=True):
848 """Prepares the response for a query.
850 @type query: L{Query}
851 @param ctx: Data container, see L{Query.Query}
852 @type sort_by_name: boolean
853 @param sort_by_name: Whether to sort by name or keep the input data's
857 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
858 fields=query.GetFields()).ToDict()
861 def QueryFields(fielddefs, selected):
862 """Returns list of available fields.
864 @type fielddefs: dict
865 @param fielddefs: Field definitions
866 @type selected: list of strings
867 @param selected: List of selected fields
868 @return: List of L{objects.QueryFieldDefinition}
872 # Client requests all fields, sort by name
873 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
874 key=operator.attrgetter("name"))
876 # Keep order as requested by client
877 fdefs = Query(fielddefs, selected).GetFields()
879 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
882 def _MakeField(name, title, kind, doc):
883 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
885 @param name: Field name as a regular expression
886 @param title: Human-readable title
887 @param kind: Field type
888 @param doc: Human-readable description
891 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
895 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
896 """Returns a static value.
902 def _StaticValue(value):
903 """Prepares a function to return a static value.
906 return compat.partial(_StaticValueInner, value)
909 def _GetNodeRole(node, master_name):
910 """Determine node role.
912 @type node: L{objects.Node}
913 @param node: Node object
914 @type master_name: string
915 @param master_name: Master node name
918 if node.name == master_name:
919 return constants.NR_MASTER
920 elif node.master_candidate:
921 return constants.NR_MCANDIDATE
923 return constants.NR_DRAINED
925 return constants.NR_OFFLINE
927 return constants.NR_REGULAR
930 def _GetItemAttr(attr):
931 """Returns a field function to return an attribute of the item.
933 @param attr: Attribute name
936 getter = operator.attrgetter(attr)
937 return lambda _, item: getter(item)
940 def _GetNDParam(name):
941 """Return a field function to return an ND parameter out of the context.
945 if ctx.ndparams is None:
948 return ctx.ndparams.get(name, None)
952 def _BuildNDFields(is_group):
953 """Builds all the ndparam fields.
955 @param is_group: whether this is called at group or node level
959 field_kind = GQ_CONFIG
961 field_kind = NQ_GROUP
962 return [(_MakeField("ndp/%s" % name, NDP_TITLE.get(name, "ndp/%s" % name),
963 _VTToQFT[kind], "The \"%s\" node parameter" % name),
964 field_kind, 0, _GetNDParam(name))
965 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
968 def _ConvWrapInner(convert, fn, ctx, item):
969 """Wrapper for converting values.
971 @param convert: Conversion function receiving value as single parameter
972 @param fn: Retrieval function
975 value = fn(ctx, item)
977 # Is the value an abnormal status?
978 if compat.any(value is fs for fs in _FS_ALL):
982 # TODO: Should conversion function also receive context, item or both?
983 return convert(value)
986 def _ConvWrap(convert, fn):
987 """Convenience wrapper for L{_ConvWrapInner}.
989 @param convert: Conversion function receiving value as single parameter
990 @param fn: Retrieval function
993 return compat.partial(_ConvWrapInner, convert, fn)
996 def _GetItemTimestamp(getter):
997 """Returns function for getting timestamp of item.
999 @type getter: callable
1000 @param getter: Function to retrieve timestamp attribute
1004 """Returns a timestamp of item.
1007 timestamp = getter(item)
1008 if timestamp is None:
1009 # Old configs might not have all timestamps
1017 def _GetItemTimestampFields(datatype):
1018 """Returns common timestamp fields.
1020 @param datatype: Field data type for use by L{Query.RequestedData}
1024 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1025 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1026 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1027 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1031 class NodeQueryData:
1032 """Data container for node data queries.
1035 def __init__(self, nodes, live_data, master_name, node_to_primary,
1036 node_to_secondary, groups, oob_support, cluster):
1037 """Initializes this class.
1041 self.live_data = live_data
1042 self.master_name = master_name
1043 self.node_to_primary = node_to_primary
1044 self.node_to_secondary = node_to_secondary
1045 self.groups = groups
1046 self.oob_support = oob_support
1047 self.cluster = cluster
1049 # Used for individual rows
1050 self.curlive_data = None
1051 self.ndparams = None
1054 """Iterate over all nodes.
1056 This function has side-effects and only one instance of the resulting
1057 generator should be used at a time.
1060 for node in self.nodes:
1061 group = self.groups.get(node.group, None)
1063 self.ndparams = None
1065 self.ndparams = self.cluster.FillND(node, group)
1067 self.curlive_data = self.live_data.get(node.name, None)
1069 self.curlive_data = None
1073 #: Fields that are direct attributes of an L{objects.Node} object
1074 _NODE_SIMPLE_FIELDS = {
1075 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1076 "master_candidate": ("MasterC", QFT_BOOL, 0,
1077 "Whether node is a master candidate"),
1078 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1079 "Whether node can become a master candidate"),
1080 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1081 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1082 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1083 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1084 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1088 #: Fields requiring talking to the node
1089 # Note that none of these are available for non-vm_capable nodes
1090 _NODE_LIVE_FIELDS = {
1091 "bootid": ("BootID", QFT_TEXT, "bootid",
1092 "Random UUID renewed for each system reboot, can be used"
1093 " for detecting reboots by tracking changes"),
1094 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1095 "Number of NUMA domains on node (if exported by hypervisor)"),
1096 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1097 "Number of physical CPU sockets (if exported by hypervisor)"),
1098 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1099 "dfree": ("DFree", QFT_UNIT, "vg_free",
1100 "Available disk space in volume group"),
1101 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1102 "Total disk space in volume group used for instance disk"
1104 "mfree": ("MFree", QFT_UNIT, "memory_free",
1105 "Memory available for instance allocations"),
1106 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1107 "Amount of memory used by node (dom0 for Xen)"),
1108 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1109 "Total amount of memory of physical machine"),
1114 """Build function for calling another function with an node group.
1116 @param cb: The callback to be called with the nodegroup
1120 """Get group data for a node.
1122 @type ctx: L{NodeQueryData}
1123 @type inst: L{objects.Node}
1124 @param inst: Node object
1127 ng = ctx.groups.get(node.group, None)
1129 # Nodes always have a group, or the configuration is corrupt
1132 return cb(ctx, node, ng)
1137 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1138 """Returns the name of a node's group.
1140 @type ctx: L{NodeQueryData}
1141 @type node: L{objects.Node}
1142 @param node: Node object
1143 @type ng: L{objects.NodeGroup}
1144 @param ng: The node group this node belongs to
1150 def _GetNodePower(ctx, node):
1151 """Returns the node powered state
1153 @type ctx: L{NodeQueryData}
1154 @type node: L{objects.Node}
1155 @param node: Node object
1158 if ctx.oob_support[node.name]:
1164 def _GetNdParams(ctx, node, ng):
1165 """Returns the ndparams for this node.
1167 @type ctx: L{NodeQueryData}
1168 @type node: L{objects.Node}
1169 @param node: Node object
1170 @type ng: L{objects.NodeGroup}
1171 @param ng: The node group this node belongs to
1174 return ctx.cluster.SimpleFillND(ng.FillND(node))
1177 def _GetLiveNodeField(field, kind, ctx, node):
1178 """Gets the value of a "live" field from L{NodeQueryData}.
1180 @param field: Live field name
1181 @param kind: Data kind, one of L{constants.QFT_ALL}
1182 @type ctx: L{NodeQueryData}
1183 @type node: L{objects.Node}
1184 @param node: Node object
1190 if not node.vm_capable:
1193 if not ctx.curlive_data:
1197 value = ctx.curlive_data[field]
1201 if kind == QFT_TEXT:
1204 assert kind in (QFT_NUMBER, QFT_UNIT)
1206 # Try to convert into number
1209 except (ValueError, TypeError):
1210 logging.exception("Failed to convert node field '%s' (value %r) to int",
1215 def _GetNodeHvState(_, node):
1216 """Converts node's hypervisor state for query result.
1219 hv_state = node.hv_state
1221 if hv_state is None:
1224 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1227 def _GetNodeDiskState(_, node):
1228 """Converts node's disk state for query result.
1231 disk_state = node.disk_state
1233 if disk_state is None:
1236 return dict((disk_kind, dict((name, value.ToDict())
1237 for (name, value) in kind_state.items()))
1238 for (disk_kind, kind_state) in disk_state.items())
1241 def _BuildNodeFields():
1242 """Builds list of fields for node queries.
1246 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1247 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1248 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1249 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1250 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1251 lambda ctx, node: list(node.GetTags())),
1252 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1253 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1254 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1255 _GetGroup(_GetNodeGroup)),
1256 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1257 NQ_CONFIG, 0, _GetItemAttr("group")),
1258 (_MakeField("powered", "Powered", QFT_BOOL,
1259 "Whether node is thought to be powered on"),
1260 NQ_OOB, 0, _GetNodePower),
1261 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1262 "Merged node parameters"),
1263 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1264 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1265 "Custom node parameters"),
1266 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1267 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1268 NQ_CONFIG, 0, _GetNodeHvState),
1269 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1270 NQ_CONFIG, 0, _GetNodeDiskState),
1273 fields.extend(_BuildNDFields(False))
1276 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1277 constants.NR_REGULAR, constants.NR_DRAINED,
1278 constants.NR_OFFLINE)
1279 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1280 " \"%s\" for regular, \"%s\" for a drained, \"%s\" for offline" %
1282 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1283 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1284 assert set(role_values) == constants.NR_ALL
1286 def _GetLength(getter):
1287 return lambda ctx, node: len(getter(ctx)[node.name])
1289 def _GetList(getter):
1290 return lambda ctx, node: list(getter(ctx)[node.name])
1292 # Add fields operating on instance lists
1293 for prefix, titleprefix, docword, getter in \
1294 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1295 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1296 # TODO: Allow filterting by hostname in list
1298 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1299 "Number of instances with this node as %s" % docword),
1300 NQ_INST, 0, _GetLength(getter)),
1301 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1303 "List of instances with this node as %s" % docword),
1304 NQ_INST, 0, _GetList(getter)),
1309 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1310 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1313 # Add fields requiring live data
1315 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1316 compat.partial(_GetLiveNodeField, nfield, kind))
1317 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1321 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1323 return _PrepareFieldList(fields, [])
1326 class InstanceQueryData:
1327 """Data container for instance data queries.
1330 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1331 live_data, wrongnode_inst, console, nodes, groups):
1332 """Initializes this class.
1334 @param instances: List of instance objects
1335 @param cluster: Cluster object
1336 @type disk_usage: dict; instance name as key
1337 @param disk_usage: Per-instance disk usage
1338 @type offline_nodes: list of strings
1339 @param offline_nodes: List of offline nodes
1340 @type bad_nodes: list of strings
1341 @param bad_nodes: List of faulty nodes
1342 @type live_data: dict; instance name as key
1343 @param live_data: Per-instance live data
1344 @type wrongnode_inst: set
1345 @param wrongnode_inst: Set of instances running on wrong node(s)
1346 @type console: dict; instance name as key
1347 @param console: Per-instance console information
1348 @type nodes: dict; node name as key
1349 @param nodes: Node objects
1352 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1353 "Offline nodes not included in bad nodes"
1354 assert not (set(live_data.keys()) & set(bad_nodes)), \
1355 "Found live data for bad or offline nodes"
1357 self.instances = instances
1358 self.cluster = cluster
1359 self.disk_usage = disk_usage
1360 self.offline_nodes = offline_nodes
1361 self.bad_nodes = bad_nodes
1362 self.live_data = live_data
1363 self.wrongnode_inst = wrongnode_inst
1364 self.console = console
1366 self.groups = groups
1368 # Used for individual rows
1369 self.inst_hvparams = None
1370 self.inst_beparams = None
1371 self.inst_osparams = None
1372 self.inst_nicparams = None
1375 """Iterate over all instances.
1377 This function has side-effects and only one instance of the resulting
1378 generator should be used at a time.
1381 for inst in self.instances:
1382 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1383 self.inst_beparams = self.cluster.FillBE(inst)
1384 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1385 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1386 for nic in inst.nics]
1391 def _GetInstOperState(ctx, inst):
1392 """Get instance's operational status.
1394 @type ctx: L{InstanceQueryData}
1395 @type inst: L{objects.Instance}
1396 @param inst: Instance object
1399 # Can't use RS_OFFLINE here as it would describe the instance to
1400 # be offline when we actually don't know due to missing data
1401 if inst.primary_node in ctx.bad_nodes:
1404 return bool(ctx.live_data.get(inst.name))
1407 def _GetInstLiveData(name):
1408 """Build function for retrieving live data.
1411 @param name: Live data field name
1415 """Get live data for an instance.
1417 @type ctx: L{InstanceQueryData}
1418 @type inst: L{objects.Instance}
1419 @param inst: Instance object
1422 if (inst.primary_node in ctx.bad_nodes or
1423 inst.primary_node in ctx.offline_nodes):
1424 # Can't use RS_OFFLINE here as it would describe the instance to be
1425 # offline when we actually don't know due to missing data
1428 if inst.name in ctx.live_data:
1429 data = ctx.live_data[inst.name]
1438 def _GetInstStatus(ctx, inst):
1439 """Get instance status.
1441 @type ctx: L{InstanceQueryData}
1442 @type inst: L{objects.Instance}
1443 @param inst: Instance object
1446 if inst.primary_node in ctx.offline_nodes:
1447 return constants.INSTST_NODEOFFLINE
1449 if inst.primary_node in ctx.bad_nodes:
1450 return constants.INSTST_NODEDOWN
1452 if bool(ctx.live_data.get(inst.name)):
1453 if inst.name in ctx.wrongnode_inst:
1454 return constants.INSTST_WRONGNODE
1455 elif inst.admin_state == constants.ADMINST_UP:
1456 return constants.INSTST_RUNNING
1458 return constants.INSTST_ERRORUP
1460 if inst.admin_state == constants.ADMINST_UP:
1461 return constants.INSTST_ERRORDOWN
1462 elif inst.admin_state == constants.ADMINST_DOWN:
1463 return constants.INSTST_ADMINDOWN
1465 return constants.INSTST_ADMINOFFLINE
1468 def _GetInstDiskSize(index):
1469 """Build function for retrieving disk size.
1472 @param index: Disk index
1476 """Get size of a disk.
1478 @type inst: L{objects.Instance}
1479 @param inst: Instance object
1483 return inst.disks[index].size
1490 def _GetInstNic(index, cb):
1491 """Build function for calling another function with an instance NIC.
1494 @param index: NIC index
1500 """Call helper function with instance NIC.
1502 @type ctx: L{InstanceQueryData}
1503 @type inst: L{objects.Instance}
1504 @param inst: Instance object
1508 nic = inst.nics[index]
1512 return cb(ctx, index, nic)
1517 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1518 """Get a NIC's IP address.
1520 @type ctx: L{InstanceQueryData}
1521 @type nic: L{objects.NIC}
1522 @param nic: NIC object
1531 def _GetInstNicBridge(ctx, index, _):
1532 """Get a NIC's bridge.
1534 @type ctx: L{InstanceQueryData}
1536 @param index: NIC index
1539 assert len(ctx.inst_nicparams) >= index
1541 nicparams = ctx.inst_nicparams[index]
1543 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1544 return nicparams[constants.NIC_LINK]
1549 def _GetInstAllNicBridges(ctx, inst):
1550 """Get all network bridges for an instance.
1552 @type ctx: L{InstanceQueryData}
1553 @type inst: L{objects.Instance}
1554 @param inst: Instance object
1557 assert len(ctx.inst_nicparams) == len(inst.nics)
1561 for nicp in ctx.inst_nicparams:
1562 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1563 result.append(nicp[constants.NIC_LINK])
1567 assert len(result) == len(inst.nics)
1572 def _GetInstNicParam(name):
1573 """Build function for retrieving a NIC parameter.
1576 @param name: Parameter name
1579 def fn(ctx, index, _):
1580 """Get a NIC's bridge.
1582 @type ctx: L{InstanceQueryData}
1583 @type inst: L{objects.Instance}
1584 @param inst: Instance object
1585 @type nic: L{objects.NIC}
1586 @param nic: NIC object
1589 assert len(ctx.inst_nicparams) >= index
1590 return ctx.inst_nicparams[index][name]
1595 def _GetInstanceNetworkFields():
1596 """Get instance fields involving network interfaces.
1598 @return: Tuple containing list of field definitions used as input for
1599 L{_PrepareFieldList} and a list of aliases
1602 nic_mac_fn = lambda ctx, _, nic: nic.mac
1603 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1604 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1608 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1609 "Number of network interfaces"),
1610 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1611 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1612 "List containing each network interface's MAC address"),
1613 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1614 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1615 "List containing each network interface's IP address"),
1616 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1617 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1618 "List containing each network interface's mode"), IQ_CONFIG, 0,
1619 lambda ctx, inst: [nicp[constants.NIC_MODE]
1620 for nicp in ctx.inst_nicparams]),
1621 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1622 "List containing each network interface's link"), IQ_CONFIG, 0,
1623 lambda ctx, inst: [nicp[constants.NIC_LINK]
1624 for nicp in ctx.inst_nicparams]),
1625 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1626 "List containing each network interface's bridge"),
1627 IQ_CONFIG, 0, _GetInstAllNicBridges),
1631 for i in range(constants.MAX_NICS):
1632 numtext = utils.FormatOrdinal(i + 1)
1634 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1635 "IP address of %s network interface" % numtext),
1636 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1637 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1638 "MAC address of %s network interface" % numtext),
1639 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1640 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1641 "Mode of %s network interface" % numtext),
1642 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1643 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1644 "Link of %s network interface" % numtext),
1645 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1646 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1647 "Bridge of %s network interface" % numtext),
1648 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1652 # Legacy fields for first NIC
1654 ("mac", "nic.mac/0"),
1655 ("bridge", "nic.bridge/0"),
1656 ("nic_mode", "nic.mode/0"),
1657 ("nic_link", "nic.link/0"),
1660 return (fields, aliases)
1663 def _GetInstDiskUsage(ctx, inst):
1664 """Get disk usage for an instance.
1666 @type ctx: L{InstanceQueryData}
1667 @type inst: L{objects.Instance}
1668 @param inst: Instance object
1671 usage = ctx.disk_usage[inst.name]
1679 def _GetInstanceConsole(ctx, inst):
1680 """Get console information for instance.
1682 @type ctx: L{InstanceQueryData}
1683 @type inst: L{objects.Instance}
1684 @param inst: Instance object
1687 consinfo = ctx.console[inst.name]
1689 if consinfo is None:
1695 def _GetInstanceDiskFields():
1696 """Get instance fields involving disks.
1698 @return: List of field definitions used as input for L{_PrepareFieldList}
1702 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1703 "Total disk space used by instance on each of its nodes;"
1704 " this is not the disk size visible to the instance, but"
1705 " the usage on the node"),
1706 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1707 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1708 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1709 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1710 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1715 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1716 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1717 IQ_CONFIG, 0, _GetInstDiskSize(i))
1718 for i in range(constants.MAX_DISKS)
1724 def _GetInstanceParameterFields():
1725 """Get instance fields involving parameters.
1727 @return: List of field definitions used as input for L{_PrepareFieldList}
1730 # TODO: Consider moving titles closer to constants
1732 constants.BE_AUTO_BALANCE: "Auto_balance",
1733 constants.BE_MAXMEM: "ConfigMaxMem",
1734 constants.BE_MINMEM: "ConfigMinMem",
1735 constants.BE_VCPUS: "ConfigVCPUs",
1739 constants.HV_ACPI: "ACPI",
1740 constants.HV_BOOT_ORDER: "Boot_order",
1741 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1742 constants.HV_DISK_TYPE: "Disk_type",
1743 constants.HV_INITRD_PATH: "Initrd_path",
1744 constants.HV_KERNEL_PATH: "Kernel_path",
1745 constants.HV_NIC_TYPE: "NIC_type",
1746 constants.HV_PAE: "PAE",
1747 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1752 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1753 "Hypervisor parameters (merged)"),
1754 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1755 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1756 "Backend parameters (merged)"),
1757 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1758 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1759 "Operating system parameters (merged)"),
1760 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1762 # Unfilled parameters
1763 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1764 "Custom hypervisor parameters"),
1765 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1766 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1767 "Custom backend parameters",),
1768 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1769 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1770 "Custom operating system parameters",),
1771 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1772 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1773 "Custom network interface parameters"),
1774 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1778 def _GetInstHvParam(name):
1779 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1782 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1783 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1784 IQ_CONFIG, 0, _GetInstHvParam(name))
1785 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1786 if name not in constants.HVC_GLOBALS
1790 def _GetInstBeParam(name):
1791 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1794 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1795 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1796 IQ_CONFIG, 0, _GetInstBeParam(name))
1797 for name, kind in constants.BES_PARAMETER_TYPES.items()
1803 _INST_SIMPLE_FIELDS = {
1804 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1805 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1806 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1807 # Depending on the hypervisor, the port can be None
1808 "network_port": ("Network_port", QFT_OTHER, 0,
1809 "Instance network port if available (e.g. for VNC console)"),
1810 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1811 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1812 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1816 def _GetInstNodeGroup(ctx, default, node_name):
1817 """Gets group UUID of an instance node.
1819 @type ctx: L{InstanceQueryData}
1820 @param default: Default value
1821 @type node_name: string
1822 @param node_name: Node name
1826 node = ctx.nodes[node_name]
1833 def _GetInstNodeGroupName(ctx, default, node_name):
1834 """Gets group name of an instance node.
1836 @type ctx: L{InstanceQueryData}
1837 @param default: Default value
1838 @type node_name: string
1839 @param node_name: Node name
1843 node = ctx.nodes[node_name]
1848 group = ctx.groups[node.group]
1855 def _BuildInstanceFields():
1856 """Builds list of fields for instance queries.
1860 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1861 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1862 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1863 "Primary node's group"),
1865 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1866 inst.primary_node)),
1867 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1868 "Primary node's group UUID"),
1870 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1871 # TODO: Allow filtering by secondary node as hostname
1872 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1873 "Secondary nodes; usually this will just be one node"),
1874 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1875 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1876 "Node groups of secondary nodes"),
1878 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1879 inst.secondary_nodes)),
1880 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1881 "Node group UUIDs of secondary nodes"),
1883 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1884 inst.secondary_nodes)),
1885 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1886 "Desired state of instance"),
1887 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1888 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1889 "Desired state of instance"),
1890 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1891 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1892 lambda ctx, inst: list(inst.GetTags())),
1893 (_MakeField("console", "Console", QFT_OTHER,
1894 "Instance console information"), IQ_CONSOLE, 0,
1895 _GetInstanceConsole),
1900 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1901 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1904 # Fields requiring talking to the node
1906 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1907 IQ_LIVE, 0, _GetInstOperState),
1908 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1909 "Actual memory usage as seen by hypervisor"),
1910 IQ_LIVE, 0, _GetInstLiveData("memory")),
1911 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1912 "Actual number of VCPUs as seen by hypervisor"),
1913 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1917 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1918 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1919 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1920 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1921 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1922 " and actually is, \"%s\" if instance is stopped and"
1923 " is not running, \"%s\" if instance running, but not on its"
1924 " designated primary node, \"%s\" if instance should be"
1925 " stopped, but is actually running, \"%s\" if instance should"
1926 " run, but doesn't, \"%s\" if instance's primary node is down,"
1927 " \"%s\" if instance's primary node is marked offline,"
1928 " \"%s\" if instance is offline and does not use dynamic"
1929 " resources" % status_values)
1930 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1931 IQ_LIVE, 0, _GetInstStatus))
1932 assert set(status_values) == constants.INSTST_ALL, \
1933 "Status documentation mismatch"
1935 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1937 fields.extend(network_fields)
1938 fields.extend(_GetInstanceParameterFields())
1939 fields.extend(_GetInstanceDiskFields())
1940 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1943 ("vcpus", "be/vcpus"),
1944 ("be/memory", "be/maxmem"),
1945 ("sda_size", "disk.size/0"),
1946 ("sdb_size", "disk.size/1"),
1949 return _PrepareFieldList(fields, aliases)
1952 class LockQueryData:
1953 """Data container for lock data queries.
1956 def __init__(self, lockdata):
1957 """Initializes this class.
1960 self.lockdata = lockdata
1963 """Iterate over all locks.
1966 return iter(self.lockdata)
1969 def _GetLockOwners(_, data):
1970 """Returns a sorted list of a lock's current owners.
1973 (_, _, owners, _) = data
1976 owners = utils.NiceSort(owners)
1981 def _GetLockPending(_, data):
1982 """Returns a sorted list of a lock's pending acquires.
1985 (_, _, _, pending) = data
1988 pending = [(mode, utils.NiceSort(names))
1989 for (mode, names) in pending]
1994 def _BuildLockFields():
1995 """Builds list of fields for lock queries.
1998 return _PrepareFieldList([
1999 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2000 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2001 lambda ctx, (name, mode, owners, pending): name),
2002 (_MakeField("mode", "Mode", QFT_OTHER,
2003 "Mode in which the lock is currently acquired"
2004 " (exclusive or shared)"),
2005 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2006 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2007 LQ_OWNER, 0, _GetLockOwners),
2008 (_MakeField("pending", "Pending", QFT_OTHER,
2009 "Threads waiting for the lock"),
2010 LQ_PENDING, 0, _GetLockPending),
2014 class GroupQueryData:
2015 """Data container for node group data queries.
2018 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2020 """Initializes this class.
2022 @param cluster: Cluster object
2023 @param groups: List of node group objects
2024 @type group_to_nodes: dict; group UUID as key
2025 @param group_to_nodes: Per-group list of nodes
2026 @type group_to_instances: dict; group UUID as key
2027 @param group_to_instances: Per-group list of (primary) instances
2028 @type want_diskparams: bool
2029 @param want_diskparams: Whether diskparamters should be calculated
2032 self.groups = groups
2033 self.group_to_nodes = group_to_nodes
2034 self.group_to_instances = group_to_instances
2035 self.cluster = cluster
2036 self.want_diskparams = want_diskparams
2038 # Used for individual rows
2039 self.group_ipolicy = None
2040 self.ndparams = None
2041 self.group_dp = None
2044 """Iterate over all node groups.
2046 This function has side-effects and only one instance of the resulting
2047 generator should be used at a time.
2050 for group in self.groups:
2051 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2052 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2053 if self.want_diskparams:
2054 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2056 self.group_dp = None
2060 _GROUP_SIMPLE_FIELDS = {
2061 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2062 "name": ("Group", QFT_TEXT, "Group name"),
2063 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2064 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2068 def _BuildGroupFields():
2069 """Builds list of fields for node group queries.
2073 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2075 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2077 def _GetLength(getter):
2078 return lambda ctx, group: len(getter(ctx)[group.uuid])
2080 def _GetSortedList(getter):
2081 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2083 group_to_nodes = operator.attrgetter("group_to_nodes")
2084 group_to_instances = operator.attrgetter("group_to_instances")
2086 # Add fields for nodes
2088 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2089 GQ_NODE, 0, _GetLength(group_to_nodes)),
2090 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2091 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2094 # Add fields for instances
2096 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2097 "Number of primary instances"),
2098 GQ_INST, 0, _GetLength(group_to_instances)),
2099 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2100 "List of primary instances"),
2101 GQ_INST, 0, _GetSortedList(group_to_instances)),
2106 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2107 lambda ctx, group: list(group.GetTags())),
2108 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2109 "Instance policy limitations (merged)"),
2110 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2111 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2112 "Custom instance policy limitations"),
2113 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2114 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2115 "Custom node parameters"),
2116 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2117 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2119 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2120 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2121 "Disk parameters (merged)"),
2122 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2123 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2124 "Custom disk parameters"),
2125 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2129 fields.extend(_BuildNDFields(True))
2131 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2133 return _PrepareFieldList(fields, [])
2136 class OsInfo(objects.ConfigObject):
2149 def _BuildOsFields():
2150 """Builds list of fields for operating system queries.
2154 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2155 None, 0, _GetItemAttr("name")),
2156 (_MakeField("valid", "Valid", QFT_BOOL,
2157 "Whether operating system definition is valid"),
2158 None, 0, _GetItemAttr("valid")),
2159 (_MakeField("hidden", "Hidden", QFT_BOOL,
2160 "Whether operating system is hidden"),
2161 None, 0, _GetItemAttr("hidden")),
2162 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2163 "Whether operating system is blacklisted"),
2164 None, 0, _GetItemAttr("blacklisted")),
2165 (_MakeField("variants", "Variants", QFT_OTHER,
2166 "Operating system variants"),
2167 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2168 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2169 "Operating system API versions"),
2170 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2171 (_MakeField("parameters", "Parameters", QFT_OTHER,
2172 "Operating system parameters"),
2173 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2174 _GetItemAttr("parameters"))),
2175 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2176 "Status from node"),
2177 None, 0, _GetItemAttr("node_status")),
2180 return _PrepareFieldList(fields, [])
2183 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2184 """Return L{_FS_UNAVAIL} if job is None.
2186 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2187 found, in which case this function converts it to L{_FS_UNAVAIL}.
2196 def _JobUnavail(inner):
2197 """Wrapper for L{_JobUnavailInner}.
2200 return compat.partial(_JobUnavailInner, inner)
2203 def _PerJobOpInner(fn, job):
2204 """Executes a function per opcode in a job.
2207 return map(fn, job.ops)
2211 """Wrapper for L{_PerJobOpInner}.
2214 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2217 def _JobTimestampInner(fn, job):
2218 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2223 if timestamp is None:
2229 def _JobTimestamp(fn):
2230 """Wrapper for L{_JobTimestampInner}.
2233 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2236 def _BuildJobFields():
2237 """Builds list of fields for job queries.
2241 (_MakeField("id", "ID", QFT_TEXT, "Job ID"),
2242 None, 0, lambda _, (job_id, job): job_id),
2243 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2244 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2245 (_MakeField("priority", "Priority", QFT_NUMBER,
2246 ("Current job priority (%s to %s)" %
2247 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2248 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2249 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2250 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2251 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2252 "List of opcodes results"),
2253 None, 0, _PerJobOp(operator.attrgetter("result"))),
2254 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2255 "List of opcodes status"),
2256 None, 0, _PerJobOp(operator.attrgetter("status"))),
2257 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2258 "List of opcode output logs"),
2259 None, 0, _PerJobOp(operator.attrgetter("log"))),
2260 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2261 "List of opcode start timestamps (before acquiring locks)"),
2262 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2263 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2264 "List of opcode execution start timestamps (after acquiring"
2266 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2267 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2268 "List of opcode execution end timestamps"),
2269 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2270 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2271 "List of opcode priorities"),
2272 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2273 (_MakeField("received_ts", "Received", QFT_OTHER,
2274 "Timestamp of when job was received"),
2275 None, 0, _JobTimestamp(operator.attrgetter("received_timestamp"))),
2276 (_MakeField("start_ts", "Start", QFT_OTHER,
2277 "Timestamp of job start"),
2278 None, 0, _JobTimestamp(operator.attrgetter("start_timestamp"))),
2279 (_MakeField("end_ts", "End", QFT_OTHER,
2280 "Timestamp of job end"),
2281 None, 0, _JobTimestamp(operator.attrgetter("end_timestamp"))),
2282 (_MakeField("summary", "Summary", QFT_OTHER,
2283 "List of per-opcode summaries"),
2284 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2287 return _PrepareFieldList(fields, [])
2290 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2291 """Returns an export name if available.
2300 def _BuildExportFields():
2301 """Builds list of fields for exports.
2305 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2306 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2307 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2308 None, 0, _GetExportName),
2311 return _PrepareFieldList(fields, [])
2314 _CLUSTER_VERSION_FIELDS = {
2315 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2316 "Software version"),
2317 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2318 constants.PROTOCOL_VERSION,
2319 "RPC protocol version"),
2320 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2321 "Configuration format version"),
2322 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2323 "API version for OS template scripts"),
2324 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2325 "Import/export file format version"),
2329 _CLUSTER_SIMPLE_FIELDS = {
2330 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2331 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2332 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2336 class ClusterQueryData:
2337 def __init__(self, cluster, drain_flag, watcher_pause):
2338 """Initializes this class.
2340 @type cluster: L{objects.Cluster}
2341 @param cluster: Instance of cluster object
2342 @type drain_flag: bool
2343 @param drain_flag: Whether job queue is drained
2344 @type watcher_pause: number
2345 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2348 self._cluster = cluster
2349 self.drain_flag = drain_flag
2350 self.watcher_pause = watcher_pause
2353 return iter([self._cluster])
2356 def _ClusterWatcherPause(ctx, _):
2357 """Returns until when watcher is paused (if available).
2360 if ctx.watcher_pause is None:
2363 return ctx.watcher_pause
2366 def _BuildClusterFields():
2367 """Builds list of fields for cluster information.
2371 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2372 lambda ctx, cluster: list(cluster.GetTags())),
2373 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2374 "Architecture information"), None, 0,
2375 lambda ctx, _: runtime.GetArchInfo()),
2376 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2377 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2378 lambda ctx, _: ctx.drain_flag),
2379 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2380 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2381 _ClusterWatcherPause),
2386 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2387 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2392 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2393 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
2397 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2399 return _PrepareFieldList(fields, [
2400 ("name", "cluster_name"),
2404 #: Fields for cluster information
2405 CLUSTER_FIELDS = _BuildClusterFields()
2407 #: Fields available for node queries
2408 NODE_FIELDS = _BuildNodeFields()
2410 #: Fields available for instance queries
2411 INSTANCE_FIELDS = _BuildInstanceFields()
2413 #: Fields available for lock queries
2414 LOCK_FIELDS = _BuildLockFields()
2416 #: Fields available for node group queries
2417 GROUP_FIELDS = _BuildGroupFields()
2419 #: Fields available for operating system queries
2420 OS_FIELDS = _BuildOsFields()
2422 #: Fields available for job queries
2423 JOB_FIELDS = _BuildJobFields()
2425 #: Fields available for exports
2426 EXPORT_FIELDS = _BuildExportFields()
2428 #: All available resources
2430 constants.QR_CLUSTER: CLUSTER_FIELDS,
2431 constants.QR_INSTANCE: INSTANCE_FIELDS,
2432 constants.QR_NODE: NODE_FIELDS,
2433 constants.QR_LOCK: LOCK_FIELDS,
2434 constants.QR_GROUP: GROUP_FIELDS,
2435 constants.QR_OS: OS_FIELDS,
2436 constants.QR_JOB: JOB_FIELDS,
2437 constants.QR_EXPORT: EXPORT_FIELDS,
2440 #: All available field lists
2441 ALL_FIELD_LISTS = ALL_FIELDS.values()