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
67 from ganeti import jstore
69 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
70 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
71 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
72 RS_UNAVAIL, RS_OFFLINE)
75 # Constants for requesting data from the caller/data provider. Each property
76 # collected/computed separately by the data provider should have its own to
77 # only collect the requested data and not more.
89 IQ_NODES) = range(100, 105)
93 LQ_PENDING) = range(10, 13)
98 GQ_DISKPARAMS) = range(200, 204)
102 CQ_WATCHER_PAUSE) = range(300, 303)
104 (JQ_ARCHIVED, ) = range(400, 401)
108 QFF_IP_ADDRESS = 0x02
110 QFF_SPLIT_TIMESTAMP = 0x08
111 # Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
112 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP)
114 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
115 TITLE_RE = re.compile(r"^[^\s]+$")
116 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
118 #: Verification function for each field type
120 QFT_UNKNOWN: ht.TNone,
121 QFT_TEXT: ht.TString,
125 QFT_TIMESTAMP: ht.TNumber,
126 QFT_OTHER: lambda _: True,
129 # Unique objects for special field statuses
130 _FS_UNKNOWN = object()
131 _FS_NODATA = object()
132 _FS_UNAVAIL = object()
133 _FS_OFFLINE = object()
135 #: List of all special status
136 _FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE])
138 #: VType to QFT mapping
140 # TODO: fix validation of empty strings
141 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
142 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
143 constants.VTYPE_BOOL: QFT_BOOL,
144 constants.VTYPE_SIZE: QFT_UNIT,
145 constants.VTYPE_INT: QFT_NUMBER,
148 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
151 def _GetUnknownField(ctx, item): # pylint: disable=W0613
152 """Gets the contents of an unknown field.
158 def _GetQueryFields(fielddefs, selected):
159 """Calculates the internal list of selected fields.
161 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
163 @type fielddefs: dict
164 @param fielddefs: Field definitions
165 @type selected: list of strings
166 @param selected: List of selected fields
171 for name in selected:
173 fdef = fielddefs[name]
175 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
176 None, 0, _GetUnknownField)
178 assert len(fdef) == 4
185 def GetAllFields(fielddefs):
186 """Extract L{objects.QueryFieldDefinition} from field definitions.
188 @rtype: list of L{objects.QueryFieldDefinition}
191 return [fdef for (fdef, _, _, _) in fielddefs]
195 """Class for filter analytics.
197 When filters are used, the user of the L{Query} class usually doesn't know
198 exactly which items will be necessary for building the result. It therefore
199 has to prepare and compute the input data for potentially returning
202 There are two ways to optimize this. The first, and simpler, is to assign
203 each field a group of data, so that the caller can determine which
204 computations are necessary depending on the data groups requested. The list
205 of referenced groups must also be computed for fields referenced in the
208 The second is restricting the items based on a primary key. The primary key
209 is usually a unique name (e.g. a node name). This class extracts all
210 referenced names from a filter. If it encounters any filter condition which
211 disallows such a list to be determined (e.g. a non-equality filter), all
212 names will be requested.
214 The end-effect is that any operation other than L{qlang.OP_OR} and
215 L{qlang.OP_EQUAL} will make the query more expensive.
218 def __init__(self, namefield):
219 """Initializes this class.
221 @type namefield: string
222 @param namefield: Field caller is interested in
225 self._namefield = namefield
227 #: Whether all names need to be requested (e.g. if a non-equality operator
229 self._allnames = False
231 #: Which names to request
234 #: Data kinds referenced by the filter (used by L{Query.RequestedData})
235 self._datakinds = set()
237 def RequestedNames(self):
238 """Returns all requested values.
240 Returns C{None} if list of values can't be determined (e.g. encountered
241 non-equality operators).
246 if self._allnames or self._names is None:
249 return utils.UniqueSequence(self._names)
251 def ReferencedData(self):
252 """Returns all kinds of data referenced by the filter.
255 return frozenset(self._datakinds)
257 def _NeedAllNames(self):
258 """Changes internal state to request all names.
261 self._allnames = True
264 def NoteLogicOp(self, op):
265 """Called when handling a logic operation.
271 if op != qlang.OP_OR:
274 def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
275 """Called when handling an unary operation.
281 if datakind is not None:
282 self._datakinds.add(datakind)
286 def NoteBinaryOp(self, op, datakind, name, value):
287 """Called when handling a binary operation.
292 @param name: Left-hand side of operator (field name)
293 @param value: Right-hand side of operator
296 if datakind is not None:
297 self._datakinds.add(datakind)
302 # If any operator other than equality was used, all names need to be
304 if op == qlang.OP_EQUAL and name == self._namefield:
305 if self._names is None:
307 self._names.append(value)
312 def _WrapLogicOp(op_fn, sentences, ctx, item):
313 """Wrapper for logic operator functions.
316 return op_fn(fn(ctx, item) for fn in sentences)
319 def _WrapUnaryOp(op_fn, inner, ctx, item):
320 """Wrapper for unary operator functions.
323 return op_fn(inner(ctx, item))
326 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
327 """Wrapper for binary operator functions.
330 return op_fn(retrieval_fn(ctx, item), value)
333 def _WrapNot(fn, lhs, rhs):
334 """Negates the result of a wrapped function.
337 return not fn(lhs, rhs)
340 def _PrepareRegex(pattern):
341 """Compiles a regular expression.
345 return re.compile(pattern)
346 except re.error, err:
347 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
350 def _PrepareSplitTimestamp(value):
351 """Prepares a value for comparison by L{_MakeSplitTimestampComparison}.
354 if ht.TNumber(value):
357 return utils.MergeTime(value)
360 def _MakeSplitTimestampComparison(fn):
361 """Compares split timestamp values after converting to float.
364 return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
367 def _MakeComparisonChecks(fn):
368 """Prepares flag-specific comparisons using a comparison function.
372 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn),
373 _PrepareSplitTimestamp),
374 (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs),
380 class _FilterCompilerHelper:
381 """Converts a query filter to a callable usable for filtering.
384 # String statement has no effect, pylint: disable=W0105
386 #: How deep filters can be nested
389 # Unique identifiers for operator groups
392 _OPTYPE_BINARY) = range(1, 4)
394 """Functions for equality checks depending on field flags.
396 List of tuples containing flags and a callable receiving the left- and
397 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
398 (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
400 Order matters. The first item with flags will be used. Flags are checked
406 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
407 case_sensitive=False),
409 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq),
410 _PrepareSplitTimestamp),
411 (None, operator.eq, None),
416 Operator as key (C{qlang.OP_*}), value a tuple of operator group
417 (C{_OPTYPE_*}) and a group-specific value:
419 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
421 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
422 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
423 right-hand side of the operator, used by L{_HandleBinaryOp}
428 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
429 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
432 qlang.OP_NOT: (_OPTYPE_UNARY, None),
433 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
436 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
438 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
439 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
440 qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)),
441 qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)),
442 qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)),
443 qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)),
444 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
445 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
447 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
448 (None, operator.contains, None),
452 def __init__(self, fields):
453 """Initializes this class.
455 @param fields: Field definitions (return value of L{_PrepareFieldList})
458 self._fields = fields
460 self._op_handler = None
462 def __call__(self, hints, qfilter):
463 """Converts a query filter into a callable function.
465 @type hints: L{_FilterHints} or None
466 @param hints: Callbacks doing analysis on filter
468 @param qfilter: Filter structure
470 @return: Function receiving context and item as parameters, returning
471 boolean as to whether item matches filter
476 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
478 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
480 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
484 filter_fn = self._Compile(qfilter, 0)
486 self._op_handler = None
490 def _Compile(self, qfilter, level):
491 """Inner function for converting filters.
493 Calls the correct handler functions for the top-level operator. This
494 function is called recursively (e.g. for logic operators).
497 if not (isinstance(qfilter, (list, tuple)) and qfilter):
498 raise errors.ParameterError("Invalid filter on level %s" % level)
501 if level >= self._LEVELS_MAX:
502 raise errors.ParameterError("Only up to %s levels are allowed (filter"
503 " nested too deep)" % self._LEVELS_MAX)
505 # Create copy to be modified
506 operands = qfilter[:]
510 (kind, op_data) = self._OPS[op]
512 raise errors.ParameterError("Unknown operator '%s'" % op)
514 (handler, hints_cb) = self._op_handler[kind]
516 return handler(hints_cb, level, op, op_data, operands)
518 def _LookupField(self, name):
519 """Returns a field definition by name.
523 return self._fields[name]
525 raise errors.ParameterError("Unknown field '%s'" % name)
527 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
528 """Handles logic operators.
530 @type hints_fn: callable
531 @param hints_fn: Callback doing some analysis on the filter
533 @param level: Current depth
536 @type op_fn: callable
537 @param op_fn: Function implementing operator
539 @param operands: List of operands
545 return compat.partial(_WrapLogicOp, op_fn,
546 [self._Compile(op, level + 1) for op in operands])
548 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
549 """Handles unary operators.
551 @type hints_fn: callable
552 @param hints_fn: Callback doing some analysis on the filter
554 @param level: Current depth
557 @type op_fn: callable
558 @param op_fn: Function implementing operator
560 @param operands: List of operands
565 if len(operands) != 1:
566 raise errors.ParameterError("Unary operator '%s' expects exactly one"
569 if op == qlang.OP_TRUE:
570 (_, datakind, _, retrieval_fn) = self._LookupField(operands[0])
573 hints_fn(op, datakind)
575 op_fn = operator.truth
577 elif op == qlang.OP_NOT:
581 op_fn = operator.not_
582 arg = self._Compile(operands[0], level + 1)
584 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
586 return compat.partial(_WrapUnaryOp, op_fn, arg)
588 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
589 """Handles binary operators.
591 @type hints_fn: callable
592 @param hints_fn: Callback doing some analysis on the filter
594 @param level: Current depth
597 @param op_data: Functions implementing operators
599 @param operands: List of operands
602 # Unused arguments, pylint: disable=W0613
604 (name, value) = operands
605 except (ValueError, TypeError):
606 raise errors.ParameterError("Invalid binary operator, expected exactly"
609 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
611 assert fdef.kind != QFT_UNKNOWN
613 # TODO: Type conversions?
615 verify_fn = _VERIFY_FN[fdef.kind]
616 if not verify_fn(value):
617 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
618 " with '%s', expected %s" %
619 (name, fdef.kind, value.__class__.__name__,
623 hints_fn(op, datakind, name, value)
625 for (fn_flags, fn, valprepfn) in op_data:
626 if fn_flags is None or fn_flags & field_flags:
627 # Prepare value if necessary (e.g. compile regular expression)
629 value = valprepfn(value)
631 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
633 raise errors.ProgrammerError("Unable to find operator implementation"
634 " (op '%s', flags %s)" % (op, field_flags))
637 def _CompileFilter(fields, hints, qfilter):
638 """Converts a query filter into a callable function.
640 See L{_FilterCompilerHelper} for details.
645 return _FilterCompilerHelper(fields)(hints, qfilter)
649 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
650 """Initializes this class.
652 The field definition is a dictionary with the field's name as a key and a
653 tuple containing, in order, the field definition object
654 (L{objects.QueryFieldDefinition}, the data kind to help calling code
655 collect data and a retrieval function. The retrieval function is called
656 with two parameters, in order, the data container and the item in container
657 (see L{Query.Query}).
659 Users of this class can call L{RequestedData} before preparing the data
660 container to determine what data is needed.
662 @type fieldlist: dictionary
663 @param fieldlist: Field definitions
664 @type selected: list of strings
665 @param selected: List of selected fields
668 assert namefield is None or namefield in fieldlist
670 self._fields = _GetQueryFields(fieldlist, selected)
672 self._filter_fn = None
673 self._requested_names = None
674 self._filter_datakinds = frozenset()
676 if qfilter is not None:
677 # Collect requested names if wanted
679 hints = _FilterHints(namefield)
683 # Build filter function
684 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
686 self._requested_names = hints.RequestedNames()
687 self._filter_datakinds = hints.ReferencedData()
689 if namefield is None:
692 (_, _, _, self._name_fn) = fieldlist[namefield]
694 def RequestedNames(self):
695 """Returns all names referenced in the filter.
697 If there is no filter or operators are preventing determining the exact
698 names, C{None} is returned.
701 return self._requested_names
703 def RequestedData(self):
704 """Gets requested kinds of data.
709 return (self._filter_datakinds |
710 frozenset(datakind for (_, datakind, _, _) in self._fields
711 if datakind is not None))
714 """Returns the list of fields for this query.
716 Includes unknown fields.
718 @rtype: List of L{objects.QueryFieldDefinition}
721 return GetAllFields(self._fields)
723 def Query(self, ctx, sort_by_name=True):
726 @param ctx: Data container passed to field retrieval functions, must
727 support iteration using C{__iter__}
728 @type sort_by_name: boolean
729 @param sort_by_name: Whether to sort by name or keep the input data's
733 sort = (self._name_fn and sort_by_name)
737 for idx, item in enumerate(ctx):
738 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
741 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
745 _VerifyResultRow(self._fields, row)
748 (status, name) = _ProcessResult(self._name_fn(ctx, item))
749 assert status == constants.RS_NORMAL
750 # TODO: Are there cases where we wouldn't want to use NiceSort?
751 # Answer: if the name field is non-string...
752 result.append((utils.NiceSortKey(name), idx, row))
759 # TODO: Would "heapq" be more efficient than sorting?
761 # Sorting in-place instead of using "sorted()"
764 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
766 return map(operator.itemgetter(2), result)
768 def OldStyleQuery(self, ctx, sort_by_name=True):
769 """Query with "old" query result format.
771 See L{Query.Query} for arguments.
774 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
775 if fdef.kind == QFT_UNKNOWN)
777 raise errors.OpPrereqError("Unknown output fields selected: %s" %
778 (utils.CommaJoin(unknown), ),
781 return [[value for (_, value) in row]
782 for row in self.Query(ctx, sort_by_name=sort_by_name)]
785 def _ProcessResult(value):
786 """Converts result values into externally-visible ones.
789 if value is _FS_UNKNOWN:
790 return (RS_UNKNOWN, None)
791 elif value is _FS_NODATA:
792 return (RS_NODATA, None)
793 elif value is _FS_UNAVAIL:
794 return (RS_UNAVAIL, None)
795 elif value is _FS_OFFLINE:
796 return (RS_OFFLINE, None)
798 return (RS_NORMAL, value)
801 def _VerifyResultRow(fields, row):
802 """Verifies the contents of a query result row.
805 @param fields: Field definitions for result
806 @type row: list of tuples
810 assert len(row) == len(fields)
812 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
813 if status == RS_NORMAL:
814 if not _VERIFY_FN[fdef.kind](value):
815 errs.append("normal field %s fails validation (value is %s)" %
817 elif value is not None:
818 errs.append("abnormal field %s has a non-None value" % fdef.name)
819 assert not errs, ("Failed validation: %s in row %s" %
820 (utils.CommaJoin(errs), row))
823 def _FieldDictKey((fdef, _, flags, fn)):
824 """Generates key for field dictionary.
827 assert fdef.name and fdef.title, "Name and title are required"
828 assert FIELD_NAME_RE.match(fdef.name)
829 assert TITLE_RE.match(fdef.title)
830 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
831 fdef.doc.strip() == fdef.doc), \
832 "Invalid description for field '%s'" % fdef.name
834 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
839 def _PrepareFieldList(fields, aliases):
840 """Prepares field list for use by L{Query}.
842 Converts the list to a dictionary and does some verification.
844 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
845 kind, retrieval function)
846 @param fields: List of fields, see L{Query.__init__} for a better
848 @type aliases: list of tuples; (alias, target)
849 @param aliases: list of tuples containing aliases; for each
850 alias/target pair, a duplicate will be created in the field list
852 @return: Field dictionary for L{Query}
856 duplicates = utils.FindDuplicates(fdef.title.lower()
857 for (fdef, _, _, _) in fields)
858 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
860 result = utils.SequenceToDict(fields, key=_FieldDictKey)
862 for alias, target in aliases:
863 assert alias not in result, "Alias %s overrides an existing field" % alias
864 assert target in result, "Missing target %s for alias %s" % (target, alias)
865 (fdef, k, flags, fn) = result[target]
868 result[alias] = (fdef, k, flags, fn)
870 assert len(result) == len(fields) + len(aliases)
871 assert compat.all(name == fdef.name
872 for (name, (fdef, _, _, _)) in result.items())
877 def GetQueryResponse(query, ctx, sort_by_name=True):
878 """Prepares the response for a query.
880 @type query: L{Query}
881 @param ctx: Data container, see L{Query.Query}
882 @type sort_by_name: boolean
883 @param sort_by_name: Whether to sort by name or keep the input data's
887 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
888 fields=query.GetFields()).ToDict()
891 def QueryFields(fielddefs, selected):
892 """Returns list of available fields.
894 @type fielddefs: dict
895 @param fielddefs: Field definitions
896 @type selected: list of strings
897 @param selected: List of selected fields
898 @return: List of L{objects.QueryFieldDefinition}
902 # Client requests all fields, sort by name
903 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
904 key=operator.attrgetter("name"))
906 # Keep order as requested by client
907 fdefs = Query(fielddefs, selected).GetFields()
909 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
912 def _MakeField(name, title, kind, doc):
913 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
915 @param name: Field name as a regular expression
916 @param title: Human-readable title
917 @param kind: Field type
918 @param doc: Human-readable description
921 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
925 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
926 """Returns a static value.
932 def _StaticValue(value):
933 """Prepares a function to return a static value.
936 return compat.partial(_StaticValueInner, value)
939 def _GetNodeRole(node, master_name):
940 """Determine node role.
942 @type node: L{objects.Node}
943 @param node: Node object
944 @type master_name: string
945 @param master_name: Master node name
948 if node.name == master_name:
949 return constants.NR_MASTER
950 elif node.master_candidate:
951 return constants.NR_MCANDIDATE
953 return constants.NR_DRAINED
955 return constants.NR_OFFLINE
957 return constants.NR_REGULAR
960 def _GetItemAttr(attr):
961 """Returns a field function to return an attribute of the item.
963 @param attr: Attribute name
966 getter = operator.attrgetter(attr)
967 return lambda _, item: getter(item)
970 def _GetNDParam(name):
971 """Return a field function to return an ND parameter out of the context.
975 if ctx.ndparams is None:
978 return ctx.ndparams.get(name, None)
982 def _BuildNDFields(is_group):
983 """Builds all the ndparam fields.
985 @param is_group: whether this is called at group or node level
989 field_kind = GQ_CONFIG
991 field_kind = NQ_GROUP
992 return [(_MakeField("ndp/%s" % name,
993 constants.NDS_PARAMETER_TITLES.get(name,
995 _VTToQFT[kind], "The \"%s\" node parameter" % name),
996 field_kind, 0, _GetNDParam(name))
997 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1000 def _ConvWrapInner(convert, fn, ctx, item):
1001 """Wrapper for converting values.
1003 @param convert: Conversion function receiving value as single parameter
1004 @param fn: Retrieval function
1007 value = fn(ctx, item)
1009 # Is the value an abnormal status?
1010 if compat.any(value is fs for fs in _FS_ALL):
1014 # TODO: Should conversion function also receive context, item or both?
1015 return convert(value)
1018 def _ConvWrap(convert, fn):
1019 """Convenience wrapper for L{_ConvWrapInner}.
1021 @param convert: Conversion function receiving value as single parameter
1022 @param fn: Retrieval function
1025 return compat.partial(_ConvWrapInner, convert, fn)
1028 def _GetItemTimestamp(getter):
1029 """Returns function for getting timestamp of item.
1031 @type getter: callable
1032 @param getter: Function to retrieve timestamp attribute
1036 """Returns a timestamp of item.
1039 timestamp = getter(item)
1040 if timestamp is None:
1041 # Old configs might not have all timestamps
1049 def _GetItemTimestampFields(datatype):
1050 """Returns common timestamp fields.
1052 @param datatype: Field data type for use by L{Query.RequestedData}
1056 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1057 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1058 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1059 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1063 class NodeQueryData:
1064 """Data container for node data queries.
1067 def __init__(self, nodes, live_data, master_name, node_to_primary,
1068 node_to_secondary, groups, oob_support, cluster):
1069 """Initializes this class.
1073 self.live_data = live_data
1074 self.master_name = master_name
1075 self.node_to_primary = node_to_primary
1076 self.node_to_secondary = node_to_secondary
1077 self.groups = groups
1078 self.oob_support = oob_support
1079 self.cluster = cluster
1081 # Used for individual rows
1082 self.curlive_data = None
1083 self.ndparams = None
1086 """Iterate over all nodes.
1088 This function has side-effects and only one instance of the resulting
1089 generator should be used at a time.
1092 for node in self.nodes:
1093 group = self.groups.get(node.group, None)
1095 self.ndparams = None
1097 self.ndparams = self.cluster.FillND(node, group)
1099 self.curlive_data = self.live_data.get(node.name, None)
1101 self.curlive_data = None
1105 #: Fields that are direct attributes of an L{objects.Node} object
1106 _NODE_SIMPLE_FIELDS = {
1107 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1108 "master_candidate": ("MasterC", QFT_BOOL, 0,
1109 "Whether node is a master candidate"),
1110 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1111 "Whether node can become a master candidate"),
1112 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1113 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1114 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1115 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1116 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1120 #: Fields requiring talking to the node
1121 # Note that none of these are available for non-vm_capable nodes
1122 _NODE_LIVE_FIELDS = {
1123 "bootid": ("BootID", QFT_TEXT, "bootid",
1124 "Random UUID renewed for each system reboot, can be used"
1125 " for detecting reboots by tracking changes"),
1126 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1127 "Number of NUMA domains on node (if exported by hypervisor)"),
1128 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1129 "Number of physical CPU sockets (if exported by hypervisor)"),
1130 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1131 "dfree": ("DFree", QFT_UNIT, "vg_free",
1132 "Available disk space in volume group"),
1133 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1134 "Total disk space in volume group used for instance disk"
1136 "mfree": ("MFree", QFT_UNIT, "memory_free",
1137 "Memory available for instance allocations"),
1138 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1139 "Amount of memory used by node (dom0 for Xen)"),
1140 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1141 "Total amount of memory of physical machine"),
1146 """Build function for calling another function with an node group.
1148 @param cb: The callback to be called with the nodegroup
1152 """Get group data for a node.
1154 @type ctx: L{NodeQueryData}
1155 @type inst: L{objects.Node}
1156 @param inst: Node object
1159 ng = ctx.groups.get(node.group, None)
1161 # Nodes always have a group, or the configuration is corrupt
1164 return cb(ctx, node, ng)
1169 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1170 """Returns the name of a node's group.
1172 @type ctx: L{NodeQueryData}
1173 @type node: L{objects.Node}
1174 @param node: Node object
1175 @type ng: L{objects.NodeGroup}
1176 @param ng: The node group this node belongs to
1182 def _GetNodePower(ctx, node):
1183 """Returns the node powered state
1185 @type ctx: L{NodeQueryData}
1186 @type node: L{objects.Node}
1187 @param node: Node object
1190 if ctx.oob_support[node.name]:
1196 def _GetNdParams(ctx, node, ng):
1197 """Returns the ndparams for this node.
1199 @type ctx: L{NodeQueryData}
1200 @type node: L{objects.Node}
1201 @param node: Node object
1202 @type ng: L{objects.NodeGroup}
1203 @param ng: The node group this node belongs to
1206 return ctx.cluster.SimpleFillND(ng.FillND(node))
1209 def _GetLiveNodeField(field, kind, ctx, node):
1210 """Gets the value of a "live" field from L{NodeQueryData}.
1212 @param field: Live field name
1213 @param kind: Data kind, one of L{constants.QFT_ALL}
1214 @type ctx: L{NodeQueryData}
1215 @type node: L{objects.Node}
1216 @param node: Node object
1222 if not node.vm_capable:
1225 if not ctx.curlive_data:
1229 value = ctx.curlive_data[field]
1233 if kind == QFT_TEXT:
1236 assert kind in (QFT_NUMBER, QFT_UNIT)
1238 # Try to convert into number
1241 except (ValueError, TypeError):
1242 logging.exception("Failed to convert node field '%s' (value %r) to int",
1247 def _GetNodeHvState(_, node):
1248 """Converts node's hypervisor state for query result.
1251 hv_state = node.hv_state
1253 if hv_state is None:
1256 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1259 def _GetNodeDiskState(_, node):
1260 """Converts node's disk state for query result.
1263 disk_state = node.disk_state
1265 if disk_state is None:
1268 return dict((disk_kind, dict((name, value.ToDict())
1269 for (name, value) in kind_state.items()))
1270 for (disk_kind, kind_state) in disk_state.items())
1273 def _BuildNodeFields():
1274 """Builds list of fields for node queries.
1278 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1279 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1280 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1281 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1282 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1283 lambda ctx, node: list(node.GetTags())),
1284 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1285 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1286 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1287 _GetGroup(_GetNodeGroup)),
1288 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1289 NQ_CONFIG, 0, _GetItemAttr("group")),
1290 (_MakeField("powered", "Powered", QFT_BOOL,
1291 "Whether node is thought to be powered on"),
1292 NQ_OOB, 0, _GetNodePower),
1293 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1294 "Merged node parameters"),
1295 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1296 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1297 "Custom node parameters"),
1298 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1299 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1300 NQ_CONFIG, 0, _GetNodeHvState),
1301 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1302 NQ_CONFIG, 0, _GetNodeDiskState),
1305 fields.extend(_BuildNDFields(False))
1308 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1309 constants.NR_REGULAR, constants.NR_DRAINED,
1310 constants.NR_OFFLINE)
1311 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1312 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1314 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1315 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1316 assert set(role_values) == constants.NR_ALL
1318 def _GetLength(getter):
1319 return lambda ctx, node: len(getter(ctx)[node.name])
1321 def _GetList(getter):
1322 return lambda ctx, node: list(getter(ctx)[node.name])
1324 # Add fields operating on instance lists
1325 for prefix, titleprefix, docword, getter in \
1326 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1327 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1328 # TODO: Allow filterting by hostname in list
1330 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1331 "Number of instances with this node as %s" % docword),
1332 NQ_INST, 0, _GetLength(getter)),
1333 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1335 "List of instances with this node as %s" % docword),
1336 NQ_INST, 0, _GetList(getter)),
1341 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1342 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1345 # Add fields requiring live data
1347 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1348 compat.partial(_GetLiveNodeField, nfield, kind))
1349 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1353 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1355 return _PrepareFieldList(fields, [])
1358 class InstanceQueryData:
1359 """Data container for instance data queries.
1362 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1363 live_data, wrongnode_inst, console, nodes, groups):
1364 """Initializes this class.
1366 @param instances: List of instance objects
1367 @param cluster: Cluster object
1368 @type disk_usage: dict; instance name as key
1369 @param disk_usage: Per-instance disk usage
1370 @type offline_nodes: list of strings
1371 @param offline_nodes: List of offline nodes
1372 @type bad_nodes: list of strings
1373 @param bad_nodes: List of faulty nodes
1374 @type live_data: dict; instance name as key
1375 @param live_data: Per-instance live data
1376 @type wrongnode_inst: set
1377 @param wrongnode_inst: Set of instances running on wrong node(s)
1378 @type console: dict; instance name as key
1379 @param console: Per-instance console information
1380 @type nodes: dict; node name as key
1381 @param nodes: Node objects
1384 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1385 "Offline nodes not included in bad nodes"
1386 assert not (set(live_data.keys()) & set(bad_nodes)), \
1387 "Found live data for bad or offline nodes"
1389 self.instances = instances
1390 self.cluster = cluster
1391 self.disk_usage = disk_usage
1392 self.offline_nodes = offline_nodes
1393 self.bad_nodes = bad_nodes
1394 self.live_data = live_data
1395 self.wrongnode_inst = wrongnode_inst
1396 self.console = console
1398 self.groups = groups
1400 # Used for individual rows
1401 self.inst_hvparams = None
1402 self.inst_beparams = None
1403 self.inst_osparams = None
1404 self.inst_nicparams = None
1407 """Iterate over all instances.
1409 This function has side-effects and only one instance of the resulting
1410 generator should be used at a time.
1413 for inst in self.instances:
1414 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1415 self.inst_beparams = self.cluster.FillBE(inst)
1416 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1417 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1418 for nic in inst.nics]
1423 def _GetInstOperState(ctx, inst):
1424 """Get instance's operational status.
1426 @type ctx: L{InstanceQueryData}
1427 @type inst: L{objects.Instance}
1428 @param inst: Instance object
1431 # Can't use RS_OFFLINE here as it would describe the instance to
1432 # be offline when we actually don't know due to missing data
1433 if inst.primary_node in ctx.bad_nodes:
1436 return bool(ctx.live_data.get(inst.name))
1439 def _GetInstLiveData(name):
1440 """Build function for retrieving live data.
1443 @param name: Live data field name
1447 """Get live data for an instance.
1449 @type ctx: L{InstanceQueryData}
1450 @type inst: L{objects.Instance}
1451 @param inst: Instance object
1454 if (inst.primary_node in ctx.bad_nodes or
1455 inst.primary_node in ctx.offline_nodes):
1456 # Can't use RS_OFFLINE here as it would describe the instance to be
1457 # offline when we actually don't know due to missing data
1460 if inst.name in ctx.live_data:
1461 data = ctx.live_data[inst.name]
1470 def _GetInstStatus(ctx, inst):
1471 """Get instance status.
1473 @type ctx: L{InstanceQueryData}
1474 @type inst: L{objects.Instance}
1475 @param inst: Instance object
1478 if inst.primary_node in ctx.offline_nodes:
1479 return constants.INSTST_NODEOFFLINE
1481 if inst.primary_node in ctx.bad_nodes:
1482 return constants.INSTST_NODEDOWN
1484 if bool(ctx.live_data.get(inst.name)):
1485 if inst.name in ctx.wrongnode_inst:
1486 return constants.INSTST_WRONGNODE
1487 elif inst.admin_state == constants.ADMINST_UP:
1488 return constants.INSTST_RUNNING
1490 return constants.INSTST_ERRORUP
1492 if inst.admin_state == constants.ADMINST_UP:
1493 return constants.INSTST_ERRORDOWN
1494 elif inst.admin_state == constants.ADMINST_DOWN:
1495 return constants.INSTST_ADMINDOWN
1497 return constants.INSTST_ADMINOFFLINE
1500 def _GetInstDiskSize(index):
1501 """Build function for retrieving disk size.
1504 @param index: Disk index
1508 """Get size of a disk.
1510 @type inst: L{objects.Instance}
1511 @param inst: Instance object
1515 return inst.disks[index].size
1522 def _GetInstNic(index, cb):
1523 """Build function for calling another function with an instance NIC.
1526 @param index: NIC index
1532 """Call helper function with instance NIC.
1534 @type ctx: L{InstanceQueryData}
1535 @type inst: L{objects.Instance}
1536 @param inst: Instance object
1540 nic = inst.nics[index]
1544 return cb(ctx, index, nic)
1549 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1550 """Get a NIC's IP address.
1552 @type ctx: L{InstanceQueryData}
1553 @type nic: L{objects.NIC}
1554 @param nic: NIC object
1563 def _GetInstNicBridge(ctx, index, _):
1564 """Get a NIC's bridge.
1566 @type ctx: L{InstanceQueryData}
1568 @param index: NIC index
1571 assert len(ctx.inst_nicparams) >= index
1573 nicparams = ctx.inst_nicparams[index]
1575 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1576 return nicparams[constants.NIC_LINK]
1581 def _GetInstAllNicBridges(ctx, inst):
1582 """Get all network bridges for an instance.
1584 @type ctx: L{InstanceQueryData}
1585 @type inst: L{objects.Instance}
1586 @param inst: Instance object
1589 assert len(ctx.inst_nicparams) == len(inst.nics)
1593 for nicp in ctx.inst_nicparams:
1594 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1595 result.append(nicp[constants.NIC_LINK])
1599 assert len(result) == len(inst.nics)
1604 def _GetInstNicParam(name):
1605 """Build function for retrieving a NIC parameter.
1608 @param name: Parameter name
1611 def fn(ctx, index, _):
1612 """Get a NIC's bridge.
1614 @type ctx: L{InstanceQueryData}
1615 @type inst: L{objects.Instance}
1616 @param inst: Instance object
1617 @type nic: L{objects.NIC}
1618 @param nic: NIC object
1621 assert len(ctx.inst_nicparams) >= index
1622 return ctx.inst_nicparams[index][name]
1627 def _GetInstanceNetworkFields():
1628 """Get instance fields involving network interfaces.
1630 @return: Tuple containing list of field definitions used as input for
1631 L{_PrepareFieldList} and a list of aliases
1634 nic_mac_fn = lambda ctx, _, nic: nic.mac
1635 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1636 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1640 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1641 "Number of network interfaces"),
1642 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1643 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1644 "List containing each network interface's MAC address"),
1645 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1646 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1647 "List containing each network interface's IP address"),
1648 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1649 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1650 "List containing each network interface's mode"), IQ_CONFIG, 0,
1651 lambda ctx, inst: [nicp[constants.NIC_MODE]
1652 for nicp in ctx.inst_nicparams]),
1653 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1654 "List containing each network interface's link"), IQ_CONFIG, 0,
1655 lambda ctx, inst: [nicp[constants.NIC_LINK]
1656 for nicp in ctx.inst_nicparams]),
1657 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1658 "List containing each network interface's bridge"),
1659 IQ_CONFIG, 0, _GetInstAllNicBridges),
1663 for i in range(constants.MAX_NICS):
1664 numtext = utils.FormatOrdinal(i + 1)
1666 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1667 "IP address of %s network interface" % numtext),
1668 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1669 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1670 "MAC address of %s network interface" % numtext),
1671 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1672 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1673 "Mode of %s network interface" % numtext),
1674 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1675 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1676 "Link of %s network interface" % numtext),
1677 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1678 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1679 "Bridge of %s network interface" % numtext),
1680 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1684 # Legacy fields for first NIC
1686 ("mac", "nic.mac/0"),
1687 ("bridge", "nic.bridge/0"),
1688 ("nic_mode", "nic.mode/0"),
1689 ("nic_link", "nic.link/0"),
1692 return (fields, aliases)
1695 def _GetInstDiskUsage(ctx, inst):
1696 """Get disk usage for an instance.
1698 @type ctx: L{InstanceQueryData}
1699 @type inst: L{objects.Instance}
1700 @param inst: Instance object
1703 usage = ctx.disk_usage[inst.name]
1711 def _GetInstanceConsole(ctx, inst):
1712 """Get console information for instance.
1714 @type ctx: L{InstanceQueryData}
1715 @type inst: L{objects.Instance}
1716 @param inst: Instance object
1719 consinfo = ctx.console[inst.name]
1721 if consinfo is None:
1727 def _GetInstanceDiskFields():
1728 """Get instance fields involving disks.
1730 @return: List of field definitions used as input for L{_PrepareFieldList}
1734 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1735 "Total disk space used by instance on each of its nodes;"
1736 " this is not the disk size visible to the instance, but"
1737 " the usage on the node"),
1738 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1739 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1740 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1741 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1742 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1747 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1748 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1749 IQ_CONFIG, 0, _GetInstDiskSize(i))
1750 for i in range(constants.MAX_DISKS)
1756 def _GetInstanceParameterFields():
1757 """Get instance fields involving parameters.
1759 @return: List of field definitions used as input for L{_PrepareFieldList}
1764 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1765 "Hypervisor parameters (merged)"),
1766 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1767 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1768 "Backend parameters (merged)"),
1769 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1770 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1771 "Operating system parameters (merged)"),
1772 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1774 # Unfilled parameters
1775 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1776 "Custom hypervisor parameters"),
1777 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1778 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1779 "Custom backend parameters",),
1780 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1781 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1782 "Custom operating system parameters",),
1783 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1784 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1785 "Custom network interface parameters"),
1786 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1790 def _GetInstHvParam(name):
1791 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1794 (_MakeField("hv/%s" % name,
1795 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
1796 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1797 IQ_CONFIG, 0, _GetInstHvParam(name))
1798 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1799 if name not in constants.HVC_GLOBALS
1803 def _GetInstBeParam(name):
1804 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1807 (_MakeField("be/%s" % name,
1808 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
1809 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1810 IQ_CONFIG, 0, _GetInstBeParam(name))
1811 for name, kind in constants.BES_PARAMETER_TYPES.items()
1817 _INST_SIMPLE_FIELDS = {
1818 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1819 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1820 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1821 # Depending on the hypervisor, the port can be None
1822 "network_port": ("Network_port", QFT_OTHER, 0,
1823 "Instance network port if available (e.g. for VNC console)"),
1824 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1825 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1826 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1830 def _GetInstNodeGroup(ctx, default, node_name):
1831 """Gets group UUID of an instance node.
1833 @type ctx: L{InstanceQueryData}
1834 @param default: Default value
1835 @type node_name: string
1836 @param node_name: Node name
1840 node = ctx.nodes[node_name]
1847 def _GetInstNodeGroupName(ctx, default, node_name):
1848 """Gets group name of an instance node.
1850 @type ctx: L{InstanceQueryData}
1851 @param default: Default value
1852 @type node_name: string
1853 @param node_name: Node name
1857 node = ctx.nodes[node_name]
1862 group = ctx.groups[node.group]
1869 def _BuildInstanceFields():
1870 """Builds list of fields for instance queries.
1874 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1875 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1876 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1877 "Primary node's group"),
1879 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1880 inst.primary_node)),
1881 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1882 "Primary node's group UUID"),
1884 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1885 # TODO: Allow filtering by secondary node as hostname
1886 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1887 "Secondary nodes; usually this will just be one node"),
1888 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1889 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1890 "Node groups of secondary nodes"),
1892 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1893 inst.secondary_nodes)),
1894 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1895 "Node group UUIDs of secondary nodes"),
1897 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1898 inst.secondary_nodes)),
1899 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1900 "Desired state of instance"),
1901 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1902 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1903 "Desired state of instance"),
1904 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1905 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1906 lambda ctx, inst: list(inst.GetTags())),
1907 (_MakeField("console", "Console", QFT_OTHER,
1908 "Instance console information"), IQ_CONSOLE, 0,
1909 _GetInstanceConsole),
1914 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1915 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1918 # Fields requiring talking to the node
1920 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1921 IQ_LIVE, 0, _GetInstOperState),
1922 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1923 "Actual memory usage as seen by hypervisor"),
1924 IQ_LIVE, 0, _GetInstLiveData("memory")),
1925 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1926 "Actual number of VCPUs as seen by hypervisor"),
1927 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1931 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1932 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1933 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1934 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1935 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1936 " and actually is, \"%s\" if instance is stopped and"
1937 " is not running, \"%s\" if instance running, but not on its"
1938 " designated primary node, \"%s\" if instance should be"
1939 " stopped, but is actually running, \"%s\" if instance should"
1940 " run, but doesn't, \"%s\" if instance's primary node is down,"
1941 " \"%s\" if instance's primary node is marked offline,"
1942 " \"%s\" if instance is offline and does not use dynamic"
1943 " resources" % status_values)
1944 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1945 IQ_LIVE, 0, _GetInstStatus))
1946 assert set(status_values) == constants.INSTST_ALL, \
1947 "Status documentation mismatch"
1949 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1951 fields.extend(network_fields)
1952 fields.extend(_GetInstanceParameterFields())
1953 fields.extend(_GetInstanceDiskFields())
1954 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1957 ("vcpus", "be/vcpus"),
1958 ("be/memory", "be/maxmem"),
1959 ("sda_size", "disk.size/0"),
1960 ("sdb_size", "disk.size/1"),
1963 return _PrepareFieldList(fields, aliases)
1966 class LockQueryData:
1967 """Data container for lock data queries.
1970 def __init__(self, lockdata):
1971 """Initializes this class.
1974 self.lockdata = lockdata
1977 """Iterate over all locks.
1980 return iter(self.lockdata)
1983 def _GetLockOwners(_, data):
1984 """Returns a sorted list of a lock's current owners.
1987 (_, _, owners, _) = data
1990 owners = utils.NiceSort(owners)
1995 def _GetLockPending(_, data):
1996 """Returns a sorted list of a lock's pending acquires.
1999 (_, _, _, pending) = data
2002 pending = [(mode, utils.NiceSort(names))
2003 for (mode, names) in pending]
2008 def _BuildLockFields():
2009 """Builds list of fields for lock queries.
2012 return _PrepareFieldList([
2013 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2014 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2015 lambda ctx, (name, mode, owners, pending): name),
2016 (_MakeField("mode", "Mode", QFT_OTHER,
2017 "Mode in which the lock is currently acquired"
2018 " (exclusive or shared)"),
2019 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2020 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2021 LQ_OWNER, 0, _GetLockOwners),
2022 (_MakeField("pending", "Pending", QFT_OTHER,
2023 "Threads waiting for the lock"),
2024 LQ_PENDING, 0, _GetLockPending),
2028 class GroupQueryData:
2029 """Data container for node group data queries.
2032 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2034 """Initializes this class.
2036 @param cluster: Cluster object
2037 @param groups: List of node group objects
2038 @type group_to_nodes: dict; group UUID as key
2039 @param group_to_nodes: Per-group list of nodes
2040 @type group_to_instances: dict; group UUID as key
2041 @param group_to_instances: Per-group list of (primary) instances
2042 @type want_diskparams: bool
2043 @param want_diskparams: Whether diskparamters should be calculated
2046 self.groups = groups
2047 self.group_to_nodes = group_to_nodes
2048 self.group_to_instances = group_to_instances
2049 self.cluster = cluster
2050 self.want_diskparams = want_diskparams
2052 # Used for individual rows
2053 self.group_ipolicy = None
2054 self.ndparams = None
2055 self.group_dp = None
2058 """Iterate over all node groups.
2060 This function has side-effects and only one instance of the resulting
2061 generator should be used at a time.
2064 for group in self.groups:
2065 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2066 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2067 if self.want_diskparams:
2068 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2070 self.group_dp = None
2074 _GROUP_SIMPLE_FIELDS = {
2075 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2076 "name": ("Group", QFT_TEXT, "Group name"),
2077 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2078 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2082 def _BuildGroupFields():
2083 """Builds list of fields for node group queries.
2087 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2089 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2091 def _GetLength(getter):
2092 return lambda ctx, group: len(getter(ctx)[group.uuid])
2094 def _GetSortedList(getter):
2095 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2097 group_to_nodes = operator.attrgetter("group_to_nodes")
2098 group_to_instances = operator.attrgetter("group_to_instances")
2100 # Add fields for nodes
2102 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2103 GQ_NODE, 0, _GetLength(group_to_nodes)),
2104 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2105 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2108 # Add fields for instances
2110 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2111 "Number of primary instances"),
2112 GQ_INST, 0, _GetLength(group_to_instances)),
2113 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2114 "List of primary instances"),
2115 GQ_INST, 0, _GetSortedList(group_to_instances)),
2120 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2121 lambda ctx, group: list(group.GetTags())),
2122 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2123 "Instance policy limitations (merged)"),
2124 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2125 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2126 "Custom instance policy limitations"),
2127 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2128 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2129 "Custom node parameters"),
2130 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2131 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2133 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2134 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2135 "Disk parameters (merged)"),
2136 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2137 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2138 "Custom disk parameters"),
2139 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2143 fields.extend(_BuildNDFields(True))
2145 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2147 return _PrepareFieldList(fields, [])
2150 class OsInfo(objects.ConfigObject):
2163 def _BuildOsFields():
2164 """Builds list of fields for operating system queries.
2168 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2169 None, 0, _GetItemAttr("name")),
2170 (_MakeField("valid", "Valid", QFT_BOOL,
2171 "Whether operating system definition is valid"),
2172 None, 0, _GetItemAttr("valid")),
2173 (_MakeField("hidden", "Hidden", QFT_BOOL,
2174 "Whether operating system is hidden"),
2175 None, 0, _GetItemAttr("hidden")),
2176 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2177 "Whether operating system is blacklisted"),
2178 None, 0, _GetItemAttr("blacklisted")),
2179 (_MakeField("variants", "Variants", QFT_OTHER,
2180 "Operating system variants"),
2181 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2182 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2183 "Operating system API versions"),
2184 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2185 (_MakeField("parameters", "Parameters", QFT_OTHER,
2186 "Operating system parameters"),
2187 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2188 _GetItemAttr("parameters"))),
2189 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2190 "Status from node"),
2191 None, 0, _GetItemAttr("node_status")),
2194 return _PrepareFieldList(fields, [])
2197 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2198 """Return L{_FS_UNAVAIL} if job is None.
2200 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2201 found, in which case this function converts it to L{_FS_UNAVAIL}.
2210 def _JobUnavail(inner):
2211 """Wrapper for L{_JobUnavailInner}.
2214 return compat.partial(_JobUnavailInner, inner)
2217 def _PerJobOpInner(fn, job):
2218 """Executes a function per opcode in a job.
2221 return map(fn, job.ops)
2225 """Wrapper for L{_PerJobOpInner}.
2228 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2231 def _JobTimestampInner(fn, job):
2232 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2237 if timestamp is None:
2243 def _JobTimestamp(fn):
2244 """Wrapper for L{_JobTimestampInner}.
2247 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2250 def _BuildJobFields():
2251 """Builds list of fields for job queries.
2255 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2256 None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2257 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2258 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2259 (_MakeField("priority", "Priority", QFT_NUMBER,
2260 ("Current job priority (%s to %s)" %
2261 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2262 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2263 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2264 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2265 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2266 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2267 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2268 "List of opcodes results"),
2269 None, 0, _PerJobOp(operator.attrgetter("result"))),
2270 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2271 "List of opcodes status"),
2272 None, 0, _PerJobOp(operator.attrgetter("status"))),
2273 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2274 "List of opcode output logs"),
2275 None, 0, _PerJobOp(operator.attrgetter("log"))),
2276 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2277 "List of opcode start timestamps (before acquiring locks)"),
2278 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2279 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2280 "List of opcode execution start timestamps (after acquiring"
2282 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2283 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2284 "List of opcode execution end timestamps"),
2285 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2286 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2287 "List of opcode priorities"),
2288 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2289 (_MakeField("summary", "Summary", QFT_OTHER,
2290 "List of per-opcode summaries"),
2291 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2295 for (name, attr, title, desc) in [
2296 ("received_ts", "received_timestamp", "Received",
2297 "Timestamp of when job was received"),
2298 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2299 ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2301 getter = operator.attrgetter(attr)
2303 (_MakeField(name, title, QFT_OTHER,
2304 "%s (tuple containing seconds and microseconds)" % desc),
2305 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2308 return _PrepareFieldList(fields, [])
2311 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2312 """Returns an export name if available.
2321 def _BuildExportFields():
2322 """Builds list of fields for exports.
2326 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2327 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2328 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2329 None, 0, _GetExportName),
2332 return _PrepareFieldList(fields, [])
2335 _CLUSTER_VERSION_FIELDS = {
2336 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2337 "Software version"),
2338 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2339 constants.PROTOCOL_VERSION,
2340 "RPC protocol version"),
2341 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2342 "Configuration format version"),
2343 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2344 "API version for OS template scripts"),
2345 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2346 "Import/export file format version"),
2350 _CLUSTER_SIMPLE_FIELDS = {
2351 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2352 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2353 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2357 class ClusterQueryData:
2358 def __init__(self, cluster, drain_flag, watcher_pause):
2359 """Initializes this class.
2361 @type cluster: L{objects.Cluster}
2362 @param cluster: Instance of cluster object
2363 @type drain_flag: bool
2364 @param drain_flag: Whether job queue is drained
2365 @type watcher_pause: number
2366 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2369 self._cluster = cluster
2370 self.drain_flag = drain_flag
2371 self.watcher_pause = watcher_pause
2374 return iter([self._cluster])
2377 def _ClusterWatcherPause(ctx, _):
2378 """Returns until when watcher is paused (if available).
2381 if ctx.watcher_pause is None:
2384 return ctx.watcher_pause
2387 def _BuildClusterFields():
2388 """Builds list of fields for cluster information.
2392 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2393 lambda ctx, cluster: list(cluster.GetTags())),
2394 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2395 "Architecture information"), None, 0,
2396 lambda ctx, _: runtime.GetArchInfo()),
2397 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2398 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2399 lambda ctx, _: ctx.drain_flag),
2400 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2401 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2402 _ClusterWatcherPause),
2407 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2408 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2413 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2414 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
2418 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2420 return _PrepareFieldList(fields, [
2421 ("name", "cluster_name"),
2425 #: Fields for cluster information
2426 CLUSTER_FIELDS = _BuildClusterFields()
2428 #: Fields available for node queries
2429 NODE_FIELDS = _BuildNodeFields()
2431 #: Fields available for instance queries
2432 INSTANCE_FIELDS = _BuildInstanceFields()
2434 #: Fields available for lock queries
2435 LOCK_FIELDS = _BuildLockFields()
2437 #: Fields available for node group queries
2438 GROUP_FIELDS = _BuildGroupFields()
2440 #: Fields available for operating system queries
2441 OS_FIELDS = _BuildOsFields()
2443 #: Fields available for job queries
2444 JOB_FIELDS = _BuildJobFields()
2446 #: Fields available for exports
2447 EXPORT_FIELDS = _BuildExportFields()
2449 #: All available resources
2451 constants.QR_CLUSTER: CLUSTER_FIELDS,
2452 constants.QR_INSTANCE: INSTANCE_FIELDS,
2453 constants.QR_NODE: NODE_FIELDS,
2454 constants.QR_LOCK: LOCK_FIELDS,
2455 constants.QR_GROUP: GROUP_FIELDS,
2456 constants.QR_OS: OS_FIELDS,
2457 constants.QR_JOB: JOB_FIELDS,
2458 constants.QR_EXPORT: EXPORT_FIELDS,
2461 #: All available field lists
2462 ALL_FIELD_LISTS = ALL_FIELDS.values()