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)
76 NETQ_INST) = range(300, 304)
78 # Constants for requesting data from the caller/data provider. Each property
79 # collected/computed separately by the data provider should have its own to
80 # only collect the requested data and not more.
92 IQ_NODES) = range(100, 105)
96 LQ_PENDING) = range(10, 13)
101 GQ_DISKPARAMS) = range(200, 204)
105 CQ_WATCHER_PAUSE) = range(300, 303)
109 QFF_IP_ADDRESS = 0x02
110 # Next values: 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
111 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS)
113 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
114 TITLE_RE = re.compile(r"^[^\s]+$")
115 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
117 #: Verification function for each field type
119 QFT_UNKNOWN: ht.TNone,
120 QFT_TEXT: ht.TString,
124 QFT_TIMESTAMP: ht.TNumber,
125 QFT_OTHER: lambda _: True,
128 # Unique objects for special field statuses
129 _FS_UNKNOWN = object()
130 _FS_NODATA = object()
131 _FS_UNAVAIL = object()
132 _FS_OFFLINE = object()
134 #: List of all special status
135 _FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE])
137 #: VType to QFT mapping
139 # TODO: fix validation of empty strings
140 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
141 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
142 constants.VTYPE_BOOL: QFT_BOOL,
143 constants.VTYPE_SIZE: QFT_UNIT,
144 constants.VTYPE_INT: QFT_NUMBER,
147 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
149 # TODO: Consider moving titles closer to constants
151 constants.ND_OOB_PROGRAM: "OutOfBandProgram",
152 constants.ND_SPINDLE_COUNT: "SpindleCount",
156 def _GetUnknownField(ctx, item): # pylint: disable=W0613
157 """Gets the contents of an unknown field.
163 def _GetQueryFields(fielddefs, selected):
164 """Calculates the internal list of selected fields.
166 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
168 @type fielddefs: dict
169 @param fielddefs: Field definitions
170 @type selected: list of strings
171 @param selected: List of selected fields
176 for name in selected:
178 fdef = fielddefs[name]
180 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
181 None, 0, _GetUnknownField)
183 assert len(fdef) == 4
190 def GetAllFields(fielddefs):
191 """Extract L{objects.QueryFieldDefinition} from field definitions.
193 @rtype: list of L{objects.QueryFieldDefinition}
196 return [fdef for (fdef, _, _, _) in fielddefs]
200 """Class for filter analytics.
202 When filters are used, the user of the L{Query} class usually doesn't know
203 exactly which items will be necessary for building the result. It therefore
204 has to prepare and compute the input data for potentially returning
207 There are two ways to optimize this. The first, and simpler, is to assign
208 each field a group of data, so that the caller can determine which
209 computations are necessary depending on the data groups requested. The list
210 of referenced groups must also be computed for fields referenced in the
213 The second is restricting the items based on a primary key. The primary key
214 is usually a unique name (e.g. a node name). This class extracts all
215 referenced names from a filter. If it encounters any filter condition which
216 disallows such a list to be determined (e.g. a non-equality filter), all
217 names will be requested.
219 The end-effect is that any operation other than L{qlang.OP_OR} and
220 L{qlang.OP_EQUAL} will make the query more expensive.
223 def __init__(self, namefield):
224 """Initializes this class.
226 @type namefield: string
227 @param namefield: Field caller is interested in
230 self._namefield = namefield
232 #: Whether all names need to be requested (e.g. if a non-equality operator
234 self._allnames = False
236 #: Which names to request
239 #: Data kinds referenced by the filter (used by L{Query.RequestedData})
240 self._datakinds = set()
242 def RequestedNames(self):
243 """Returns all requested values.
245 Returns C{None} if list of values can't be determined (e.g. encountered
246 non-equality operators).
251 if self._allnames or self._names is None:
254 return utils.UniqueSequence(self._names)
256 def ReferencedData(self):
257 """Returns all kinds of data referenced by the filter.
260 return frozenset(self._datakinds)
262 def _NeedAllNames(self):
263 """Changes internal state to request all names.
266 self._allnames = True
269 def NoteLogicOp(self, op):
270 """Called when handling a logic operation.
276 if op != qlang.OP_OR:
279 def NoteUnaryOp(self, op): # pylint: disable=W0613
280 """Called when handling an unary operation.
288 def NoteBinaryOp(self, op, datakind, name, value):
289 """Called when handling a binary operation.
294 @param name: Left-hand side of operator (field name)
295 @param value: Right-hand side of operator
298 if datakind is not None:
299 self._datakinds.add(datakind)
304 # If any operator other than equality was used, all names need to be
306 if op == qlang.OP_EQUAL and name == self._namefield:
307 if self._names is None:
309 self._names.append(value)
314 def _WrapLogicOp(op_fn, sentences, ctx, item):
315 """Wrapper for logic operator functions.
318 return op_fn(fn(ctx, item) for fn in sentences)
321 def _WrapUnaryOp(op_fn, inner, ctx, item):
322 """Wrapper for unary operator functions.
325 return op_fn(inner(ctx, item))
328 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
329 """Wrapper for binary operator functions.
332 return op_fn(retrieval_fn(ctx, item), value)
335 def _WrapNot(fn, lhs, rhs):
336 """Negates the result of a wrapped function.
339 return not fn(lhs, rhs)
342 def _PrepareRegex(pattern):
343 """Compiles a regular expression.
347 return re.compile(pattern)
348 except re.error, err:
349 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
352 class _FilterCompilerHelper:
353 """Converts a query filter to a callable usable for filtering.
356 # String statement has no effect, pylint: disable=W0105
358 #: How deep filters can be nested
361 # Unique identifiers for operator groups
364 _OPTYPE_BINARY) = range(1, 4)
366 """Functions for equality checks depending on field flags.
368 List of tuples containing flags and a callable receiving the left- and
369 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
370 (e.g. L{QFF_HOSTNAME}).
372 Order matters. The first item with flags will be used. Flags are checked
378 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
379 case_sensitive=False),
381 (None, operator.eq, None),
386 Operator as key (C{qlang.OP_*}), value a tuple of operator group
387 (C{_OPTYPE_*}) and a group-specific value:
389 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
391 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
392 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
393 right-hand side of the operator, used by L{_HandleBinaryOp}
398 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
399 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
402 qlang.OP_NOT: (_OPTYPE_UNARY, None),
403 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
406 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
408 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
409 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
410 qlang.OP_LT: (_OPTYPE_BINARY, [
411 (None, operator.lt, None),
413 qlang.OP_GT: (_OPTYPE_BINARY, [
414 (None, operator.gt, None),
416 qlang.OP_LE: (_OPTYPE_BINARY, [
417 (None, operator.le, None),
419 qlang.OP_GE: (_OPTYPE_BINARY, [
420 (None, operator.ge, None),
422 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
423 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
425 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
426 (None, operator.contains, None),
430 def __init__(self, fields):
431 """Initializes this class.
433 @param fields: Field definitions (return value of L{_PrepareFieldList})
436 self._fields = fields
438 self._op_handler = None
440 def __call__(self, hints, qfilter):
441 """Converts a query filter into a callable function.
443 @type hints: L{_FilterHints} or None
444 @param hints: Callbacks doing analysis on filter
446 @param qfilter: Filter structure
448 @return: Function receiving context and item as parameters, returning
449 boolean as to whether item matches filter
454 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
456 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
458 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
462 filter_fn = self._Compile(qfilter, 0)
464 self._op_handler = None
468 def _Compile(self, qfilter, level):
469 """Inner function for converting filters.
471 Calls the correct handler functions for the top-level operator. This
472 function is called recursively (e.g. for logic operators).
475 if not (isinstance(qfilter, (list, tuple)) and qfilter):
476 raise errors.ParameterError("Invalid filter on level %s" % level)
479 if level >= self._LEVELS_MAX:
480 raise errors.ParameterError("Only up to %s levels are allowed (filter"
481 " nested too deep)" % self._LEVELS_MAX)
483 # Create copy to be modified
484 operands = qfilter[:]
488 (kind, op_data) = self._OPS[op]
490 raise errors.ParameterError("Unknown operator '%s'" % op)
492 (handler, hints_cb) = self._op_handler[kind]
494 return handler(hints_cb, level, op, op_data, operands)
496 def _LookupField(self, name):
497 """Returns a field definition by name.
501 return self._fields[name]
503 raise errors.ParameterError("Unknown field '%s'" % name)
505 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
506 """Handles logic operators.
508 @type hints_fn: callable
509 @param hints_fn: Callback doing some analysis on the filter
511 @param level: Current depth
514 @type op_fn: callable
515 @param op_fn: Function implementing operator
517 @param operands: List of operands
523 return compat.partial(_WrapLogicOp, op_fn,
524 [self._Compile(op, level + 1) for op in operands])
526 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
527 """Handles unary operators.
529 @type hints_fn: callable
530 @param hints_fn: Callback doing some analysis on the filter
532 @param level: Current depth
535 @type op_fn: callable
536 @param op_fn: Function implementing operator
538 @param operands: List of operands
546 if len(operands) != 1:
547 raise errors.ParameterError("Unary operator '%s' expects exactly one"
550 if op == qlang.OP_TRUE:
551 (_, _, _, retrieval_fn) = self._LookupField(operands[0])
553 op_fn = operator.truth
555 elif op == qlang.OP_NOT:
556 op_fn = operator.not_
557 arg = self._Compile(operands[0], level + 1)
559 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
561 return compat.partial(_WrapUnaryOp, op_fn, arg)
563 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
564 """Handles binary operators.
566 @type hints_fn: callable
567 @param hints_fn: Callback doing some analysis on the filter
569 @param level: Current depth
572 @param op_data: Functions implementing operators
574 @param operands: List of operands
577 # Unused arguments, pylint: disable=W0613
579 (name, value) = operands
580 except (ValueError, TypeError):
581 raise errors.ParameterError("Invalid binary operator, expected exactly"
584 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
586 assert fdef.kind != QFT_UNKNOWN
588 # TODO: Type conversions?
590 verify_fn = _VERIFY_FN[fdef.kind]
591 if not verify_fn(value):
592 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
593 " with '%s', expected %s" %
594 (name, fdef.kind, value.__class__.__name__,
598 hints_fn(op, datakind, name, value)
600 for (fn_flags, fn, valprepfn) in op_data:
601 if fn_flags is None or fn_flags & field_flags:
602 # Prepare value if necessary (e.g. compile regular expression)
604 value = valprepfn(value)
606 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
608 raise errors.ProgrammerError("Unable to find operator implementation"
609 " (op '%s', flags %s)" % (op, field_flags))
612 def _CompileFilter(fields, hints, qfilter):
613 """Converts a query filter into a callable function.
615 See L{_FilterCompilerHelper} for details.
620 return _FilterCompilerHelper(fields)(hints, qfilter)
624 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
625 """Initializes this class.
627 The field definition is a dictionary with the field's name as a key and a
628 tuple containing, in order, the field definition object
629 (L{objects.QueryFieldDefinition}, the data kind to help calling code
630 collect data and a retrieval function. The retrieval function is called
631 with two parameters, in order, the data container and the item in container
632 (see L{Query.Query}).
634 Users of this class can call L{RequestedData} before preparing the data
635 container to determine what data is needed.
637 @type fieldlist: dictionary
638 @param fieldlist: Field definitions
639 @type selected: list of strings
640 @param selected: List of selected fields
643 assert namefield is None or namefield in fieldlist
645 self._fields = _GetQueryFields(fieldlist, selected)
647 self._filter_fn = None
648 self._requested_names = None
649 self._filter_datakinds = frozenset()
651 if qfilter is not None:
652 # Collect requested names if wanted
654 hints = _FilterHints(namefield)
658 # Build filter function
659 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
661 self._requested_names = hints.RequestedNames()
662 self._filter_datakinds = hints.ReferencedData()
664 if namefield is None:
667 (_, _, _, self._name_fn) = fieldlist[namefield]
669 def RequestedNames(self):
670 """Returns all names referenced in the filter.
672 If there is no filter or operators are preventing determining the exact
673 names, C{None} is returned.
676 return self._requested_names
678 def RequestedData(self):
679 """Gets requested kinds of data.
684 return (self._filter_datakinds |
685 frozenset(datakind for (_, datakind, _, _) in self._fields
686 if datakind is not None))
689 """Returns the list of fields for this query.
691 Includes unknown fields.
693 @rtype: List of L{objects.QueryFieldDefinition}
696 return GetAllFields(self._fields)
698 def Query(self, ctx, sort_by_name=True):
701 @param ctx: Data container passed to field retrieval functions, must
702 support iteration using C{__iter__}
703 @type sort_by_name: boolean
704 @param sort_by_name: Whether to sort by name or keep the input data's
708 sort = (self._name_fn and sort_by_name)
712 for idx, item in enumerate(ctx):
713 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
716 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
720 _VerifyResultRow(self._fields, row)
723 (status, name) = _ProcessResult(self._name_fn(ctx, item))
724 assert status == constants.RS_NORMAL
725 # TODO: Are there cases where we wouldn't want to use NiceSort?
726 result.append((utils.NiceSortKey(name), idx, row))
733 # TODO: Would "heapq" be more efficient than sorting?
735 # Sorting in-place instead of using "sorted()"
738 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
740 return map(operator.itemgetter(2), result)
742 def OldStyleQuery(self, ctx, sort_by_name=True):
743 """Query with "old" query result format.
745 See L{Query.Query} for arguments.
748 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
749 if fdef.kind == QFT_UNKNOWN)
751 raise errors.OpPrereqError("Unknown output fields selected: %s" %
752 (utils.CommaJoin(unknown), ),
755 return [[value for (_, value) in row]
756 for row in self.Query(ctx, sort_by_name=sort_by_name)]
759 def _ProcessResult(value):
760 """Converts result values into externally-visible ones.
763 if value is _FS_UNKNOWN:
764 return (RS_UNKNOWN, None)
765 elif value is _FS_NODATA:
766 return (RS_NODATA, None)
767 elif value is _FS_UNAVAIL:
768 return (RS_UNAVAIL, None)
769 elif value is _FS_OFFLINE:
770 return (RS_OFFLINE, None)
772 return (RS_NORMAL, value)
775 def _VerifyResultRow(fields, row):
776 """Verifies the contents of a query result row.
779 @param fields: Field definitions for result
780 @type row: list of tuples
784 assert len(row) == len(fields)
786 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
787 if status == RS_NORMAL:
788 if not _VERIFY_FN[fdef.kind](value):
789 errs.append("normal field %s fails validation (value is %s)" %
791 elif value is not None:
792 errs.append("abnormal field %s has a non-None value" % fdef.name)
793 assert not errs, ("Failed validation: %s in row %s" %
794 (utils.CommaJoin(errs), row))
797 def _FieldDictKey((fdef, _, flags, fn)):
798 """Generates key for field dictionary.
801 assert fdef.name and fdef.title, "Name and title are required"
802 assert FIELD_NAME_RE.match(fdef.name)
803 assert TITLE_RE.match(fdef.title)
804 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
805 fdef.doc.strip() == fdef.doc), \
806 "Invalid description for field '%s'" % fdef.name
808 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
813 def _PrepareFieldList(fields, aliases):
814 """Prepares field list for use by L{Query}.
816 Converts the list to a dictionary and does some verification.
818 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
819 kind, retrieval function)
820 @param fields: List of fields, see L{Query.__init__} for a better
822 @type aliases: list of tuples; (alias, target)
823 @param aliases: list of tuples containing aliases; for each
824 alias/target pair, a duplicate will be created in the field list
826 @return: Field dictionary for L{Query}
830 duplicates = utils.FindDuplicates(fdef.title.lower()
831 for (fdef, _, _, _) in fields)
832 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
834 result = utils.SequenceToDict(fields, key=_FieldDictKey)
836 for alias, target in aliases:
837 assert alias not in result, "Alias %s overrides an existing field" % alias
838 assert target in result, "Missing target %s for alias %s" % (target, alias)
839 (fdef, k, flags, fn) = result[target]
842 result[alias] = (fdef, k, flags, fn)
844 assert len(result) == len(fields) + len(aliases)
845 assert compat.all(name == fdef.name
846 for (name, (fdef, _, _, _)) in result.items())
851 def GetQueryResponse(query, ctx, sort_by_name=True):
852 """Prepares the response for a query.
854 @type query: L{Query}
855 @param ctx: Data container, see L{Query.Query}
856 @type sort_by_name: boolean
857 @param sort_by_name: Whether to sort by name or keep the input data's
861 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
862 fields=query.GetFields()).ToDict()
865 def QueryFields(fielddefs, selected):
866 """Returns list of available fields.
868 @type fielddefs: dict
869 @param fielddefs: Field definitions
870 @type selected: list of strings
871 @param selected: List of selected fields
872 @return: List of L{objects.QueryFieldDefinition}
876 # Client requests all fields, sort by name
877 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
878 key=operator.attrgetter("name"))
880 # Keep order as requested by client
881 fdefs = Query(fielddefs, selected).GetFields()
883 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
886 def _MakeField(name, title, kind, doc):
887 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
889 @param name: Field name as a regular expression
890 @param title: Human-readable title
891 @param kind: Field type
892 @param doc: Human-readable description
895 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
899 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
900 """Returns a static value.
906 def _StaticValue(value):
907 """Prepares a function to return a static value.
910 return compat.partial(_StaticValueInner, value)
913 def _GetNodeRole(node, master_name):
914 """Determine node role.
916 @type node: L{objects.Node}
917 @param node: Node object
918 @type master_name: string
919 @param master_name: Master node name
922 if node.name == master_name:
923 return constants.NR_MASTER
924 elif node.master_candidate:
925 return constants.NR_MCANDIDATE
927 return constants.NR_DRAINED
929 return constants.NR_OFFLINE
931 return constants.NR_REGULAR
934 def _GetItemAttr(attr):
935 """Returns a field function to return an attribute of the item.
937 @param attr: Attribute name
940 getter = operator.attrgetter(attr)
941 return lambda _, item: getter(item)
944 def _GetNDParam(name):
945 """Return a field function to return an ND parameter out of the context.
949 if ctx.ndparams is None:
952 return ctx.ndparams.get(name, None)
956 def _BuildNDFields(is_group):
957 """Builds all the ndparam fields.
959 @param is_group: whether this is called at group or node level
963 field_kind = GQ_CONFIG
965 field_kind = NQ_GROUP
966 return [(_MakeField("ndp/%s" % name, NDP_TITLE.get(name, "ndp/%s" % name),
967 _VTToQFT[kind], "The \"%s\" node parameter" % name),
968 field_kind, 0, _GetNDParam(name))
969 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
972 def _ConvWrapInner(convert, fn, ctx, item):
973 """Wrapper for converting values.
975 @param convert: Conversion function receiving value as single parameter
976 @param fn: Retrieval function
979 value = fn(ctx, item)
981 # Is the value an abnormal status?
982 if compat.any(value is fs for fs in _FS_ALL):
986 # TODO: Should conversion function also receive context, item or both?
987 return convert(value)
990 def _ConvWrap(convert, fn):
991 """Convenience wrapper for L{_ConvWrapInner}.
993 @param convert: Conversion function receiving value as single parameter
994 @param fn: Retrieval function
997 return compat.partial(_ConvWrapInner, convert, fn)
1000 def _GetItemTimestamp(getter):
1001 """Returns function for getting timestamp of item.
1003 @type getter: callable
1004 @param getter: Function to retrieve timestamp attribute
1008 """Returns a timestamp of item.
1011 timestamp = getter(item)
1012 if timestamp is None:
1013 # Old configs might not have all timestamps
1021 def _GetItemTimestampFields(datatype):
1022 """Returns common timestamp fields.
1024 @param datatype: Field data type for use by L{Query.RequestedData}
1028 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1029 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1030 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1031 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1035 class NodeQueryData:
1036 """Data container for node data queries.
1039 def __init__(self, nodes, live_data, master_name, node_to_primary,
1040 node_to_secondary, groups, oob_support, cluster):
1041 """Initializes this class.
1045 self.live_data = live_data
1046 self.master_name = master_name
1047 self.node_to_primary = node_to_primary
1048 self.node_to_secondary = node_to_secondary
1049 self.groups = groups
1050 self.oob_support = oob_support
1051 self.cluster = cluster
1053 # Used for individual rows
1054 self.curlive_data = None
1055 self.ndparams = None
1058 """Iterate over all nodes.
1060 This function has side-effects and only one instance of the resulting
1061 generator should be used at a time.
1064 for node in self.nodes:
1065 group = self.groups.get(node.group, None)
1067 self.ndparams = None
1069 self.ndparams = self.cluster.FillND(node, group)
1071 self.curlive_data = self.live_data.get(node.name, None)
1073 self.curlive_data = None
1077 #: Fields that are direct attributes of an L{objects.Node} object
1078 _NODE_SIMPLE_FIELDS = {
1079 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1080 "master_candidate": ("MasterC", QFT_BOOL, 0,
1081 "Whether node is a master candidate"),
1082 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1083 "Whether node can become a master candidate"),
1084 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1085 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1086 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1087 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1088 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1092 #: Fields requiring talking to the node
1093 # Note that none of these are available for non-vm_capable nodes
1094 _NODE_LIVE_FIELDS = {
1095 "bootid": ("BootID", QFT_TEXT, "bootid",
1096 "Random UUID renewed for each system reboot, can be used"
1097 " for detecting reboots by tracking changes"),
1098 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1099 "Number of NUMA domains on node (if exported by hypervisor)"),
1100 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1101 "Number of physical CPU sockets (if exported by hypervisor)"),
1102 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1103 "dfree": ("DFree", QFT_UNIT, "vg_free",
1104 "Available disk space in volume group"),
1105 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1106 "Total disk space in volume group used for instance disk"
1108 "mfree": ("MFree", QFT_UNIT, "memory_free",
1109 "Memory available for instance allocations"),
1110 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1111 "Amount of memory used by node (dom0 for Xen)"),
1112 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1113 "Total amount of memory of physical machine"),
1118 """Build function for calling another function with an node group.
1120 @param cb: The callback to be called with the nodegroup
1124 """Get group data for a node.
1126 @type ctx: L{NodeQueryData}
1127 @type inst: L{objects.Node}
1128 @param inst: Node object
1131 ng = ctx.groups.get(node.group, None)
1133 # Nodes always have a group, or the configuration is corrupt
1136 return cb(ctx, node, ng)
1141 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1142 """Returns the name of a node's group.
1144 @type ctx: L{NodeQueryData}
1145 @type node: L{objects.Node}
1146 @param node: Node object
1147 @type ng: L{objects.NodeGroup}
1148 @param ng: The node group this node belongs to
1154 def _GetNodePower(ctx, node):
1155 """Returns the node powered state
1157 @type ctx: L{NodeQueryData}
1158 @type node: L{objects.Node}
1159 @param node: Node object
1162 if ctx.oob_support[node.name]:
1168 def _GetNdParams(ctx, node, ng):
1169 """Returns the ndparams for this node.
1171 @type ctx: L{NodeQueryData}
1172 @type node: L{objects.Node}
1173 @param node: Node object
1174 @type ng: L{objects.NodeGroup}
1175 @param ng: The node group this node belongs to
1178 return ctx.cluster.SimpleFillND(ng.FillND(node))
1181 def _GetLiveNodeField(field, kind, ctx, node):
1182 """Gets the value of a "live" field from L{NodeQueryData}.
1184 @param field: Live field name
1185 @param kind: Data kind, one of L{constants.QFT_ALL}
1186 @type ctx: L{NodeQueryData}
1187 @type node: L{objects.Node}
1188 @param node: Node object
1194 if not node.vm_capable:
1197 if not ctx.curlive_data:
1201 value = ctx.curlive_data[field]
1205 if kind == QFT_TEXT:
1208 assert kind in (QFT_NUMBER, QFT_UNIT)
1210 # Try to convert into number
1213 except (ValueError, TypeError):
1214 logging.exception("Failed to convert node field '%s' (value %r) to int",
1219 def _GetNodeHvState(_, node):
1220 """Converts node's hypervisor state for query result.
1223 hv_state = node.hv_state
1225 if hv_state is None:
1228 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1231 def _GetNodeDiskState(_, node):
1232 """Converts node's disk state for query result.
1235 disk_state = node.disk_state
1237 if disk_state is None:
1240 return dict((disk_kind, dict((name, value.ToDict())
1241 for (name, value) in kind_state.items()))
1242 for (disk_kind, kind_state) in disk_state.items())
1245 def _BuildNodeFields():
1246 """Builds list of fields for node queries.
1250 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1251 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1252 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1253 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1254 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1255 lambda ctx, node: list(node.GetTags())),
1256 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1257 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1258 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1259 _GetGroup(_GetNodeGroup)),
1260 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1261 NQ_CONFIG, 0, _GetItemAttr("group")),
1262 (_MakeField("powered", "Powered", QFT_BOOL,
1263 "Whether node is thought to be powered on"),
1264 NQ_OOB, 0, _GetNodePower),
1265 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1266 "Merged node parameters"),
1267 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1268 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1269 "Custom node parameters"),
1270 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1271 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1272 NQ_CONFIG, 0, _GetNodeHvState),
1273 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1274 NQ_CONFIG, 0, _GetNodeDiskState),
1277 fields.extend(_BuildNDFields(False))
1280 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1281 constants.NR_REGULAR, constants.NR_DRAINED,
1282 constants.NR_OFFLINE)
1283 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1284 " \"%s\" for regular, \"%s\" for a drained, \"%s\" for offline" %
1286 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1287 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1288 assert set(role_values) == constants.NR_ALL
1290 def _GetLength(getter):
1291 return lambda ctx, node: len(getter(ctx)[node.name])
1293 def _GetList(getter):
1294 return lambda ctx, node: list(getter(ctx)[node.name])
1296 # Add fields operating on instance lists
1297 for prefix, titleprefix, docword, getter in \
1298 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1299 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1300 # TODO: Allow filterting by hostname in list
1302 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1303 "Number of instances with this node as %s" % docword),
1304 NQ_INST, 0, _GetLength(getter)),
1305 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1307 "List of instances with this node as %s" % docword),
1308 NQ_INST, 0, _GetList(getter)),
1313 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1314 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1317 # Add fields requiring live data
1319 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1320 compat.partial(_GetLiveNodeField, nfield, kind))
1321 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1325 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1327 return _PrepareFieldList(fields, [])
1330 class InstanceQueryData:
1331 """Data container for instance data queries.
1334 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1335 live_data, wrongnode_inst, console, nodes, groups):
1336 """Initializes this class.
1338 @param instances: List of instance objects
1339 @param cluster: Cluster object
1340 @type disk_usage: dict; instance name as key
1341 @param disk_usage: Per-instance disk usage
1342 @type offline_nodes: list of strings
1343 @param offline_nodes: List of offline nodes
1344 @type bad_nodes: list of strings
1345 @param bad_nodes: List of faulty nodes
1346 @type live_data: dict; instance name as key
1347 @param live_data: Per-instance live data
1348 @type wrongnode_inst: set
1349 @param wrongnode_inst: Set of instances running on wrong node(s)
1350 @type console: dict; instance name as key
1351 @param console: Per-instance console information
1352 @type nodes: dict; node name as key
1353 @param nodes: Node objects
1356 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1357 "Offline nodes not included in bad nodes"
1358 assert not (set(live_data.keys()) & set(bad_nodes)), \
1359 "Found live data for bad or offline nodes"
1361 self.instances = instances
1362 self.cluster = cluster
1363 self.disk_usage = disk_usage
1364 self.offline_nodes = offline_nodes
1365 self.bad_nodes = bad_nodes
1366 self.live_data = live_data
1367 self.wrongnode_inst = wrongnode_inst
1368 self.console = console
1370 self.groups = groups
1372 # Used for individual rows
1373 self.inst_hvparams = None
1374 self.inst_beparams = None
1375 self.inst_osparams = None
1376 self.inst_nicparams = None
1379 """Iterate over all instances.
1381 This function has side-effects and only one instance of the resulting
1382 generator should be used at a time.
1385 for inst in self.instances:
1386 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1387 self.inst_beparams = self.cluster.FillBE(inst)
1388 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1389 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1390 for nic in inst.nics]
1395 def _GetInstOperState(ctx, inst):
1396 """Get instance's operational status.
1398 @type ctx: L{InstanceQueryData}
1399 @type inst: L{objects.Instance}
1400 @param inst: Instance object
1403 # Can't use RS_OFFLINE here as it would describe the instance to
1404 # be offline when we actually don't know due to missing data
1405 if inst.primary_node in ctx.bad_nodes:
1408 return bool(ctx.live_data.get(inst.name))
1411 def _GetInstLiveData(name):
1412 """Build function for retrieving live data.
1415 @param name: Live data field name
1419 """Get live data for an instance.
1421 @type ctx: L{InstanceQueryData}
1422 @type inst: L{objects.Instance}
1423 @param inst: Instance object
1426 if (inst.primary_node in ctx.bad_nodes or
1427 inst.primary_node in ctx.offline_nodes):
1428 # Can't use RS_OFFLINE here as it would describe the instance to be
1429 # offline when we actually don't know due to missing data
1432 if inst.name in ctx.live_data:
1433 data = ctx.live_data[inst.name]
1442 def _GetInstStatus(ctx, inst):
1443 """Get instance status.
1445 @type ctx: L{InstanceQueryData}
1446 @type inst: L{objects.Instance}
1447 @param inst: Instance object
1450 if inst.primary_node in ctx.offline_nodes:
1451 return constants.INSTST_NODEOFFLINE
1453 if inst.primary_node in ctx.bad_nodes:
1454 return constants.INSTST_NODEDOWN
1456 if bool(ctx.live_data.get(inst.name)):
1457 if inst.name in ctx.wrongnode_inst:
1458 return constants.INSTST_WRONGNODE
1459 elif inst.admin_state == constants.ADMINST_UP:
1460 return constants.INSTST_RUNNING
1462 return constants.INSTST_ERRORUP
1464 if inst.admin_state == constants.ADMINST_UP:
1465 return constants.INSTST_ERRORDOWN
1466 elif inst.admin_state == constants.ADMINST_DOWN:
1467 return constants.INSTST_ADMINDOWN
1469 return constants.INSTST_ADMINOFFLINE
1472 def _GetInstDiskSize(index):
1473 """Build function for retrieving disk size.
1476 @param index: Disk index
1480 """Get size of a disk.
1482 @type inst: L{objects.Instance}
1483 @param inst: Instance object
1487 return inst.disks[index].size
1494 def _GetInstNic(index, cb):
1495 """Build function for calling another function with an instance NIC.
1498 @param index: NIC index
1504 """Call helper function with instance NIC.
1506 @type ctx: L{InstanceQueryData}
1507 @type inst: L{objects.Instance}
1508 @param inst: Instance object
1512 nic = inst.nics[index]
1516 return cb(ctx, index, nic)
1521 def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1522 """Get a NIC's Network.
1524 @type ctx: L{InstanceQueryData}
1525 @type nic: L{objects.NIC}
1526 @param nic: NIC object
1529 if nic.network is None:
1535 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1536 """Get a NIC's IP address.
1538 @type ctx: L{InstanceQueryData}
1539 @type nic: L{objects.NIC}
1540 @param nic: NIC object
1549 def _GetInstNicBridge(ctx, index, _):
1550 """Get a NIC's bridge.
1552 @type ctx: L{InstanceQueryData}
1554 @param index: NIC index
1557 assert len(ctx.inst_nicparams) >= index
1559 nicparams = ctx.inst_nicparams[index]
1561 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1562 return nicparams[constants.NIC_LINK]
1567 def _GetInstAllNicBridges(ctx, inst):
1568 """Get all network bridges for an instance.
1570 @type ctx: L{InstanceQueryData}
1571 @type inst: L{objects.Instance}
1572 @param inst: Instance object
1575 assert len(ctx.inst_nicparams) == len(inst.nics)
1579 for nicp in ctx.inst_nicparams:
1580 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1581 result.append(nicp[constants.NIC_LINK])
1585 assert len(result) == len(inst.nics)
1590 def _GetInstNicParam(name):
1591 """Build function for retrieving a NIC parameter.
1594 @param name: Parameter name
1597 def fn(ctx, index, _):
1598 """Get a NIC's bridge.
1600 @type ctx: L{InstanceQueryData}
1601 @type inst: L{objects.Instance}
1602 @param inst: Instance object
1603 @type nic: L{objects.NIC}
1604 @param nic: NIC object
1607 assert len(ctx.inst_nicparams) >= index
1608 return ctx.inst_nicparams[index][name]
1613 def _GetInstanceNetworkFields():
1614 """Get instance fields involving network interfaces.
1616 @return: Tuple containing list of field definitions used as input for
1617 L{_PrepareFieldList} and a list of aliases
1620 nic_mac_fn = lambda ctx, _, nic: nic.mac
1621 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1622 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1626 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1627 "Number of network interfaces"),
1628 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1629 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1630 "List containing each network interface's MAC address"),
1631 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1632 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1633 "List containing each network interface's IP address"),
1634 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1635 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1636 "List containing each network interface's mode"), IQ_CONFIG, 0,
1637 lambda ctx, inst: [nicp[constants.NIC_MODE]
1638 for nicp in ctx.inst_nicparams]),
1639 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1640 "List containing each network interface's link"), IQ_CONFIG, 0,
1641 lambda ctx, inst: [nicp[constants.NIC_LINK]
1642 for nicp in ctx.inst_nicparams]),
1643 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1644 "List containing each network interface's bridge"),
1645 IQ_CONFIG, 0, _GetInstAllNicBridges),
1646 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1647 "List containing each interface's network"), IQ_CONFIG, 0,
1648 lambda ctx, inst: [nic.network for nic in inst.nics]),
1652 for i in range(constants.MAX_NICS):
1653 numtext = utils.FormatOrdinal(i + 1)
1655 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1656 "IP address of %s network interface" % numtext),
1657 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1658 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1659 "MAC address of %s network interface" % numtext),
1660 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1661 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1662 "Mode of %s network interface" % numtext),
1663 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1664 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1665 "Link of %s network interface" % numtext),
1666 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1667 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1668 "Bridge of %s network interface" % numtext),
1669 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1670 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1671 "Network of %s network interface" % numtext),
1672 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1676 # Legacy fields for first NIC
1678 ("mac", "nic.mac/0"),
1679 ("bridge", "nic.bridge/0"),
1680 ("nic_mode", "nic.mode/0"),
1681 ("nic_link", "nic.link/0"),
1682 ("nic_network", "nic.network/0"),
1685 return (fields, aliases)
1688 def _GetInstDiskUsage(ctx, inst):
1689 """Get disk usage for an instance.
1691 @type ctx: L{InstanceQueryData}
1692 @type inst: L{objects.Instance}
1693 @param inst: Instance object
1696 usage = ctx.disk_usage[inst.name]
1704 def _GetInstanceConsole(ctx, inst):
1705 """Get console information for instance.
1707 @type ctx: L{InstanceQueryData}
1708 @type inst: L{objects.Instance}
1709 @param inst: Instance object
1712 consinfo = ctx.console[inst.name]
1714 if consinfo is None:
1720 def _GetInstanceDiskFields():
1721 """Get instance fields involving disks.
1723 @return: List of field definitions used as input for L{_PrepareFieldList}
1727 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1728 "Total disk space used by instance on each of its nodes;"
1729 " this is not the disk size visible to the instance, but"
1730 " the usage on the node"),
1731 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1732 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1733 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1734 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1735 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1740 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1741 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1742 IQ_CONFIG, 0, _GetInstDiskSize(i))
1743 for i in range(constants.MAX_DISKS)
1749 def _GetInstanceParameterFields():
1750 """Get instance fields involving parameters.
1752 @return: List of field definitions used as input for L{_PrepareFieldList}
1755 # TODO: Consider moving titles closer to constants
1757 constants.BE_AUTO_BALANCE: "Auto_balance",
1758 constants.BE_MAXMEM: "ConfigMaxMem",
1759 constants.BE_MINMEM: "ConfigMinMem",
1760 constants.BE_VCPUS: "ConfigVCPUs",
1764 constants.HV_ACPI: "ACPI",
1765 constants.HV_BOOT_ORDER: "Boot_order",
1766 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1767 constants.HV_DISK_TYPE: "Disk_type",
1768 constants.HV_INITRD_PATH: "Initrd_path",
1769 constants.HV_KERNEL_PATH: "Kernel_path",
1770 constants.HV_NIC_TYPE: "NIC_type",
1771 constants.HV_PAE: "PAE",
1772 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1777 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1778 "Hypervisor parameters (merged)"),
1779 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1780 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1781 "Backend parameters (merged)"),
1782 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1783 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1784 "Operating system parameters (merged)"),
1785 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1787 # Unfilled parameters
1788 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1789 "Custom hypervisor parameters"),
1790 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1791 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1792 "Custom backend parameters",),
1793 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1794 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1795 "Custom operating system parameters",),
1796 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1797 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1798 "Custom network interface parameters"),
1799 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1803 def _GetInstHvParam(name):
1804 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1807 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1808 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1809 IQ_CONFIG, 0, _GetInstHvParam(name))
1810 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1811 if name not in constants.HVC_GLOBALS
1815 def _GetInstBeParam(name):
1816 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1819 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1820 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1821 IQ_CONFIG, 0, _GetInstBeParam(name))
1822 for name, kind in constants.BES_PARAMETER_TYPES.items()
1828 _INST_SIMPLE_FIELDS = {
1829 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1830 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1831 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1832 # Depending on the hypervisor, the port can be None
1833 "network_port": ("Network_port", QFT_OTHER, 0,
1834 "Instance network port if available (e.g. for VNC console)"),
1835 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1836 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1837 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1841 def _GetInstNodeGroup(ctx, default, node_name):
1842 """Gets group UUID of an instance node.
1844 @type ctx: L{InstanceQueryData}
1845 @param default: Default value
1846 @type node_name: string
1847 @param node_name: Node name
1851 node = ctx.nodes[node_name]
1858 def _GetInstNodeGroupName(ctx, default, node_name):
1859 """Gets group name of an instance node.
1861 @type ctx: L{InstanceQueryData}
1862 @param default: Default value
1863 @type node_name: string
1864 @param node_name: Node name
1868 node = ctx.nodes[node_name]
1873 group = ctx.groups[node.group]
1880 def _BuildInstanceFields():
1881 """Builds list of fields for instance queries.
1885 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1886 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1887 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1888 "Primary node's group"),
1890 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1891 inst.primary_node)),
1892 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1893 "Primary node's group UUID"),
1895 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1896 # TODO: Allow filtering by secondary node as hostname
1897 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1898 "Secondary nodes; usually this will just be one node"),
1899 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1900 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1901 "Node groups of secondary nodes"),
1903 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1904 inst.secondary_nodes)),
1905 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1906 "Node group UUIDs of secondary nodes"),
1908 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1909 inst.secondary_nodes)),
1910 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1911 "Desired state of instance"),
1912 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1913 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1914 "Desired state of instance"),
1915 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1916 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1917 lambda ctx, inst: list(inst.GetTags())),
1918 (_MakeField("console", "Console", QFT_OTHER,
1919 "Instance console information"), IQ_CONSOLE, 0,
1920 _GetInstanceConsole),
1925 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1926 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1929 # Fields requiring talking to the node
1931 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1932 IQ_LIVE, 0, _GetInstOperState),
1933 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1934 "Actual memory usage as seen by hypervisor"),
1935 IQ_LIVE, 0, _GetInstLiveData("memory")),
1936 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1937 "Actual number of VCPUs as seen by hypervisor"),
1938 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1942 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1943 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1944 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1945 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1946 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1947 " and actually is, \"%s\" if instance is stopped and"
1948 " is not running, \"%s\" if instance running, but not on its"
1949 " designated primary node, \"%s\" if instance should be"
1950 " stopped, but is actually running, \"%s\" if instance should"
1951 " run, but doesn't, \"%s\" if instance's primary node is down,"
1952 " \"%s\" if instance's primary node is marked offline,"
1953 " \"%s\" if instance is offline and does not use dynamic"
1954 " resources" % status_values)
1955 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1956 IQ_LIVE, 0, _GetInstStatus))
1957 assert set(status_values) == constants.INSTST_ALL, \
1958 "Status documentation mismatch"
1960 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1962 fields.extend(network_fields)
1963 fields.extend(_GetInstanceParameterFields())
1964 fields.extend(_GetInstanceDiskFields())
1965 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1968 ("vcpus", "be/vcpus"),
1969 ("be/memory", "be/maxmem"),
1970 ("sda_size", "disk.size/0"),
1971 ("sdb_size", "disk.size/1"),
1974 return _PrepareFieldList(fields, aliases)
1977 class LockQueryData:
1978 """Data container for lock data queries.
1981 def __init__(self, lockdata):
1982 """Initializes this class.
1985 self.lockdata = lockdata
1988 """Iterate over all locks.
1991 return iter(self.lockdata)
1994 def _GetLockOwners(_, data):
1995 """Returns a sorted list of a lock's current owners.
1998 (_, _, owners, _) = data
2001 owners = utils.NiceSort(owners)
2006 def _GetLockPending(_, data):
2007 """Returns a sorted list of a lock's pending acquires.
2010 (_, _, _, pending) = data
2013 pending = [(mode, utils.NiceSort(names))
2014 for (mode, names) in pending]
2019 def _BuildLockFields():
2020 """Builds list of fields for lock queries.
2023 return _PrepareFieldList([
2024 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2025 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2026 lambda ctx, (name, mode, owners, pending): name),
2027 (_MakeField("mode", "Mode", QFT_OTHER,
2028 "Mode in which the lock is currently acquired"
2029 " (exclusive or shared)"),
2030 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2031 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2032 LQ_OWNER, 0, _GetLockOwners),
2033 (_MakeField("pending", "Pending", QFT_OTHER,
2034 "Threads waiting for the lock"),
2035 LQ_PENDING, 0, _GetLockPending),
2039 class GroupQueryData:
2040 """Data container for node group data queries.
2043 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2045 """Initializes this class.
2047 @param cluster: Cluster object
2048 @param groups: List of node group objects
2049 @type group_to_nodes: dict; group UUID as key
2050 @param group_to_nodes: Per-group list of nodes
2051 @type group_to_instances: dict; group UUID as key
2052 @param group_to_instances: Per-group list of (primary) instances
2053 @type want_diskparams: bool
2054 @param want_diskparams: Whether diskparamters should be calculated
2057 self.groups = groups
2058 self.group_to_nodes = group_to_nodes
2059 self.group_to_instances = group_to_instances
2060 self.cluster = cluster
2061 self.want_diskparams = want_diskparams
2063 # Used for individual rows
2064 self.group_ipolicy = None
2065 self.ndparams = None
2066 self.group_dp = None
2069 """Iterate over all node groups.
2071 This function has side-effects and only one instance of the resulting
2072 generator should be used at a time.
2075 for group in self.groups:
2076 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2077 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2078 if self.want_diskparams:
2079 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2081 self.group_dp = None
2085 _GROUP_SIMPLE_FIELDS = {
2086 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2087 "name": ("Group", QFT_TEXT, "Group name"),
2088 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2089 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2093 def _BuildGroupFields():
2094 """Builds list of fields for node group queries.
2098 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2100 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2102 def _GetLength(getter):
2103 return lambda ctx, group: len(getter(ctx)[group.uuid])
2105 def _GetSortedList(getter):
2106 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2108 group_to_nodes = operator.attrgetter("group_to_nodes")
2109 group_to_instances = operator.attrgetter("group_to_instances")
2111 # Add fields for nodes
2113 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2114 GQ_NODE, 0, _GetLength(group_to_nodes)),
2115 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2116 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2119 # Add fields for instances
2121 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2122 "Number of primary instances"),
2123 GQ_INST, 0, _GetLength(group_to_instances)),
2124 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2125 "List of primary instances"),
2126 GQ_INST, 0, _GetSortedList(group_to_instances)),
2131 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2132 lambda ctx, group: list(group.GetTags())),
2133 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2134 "Instance policy limitations (merged)"),
2135 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2136 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2137 "Custom instance policy limitations"),
2138 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2139 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2140 "Custom node parameters"),
2141 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2142 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2144 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2145 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2146 "Disk parameters (merged)"),
2147 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2148 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2149 "Custom disk parameters"),
2150 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2154 fields.extend(_BuildNDFields(True))
2156 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2158 return _PrepareFieldList(fields, [])
2161 class OsInfo(objects.ConfigObject):
2174 def _BuildOsFields():
2175 """Builds list of fields for operating system queries.
2179 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2180 None, 0, _GetItemAttr("name")),
2181 (_MakeField("valid", "Valid", QFT_BOOL,
2182 "Whether operating system definition is valid"),
2183 None, 0, _GetItemAttr("valid")),
2184 (_MakeField("hidden", "Hidden", QFT_BOOL,
2185 "Whether operating system is hidden"),
2186 None, 0, _GetItemAttr("hidden")),
2187 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2188 "Whether operating system is blacklisted"),
2189 None, 0, _GetItemAttr("blacklisted")),
2190 (_MakeField("variants", "Variants", QFT_OTHER,
2191 "Operating system variants"),
2192 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2193 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2194 "Operating system API versions"),
2195 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2196 (_MakeField("parameters", "Parameters", QFT_OTHER,
2197 "Operating system parameters"),
2198 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2199 _GetItemAttr("parameters"))),
2200 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2201 "Status from node"),
2202 None, 0, _GetItemAttr("node_status")),
2205 return _PrepareFieldList(fields, [])
2208 class ExtStorageInfo(objects.ConfigObject):
2217 def _BuildExtStorageFields():
2218 """Builds list of fields for extstorage provider queries.
2222 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2223 None, 0, _GetItemAttr("name")),
2224 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2225 "Status from node"),
2226 None, 0, _GetItemAttr("node_status")),
2227 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2228 "Overall Nodegroup status"),
2229 None, 0, _GetItemAttr("nodegroup_status")),
2230 (_MakeField("parameters", "Parameters", QFT_OTHER,
2231 "ExtStorage provider parameters"),
2232 None, 0, _GetItemAttr("parameters")),
2235 return _PrepareFieldList(fields, [])
2238 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2239 """Return L{_FS_UNAVAIL} if job is None.
2241 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2242 found, in which case this function converts it to L{_FS_UNAVAIL}.
2251 def _JobUnavail(inner):
2252 """Wrapper for L{_JobUnavailInner}.
2255 return compat.partial(_JobUnavailInner, inner)
2258 def _PerJobOpInner(fn, job):
2259 """Executes a function per opcode in a job.
2262 return map(fn, job.ops)
2266 """Wrapper for L{_PerJobOpInner}.
2269 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2272 def _JobTimestampInner(fn, job):
2273 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2278 if timestamp is None:
2284 def _JobTimestamp(fn):
2285 """Wrapper for L{_JobTimestampInner}.
2288 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2291 def _BuildJobFields():
2292 """Builds list of fields for job queries.
2296 (_MakeField("id", "ID", QFT_TEXT, "Job ID"),
2297 None, 0, lambda _, (job_id, job): job_id),
2298 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2299 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2300 (_MakeField("priority", "Priority", QFT_NUMBER,
2301 ("Current job priority (%s to %s)" %
2302 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2303 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2304 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2305 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2306 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2307 "List of opcodes results"),
2308 None, 0, _PerJobOp(operator.attrgetter("result"))),
2309 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2310 "List of opcodes status"),
2311 None, 0, _PerJobOp(operator.attrgetter("status"))),
2312 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2313 "List of opcode output logs"),
2314 None, 0, _PerJobOp(operator.attrgetter("log"))),
2315 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2316 "List of opcode start timestamps (before acquiring locks)"),
2317 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2318 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2319 "List of opcode execution start timestamps (after acquiring"
2321 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2322 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2323 "List of opcode execution end timestamps"),
2324 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2325 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2326 "List of opcode priorities"),
2327 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2328 (_MakeField("received_ts", "Received", QFT_OTHER,
2329 "Timestamp of when job was received"),
2330 None, 0, _JobTimestamp(operator.attrgetter("received_timestamp"))),
2331 (_MakeField("start_ts", "Start", QFT_OTHER,
2332 "Timestamp of job start"),
2333 None, 0, _JobTimestamp(operator.attrgetter("start_timestamp"))),
2334 (_MakeField("end_ts", "End", QFT_OTHER,
2335 "Timestamp of job end"),
2336 None, 0, _JobTimestamp(operator.attrgetter("end_timestamp"))),
2337 (_MakeField("summary", "Summary", QFT_OTHER,
2338 "List of per-opcode summaries"),
2339 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2342 return _PrepareFieldList(fields, [])
2345 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2346 """Returns an export name if available.
2355 def _BuildExportFields():
2356 """Builds list of fields for exports.
2360 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2361 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2362 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2363 None, 0, _GetExportName),
2366 return _PrepareFieldList(fields, [])
2369 _CLUSTER_VERSION_FIELDS = {
2370 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2371 "Software version"),
2372 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2373 constants.PROTOCOL_VERSION,
2374 "RPC protocol version"),
2375 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2376 "Configuration format version"),
2377 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2378 "API version for OS template scripts"),
2379 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2380 "Import/export file format version"),
2384 _CLUSTER_SIMPLE_FIELDS = {
2385 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2386 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2387 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2391 class ClusterQueryData:
2392 def __init__(self, cluster, drain_flag, watcher_pause):
2393 """Initializes this class.
2395 @type cluster: L{objects.Cluster}
2396 @param cluster: Instance of cluster object
2397 @type drain_flag: bool
2398 @param drain_flag: Whether job queue is drained
2399 @type watcher_pause: number
2400 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2403 self._cluster = cluster
2404 self.drain_flag = drain_flag
2405 self.watcher_pause = watcher_pause
2408 return iter([self._cluster])
2411 def _ClusterWatcherPause(ctx, _):
2412 """Returns until when watcher is paused (if available).
2415 if ctx.watcher_pause is None:
2418 return ctx.watcher_pause
2421 def _BuildClusterFields():
2422 """Builds list of fields for cluster information.
2426 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2427 lambda ctx, cluster: list(cluster.GetTags())),
2428 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2429 "Architecture information"), None, 0,
2430 lambda ctx, _: runtime.GetArchInfo()),
2431 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2432 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2433 lambda ctx, _: ctx.drain_flag),
2434 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2435 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2436 _ClusterWatcherPause),
2441 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2442 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2447 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2448 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
2452 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2454 return _PrepareFieldList(fields, [
2455 ("name", "cluster_name"),
2459 class NetworkQueryData:
2460 """Data container for network data queries.
2463 def __init__(self, networks, network_to_groups,
2464 network_to_instances, stats):
2465 """Initializes this class.
2467 @param networks: List of network objects
2468 @type network_to_groups: dict; network UUID as key
2469 @param network_to_groups: Per-network list of groups
2470 @type network_to_instances: dict; network UUID as key
2471 @param network_to_instances: Per-network list of instances
2472 @type stats: dict; network UUID as key
2473 @param stats: Per-network usage statistics
2476 self.networks = networks
2477 self.network_to_groups = network_to_groups
2478 self.network_to_instances = network_to_instances
2482 """Iterate over all networks.
2485 for net in self.networks:
2487 self.curstats = self.stats.get(net.uuid, None)
2489 self.curstats = None
2493 _NETWORK_SIMPLE_FIELDS = {
2494 "name": ("Network", QFT_TEXT, 0, "The network"),
2495 "network": ("Subnet", QFT_TEXT, 0, "The subnet"),
2496 "gateway": ("Gateway", QFT_OTHER, 0, "The gateway"),
2497 "network6": ("IPv6Subnet", QFT_OTHER, 0, "The ipv6 subnet"),
2498 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "The ipv6 gateway"),
2499 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "The mac prefix"),
2500 "network_type": ("NetworkType", QFT_OTHER, 0, "The network type"),
2504 _NETWORK_STATS_FIELDS = {
2505 "free_count": ("FreeCount", QFT_NUMBER, 0, "How many addresses are free"),
2506 "reserved_count": ("ReservedCount", QFT_NUMBER, 0, "How many addresses are reserved"),
2507 "map": ("Map", QFT_TEXT, 0, "The actual mapping"),
2508 "external_reservations": ("ExternalReservations", QFT_TEXT, 0, "The external reservations"),
2512 def _GetNetworkStatsField(field, kind, ctx, net):
2513 """Gets the value of a "stats" field from L{NetworkQueryData}.
2515 @param field: Field name
2516 @param kind: Data kind, one of L{constants.QFT_ALL}
2517 @type ctx: L{NetworkQueryData}
2522 value = ctx.curstats[field]
2526 if kind == QFT_TEXT:
2529 assert kind in (QFT_NUMBER, QFT_UNIT)
2531 # Try to convert into number
2534 except (ValueError, TypeError):
2535 logging.exception("Failed to convert network field '%s' (value %r) to int",
2540 def _BuildNetworkFields():
2541 """Builds list of fields for network queries.
2545 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2546 lambda ctx, inst: list(inst.GetTags())),
2551 (_MakeField(name, title, kind, doc),
2552 NETQ_CONFIG, 0, _GetItemAttr(name))
2553 for (name, (title, kind, flags, doc)) in _NETWORK_SIMPLE_FIELDS.items()
2556 def _GetLength(getter):
2557 return lambda ctx, network: len(getter(ctx)[network.uuid])
2559 def _GetSortedList(getter):
2560 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2562 network_to_groups = operator.attrgetter("network_to_groups")
2563 network_to_instances = operator.attrgetter("network_to_instances")
2565 # Add fields for node groups
2567 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2568 NETQ_GROUP, 0, _GetLength(network_to_groups)),
2569 (_MakeField("group_list", "GroupList", QFT_OTHER, "List of nodegroups"),
2570 NETQ_GROUP, 0, _GetSortedList(network_to_groups)),
2573 # Add fields for instances
2575 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2576 NETQ_INST, 0, _GetLength(network_to_instances)),
2577 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2578 NETQ_INST, 0, _GetSortedList(network_to_instances)),
2581 # Add fields for usage statistics
2583 (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2584 compat.partial(_GetNetworkStatsField, name, kind))
2585 for (name, (title, kind, flags, doc)) in _NETWORK_STATS_FIELDS.items()
2588 return _PrepareFieldList(fields, [])
2590 #: Fields for cluster information
2591 CLUSTER_FIELDS = _BuildClusterFields()
2593 #: Fields available for node queries
2594 NODE_FIELDS = _BuildNodeFields()
2596 #: Fields available for instance queries
2597 INSTANCE_FIELDS = _BuildInstanceFields()
2599 #: Fields available for lock queries
2600 LOCK_FIELDS = _BuildLockFields()
2602 #: Fields available for node group queries
2603 GROUP_FIELDS = _BuildGroupFields()
2605 #: Fields available for operating system queries
2606 OS_FIELDS = _BuildOsFields()
2608 #: Fields available for extstorage provider queries
2609 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2611 #: Fields available for job queries
2612 JOB_FIELDS = _BuildJobFields()
2614 #: Fields available for exports
2615 EXPORT_FIELDS = _BuildExportFields()
2617 #: Fields available for network queries
2618 NETWORK_FIELDS = _BuildNetworkFields()
2620 #: All available resources
2622 constants.QR_CLUSTER: CLUSTER_FIELDS,
2623 constants.QR_INSTANCE: INSTANCE_FIELDS,
2624 constants.QR_NODE: NODE_FIELDS,
2625 constants.QR_LOCK: LOCK_FIELDS,
2626 constants.QR_GROUP: GROUP_FIELDS,
2627 constants.QR_OS: OS_FIELDS,
2628 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2629 constants.QR_JOB: JOB_FIELDS,
2630 constants.QR_EXPORT: EXPORT_FIELDS,
2631 constants.QR_NETWORK: NETWORK_FIELDS,
2634 #: All available field lists
2635 ALL_FIELD_LISTS = ALL_FIELDS.values()