4 # Copyright (C) 2010, 2011, 2012, 2013 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)
77 NETQ_INST) = range(300, 304)
79 # Constants for requesting data from the caller/data provider. Each property
80 # collected/computed separately by the data provider should have its own to
81 # only collect the requested data and not more.
94 IQ_NETWORKS) = range(100, 106)
98 LQ_PENDING) = range(10, 13)
103 GQ_DISKPARAMS) = range(200, 204)
107 CQ_WATCHER_PAUSE) = range(300, 303)
109 (JQ_ARCHIVED, ) = range(400, 401)
113 QFF_IP_ADDRESS = 0x02
115 QFF_SPLIT_TIMESTAMP = 0x08
116 # Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
117 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP)
119 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
120 TITLE_RE = re.compile(r"^[^\s]+$")
121 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
123 #: Verification function for each field type
125 QFT_UNKNOWN: ht.TNone,
126 QFT_TEXT: ht.TString,
130 QFT_TIMESTAMP: ht.TNumber,
131 QFT_OTHER: lambda _: True,
134 # Unique objects for special field statuses
135 _FS_UNKNOWN = object()
136 _FS_NODATA = object()
137 _FS_UNAVAIL = object()
138 _FS_OFFLINE = object()
140 #: List of all special status
141 _FS_ALL = compat.UniqueFrozenset([
148 #: VType to QFT mapping
150 # TODO: fix validation of empty strings
151 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
152 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
153 constants.VTYPE_BOOL: QFT_BOOL,
154 constants.VTYPE_SIZE: QFT_UNIT,
155 constants.VTYPE_INT: QFT_NUMBER,
158 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
161 def _GetUnknownField(ctx, item): # pylint: disable=W0613
162 """Gets the contents of an unknown field.
168 def _GetQueryFields(fielddefs, selected):
169 """Calculates the internal list of selected fields.
171 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
173 @type fielddefs: dict
174 @param fielddefs: Field definitions
175 @type selected: list of strings
176 @param selected: List of selected fields
181 for name in selected:
183 fdef = fielddefs[name]
185 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
186 None, 0, _GetUnknownField)
188 assert len(fdef) == 4
195 def GetAllFields(fielddefs):
196 """Extract L{objects.QueryFieldDefinition} from field definitions.
198 @rtype: list of L{objects.QueryFieldDefinition}
201 return [fdef for (fdef, _, _, _) in fielddefs]
205 """Class for filter analytics.
207 When filters are used, the user of the L{Query} class usually doesn't know
208 exactly which items will be necessary for building the result. It therefore
209 has to prepare and compute the input data for potentially returning
212 There are two ways to optimize this. The first, and simpler, is to assign
213 each field a group of data, so that the caller can determine which
214 computations are necessary depending on the data groups requested. The list
215 of referenced groups must also be computed for fields referenced in the
218 The second is restricting the items based on a primary key. The primary key
219 is usually a unique name (e.g. a node name). This class extracts all
220 referenced names from a filter. If it encounters any filter condition which
221 disallows such a list to be determined (e.g. a non-equality filter), all
222 names will be requested.
224 The end-effect is that any operation other than L{qlang.OP_OR} and
225 L{qlang.OP_EQUAL} will make the query more expensive.
228 def __init__(self, namefield):
229 """Initializes this class.
231 @type namefield: string
232 @param namefield: Field caller is interested in
235 self._namefield = namefield
237 #: Whether all names need to be requested (e.g. if a non-equality operator
239 self._allnames = False
241 #: Which names to request
244 #: Data kinds referenced by the filter (used by L{Query.RequestedData})
245 self._datakinds = set()
247 def RequestedNames(self):
248 """Returns all requested values.
250 Returns C{None} if list of values can't be determined (e.g. encountered
251 non-equality operators).
256 if self._allnames or self._names is None:
259 return utils.UniqueSequence(self._names)
261 def ReferencedData(self):
262 """Returns all kinds of data referenced by the filter.
265 return frozenset(self._datakinds)
267 def _NeedAllNames(self):
268 """Changes internal state to request all names.
271 self._allnames = True
274 def NoteLogicOp(self, op):
275 """Called when handling a logic operation.
281 if op != qlang.OP_OR:
284 def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
285 """Called when handling an unary operation.
291 if datakind is not None:
292 self._datakinds.add(datakind)
296 def NoteBinaryOp(self, op, datakind, name, value):
297 """Called when handling a binary operation.
302 @param name: Left-hand side of operator (field name)
303 @param value: Right-hand side of operator
306 if datakind is not None:
307 self._datakinds.add(datakind)
312 # If any operator other than equality was used, all names need to be
314 if op == qlang.OP_EQUAL and name == self._namefield:
315 if self._names is None:
317 self._names.append(value)
322 def _WrapLogicOp(op_fn, sentences, ctx, item):
323 """Wrapper for logic operator functions.
326 return op_fn(fn(ctx, item) for fn in sentences)
329 def _WrapUnaryOp(op_fn, inner, ctx, item):
330 """Wrapper for unary operator functions.
333 return op_fn(inner(ctx, item))
336 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
337 """Wrapper for binary operator functions.
340 return op_fn(retrieval_fn(ctx, item), value)
343 def _WrapNot(fn, lhs, rhs):
344 """Negates the result of a wrapped function.
347 return not fn(lhs, rhs)
350 def _PrepareRegex(pattern):
351 """Compiles a regular expression.
355 return re.compile(pattern)
356 except re.error, err:
357 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
360 def _PrepareSplitTimestamp(value):
361 """Prepares a value for comparison by L{_MakeSplitTimestampComparison}.
364 if ht.TNumber(value):
367 return utils.MergeTime(value)
370 def _MakeSplitTimestampComparison(fn):
371 """Compares split timestamp values after converting to float.
374 return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
377 def _MakeComparisonChecks(fn):
378 """Prepares flag-specific comparisons using a comparison function.
382 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn),
383 _PrepareSplitTimestamp),
384 (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs),
390 class _FilterCompilerHelper:
391 """Converts a query filter to a callable usable for filtering.
394 # String statement has no effect, pylint: disable=W0105
396 #: How deep filters can be nested
399 # Unique identifiers for operator groups
402 _OPTYPE_BINARY) = range(1, 4)
404 """Functions for equality checks depending on field flags.
406 List of tuples containing flags and a callable receiving the left- and
407 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
408 (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
410 Order matters. The first item with flags will be used. Flags are checked
416 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
417 case_sensitive=False),
419 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq),
420 _PrepareSplitTimestamp),
421 (None, operator.eq, None),
426 Operator as key (C{qlang.OP_*}), value a tuple of operator group
427 (C{_OPTYPE_*}) and a group-specific value:
429 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
431 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
432 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
433 right-hand side of the operator, used by L{_HandleBinaryOp}
438 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
439 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
442 qlang.OP_NOT: (_OPTYPE_UNARY, None),
443 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
446 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
448 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
449 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
450 qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)),
451 qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)),
452 qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)),
453 qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)),
454 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
455 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
457 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
458 (None, operator.contains, None),
462 def __init__(self, fields):
463 """Initializes this class.
465 @param fields: Field definitions (return value of L{_PrepareFieldList})
468 self._fields = fields
470 self._op_handler = None
472 def __call__(self, hints, qfilter):
473 """Converts a query filter into a callable function.
475 @type hints: L{_FilterHints} or None
476 @param hints: Callbacks doing analysis on filter
478 @param qfilter: Filter structure
480 @return: Function receiving context and item as parameters, returning
481 boolean as to whether item matches filter
486 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
488 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
490 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
494 filter_fn = self._Compile(qfilter, 0)
496 self._op_handler = None
500 def _Compile(self, qfilter, level):
501 """Inner function for converting filters.
503 Calls the correct handler functions for the top-level operator. This
504 function is called recursively (e.g. for logic operators).
507 if not (isinstance(qfilter, (list, tuple)) and qfilter):
508 raise errors.ParameterError("Invalid filter on level %s" % level)
511 if level >= self._LEVELS_MAX:
512 raise errors.ParameterError("Only up to %s levels are allowed (filter"
513 " nested too deep)" % self._LEVELS_MAX)
515 # Create copy to be modified
516 operands = qfilter[:]
520 (kind, op_data) = self._OPS[op]
522 raise errors.ParameterError("Unknown operator '%s'" % op)
524 (handler, hints_cb) = self._op_handler[kind]
526 return handler(hints_cb, level, op, op_data, operands)
528 def _LookupField(self, name):
529 """Returns a field definition by name.
533 return self._fields[name]
535 raise errors.ParameterError("Unknown field '%s'" % name)
537 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
538 """Handles logic operators.
540 @type hints_fn: callable
541 @param hints_fn: Callback doing some analysis on the filter
543 @param level: Current depth
546 @type op_fn: callable
547 @param op_fn: Function implementing operator
549 @param operands: List of operands
555 return compat.partial(_WrapLogicOp, op_fn,
556 [self._Compile(op, level + 1) for op in operands])
558 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
559 """Handles unary operators.
561 @type hints_fn: callable
562 @param hints_fn: Callback doing some analysis on the filter
564 @param level: Current depth
567 @type op_fn: callable
568 @param op_fn: Function implementing operator
570 @param operands: List of operands
575 if len(operands) != 1:
576 raise errors.ParameterError("Unary operator '%s' expects exactly one"
579 if op == qlang.OP_TRUE:
580 (_, datakind, _, retrieval_fn) = self._LookupField(operands[0])
583 hints_fn(op, datakind)
585 op_fn = operator.truth
587 elif op == qlang.OP_NOT:
591 op_fn = operator.not_
592 arg = self._Compile(operands[0], level + 1)
594 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
596 return compat.partial(_WrapUnaryOp, op_fn, arg)
598 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
599 """Handles binary operators.
601 @type hints_fn: callable
602 @param hints_fn: Callback doing some analysis on the filter
604 @param level: Current depth
607 @param op_data: Functions implementing operators
609 @param operands: List of operands
612 # Unused arguments, pylint: disable=W0613
614 (name, value) = operands
615 except (ValueError, TypeError):
616 raise errors.ParameterError("Invalid binary operator, expected exactly"
619 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
621 assert fdef.kind != QFT_UNKNOWN
623 # TODO: Type conversions?
625 verify_fn = _VERIFY_FN[fdef.kind]
626 if not verify_fn(value):
627 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
628 " with '%s', expected %s" %
629 (name, fdef.kind, value.__class__.__name__,
633 hints_fn(op, datakind, name, value)
635 for (fn_flags, fn, valprepfn) in op_data:
636 if fn_flags is None or fn_flags & field_flags:
637 # Prepare value if necessary (e.g. compile regular expression)
639 value = valprepfn(value)
641 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
643 raise errors.ProgrammerError("Unable to find operator implementation"
644 " (op '%s', flags %s)" % (op, field_flags))
647 def _CompileFilter(fields, hints, qfilter):
648 """Converts a query filter into a callable function.
650 See L{_FilterCompilerHelper} for details.
655 return _FilterCompilerHelper(fields)(hints, qfilter)
659 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
660 """Initializes this class.
662 The field definition is a dictionary with the field's name as a key and a
663 tuple containing, in order, the field definition object
664 (L{objects.QueryFieldDefinition}, the data kind to help calling code
665 collect data and a retrieval function. The retrieval function is called
666 with two parameters, in order, the data container and the item in container
667 (see L{Query.Query}).
669 Users of this class can call L{RequestedData} before preparing the data
670 container to determine what data is needed.
672 @type fieldlist: dictionary
673 @param fieldlist: Field definitions
674 @type selected: list of strings
675 @param selected: List of selected fields
678 assert namefield is None or namefield in fieldlist
680 self._fields = _GetQueryFields(fieldlist, selected)
682 self._filter_fn = None
683 self._requested_names = None
684 self._filter_datakinds = frozenset()
686 if qfilter is not None:
687 # Collect requested names if wanted
689 hints = _FilterHints(namefield)
693 # Build filter function
694 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
696 self._requested_names = hints.RequestedNames()
697 self._filter_datakinds = hints.ReferencedData()
699 if namefield is None:
702 (_, _, _, self._name_fn) = fieldlist[namefield]
704 def RequestedNames(self):
705 """Returns all names referenced in the filter.
707 If there is no filter or operators are preventing determining the exact
708 names, C{None} is returned.
711 return self._requested_names
713 def RequestedData(self):
714 """Gets requested kinds of data.
719 return (self._filter_datakinds |
720 frozenset(datakind for (_, datakind, _, _) in self._fields
721 if datakind is not None))
724 """Returns the list of fields for this query.
726 Includes unknown fields.
728 @rtype: List of L{objects.QueryFieldDefinition}
731 return GetAllFields(self._fields)
733 def Query(self, ctx, sort_by_name=True):
736 @param ctx: Data container passed to field retrieval functions, must
737 support iteration using C{__iter__}
738 @type sort_by_name: boolean
739 @param sort_by_name: Whether to sort by name or keep the input data's
743 sort = (self._name_fn and sort_by_name)
747 for idx, item in enumerate(ctx):
748 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
751 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
755 _VerifyResultRow(self._fields, row)
758 (status, name) = _ProcessResult(self._name_fn(ctx, item))
759 assert status == constants.RS_NORMAL
760 # TODO: Are there cases where we wouldn't want to use NiceSort?
761 # Answer: if the name field is non-string...
762 result.append((utils.NiceSortKey(name), idx, row))
769 # TODO: Would "heapq" be more efficient than sorting?
771 # Sorting in-place instead of using "sorted()"
774 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
776 return map(operator.itemgetter(2), result)
778 def OldStyleQuery(self, ctx, sort_by_name=True):
779 """Query with "old" query result format.
781 See L{Query.Query} for arguments.
784 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
785 if fdef.kind == QFT_UNKNOWN)
787 raise errors.OpPrereqError("Unknown output fields selected: %s" %
788 (utils.CommaJoin(unknown), ),
791 return [[value for (_, value) in row]
792 for row in self.Query(ctx, sort_by_name=sort_by_name)]
795 def _ProcessResult(value):
796 """Converts result values into externally-visible ones.
799 if value is _FS_UNKNOWN:
800 return (RS_UNKNOWN, None)
801 elif value is _FS_NODATA:
802 return (RS_NODATA, None)
803 elif value is _FS_UNAVAIL:
804 return (RS_UNAVAIL, None)
805 elif value is _FS_OFFLINE:
806 return (RS_OFFLINE, None)
808 return (RS_NORMAL, value)
811 def _VerifyResultRow(fields, row):
812 """Verifies the contents of a query result row.
815 @param fields: Field definitions for result
816 @type row: list of tuples
820 assert len(row) == len(fields)
822 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
823 if status == RS_NORMAL:
824 if not _VERIFY_FN[fdef.kind](value):
825 errs.append("normal field %s fails validation (value is %s)" %
827 elif value is not None:
828 errs.append("abnormal field %s has a non-None value" % fdef.name)
829 assert not errs, ("Failed validation: %s in row %s" %
830 (utils.CommaJoin(errs), row))
833 def _FieldDictKey((fdef, _, flags, fn)):
834 """Generates key for field dictionary.
837 assert fdef.name and fdef.title, "Name and title are required"
838 assert FIELD_NAME_RE.match(fdef.name)
839 assert TITLE_RE.match(fdef.title)
840 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
841 fdef.doc.strip() == fdef.doc), \
842 "Invalid description for field '%s'" % fdef.name
844 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
849 def _PrepareFieldList(fields, aliases):
850 """Prepares field list for use by L{Query}.
852 Converts the list to a dictionary and does some verification.
854 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
855 kind, retrieval function)
856 @param fields: List of fields, see L{Query.__init__} for a better
858 @type aliases: list of tuples; (alias, target)
859 @param aliases: list of tuples containing aliases; for each
860 alias/target pair, a duplicate will be created in the field list
862 @return: Field dictionary for L{Query}
866 duplicates = utils.FindDuplicates(fdef.title.lower()
867 for (fdef, _, _, _) in fields)
868 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
870 result = utils.SequenceToDict(fields, key=_FieldDictKey)
872 for alias, target in aliases:
873 assert alias not in result, "Alias %s overrides an existing field" % alias
874 assert target in result, "Missing target %s for alias %s" % (target, alias)
875 (fdef, k, flags, fn) = result[target]
878 result[alias] = (fdef, k, flags, fn)
880 assert len(result) == len(fields) + len(aliases)
881 assert compat.all(name == fdef.name
882 for (name, (fdef, _, _, _)) in result.items())
887 def GetQueryResponse(query, ctx, sort_by_name=True):
888 """Prepares the response for a query.
890 @type query: L{Query}
891 @param ctx: Data container, see L{Query.Query}
892 @type sort_by_name: boolean
893 @param sort_by_name: Whether to sort by name or keep the input data's
897 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
898 fields=query.GetFields()).ToDict()
901 def QueryFields(fielddefs, selected):
902 """Returns list of available fields.
904 @type fielddefs: dict
905 @param fielddefs: Field definitions
906 @type selected: list of strings
907 @param selected: List of selected fields
908 @return: List of L{objects.QueryFieldDefinition}
912 # Client requests all fields, sort by name
913 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
914 key=operator.attrgetter("name"))
916 # Keep order as requested by client
917 fdefs = Query(fielddefs, selected).GetFields()
919 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
922 def _MakeField(name, title, kind, doc):
923 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
925 @param name: Field name as a regular expression
926 @param title: Human-readable title
927 @param kind: Field type
928 @param doc: Human-readable description
931 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
935 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
936 """Returns a static value.
942 def _StaticValue(value):
943 """Prepares a function to return a static value.
946 return compat.partial(_StaticValueInner, value)
949 def _GetNodeRole(node, master_name):
950 """Determine node role.
952 @type node: L{objects.Node}
953 @param node: Node object
954 @type master_name: string
955 @param master_name: Master node name
958 if node.name == master_name:
959 return constants.NR_MASTER
960 elif node.master_candidate:
961 return constants.NR_MCANDIDATE
963 return constants.NR_DRAINED
965 return constants.NR_OFFLINE
967 return constants.NR_REGULAR
970 def _GetItemAttr(attr):
971 """Returns a field function to return an attribute of the item.
973 @param attr: Attribute name
976 getter = operator.attrgetter(attr)
977 return lambda _, item: getter(item)
980 def _GetItemMaybeAttr(attr):
981 """Returns a field function to return a not-None attribute of the item.
983 If the value is None, then C{_FS_UNAVAIL} will be returned instead.
985 @param attr: Attribute name
989 val = getattr(obj, attr)
997 def _GetNDParam(name):
998 """Return a field function to return an ND parameter out of the context.
1001 def _helper(ctx, _):
1002 if ctx.ndparams is None:
1005 return ctx.ndparams.get(name, None)
1009 def _BuildNDFields(is_group):
1010 """Builds all the ndparam fields.
1012 @param is_group: whether this is called at group or node level
1016 field_kind = GQ_CONFIG
1018 field_kind = NQ_GROUP
1019 return [(_MakeField("ndp/%s" % name,
1020 constants.NDS_PARAMETER_TITLES.get(name,
1022 _VTToQFT[kind], "The \"%s\" node parameter" % name),
1023 field_kind, 0, _GetNDParam(name))
1024 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1027 def _ConvWrapInner(convert, fn, ctx, item):
1028 """Wrapper for converting values.
1030 @param convert: Conversion function receiving value as single parameter
1031 @param fn: Retrieval function
1034 value = fn(ctx, item)
1036 # Is the value an abnormal status?
1037 if compat.any(value is fs for fs in _FS_ALL):
1041 # TODO: Should conversion function also receive context, item or both?
1042 return convert(value)
1045 def _ConvWrap(convert, fn):
1046 """Convenience wrapper for L{_ConvWrapInner}.
1048 @param convert: Conversion function receiving value as single parameter
1049 @param fn: Retrieval function
1052 return compat.partial(_ConvWrapInner, convert, fn)
1055 def _GetItemTimestamp(getter):
1056 """Returns function for getting timestamp of item.
1058 @type getter: callable
1059 @param getter: Function to retrieve timestamp attribute
1063 """Returns a timestamp of item.
1066 timestamp = getter(item)
1067 if timestamp is None:
1068 # Old configs might not have all timestamps
1076 def _GetItemTimestampFields(datatype):
1077 """Returns common timestamp fields.
1079 @param datatype: Field data type for use by L{Query.RequestedData}
1083 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1084 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1085 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1086 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1090 class NodeQueryData:
1091 """Data container for node data queries.
1094 def __init__(self, nodes, live_data, master_uuid, node_to_primary,
1095 node_to_secondary, inst_uuid_to_inst_name, groups, oob_support,
1097 """Initializes this class.
1101 self.live_data = live_data
1102 self.master_uuid = master_uuid
1103 self.node_to_primary = node_to_primary
1104 self.node_to_secondary = node_to_secondary
1105 self.inst_uuid_to_inst_name = inst_uuid_to_inst_name
1106 self.groups = groups
1107 self.oob_support = oob_support
1108 self.cluster = cluster
1110 # Used for individual rows
1111 self.curlive_data = None
1112 self.ndparams = None
1115 """Iterate over all nodes.
1117 This function has side-effects and only one instance of the resulting
1118 generator should be used at a time.
1121 for node in self.nodes:
1122 group = self.groups.get(node.group, None)
1124 self.ndparams = None
1126 self.ndparams = self.cluster.FillND(node, group)
1128 self.curlive_data = self.live_data.get(node.uuid, None)
1130 self.curlive_data = None
1134 #: Fields that are direct attributes of an L{objects.Node} object
1135 _NODE_SIMPLE_FIELDS = {
1136 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1137 "master_candidate": ("MasterC", QFT_BOOL, 0,
1138 "Whether node is a master candidate"),
1139 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1140 "Whether node can become a master candidate"),
1141 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1142 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1143 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1144 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1145 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1149 #: Fields requiring talking to the node
1150 # Note that none of these are available for non-vm_capable nodes
1151 _NODE_LIVE_FIELDS = {
1152 "bootid": ("BootID", QFT_TEXT, "bootid",
1153 "Random UUID renewed for each system reboot, can be used"
1154 " for detecting reboots by tracking changes"),
1155 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1156 "Number of NUMA domains on node (if exported by hypervisor)"),
1157 "cnos": ("CNOs", QFT_NUMBER, "cpu_dom0",
1158 "Number of logical processors used by the node OS (dom0 for Xen)"),
1159 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1160 "Number of physical CPU sockets (if exported by hypervisor)"),
1161 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1162 "dfree": ("DFree", QFT_UNIT, "storage_free",
1163 "Available storage space in storage unit"),
1164 "dtotal": ("DTotal", QFT_UNIT, "storage_size",
1165 "Total storage space in storage unit used for instance disk"
1167 "spfree": ("SpFree", QFT_NUMBER, "spindles_free",
1168 "Available spindles in volume group (exclusive storage only)"),
1169 "sptotal": ("SpTotal", QFT_NUMBER, "spindles_total",
1170 "Total spindles in volume group (exclusive storage only)"),
1171 "mfree": ("MFree", QFT_UNIT, "memory_free",
1172 "Memory available for instance allocations"),
1173 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1174 "Amount of memory used by node (dom0 for Xen)"),
1175 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1176 "Total amount of memory of physical machine"),
1181 """Build function for calling another function with an node group.
1183 @param cb: The callback to be called with the nodegroup
1187 """Get group data for a node.
1189 @type ctx: L{NodeQueryData}
1190 @type inst: L{objects.Node}
1191 @param inst: Node object
1194 ng = ctx.groups.get(node.group, None)
1196 # Nodes always have a group, or the configuration is corrupt
1199 return cb(ctx, node, ng)
1204 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1205 """Returns the name of a node's group.
1207 @type ctx: L{NodeQueryData}
1208 @type node: L{objects.Node}
1209 @param node: Node object
1210 @type ng: L{objects.NodeGroup}
1211 @param ng: The node group this node belongs to
1217 def _GetNodePower(ctx, node):
1218 """Returns the node powered state
1220 @type ctx: L{NodeQueryData}
1221 @type node: L{objects.Node}
1222 @param node: Node object
1225 if ctx.oob_support[node.uuid]:
1231 def _GetNdParams(ctx, node, ng):
1232 """Returns the ndparams for this node.
1234 @type ctx: L{NodeQueryData}
1235 @type node: L{objects.Node}
1236 @param node: Node object
1237 @type ng: L{objects.NodeGroup}
1238 @param ng: The node group this node belongs to
1241 return ctx.cluster.SimpleFillND(ng.FillND(node))
1244 def _GetLiveNodeField(field, kind, ctx, node):
1245 """Gets the value of a "live" field from L{NodeQueryData}.
1247 @param field: Live field name
1248 @param kind: Data kind, one of L{constants.QFT_ALL}
1249 @type ctx: L{NodeQueryData}
1250 @type node: L{objects.Node}
1251 @param node: Node object
1257 if not node.vm_capable:
1260 if not ctx.curlive_data:
1263 return _GetStatsField(field, kind, ctx.curlive_data)
1266 def _GetStatsField(field, kind, data):
1267 """Gets a value from live statistics.
1269 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is
1270 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL}
1273 @param field: Live field name
1274 @param kind: Data kind, one of L{constants.QFT_ALL}
1276 @param data: Statistics
1284 if kind == QFT_TEXT:
1287 assert kind in (QFT_NUMBER, QFT_UNIT)
1289 # Try to convert into number
1292 except (ValueError, TypeError):
1293 logging.exception("Failed to convert node field '%s' (value %r) to int",
1298 def _GetNodeHvState(_, node):
1299 """Converts node's hypervisor state for query result.
1302 hv_state = node.hv_state
1304 if hv_state is None:
1307 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1310 def _GetNodeDiskState(_, node):
1311 """Converts node's disk state for query result.
1314 disk_state = node.disk_state
1316 if disk_state is None:
1319 return dict((disk_kind, dict((name, value.ToDict())
1320 for (name, value) in kind_state.items()))
1321 for (disk_kind, kind_state) in disk_state.items())
1324 def _BuildNodeFields():
1325 """Builds list of fields for node queries.
1329 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1330 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1331 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1332 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1333 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1334 lambda ctx, node: list(node.GetTags())),
1335 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1336 NQ_CONFIG, 0, lambda ctx, node: node.uuid == ctx.master_uuid),
1337 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1338 _GetGroup(_GetNodeGroup)),
1339 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1340 NQ_CONFIG, 0, _GetItemAttr("group")),
1341 (_MakeField("powered", "Powered", QFT_BOOL,
1342 "Whether node is thought to be powered on"),
1343 NQ_OOB, 0, _GetNodePower),
1344 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1345 "Merged node parameters"),
1346 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1347 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1348 "Custom node parameters"),
1349 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1350 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1351 NQ_CONFIG, 0, _GetNodeHvState),
1352 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1353 NQ_CONFIG, 0, _GetNodeDiskState),
1356 fields.extend(_BuildNDFields(False))
1359 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1360 constants.NR_REGULAR, constants.NR_DRAINED,
1361 constants.NR_OFFLINE)
1362 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1363 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1365 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1366 lambda ctx, node: _GetNodeRole(node, ctx.master_uuid)))
1367 assert set(role_values) == constants.NR_ALL
1369 def _GetLength(getter):
1370 return lambda ctx, node: len(getter(ctx)[node.uuid])
1372 def _GetList(getter):
1373 return lambda ctx, node: utils.NiceSort(
1374 [ctx.inst_uuid_to_inst_name[uuid]
1375 for uuid in getter(ctx)[node.uuid]])
1377 # Add fields operating on instance lists
1378 for prefix, titleprefix, docword, getter in \
1379 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1380 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1381 # TODO: Allow filterting by hostname in list
1383 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1384 "Number of instances with this node as %s" % docword),
1385 NQ_INST, 0, _GetLength(getter)),
1386 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1388 "List of instances with this node as %s" % docword),
1389 NQ_INST, 0, _GetList(getter)),
1394 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1395 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
1397 # Add fields requiring live data
1399 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1400 compat.partial(_GetLiveNodeField, nfield, kind))
1401 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
1404 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1406 return _PrepareFieldList(fields, [])
1409 class InstanceQueryData:
1410 """Data container for instance data queries.
1413 def __init__(self, instances, cluster, disk_usage, offline_node_uuids,
1414 bad_node_uuids, live_data, wrongnode_inst, console, nodes,
1416 """Initializes this class.
1418 @param instances: List of instance objects
1419 @param cluster: Cluster object
1420 @type disk_usage: dict; instance UUID as key
1421 @param disk_usage: Per-instance disk usage
1422 @type offline_node_uuids: list of strings
1423 @param offline_node_uuids: List of offline nodes
1424 @type bad_node_uuids: list of strings
1425 @param bad_node_uuids: List of faulty nodes
1426 @type live_data: dict; instance UUID as key
1427 @param live_data: Per-instance live data
1428 @type wrongnode_inst: set
1429 @param wrongnode_inst: Set of instances running on wrong node(s)
1430 @type console: dict; instance UUID as key
1431 @param console: Per-instance console information
1432 @type nodes: dict; node UUID as key
1433 @param nodes: Node objects
1434 @type networks: dict; net_uuid as key
1435 @param networks: Network objects
1438 assert len(set(bad_node_uuids) & set(offline_node_uuids)) == \
1439 len(offline_node_uuids), \
1440 "Offline nodes not included in bad nodes"
1441 assert not (set(live_data.keys()) & set(bad_node_uuids)), \
1442 "Found live data for bad or offline nodes"
1444 self.instances = instances
1445 self.cluster = cluster
1446 self.disk_usage = disk_usage
1447 self.offline_nodes = offline_node_uuids
1448 self.bad_nodes = bad_node_uuids
1449 self.live_data = live_data
1450 self.wrongnode_inst = wrongnode_inst
1451 self.console = console
1453 self.groups = groups
1454 self.networks = networks
1456 # Used for individual rows
1457 self.inst_hvparams = None
1458 self.inst_beparams = None
1459 self.inst_osparams = None
1460 self.inst_nicparams = None
1463 """Iterate over all instances.
1465 This function has side-effects and only one instance of the resulting
1466 generator should be used at a time.
1469 for inst in self.instances:
1470 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1471 self.inst_beparams = self.cluster.FillBE(inst)
1472 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1473 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1474 for nic in inst.nics]
1479 def _GetInstOperState(ctx, inst):
1480 """Get instance's operational status.
1482 @type ctx: L{InstanceQueryData}
1483 @type inst: L{objects.Instance}
1484 @param inst: Instance object
1487 # Can't use RS_OFFLINE here as it would describe the instance to
1488 # be offline when we actually don't know due to missing data
1489 if inst.primary_node in ctx.bad_nodes:
1492 return bool(ctx.live_data.get(inst.uuid))
1495 def _GetInstLiveData(name):
1496 """Build function for retrieving live data.
1499 @param name: Live data field name
1503 """Get live data for an instance.
1505 @type ctx: L{InstanceQueryData}
1506 @type inst: L{objects.Instance}
1507 @param inst: Instance object
1510 if (inst.primary_node in ctx.bad_nodes or
1511 inst.primary_node in ctx.offline_nodes):
1512 # Can't use RS_OFFLINE here as it would describe the instance to be
1513 # offline when we actually don't know due to missing data
1516 if inst.uuid in ctx.live_data:
1517 data = ctx.live_data[inst.uuid]
1526 def _GetInstStatus(ctx, inst):
1527 """Get instance status.
1529 @type ctx: L{InstanceQueryData}
1530 @type inst: L{objects.Instance}
1531 @param inst: Instance object
1534 if inst.primary_node in ctx.offline_nodes:
1535 return constants.INSTST_NODEOFFLINE
1537 if inst.primary_node in ctx.bad_nodes:
1538 return constants.INSTST_NODEDOWN
1540 if bool(ctx.live_data.get(inst.uuid)):
1541 if inst.uuid in ctx.wrongnode_inst:
1542 return constants.INSTST_WRONGNODE
1543 elif inst.admin_state == constants.ADMINST_UP:
1544 return constants.INSTST_RUNNING
1546 return constants.INSTST_ERRORUP
1548 if inst.admin_state == constants.ADMINST_UP:
1549 return constants.INSTST_ERRORDOWN
1550 elif inst.admin_state == constants.ADMINST_DOWN:
1551 return constants.INSTST_ADMINDOWN
1553 return constants.INSTST_ADMINOFFLINE
1556 def _GetInstDisk(index, cb):
1557 """Build function for calling another function with an instance Disk.
1560 @param index: Disk index
1566 """Call helper function with instance Disk.
1568 @type ctx: L{InstanceQueryData}
1569 @type inst: L{objects.Instance}
1570 @param inst: Instance object
1574 nic = inst.disks[index]
1578 return cb(ctx, index, nic)
1583 def _GetInstDiskSize(ctx, _, disk): # pylint: disable=W0613
1584 """Get a Disk's size.
1586 @type ctx: L{InstanceQueryData}
1587 @type disk: L{objects.Disk}
1588 @param disk: The Disk object
1591 if disk.size is None:
1597 def _GetInstDiskSpindles(ctx, _, disk): # pylint: disable=W0613
1598 """Get a Disk's spindles.
1600 @type disk: L{objects.Disk}
1601 @param disk: The Disk object
1604 if disk.spindles is None:
1607 return disk.spindles
1610 def _GetInstDeviceName(ctx, _, device): # pylint: disable=W0613
1611 """Get a Device's Name.
1613 @type ctx: L{InstanceQueryData}
1614 @type device: L{objects.NIC} or L{objects.Disk}
1615 @param device: The NIC or Disk object
1618 if device.name is None:
1624 def _GetInstDeviceUUID(ctx, _, device): # pylint: disable=W0613
1625 """Get a Device's UUID.
1627 @type ctx: L{InstanceQueryData}
1628 @type device: L{objects.NIC} or L{objects.Disk}
1629 @param device: The NIC or Disk object
1632 if device.uuid is None:
1638 def _GetInstNic(index, cb):
1639 """Build function for calling another function with an instance NIC.
1642 @param index: NIC index
1648 """Call helper function with instance NIC.
1650 @type ctx: L{InstanceQueryData}
1651 @type inst: L{objects.Instance}
1652 @param inst: Instance object
1656 nic = inst.nics[index]
1660 return cb(ctx, index, nic)
1665 def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
1666 """Get a NIC's Network.
1668 @type ctx: L{InstanceQueryData}
1669 @type nic: L{objects.NIC}
1670 @param nic: NIC object
1673 if nic.network is None:
1676 return ctx.networks[nic.network].name
1679 def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1680 """Get a NIC's Network.
1682 @type ctx: L{InstanceQueryData}
1683 @type nic: L{objects.NIC}
1684 @param nic: NIC object
1687 if nic.network is None:
1693 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1694 """Get a NIC's IP address.
1696 @type ctx: L{InstanceQueryData}
1697 @type nic: L{objects.NIC}
1698 @param nic: NIC object
1707 def _GetInstNicBridge(ctx, index, _):
1708 """Get a NIC's bridge.
1710 @type ctx: L{InstanceQueryData}
1712 @param index: NIC index
1715 assert len(ctx.inst_nicparams) >= index
1717 nicparams = ctx.inst_nicparams[index]
1719 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1720 return nicparams[constants.NIC_LINK]
1725 def _GetInstAllNicNetworkNames(ctx, inst):
1726 """Get all network names for an instance.
1728 @type ctx: L{InstanceQueryData}
1729 @type inst: L{objects.Instance}
1730 @param inst: Instance object
1735 for nic in inst.nics:
1738 name = ctx.networks[nic.network].name
1741 assert len(result) == len(inst.nics)
1746 def _GetInstAllNicBridges(ctx, inst):
1747 """Get all network bridges for an instance.
1749 @type ctx: L{InstanceQueryData}
1750 @type inst: L{objects.Instance}
1751 @param inst: Instance object
1754 assert len(ctx.inst_nicparams) == len(inst.nics)
1758 for nicp in ctx.inst_nicparams:
1759 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1760 result.append(nicp[constants.NIC_LINK])
1764 assert len(result) == len(inst.nics)
1769 def _GetInstNicParam(name):
1770 """Build function for retrieving a NIC parameter.
1773 @param name: Parameter name
1776 def fn(ctx, index, _):
1777 """Get a NIC's bridge.
1779 @type ctx: L{InstanceQueryData}
1780 @type inst: L{objects.Instance}
1781 @param inst: Instance object
1782 @type nic: L{objects.NIC}
1783 @param nic: NIC object
1786 assert len(ctx.inst_nicparams) >= index
1787 return ctx.inst_nicparams[index][name]
1792 def _GetInstanceNetworkFields():
1793 """Get instance fields involving network interfaces.
1795 @return: Tuple containing list of field definitions used as input for
1796 L{_PrepareFieldList} and a list of aliases
1799 nic_mac_fn = lambda ctx, _, nic: nic.mac
1800 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1801 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1805 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1806 "Number of network interfaces"),
1807 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1808 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1809 "List containing each network interface's MAC address"),
1810 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1811 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1812 "List containing each network interface's IP address"),
1813 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1814 (_MakeField("nic.names", "NIC_Names", QFT_OTHER,
1815 "List containing each network interface's name"),
1816 IQ_CONFIG, 0, lambda ctx, inst: [nic.name for nic in inst.nics]),
1817 (_MakeField("nic.uuids", "NIC_UUIDs", QFT_OTHER,
1818 "List containing each network interface's UUID"),
1819 IQ_CONFIG, 0, lambda ctx, inst: [nic.uuid for nic in inst.nics]),
1820 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1821 "List containing each network interface's mode"), IQ_CONFIG, 0,
1822 lambda ctx, inst: [nicp[constants.NIC_MODE]
1823 for nicp in ctx.inst_nicparams]),
1824 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1825 "List containing each network interface's link"), IQ_CONFIG, 0,
1826 lambda ctx, inst: [nicp[constants.NIC_LINK]
1827 for nicp in ctx.inst_nicparams]),
1828 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1829 "List containing each network interface's bridge"),
1830 IQ_CONFIG, 0, _GetInstAllNicBridges),
1831 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1832 "List containing each interface's network"), IQ_CONFIG, 0,
1833 lambda ctx, inst: [nic.network for nic in inst.nics]),
1834 (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER,
1835 "List containing each interface's network"),
1836 IQ_NETWORKS, 0, _GetInstAllNicNetworkNames)
1840 for i in range(constants.MAX_NICS):
1841 numtext = utils.FormatOrdinal(i + 1)
1843 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1844 "IP address of %s network interface" % numtext),
1845 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1846 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1847 "MAC address of %s network interface" % numtext),
1848 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1849 (_MakeField("nic.name/%s" % i, "NicName/%s" % i, QFT_TEXT,
1850 "Name address of %s network interface" % numtext),
1851 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceName)),
1852 (_MakeField("nic.uuid/%s" % i, "NicUUID/%s" % i, QFT_TEXT,
1853 "UUID address of %s network interface" % numtext),
1854 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceUUID)),
1855 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1856 "Mode of %s network interface" % numtext),
1857 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1858 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1859 "Link of %s network interface" % numtext),
1860 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1861 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1862 "Bridge of %s network interface" % numtext),
1863 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1864 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1865 "Network of %s network interface" % numtext),
1866 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1867 (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT,
1868 "Network name of %s network interface" % numtext),
1869 IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)),
1873 # Legacy fields for first NIC
1875 ("mac", "nic.mac/0"),
1876 ("bridge", "nic.bridge/0"),
1877 ("nic_mode", "nic.mode/0"),
1878 ("nic_link", "nic.link/0"),
1879 ("nic_network", "nic.network/0"),
1882 return (fields, aliases)
1885 def _GetInstDiskUsage(ctx, inst):
1886 """Get disk usage for an instance.
1888 @type ctx: L{InstanceQueryData}
1889 @type inst: L{objects.Instance}
1890 @param inst: Instance object
1893 usage = ctx.disk_usage[inst.uuid]
1901 def _GetInstanceConsole(ctx, inst):
1902 """Get console information for instance.
1904 @type ctx: L{InstanceQueryData}
1905 @type inst: L{objects.Instance}
1906 @param inst: Instance object
1909 consinfo = ctx.console[inst.uuid]
1911 if consinfo is None:
1917 def _GetInstanceDiskFields():
1918 """Get instance fields involving disks.
1920 @return: List of field definitions used as input for L{_PrepareFieldList}
1924 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1925 "Total disk space used by instance on each of its nodes;"
1926 " this is not the disk size visible to the instance, but"
1927 " the usage on the node"),
1928 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1929 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1930 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1931 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1932 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1933 (_MakeField("disk.spindles", "Disk_spindles", QFT_OTHER,
1934 "List of disk spindles"),
1935 IQ_CONFIG, 0, lambda ctx, inst: [disk.spindles for disk in inst.disks]),
1936 (_MakeField("disk.names", "Disk_names", QFT_OTHER, "List of disk names"),
1937 IQ_CONFIG, 0, lambda ctx, inst: [disk.name for disk in inst.disks]),
1938 (_MakeField("disk.uuids", "Disk_UUIDs", QFT_OTHER, "List of disk UUIDs"),
1939 IQ_CONFIG, 0, lambda ctx, inst: [disk.uuid for disk in inst.disks]),
1943 for i in range(constants.MAX_DISKS):
1944 numtext = utils.FormatOrdinal(i + 1)
1946 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1947 "Disk size of %s disk" % numtext),
1948 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSize)),
1949 (_MakeField("disk.spindles/%s" % i, "DiskSpindles/%s" % i, QFT_NUMBER,
1950 "Spindles of %s disk" % numtext),
1951 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSpindles)),
1952 (_MakeField("disk.name/%s" % i, "DiskName/%s" % i, QFT_TEXT,
1953 "Name of %s disk" % numtext),
1954 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceName)),
1955 (_MakeField("disk.uuid/%s" % i, "DiskUUID/%s" % i, QFT_TEXT,
1956 "UUID of %s disk" % numtext),
1957 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceUUID))])
1962 def _GetInstanceParameterFields():
1963 """Get instance fields involving parameters.
1965 @return: List of field definitions used as input for L{_PrepareFieldList}
1970 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1971 "Hypervisor parameters (merged)"),
1972 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1973 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1974 "Backend parameters (merged)"),
1975 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1976 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1977 "Operating system parameters (merged)"),
1978 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1980 # Unfilled parameters
1981 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1982 "Custom hypervisor parameters"),
1983 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1984 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1985 "Custom backend parameters",),
1986 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1987 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1988 "Custom operating system parameters",),
1989 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1990 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1991 "Custom network interface parameters"),
1992 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1996 def _GetInstHvParam(name):
1997 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
2000 (_MakeField("hv/%s" % name,
2001 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
2002 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
2003 IQ_CONFIG, 0, _GetInstHvParam(name))
2004 for name, kind in constants.HVS_PARAMETER_TYPES.items()
2005 if name not in constants.HVC_GLOBALS])
2008 def _GetInstBeParam(name):
2009 return lambda ctx, _: ctx.inst_beparams.get(name, None)
2012 (_MakeField("be/%s" % name,
2013 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
2014 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
2015 IQ_CONFIG, 0, _GetInstBeParam(name))
2016 for name, kind in constants.BES_PARAMETER_TYPES.items()])
2021 _INST_SIMPLE_FIELDS = {
2022 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
2023 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
2024 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
2025 # Depending on the hypervisor, the port can be None
2026 "network_port": ("Network_port", QFT_OTHER, 0,
2027 "Instance network port if available (e.g. for VNC console)"),
2028 "os": ("OS", QFT_TEXT, 0, "Operating system"),
2029 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
2030 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
2034 def _GetNodeName(ctx, default, node_uuid):
2035 """Gets node name of a node.
2037 @type ctx: L{InstanceQueryData}
2038 @param default: Default value
2039 @type node_uuid: string
2040 @param node_uuid: Node UUID
2044 node = ctx.nodes[node_uuid]
2051 def _GetInstNodeGroup(ctx, default, node_uuid):
2052 """Gets group UUID of an instance node.
2054 @type ctx: L{InstanceQueryData}
2055 @param default: Default value
2056 @type node_uuid: string
2057 @param node_uuid: Node UUID
2061 node = ctx.nodes[node_uuid]
2068 def _GetInstNodeGroupName(ctx, default, node_uuid):
2069 """Gets group name of an instance node.
2071 @type ctx: L{InstanceQueryData}
2072 @param default: Default value
2073 @type node_uuid: string
2074 @param node_uuid: Node UUID
2078 node = ctx.nodes[node_uuid]
2083 group = ctx.groups[node.group]
2090 def _BuildInstanceFields():
2091 """Builds list of fields for instance queries.
2095 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
2096 IQ_NODES, QFF_HOSTNAME,
2097 lambda ctx, inst: _GetNodeName(ctx, None, inst.primary_node)),
2098 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
2099 "Primary node's group"),
2101 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
2102 inst.primary_node)),
2103 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
2104 "Primary node's group UUID"),
2106 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
2107 # TODO: Allow filtering by secondary node as hostname
2108 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
2109 "Secondary nodes; usually this will just be one node"),
2111 lambda ctx, inst: map(compat.partial(_GetNodeName, ctx, None),
2112 inst.secondary_nodes)),
2113 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
2114 "Node groups of secondary nodes"),
2116 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
2117 inst.secondary_nodes)),
2118 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
2119 "Node group UUIDs of secondary nodes"),
2121 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
2122 inst.secondary_nodes)),
2123 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
2124 "Desired state of instance"),
2125 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
2126 (_MakeField("admin_up", "Autostart", QFT_BOOL,
2127 "Desired state of instance"),
2128 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
2129 (_MakeField("disks_active", "DisksActive", QFT_BOOL,
2130 "Desired state of instance disks"),
2131 IQ_CONFIG, 0, _GetItemAttr("disks_active")),
2132 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2133 lambda ctx, inst: list(inst.GetTags())),
2134 (_MakeField("console", "Console", QFT_OTHER,
2135 "Instance console information"), IQ_CONSOLE, 0,
2136 _GetInstanceConsole),
2141 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
2142 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
2144 # Fields requiring talking to the node
2146 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
2147 IQ_LIVE, 0, _GetInstOperState),
2148 (_MakeField("oper_ram", "Memory", QFT_UNIT,
2149 "Actual memory usage as seen by hypervisor"),
2150 IQ_LIVE, 0, _GetInstLiveData("memory")),
2151 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
2152 "Actual number of VCPUs as seen by hypervisor"),
2153 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
2157 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
2158 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
2159 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
2160 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
2161 status_doc = ("Instance status; \"%s\" if instance is set to be running"
2162 " and actually is, \"%s\" if instance is stopped and"
2163 " is not running, \"%s\" if instance running, but not on its"
2164 " designated primary node, \"%s\" if instance should be"
2165 " stopped, but is actually running, \"%s\" if instance should"
2166 " run, but doesn't, \"%s\" if instance's primary node is down,"
2167 " \"%s\" if instance's primary node is marked offline,"
2168 " \"%s\" if instance is offline and does not use dynamic"
2169 " resources" % status_values)
2170 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
2171 IQ_LIVE, 0, _GetInstStatus))
2172 assert set(status_values) == constants.INSTST_ALL, \
2173 "Status documentation mismatch"
2175 (network_fields, network_aliases) = _GetInstanceNetworkFields()
2177 fields.extend(network_fields)
2178 fields.extend(_GetInstanceParameterFields())
2179 fields.extend(_GetInstanceDiskFields())
2180 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
2183 ("vcpus", "be/vcpus"),
2184 ("be/memory", "be/maxmem"),
2185 ("sda_size", "disk.size/0"),
2186 ("sdb_size", "disk.size/1"),
2189 return _PrepareFieldList(fields, aliases)
2192 class LockQueryData:
2193 """Data container for lock data queries.
2196 def __init__(self, lockdata):
2197 """Initializes this class.
2200 self.lockdata = lockdata
2203 """Iterate over all locks.
2206 return iter(self.lockdata)
2209 def _GetLockOwners(_, data):
2210 """Returns a sorted list of a lock's current owners.
2213 (_, _, owners, _) = data
2216 owners = utils.NiceSort(owners)
2221 def _GetLockPending(_, data):
2222 """Returns a sorted list of a lock's pending acquires.
2225 (_, _, _, pending) = data
2228 pending = [(mode, utils.NiceSort(names))
2229 for (mode, names) in pending]
2234 def _BuildLockFields():
2235 """Builds list of fields for lock queries.
2238 return _PrepareFieldList([
2239 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2240 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2241 lambda ctx, (name, mode, owners, pending): name),
2242 (_MakeField("mode", "Mode", QFT_OTHER,
2243 "Mode in which the lock is currently acquired"
2244 " (exclusive or shared)"),
2245 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2246 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2247 LQ_OWNER, 0, _GetLockOwners),
2248 (_MakeField("pending", "Pending", QFT_OTHER,
2249 "Threads waiting for the lock"),
2250 LQ_PENDING, 0, _GetLockPending),
2254 class GroupQueryData:
2255 """Data container for node group data queries.
2258 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2260 """Initializes this class.
2262 @param cluster: Cluster object
2263 @param groups: List of node group objects
2264 @type group_to_nodes: dict; group UUID as key
2265 @param group_to_nodes: Per-group list of nodes
2266 @type group_to_instances: dict; group UUID as key
2267 @param group_to_instances: Per-group list of (primary) instances
2268 @type want_diskparams: bool
2269 @param want_diskparams: Whether diskparamters should be calculated
2272 self.groups = groups
2273 self.group_to_nodes = group_to_nodes
2274 self.group_to_instances = group_to_instances
2275 self.cluster = cluster
2276 self.want_diskparams = want_diskparams
2278 # Used for individual rows
2279 self.group_ipolicy = None
2280 self.ndparams = None
2281 self.group_dp = None
2284 """Iterate over all node groups.
2286 This function has side-effects and only one instance of the resulting
2287 generator should be used at a time.
2290 for group in self.groups:
2291 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2292 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2293 if self.want_diskparams:
2294 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2296 self.group_dp = None
2300 _GROUP_SIMPLE_FIELDS = {
2301 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2302 "name": ("Group", QFT_TEXT, "Group name"),
2303 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2304 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2308 def _BuildGroupFields():
2309 """Builds list of fields for node group queries.
2313 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2315 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2317 def _GetLength(getter):
2318 return lambda ctx, group: len(getter(ctx)[group.uuid])
2320 def _GetSortedList(getter):
2321 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2323 group_to_nodes = operator.attrgetter("group_to_nodes")
2324 group_to_instances = operator.attrgetter("group_to_instances")
2326 # Add fields for nodes
2328 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2329 GQ_NODE, 0, _GetLength(group_to_nodes)),
2330 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2331 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2334 # Add fields for instances
2336 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2337 "Number of primary instances"),
2338 GQ_INST, 0, _GetLength(group_to_instances)),
2339 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2340 "List of primary instances"),
2341 GQ_INST, 0, _GetSortedList(group_to_instances)),
2346 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2347 lambda ctx, group: list(group.GetTags())),
2348 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2349 "Instance policy limitations (merged)"),
2350 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2351 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2352 "Custom instance policy limitations"),
2353 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2354 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2355 "Custom node parameters"),
2356 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2357 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2359 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2360 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2361 "Disk parameters (merged)"),
2362 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2363 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2364 "Custom disk parameters"),
2365 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2369 fields.extend(_BuildNDFields(True))
2371 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2373 return _PrepareFieldList(fields, [])
2376 class OsInfo(objects.ConfigObject):
2389 def _BuildOsFields():
2390 """Builds list of fields for operating system queries.
2394 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2395 None, 0, _GetItemAttr("name")),
2396 (_MakeField("valid", "Valid", QFT_BOOL,
2397 "Whether operating system definition is valid"),
2398 None, 0, _GetItemAttr("valid")),
2399 (_MakeField("hidden", "Hidden", QFT_BOOL,
2400 "Whether operating system is hidden"),
2401 None, 0, _GetItemAttr("hidden")),
2402 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2403 "Whether operating system is blacklisted"),
2404 None, 0, _GetItemAttr("blacklisted")),
2405 (_MakeField("variants", "Variants", QFT_OTHER,
2406 "Operating system variants"),
2407 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2408 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2409 "Operating system API versions"),
2410 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2411 (_MakeField("parameters", "Parameters", QFT_OTHER,
2412 "Operating system parameters"),
2413 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2414 _GetItemAttr("parameters"))),
2415 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2416 "Status from node"),
2417 None, 0, _GetItemAttr("node_status")),
2420 return _PrepareFieldList(fields, [])
2423 class ExtStorageInfo(objects.ConfigObject):
2432 def _BuildExtStorageFields():
2433 """Builds list of fields for extstorage provider queries.
2437 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2438 None, 0, _GetItemAttr("name")),
2439 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2440 "Status from node"),
2441 None, 0, _GetItemAttr("node_status")),
2442 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2443 "Overall Nodegroup status"),
2444 None, 0, _GetItemAttr("nodegroup_status")),
2445 (_MakeField("parameters", "Parameters", QFT_OTHER,
2446 "ExtStorage provider parameters"),
2447 None, 0, _GetItemAttr("parameters")),
2450 return _PrepareFieldList(fields, [])
2453 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2454 """Return L{_FS_UNAVAIL} if job is None.
2456 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2457 found, in which case this function converts it to L{_FS_UNAVAIL}.
2466 def _JobUnavail(inner):
2467 """Wrapper for L{_JobUnavailInner}.
2470 return compat.partial(_JobUnavailInner, inner)
2473 def _PerJobOpInner(fn, job):
2474 """Executes a function per opcode in a job.
2477 return map(fn, job.ops)
2481 """Wrapper for L{_PerJobOpInner}.
2484 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2487 def _JobTimestampInner(fn, job):
2488 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2493 if timestamp is None:
2499 def _JobTimestamp(fn):
2500 """Wrapper for L{_JobTimestampInner}.
2503 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2506 def _BuildJobFields():
2507 """Builds list of fields for job queries.
2511 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2512 None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2513 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2514 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2515 (_MakeField("priority", "Priority", QFT_NUMBER,
2516 ("Current job priority (%s to %s)" %
2517 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2518 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2519 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2520 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2521 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2522 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2523 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2524 "List of opcodes results"),
2525 None, 0, _PerJobOp(operator.attrgetter("result"))),
2526 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2527 "List of opcodes status"),
2528 None, 0, _PerJobOp(operator.attrgetter("status"))),
2529 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2530 "List of opcode output logs"),
2531 None, 0, _PerJobOp(operator.attrgetter("log"))),
2532 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2533 "List of opcode start timestamps (before acquiring locks)"),
2534 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2535 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2536 "List of opcode execution start timestamps (after acquiring"
2538 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2539 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2540 "List of opcode execution end timestamps"),
2541 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2542 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2543 "List of opcode priorities"),
2544 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2545 (_MakeField("summary", "Summary", QFT_OTHER,
2546 "List of per-opcode summaries"),
2547 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2551 for (name, attr, title, desc) in [
2552 ("received_ts", "received_timestamp", "Received",
2553 "Timestamp of when job was received"),
2554 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2555 ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2557 getter = operator.attrgetter(attr)
2559 (_MakeField(name, title, QFT_OTHER,
2560 "%s (tuple containing seconds and microseconds)" % desc),
2561 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2564 return _PrepareFieldList(fields, [])
2567 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2568 """Returns an export name if available.
2577 def _BuildExportFields():
2578 """Builds list of fields for exports.
2582 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2583 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2584 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2585 None, 0, _GetExportName),
2588 return _PrepareFieldList(fields, [])
2591 _CLUSTER_VERSION_FIELDS = {
2592 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2593 "Software version"),
2594 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2595 constants.PROTOCOL_VERSION,
2596 "RPC protocol version"),
2597 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2598 "Configuration format version"),
2599 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2600 "API version for OS template scripts"),
2601 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2602 "Import/export file format version"),
2606 _CLUSTER_SIMPLE_FIELDS = {
2607 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2608 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2612 class ClusterQueryData:
2613 def __init__(self, cluster, nodes, drain_flag, watcher_pause):
2614 """Initializes this class.
2616 @type cluster: L{objects.Cluster}
2617 @param cluster: Instance of cluster object
2618 @type nodes: dict; node UUID as key
2619 @param nodes: Node objects
2620 @type drain_flag: bool
2621 @param drain_flag: Whether job queue is drained
2622 @type watcher_pause: number
2623 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2626 self._cluster = cluster
2628 self.drain_flag = drain_flag
2629 self.watcher_pause = watcher_pause
2632 return iter([self._cluster])
2635 def _ClusterWatcherPause(ctx, _):
2636 """Returns until when watcher is paused (if available).
2639 if ctx.watcher_pause is None:
2642 return ctx.watcher_pause
2645 def _BuildClusterFields():
2646 """Builds list of fields for cluster information.
2650 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2651 lambda ctx, cluster: list(cluster.GetTags())),
2652 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2653 "Architecture information"), None, 0,
2654 lambda ctx, _: runtime.GetArchInfo()),
2655 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2656 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2657 lambda ctx, _: ctx.drain_flag),
2658 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2659 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2660 _ClusterWatcherPause),
2661 (_MakeField("master_node", "Master", QFT_TEXT, "Master node name"),
2662 CQ_CONFIG, QFF_HOSTNAME,
2663 lambda ctx, cluster: _GetNodeName(ctx, None, cluster.master_node)),
2668 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2669 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2674 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2675 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
2678 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2680 return _PrepareFieldList(fields, [
2681 ("name", "cluster_name")])
2684 class NetworkQueryData:
2685 """Data container for network data queries.
2688 def __init__(self, networks, network_to_groups,
2689 network_to_instances, stats):
2690 """Initializes this class.
2692 @param networks: List of network objects
2693 @type network_to_groups: dict; network UUID as key
2694 @param network_to_groups: Per-network list of groups
2695 @type network_to_instances: dict; network UUID as key
2696 @param network_to_instances: Per-network list of instances
2697 @type stats: dict; network UUID as key
2698 @param stats: Per-network usage statistics
2701 self.networks = networks
2702 self.network_to_groups = network_to_groups
2703 self.network_to_instances = network_to_instances
2707 """Iterate over all networks.
2710 for net in self.networks:
2712 self.curstats = self.stats.get(net.uuid, None)
2714 self.curstats = None
2718 _NETWORK_SIMPLE_FIELDS = {
2719 "name": ("Network", QFT_TEXT, 0, "Name"),
2720 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"),
2721 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"),
2722 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"),
2723 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"),
2724 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"),
2725 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
2726 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2730 _NETWORK_STATS_FIELDS = {
2731 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"),
2733 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"),
2734 "map": ("Map", QFT_TEXT, 0, "Actual mapping"),
2735 "external_reservations":
2736 ("ExternalReservations", QFT_TEXT, 0, "External reservations"),
2740 def _GetNetworkStatsField(field, kind, ctx, _):
2741 """Gets the value of a "stats" field from L{NetworkQueryData}.
2743 @param field: Field name
2744 @param kind: Data kind, one of L{constants.QFT_ALL}
2745 @type ctx: L{NetworkQueryData}
2748 return _GetStatsField(field, kind, ctx.curstats)
2751 def _BuildNetworkFields():
2752 """Builds list of fields for network queries.
2756 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2757 lambda ctx, inst: list(inst.GetTags())),
2762 (_MakeField(name, title, kind, doc),
2763 NETQ_CONFIG, 0, _GetItemMaybeAttr(name))
2764 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
2766 def _GetLength(getter):
2767 return lambda ctx, network: len(getter(ctx)[network.uuid])
2769 def _GetSortedList(getter):
2770 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2772 network_to_groups = operator.attrgetter("network_to_groups")
2773 network_to_instances = operator.attrgetter("network_to_instances")
2775 # Add fields for node groups
2777 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2778 NETQ_GROUP, 0, _GetLength(network_to_groups)),
2779 (_MakeField("group_list", "GroupList", QFT_OTHER,
2780 "List of nodegroups (group name, NIC mode, NIC link)"),
2781 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]),
2784 # Add fields for instances
2786 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2787 NETQ_INST, 0, _GetLength(network_to_instances)),
2788 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2789 NETQ_INST, 0, _GetSortedList(network_to_instances)),
2792 # Add fields for usage statistics
2794 (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2795 compat.partial(_GetNetworkStatsField, name, kind))
2796 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
2798 return _PrepareFieldList(fields, [])
2800 #: Fields for cluster information
2801 CLUSTER_FIELDS = _BuildClusterFields()
2803 #: Fields available for node queries
2804 NODE_FIELDS = _BuildNodeFields()
2806 #: Fields available for instance queries
2807 INSTANCE_FIELDS = _BuildInstanceFields()
2809 #: Fields available for lock queries
2810 LOCK_FIELDS = _BuildLockFields()
2812 #: Fields available for node group queries
2813 GROUP_FIELDS = _BuildGroupFields()
2815 #: Fields available for operating system queries
2816 OS_FIELDS = _BuildOsFields()
2818 #: Fields available for extstorage provider queries
2819 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2821 #: Fields available for job queries
2822 JOB_FIELDS = _BuildJobFields()
2824 #: Fields available for exports
2825 EXPORT_FIELDS = _BuildExportFields()
2827 #: Fields available for network queries
2828 NETWORK_FIELDS = _BuildNetworkFields()
2830 #: All available resources
2832 constants.QR_CLUSTER: CLUSTER_FIELDS,
2833 constants.QR_INSTANCE: INSTANCE_FIELDS,
2834 constants.QR_NODE: NODE_FIELDS,
2835 constants.QR_LOCK: LOCK_FIELDS,
2836 constants.QR_GROUP: GROUP_FIELDS,
2837 constants.QR_OS: OS_FIELDS,
2838 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2839 constants.QR_JOB: JOB_FIELDS,
2840 constants.QR_EXPORT: EXPORT_FIELDS,
2841 constants.QR_NETWORK: NETWORK_FIELDS,
2844 #: All available field lists
2845 ALL_FIELD_LISTS = ALL_FIELDS.values()