4 # Copyright (C) 2010, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Module for query operations
26 - Add field definitions
27 - See how L{NODE_FIELDS} is built
29 - Query field definition (L{objects.QueryFieldDefinition}, use
30 L{_MakeField} for creating), containing:
31 - Name, must be lowercase and match L{FIELD_NAME_RE}
32 - Title for tables, must not contain whitespace and match
34 - Value data type, e.g. L{constants.QFT_NUMBER}
35 - Human-readable description, must not end with punctuation or
37 - Data request type, see e.g. C{NQ_*}
38 - OR-ed flags, see C{QFF_*}
39 - A retrieval function, see L{Query.__init__} for description
40 - Pass list of fields through L{_PrepareFieldList} for preparation and
42 - Instantiate L{Query} with prepared field list definition and selected fields
43 - Call L{Query.RequestedData} to determine what data to collect/compute
44 - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use
46 - Data container must support iteration using C{__iter__}
47 - Items are passed to retrieval functions and can have any format
48 - Call L{Query.GetFields} to get list of definitions for selected fields
50 @attention: Retrieval functions must be idempotent. They can be called multiple
51 times, in any order and any number of times.
59 from ganeti import constants
60 from ganeti import errors
61 from ganeti import utils
62 from ganeti import compat
63 from ganeti import objects
65 from ganeti import runtime
66 from ganeti import qlang
67 from ganeti import jstore
69 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
70 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
71 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
72 RS_UNAVAIL, RS_OFFLINE)
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.
93 IQ_NODES) = range(100, 105)
97 LQ_PENDING) = range(10, 13)
102 GQ_DISKPARAMS) = range(200, 204)
106 CQ_WATCHER_PAUSE) = range(300, 303)
108 (JQ_ARCHIVED, ) = range(400, 401)
112 QFF_IP_ADDRESS = 0x02
114 QFF_SPLIT_TIMESTAMP = 0x08
115 # Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
116 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP)
118 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
119 TITLE_RE = re.compile(r"^[^\s]+$")
120 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
122 #: Verification function for each field type
124 QFT_UNKNOWN: ht.TNone,
125 QFT_TEXT: ht.TString,
129 QFT_TIMESTAMP: ht.TNumber,
130 QFT_OTHER: lambda _: True,
133 # Unique objects for special field statuses
134 _FS_UNKNOWN = object()
135 _FS_NODATA = object()
136 _FS_UNAVAIL = object()
137 _FS_OFFLINE = object()
139 #: List of all special status
140 _FS_ALL = compat.UniqueFrozenset([
147 #: VType to QFT mapping
149 # TODO: fix validation of empty strings
150 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
151 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
152 constants.VTYPE_BOOL: QFT_BOOL,
153 constants.VTYPE_SIZE: QFT_UNIT,
154 constants.VTYPE_INT: QFT_NUMBER,
157 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
160 def _GetUnknownField(ctx, item): # pylint: disable=W0613
161 """Gets the contents of an unknown field.
167 def _GetQueryFields(fielddefs, selected):
168 """Calculates the internal list of selected fields.
170 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
172 @type fielddefs: dict
173 @param fielddefs: Field definitions
174 @type selected: list of strings
175 @param selected: List of selected fields
180 for name in selected:
182 fdef = fielddefs[name]
184 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
185 None, 0, _GetUnknownField)
187 assert len(fdef) == 4
194 def GetAllFields(fielddefs):
195 """Extract L{objects.QueryFieldDefinition} from field definitions.
197 @rtype: list of L{objects.QueryFieldDefinition}
200 return [fdef for (fdef, _, _, _) in fielddefs]
204 """Class for filter analytics.
206 When filters are used, the user of the L{Query} class usually doesn't know
207 exactly which items will be necessary for building the result. It therefore
208 has to prepare and compute the input data for potentially returning
211 There are two ways to optimize this. The first, and simpler, is to assign
212 each field a group of data, so that the caller can determine which
213 computations are necessary depending on the data groups requested. The list
214 of referenced groups must also be computed for fields referenced in the
217 The second is restricting the items based on a primary key. The primary key
218 is usually a unique name (e.g. a node name). This class extracts all
219 referenced names from a filter. If it encounters any filter condition which
220 disallows such a list to be determined (e.g. a non-equality filter), all
221 names will be requested.
223 The end-effect is that any operation other than L{qlang.OP_OR} and
224 L{qlang.OP_EQUAL} will make the query more expensive.
227 def __init__(self, namefield):
228 """Initializes this class.
230 @type namefield: string
231 @param namefield: Field caller is interested in
234 self._namefield = namefield
236 #: Whether all names need to be requested (e.g. if a non-equality operator
238 self._allnames = False
240 #: Which names to request
243 #: Data kinds referenced by the filter (used by L{Query.RequestedData})
244 self._datakinds = set()
246 def RequestedNames(self):
247 """Returns all requested values.
249 Returns C{None} if list of values can't be determined (e.g. encountered
250 non-equality operators).
255 if self._allnames or self._names is None:
258 return utils.UniqueSequence(self._names)
260 def ReferencedData(self):
261 """Returns all kinds of data referenced by the filter.
264 return frozenset(self._datakinds)
266 def _NeedAllNames(self):
267 """Changes internal state to request all names.
270 self._allnames = True
273 def NoteLogicOp(self, op):
274 """Called when handling a logic operation.
280 if op != qlang.OP_OR:
283 def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
284 """Called when handling an unary operation.
290 if datakind is not None:
291 self._datakinds.add(datakind)
295 def NoteBinaryOp(self, op, datakind, name, value):
296 """Called when handling a binary operation.
301 @param name: Left-hand side of operator (field name)
302 @param value: Right-hand side of operator
305 if datakind is not None:
306 self._datakinds.add(datakind)
311 # If any operator other than equality was used, all names need to be
313 if op == qlang.OP_EQUAL and name == self._namefield:
314 if self._names is None:
316 self._names.append(value)
321 def _WrapLogicOp(op_fn, sentences, ctx, item):
322 """Wrapper for logic operator functions.
325 return op_fn(fn(ctx, item) for fn in sentences)
328 def _WrapUnaryOp(op_fn, inner, ctx, item):
329 """Wrapper for unary operator functions.
332 return op_fn(inner(ctx, item))
335 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
336 """Wrapper for binary operator functions.
339 return op_fn(retrieval_fn(ctx, item), value)
342 def _WrapNot(fn, lhs, rhs):
343 """Negates the result of a wrapped function.
346 return not fn(lhs, rhs)
349 def _PrepareRegex(pattern):
350 """Compiles a regular expression.
354 return re.compile(pattern)
355 except re.error, err:
356 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
359 def _PrepareSplitTimestamp(value):
360 """Prepares a value for comparison by L{_MakeSplitTimestampComparison}.
363 if ht.TNumber(value):
366 return utils.MergeTime(value)
369 def _MakeSplitTimestampComparison(fn):
370 """Compares split timestamp values after converting to float.
373 return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
376 def _MakeComparisonChecks(fn):
377 """Prepares flag-specific comparisons using a comparison function.
381 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn),
382 _PrepareSplitTimestamp),
383 (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs),
389 class _FilterCompilerHelper:
390 """Converts a query filter to a callable usable for filtering.
393 # String statement has no effect, pylint: disable=W0105
395 #: How deep filters can be nested
398 # Unique identifiers for operator groups
401 _OPTYPE_BINARY) = range(1, 4)
403 """Functions for equality checks depending on field flags.
405 List of tuples containing flags and a callable receiving the left- and
406 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
407 (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
409 Order matters. The first item with flags will be used. Flags are checked
415 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
416 case_sensitive=False),
418 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq),
419 _PrepareSplitTimestamp),
420 (None, operator.eq, None),
425 Operator as key (C{qlang.OP_*}), value a tuple of operator group
426 (C{_OPTYPE_*}) and a group-specific value:
428 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
430 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
431 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
432 right-hand side of the operator, used by L{_HandleBinaryOp}
437 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
438 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
441 qlang.OP_NOT: (_OPTYPE_UNARY, None),
442 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
445 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
447 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
448 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
449 qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)),
450 qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)),
451 qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)),
452 qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)),
453 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
454 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
456 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
457 (None, operator.contains, None),
461 def __init__(self, fields):
462 """Initializes this class.
464 @param fields: Field definitions (return value of L{_PrepareFieldList})
467 self._fields = fields
469 self._op_handler = None
471 def __call__(self, hints, qfilter):
472 """Converts a query filter into a callable function.
474 @type hints: L{_FilterHints} or None
475 @param hints: Callbacks doing analysis on filter
477 @param qfilter: Filter structure
479 @return: Function receiving context and item as parameters, returning
480 boolean as to whether item matches filter
485 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
487 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
489 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
493 filter_fn = self._Compile(qfilter, 0)
495 self._op_handler = None
499 def _Compile(self, qfilter, level):
500 """Inner function for converting filters.
502 Calls the correct handler functions for the top-level operator. This
503 function is called recursively (e.g. for logic operators).
506 if not (isinstance(qfilter, (list, tuple)) and qfilter):
507 raise errors.ParameterError("Invalid filter on level %s" % level)
510 if level >= self._LEVELS_MAX:
511 raise errors.ParameterError("Only up to %s levels are allowed (filter"
512 " nested too deep)" % self._LEVELS_MAX)
514 # Create copy to be modified
515 operands = qfilter[:]
519 (kind, op_data) = self._OPS[op]
521 raise errors.ParameterError("Unknown operator '%s'" % op)
523 (handler, hints_cb) = self._op_handler[kind]
525 return handler(hints_cb, level, op, op_data, operands)
527 def _LookupField(self, name):
528 """Returns a field definition by name.
532 return self._fields[name]
534 raise errors.ParameterError("Unknown field '%s'" % name)
536 def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
537 """Handles logic operators.
539 @type hints_fn: callable
540 @param hints_fn: Callback doing some analysis on the filter
542 @param level: Current depth
545 @type op_fn: callable
546 @param op_fn: Function implementing operator
548 @param operands: List of operands
554 return compat.partial(_WrapLogicOp, op_fn,
555 [self._Compile(op, level + 1) for op in operands])
557 def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
558 """Handles unary operators.
560 @type hints_fn: callable
561 @param hints_fn: Callback doing some analysis on the filter
563 @param level: Current depth
566 @type op_fn: callable
567 @param op_fn: Function implementing operator
569 @param operands: List of operands
574 if len(operands) != 1:
575 raise errors.ParameterError("Unary operator '%s' expects exactly one"
578 if op == qlang.OP_TRUE:
579 (_, datakind, _, retrieval_fn) = self._LookupField(operands[0])
582 hints_fn(op, datakind)
584 op_fn = operator.truth
586 elif op == qlang.OP_NOT:
590 op_fn = operator.not_
591 arg = self._Compile(operands[0], level + 1)
593 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
595 return compat.partial(_WrapUnaryOp, op_fn, arg)
597 def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
598 """Handles binary operators.
600 @type hints_fn: callable
601 @param hints_fn: Callback doing some analysis on the filter
603 @param level: Current depth
606 @param op_data: Functions implementing operators
608 @param operands: List of operands
611 # Unused arguments, pylint: disable=W0613
613 (name, value) = operands
614 except (ValueError, TypeError):
615 raise errors.ParameterError("Invalid binary operator, expected exactly"
618 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
620 assert fdef.kind != QFT_UNKNOWN
622 # TODO: Type conversions?
624 verify_fn = _VERIFY_FN[fdef.kind]
625 if not verify_fn(value):
626 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
627 " with '%s', expected %s" %
628 (name, fdef.kind, value.__class__.__name__,
632 hints_fn(op, datakind, name, value)
634 for (fn_flags, fn, valprepfn) in op_data:
635 if fn_flags is None or fn_flags & field_flags:
636 # Prepare value if necessary (e.g. compile regular expression)
638 value = valprepfn(value)
640 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
642 raise errors.ProgrammerError("Unable to find operator implementation"
643 " (op '%s', flags %s)" % (op, field_flags))
646 def _CompileFilter(fields, hints, qfilter):
647 """Converts a query filter into a callable function.
649 See L{_FilterCompilerHelper} for details.
654 return _FilterCompilerHelper(fields)(hints, qfilter)
658 def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
659 """Initializes this class.
661 The field definition is a dictionary with the field's name as a key and a
662 tuple containing, in order, the field definition object
663 (L{objects.QueryFieldDefinition}, the data kind to help calling code
664 collect data and a retrieval function. The retrieval function is called
665 with two parameters, in order, the data container and the item in container
666 (see L{Query.Query}).
668 Users of this class can call L{RequestedData} before preparing the data
669 container to determine what data is needed.
671 @type fieldlist: dictionary
672 @param fieldlist: Field definitions
673 @type selected: list of strings
674 @param selected: List of selected fields
677 assert namefield is None or namefield in fieldlist
679 self._fields = _GetQueryFields(fieldlist, selected)
681 self._filter_fn = None
682 self._requested_names = None
683 self._filter_datakinds = frozenset()
685 if qfilter is not None:
686 # Collect requested names if wanted
688 hints = _FilterHints(namefield)
692 # Build filter function
693 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
695 self._requested_names = hints.RequestedNames()
696 self._filter_datakinds = hints.ReferencedData()
698 if namefield is None:
701 (_, _, _, self._name_fn) = fieldlist[namefield]
703 def RequestedNames(self):
704 """Returns all names referenced in the filter.
706 If there is no filter or operators are preventing determining the exact
707 names, C{None} is returned.
710 return self._requested_names
712 def RequestedData(self):
713 """Gets requested kinds of data.
718 return (self._filter_datakinds |
719 frozenset(datakind for (_, datakind, _, _) in self._fields
720 if datakind is not None))
723 """Returns the list of fields for this query.
725 Includes unknown fields.
727 @rtype: List of L{objects.QueryFieldDefinition}
730 return GetAllFields(self._fields)
732 def Query(self, ctx, sort_by_name=True):
735 @param ctx: Data container passed to field retrieval functions, must
736 support iteration using C{__iter__}
737 @type sort_by_name: boolean
738 @param sort_by_name: Whether to sort by name or keep the input data's
742 sort = (self._name_fn and sort_by_name)
746 for idx, item in enumerate(ctx):
747 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
750 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
754 _VerifyResultRow(self._fields, row)
757 (status, name) = _ProcessResult(self._name_fn(ctx, item))
758 assert status == constants.RS_NORMAL
759 # TODO: Are there cases where we wouldn't want to use NiceSort?
760 # Answer: if the name field is non-string...
761 result.append((utils.NiceSortKey(name), idx, row))
768 # TODO: Would "heapq" be more efficient than sorting?
770 # Sorting in-place instead of using "sorted()"
773 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
775 return map(operator.itemgetter(2), result)
777 def OldStyleQuery(self, ctx, sort_by_name=True):
778 """Query with "old" query result format.
780 See L{Query.Query} for arguments.
783 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
784 if fdef.kind == QFT_UNKNOWN)
786 raise errors.OpPrereqError("Unknown output fields selected: %s" %
787 (utils.CommaJoin(unknown), ),
790 return [[value for (_, value) in row]
791 for row in self.Query(ctx, sort_by_name=sort_by_name)]
794 def _ProcessResult(value):
795 """Converts result values into externally-visible ones.
798 if value is _FS_UNKNOWN:
799 return (RS_UNKNOWN, None)
800 elif value is _FS_NODATA:
801 return (RS_NODATA, None)
802 elif value is _FS_UNAVAIL:
803 return (RS_UNAVAIL, None)
804 elif value is _FS_OFFLINE:
805 return (RS_OFFLINE, None)
807 return (RS_NORMAL, value)
810 def _VerifyResultRow(fields, row):
811 """Verifies the contents of a query result row.
814 @param fields: Field definitions for result
815 @type row: list of tuples
819 assert len(row) == len(fields)
821 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
822 if status == RS_NORMAL:
823 if not _VERIFY_FN[fdef.kind](value):
824 errs.append("normal field %s fails validation (value is %s)" %
826 elif value is not None:
827 errs.append("abnormal field %s has a non-None value" % fdef.name)
828 assert not errs, ("Failed validation: %s in row %s" %
829 (utils.CommaJoin(errs), row))
832 def _FieldDictKey((fdef, _, flags, fn)):
833 """Generates key for field dictionary.
836 assert fdef.name and fdef.title, "Name and title are required"
837 assert FIELD_NAME_RE.match(fdef.name)
838 assert TITLE_RE.match(fdef.title)
839 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
840 fdef.doc.strip() == fdef.doc), \
841 "Invalid description for field '%s'" % fdef.name
843 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
848 def _PrepareFieldList(fields, aliases):
849 """Prepares field list for use by L{Query}.
851 Converts the list to a dictionary and does some verification.
853 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
854 kind, retrieval function)
855 @param fields: List of fields, see L{Query.__init__} for a better
857 @type aliases: list of tuples; (alias, target)
858 @param aliases: list of tuples containing aliases; for each
859 alias/target pair, a duplicate will be created in the field list
861 @return: Field dictionary for L{Query}
865 duplicates = utils.FindDuplicates(fdef.title.lower()
866 for (fdef, _, _, _) in fields)
867 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
869 result = utils.SequenceToDict(fields, key=_FieldDictKey)
871 for alias, target in aliases:
872 assert alias not in result, "Alias %s overrides an existing field" % alias
873 assert target in result, "Missing target %s for alias %s" % (target, alias)
874 (fdef, k, flags, fn) = result[target]
877 result[alias] = (fdef, k, flags, fn)
879 assert len(result) == len(fields) + len(aliases)
880 assert compat.all(name == fdef.name
881 for (name, (fdef, _, _, _)) in result.items())
886 def GetQueryResponse(query, ctx, sort_by_name=True):
887 """Prepares the response for a query.
889 @type query: L{Query}
890 @param ctx: Data container, see L{Query.Query}
891 @type sort_by_name: boolean
892 @param sort_by_name: Whether to sort by name or keep the input data's
896 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
897 fields=query.GetFields()).ToDict()
900 def QueryFields(fielddefs, selected):
901 """Returns list of available fields.
903 @type fielddefs: dict
904 @param fielddefs: Field definitions
905 @type selected: list of strings
906 @param selected: List of selected fields
907 @return: List of L{objects.QueryFieldDefinition}
911 # Client requests all fields, sort by name
912 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
913 key=operator.attrgetter("name"))
915 # Keep order as requested by client
916 fdefs = Query(fielddefs, selected).GetFields()
918 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
921 def _MakeField(name, title, kind, doc):
922 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
924 @param name: Field name as a regular expression
925 @param title: Human-readable title
926 @param kind: Field type
927 @param doc: Human-readable description
930 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
934 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
935 """Returns a static value.
941 def _StaticValue(value):
942 """Prepares a function to return a static value.
945 return compat.partial(_StaticValueInner, value)
948 def _GetNodeRole(node, master_name):
949 """Determine node role.
951 @type node: L{objects.Node}
952 @param node: Node object
953 @type master_name: string
954 @param master_name: Master node name
957 if node.name == master_name:
958 return constants.NR_MASTER
959 elif node.master_candidate:
960 return constants.NR_MCANDIDATE
962 return constants.NR_DRAINED
964 return constants.NR_OFFLINE
966 return constants.NR_REGULAR
969 def _GetItemAttr(attr):
970 """Returns a field function to return an attribute of the item.
972 @param attr: Attribute name
975 getter = operator.attrgetter(attr)
976 return lambda _, item: getter(item)
979 def _GetNDParam(name):
980 """Return a field function to return an ND parameter out of the context.
984 if ctx.ndparams is None:
987 return ctx.ndparams.get(name, None)
991 def _BuildNDFields(is_group):
992 """Builds all the ndparam fields.
994 @param is_group: whether this is called at group or node level
998 field_kind = GQ_CONFIG
1000 field_kind = NQ_GROUP
1001 return [(_MakeField("ndp/%s" % name,
1002 constants.NDS_PARAMETER_TITLES.get(name,
1004 _VTToQFT[kind], "The \"%s\" node parameter" % name),
1005 field_kind, 0, _GetNDParam(name))
1006 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1009 def _ConvWrapInner(convert, fn, ctx, item):
1010 """Wrapper for converting values.
1012 @param convert: Conversion function receiving value as single parameter
1013 @param fn: Retrieval function
1016 value = fn(ctx, item)
1018 # Is the value an abnormal status?
1019 if compat.any(value is fs for fs in _FS_ALL):
1023 # TODO: Should conversion function also receive context, item or both?
1024 return convert(value)
1027 def _ConvWrap(convert, fn):
1028 """Convenience wrapper for L{_ConvWrapInner}.
1030 @param convert: Conversion function receiving value as single parameter
1031 @param fn: Retrieval function
1034 return compat.partial(_ConvWrapInner, convert, fn)
1037 def _GetItemTimestamp(getter):
1038 """Returns function for getting timestamp of item.
1040 @type getter: callable
1041 @param getter: Function to retrieve timestamp attribute
1045 """Returns a timestamp of item.
1048 timestamp = getter(item)
1049 if timestamp is None:
1050 # Old configs might not have all timestamps
1058 def _GetItemTimestampFields(datatype):
1059 """Returns common timestamp fields.
1061 @param datatype: Field data type for use by L{Query.RequestedData}
1065 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1066 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1067 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1068 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1072 class NodeQueryData:
1073 """Data container for node data queries.
1076 def __init__(self, nodes, live_data, master_name, node_to_primary,
1077 node_to_secondary, groups, oob_support, cluster):
1078 """Initializes this class.
1082 self.live_data = live_data
1083 self.master_name = master_name
1084 self.node_to_primary = node_to_primary
1085 self.node_to_secondary = node_to_secondary
1086 self.groups = groups
1087 self.oob_support = oob_support
1088 self.cluster = cluster
1090 # Used for individual rows
1091 self.curlive_data = None
1092 self.ndparams = None
1095 """Iterate over all nodes.
1097 This function has side-effects and only one instance of the resulting
1098 generator should be used at a time.
1101 for node in self.nodes:
1102 group = self.groups.get(node.group, None)
1104 self.ndparams = None
1106 self.ndparams = self.cluster.FillND(node, group)
1108 self.curlive_data = self.live_data.get(node.name, None)
1110 self.curlive_data = None
1114 #: Fields that are direct attributes of an L{objects.Node} object
1115 _NODE_SIMPLE_FIELDS = {
1116 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1117 "master_candidate": ("MasterC", QFT_BOOL, 0,
1118 "Whether node is a master candidate"),
1119 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1120 "Whether node can become a master candidate"),
1121 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1122 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1123 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1124 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1125 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1129 #: Fields requiring talking to the node
1130 # Note that none of these are available for non-vm_capable nodes
1131 _NODE_LIVE_FIELDS = {
1132 "bootid": ("BootID", QFT_TEXT, "bootid",
1133 "Random UUID renewed for each system reboot, can be used"
1134 " for detecting reboots by tracking changes"),
1135 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1136 "Number of NUMA domains on node (if exported by hypervisor)"),
1137 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1138 "Number of physical CPU sockets (if exported by hypervisor)"),
1139 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1140 "dfree": ("DFree", QFT_UNIT, "vg_free",
1141 "Available disk space in volume group"),
1142 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1143 "Total disk space in volume group used for instance disk"
1145 "mfree": ("MFree", QFT_UNIT, "memory_free",
1146 "Memory available for instance allocations"),
1147 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1148 "Amount of memory used by node (dom0 for Xen)"),
1149 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1150 "Total amount of memory of physical machine"),
1155 """Build function for calling another function with an node group.
1157 @param cb: The callback to be called with the nodegroup
1161 """Get group data for a node.
1163 @type ctx: L{NodeQueryData}
1164 @type inst: L{objects.Node}
1165 @param inst: Node object
1168 ng = ctx.groups.get(node.group, None)
1170 # Nodes always have a group, or the configuration is corrupt
1173 return cb(ctx, node, ng)
1178 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1179 """Returns the name of a node's group.
1181 @type ctx: L{NodeQueryData}
1182 @type node: L{objects.Node}
1183 @param node: Node object
1184 @type ng: L{objects.NodeGroup}
1185 @param ng: The node group this node belongs to
1191 def _GetNodePower(ctx, node):
1192 """Returns the node powered state
1194 @type ctx: L{NodeQueryData}
1195 @type node: L{objects.Node}
1196 @param node: Node object
1199 if ctx.oob_support[node.name]:
1205 def _GetNdParams(ctx, node, ng):
1206 """Returns the ndparams for this node.
1208 @type ctx: L{NodeQueryData}
1209 @type node: L{objects.Node}
1210 @param node: Node object
1211 @type ng: L{objects.NodeGroup}
1212 @param ng: The node group this node belongs to
1215 return ctx.cluster.SimpleFillND(ng.FillND(node))
1218 def _GetLiveNodeField(field, kind, ctx, node):
1219 """Gets the value of a "live" field from L{NodeQueryData}.
1221 @param field: Live field name
1222 @param kind: Data kind, one of L{constants.QFT_ALL}
1223 @type ctx: L{NodeQueryData}
1224 @type node: L{objects.Node}
1225 @param node: Node object
1231 if not node.vm_capable:
1234 if not ctx.curlive_data:
1237 return _GetStatsField(field, kind, ctx.curlive_data)
1240 def _GetStatsField(field, kind, data):
1241 """Gets a value from live statistics.
1243 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is
1244 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL}
1247 @param field: Live field name
1248 @param kind: Data kind, one of L{constants.QFT_ALL}
1250 @param data: Statistics
1258 if kind == QFT_TEXT:
1261 assert kind in (QFT_NUMBER, QFT_UNIT)
1263 # Try to convert into number
1266 except (ValueError, TypeError):
1267 logging.exception("Failed to convert node field '%s' (value %r) to int",
1272 def _GetNodeHvState(_, node):
1273 """Converts node's hypervisor state for query result.
1276 hv_state = node.hv_state
1278 if hv_state is None:
1281 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1284 def _GetNodeDiskState(_, node):
1285 """Converts node's disk state for query result.
1288 disk_state = node.disk_state
1290 if disk_state is None:
1293 return dict((disk_kind, dict((name, value.ToDict())
1294 for (name, value) in kind_state.items()))
1295 for (disk_kind, kind_state) in disk_state.items())
1298 def _BuildNodeFields():
1299 """Builds list of fields for node queries.
1303 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1304 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1305 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1306 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1307 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1308 lambda ctx, node: list(node.GetTags())),
1309 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1310 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1311 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1312 _GetGroup(_GetNodeGroup)),
1313 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1314 NQ_CONFIG, 0, _GetItemAttr("group")),
1315 (_MakeField("powered", "Powered", QFT_BOOL,
1316 "Whether node is thought to be powered on"),
1317 NQ_OOB, 0, _GetNodePower),
1318 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1319 "Merged node parameters"),
1320 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1321 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1322 "Custom node parameters"),
1323 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1324 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1325 NQ_CONFIG, 0, _GetNodeHvState),
1326 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1327 NQ_CONFIG, 0, _GetNodeDiskState),
1330 fields.extend(_BuildNDFields(False))
1333 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1334 constants.NR_REGULAR, constants.NR_DRAINED,
1335 constants.NR_OFFLINE)
1336 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1337 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1339 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1340 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1341 assert set(role_values) == constants.NR_ALL
1343 def _GetLength(getter):
1344 return lambda ctx, node: len(getter(ctx)[node.name])
1346 def _GetList(getter):
1347 return lambda ctx, node: list(getter(ctx)[node.name])
1349 # Add fields operating on instance lists
1350 for prefix, titleprefix, docword, getter in \
1351 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1352 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1353 # TODO: Allow filterting by hostname in list
1355 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1356 "Number of instances with this node as %s" % docword),
1357 NQ_INST, 0, _GetLength(getter)),
1358 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1360 "List of instances with this node as %s" % docword),
1361 NQ_INST, 0, _GetList(getter)),
1366 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1367 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
1369 # Add fields requiring live data
1371 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1372 compat.partial(_GetLiveNodeField, nfield, kind))
1373 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
1376 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1378 return _PrepareFieldList(fields, [])
1381 class InstanceQueryData:
1382 """Data container for instance data queries.
1385 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1386 live_data, wrongnode_inst, console, nodes, groups):
1387 """Initializes this class.
1389 @param instances: List of instance objects
1390 @param cluster: Cluster object
1391 @type disk_usage: dict; instance name as key
1392 @param disk_usage: Per-instance disk usage
1393 @type offline_nodes: list of strings
1394 @param offline_nodes: List of offline nodes
1395 @type bad_nodes: list of strings
1396 @param bad_nodes: List of faulty nodes
1397 @type live_data: dict; instance name as key
1398 @param live_data: Per-instance live data
1399 @type wrongnode_inst: set
1400 @param wrongnode_inst: Set of instances running on wrong node(s)
1401 @type console: dict; instance name as key
1402 @param console: Per-instance console information
1403 @type nodes: dict; node name as key
1404 @param nodes: Node objects
1407 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1408 "Offline nodes not included in bad nodes"
1409 assert not (set(live_data.keys()) & set(bad_nodes)), \
1410 "Found live data for bad or offline nodes"
1412 self.instances = instances
1413 self.cluster = cluster
1414 self.disk_usage = disk_usage
1415 self.offline_nodes = offline_nodes
1416 self.bad_nodes = bad_nodes
1417 self.live_data = live_data
1418 self.wrongnode_inst = wrongnode_inst
1419 self.console = console
1421 self.groups = groups
1423 # Used for individual rows
1424 self.inst_hvparams = None
1425 self.inst_beparams = None
1426 self.inst_osparams = None
1427 self.inst_nicparams = None
1430 """Iterate over all instances.
1432 This function has side-effects and only one instance of the resulting
1433 generator should be used at a time.
1436 for inst in self.instances:
1437 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1438 self.inst_beparams = self.cluster.FillBE(inst)
1439 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1440 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1441 for nic in inst.nics]
1446 def _GetInstOperState(ctx, inst):
1447 """Get instance's operational status.
1449 @type ctx: L{InstanceQueryData}
1450 @type inst: L{objects.Instance}
1451 @param inst: Instance object
1454 # Can't use RS_OFFLINE here as it would describe the instance to
1455 # be offline when we actually don't know due to missing data
1456 if inst.primary_node in ctx.bad_nodes:
1459 return bool(ctx.live_data.get(inst.name))
1462 def _GetInstLiveData(name):
1463 """Build function for retrieving live data.
1466 @param name: Live data field name
1470 """Get live data for an instance.
1472 @type ctx: L{InstanceQueryData}
1473 @type inst: L{objects.Instance}
1474 @param inst: Instance object
1477 if (inst.primary_node in ctx.bad_nodes or
1478 inst.primary_node in ctx.offline_nodes):
1479 # Can't use RS_OFFLINE here as it would describe the instance to be
1480 # offline when we actually don't know due to missing data
1483 if inst.name in ctx.live_data:
1484 data = ctx.live_data[inst.name]
1493 def _GetInstStatus(ctx, inst):
1494 """Get instance status.
1496 @type ctx: L{InstanceQueryData}
1497 @type inst: L{objects.Instance}
1498 @param inst: Instance object
1501 if inst.primary_node in ctx.offline_nodes:
1502 return constants.INSTST_NODEOFFLINE
1504 if inst.primary_node in ctx.bad_nodes:
1505 return constants.INSTST_NODEDOWN
1507 if bool(ctx.live_data.get(inst.name)):
1508 if inst.name in ctx.wrongnode_inst:
1509 return constants.INSTST_WRONGNODE
1510 elif inst.admin_state == constants.ADMINST_UP:
1511 return constants.INSTST_RUNNING
1513 return constants.INSTST_ERRORUP
1515 if inst.admin_state == constants.ADMINST_UP:
1516 return constants.INSTST_ERRORDOWN
1517 elif inst.admin_state == constants.ADMINST_DOWN:
1518 return constants.INSTST_ADMINDOWN
1520 return constants.INSTST_ADMINOFFLINE
1523 def _GetInstDiskSize(index):
1524 """Build function for retrieving disk size.
1527 @param index: Disk index
1531 """Get size of a disk.
1533 @type inst: L{objects.Instance}
1534 @param inst: Instance object
1538 return inst.disks[index].size
1545 def _GetInstNic(index, cb):
1546 """Build function for calling another function with an instance NIC.
1549 @param index: NIC index
1555 """Call helper function with instance NIC.
1557 @type ctx: L{InstanceQueryData}
1558 @type inst: L{objects.Instance}
1559 @param inst: Instance object
1563 nic = inst.nics[index]
1567 return cb(ctx, index, nic)
1572 def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1573 """Get a NIC's Network.
1575 @type ctx: L{InstanceQueryData}
1576 @type nic: L{objects.NIC}
1577 @param nic: NIC object
1580 if nic.network is None:
1586 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1587 """Get a NIC's IP address.
1589 @type ctx: L{InstanceQueryData}
1590 @type nic: L{objects.NIC}
1591 @param nic: NIC object
1600 def _GetInstNicBridge(ctx, index, _):
1601 """Get a NIC's bridge.
1603 @type ctx: L{InstanceQueryData}
1605 @param index: NIC index
1608 assert len(ctx.inst_nicparams) >= index
1610 nicparams = ctx.inst_nicparams[index]
1612 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1613 return nicparams[constants.NIC_LINK]
1618 def _GetInstAllNicBridges(ctx, inst):
1619 """Get all network bridges for an instance.
1621 @type ctx: L{InstanceQueryData}
1622 @type inst: L{objects.Instance}
1623 @param inst: Instance object
1626 assert len(ctx.inst_nicparams) == len(inst.nics)
1630 for nicp in ctx.inst_nicparams:
1631 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1632 result.append(nicp[constants.NIC_LINK])
1636 assert len(result) == len(inst.nics)
1641 def _GetInstNicParam(name):
1642 """Build function for retrieving a NIC parameter.
1645 @param name: Parameter name
1648 def fn(ctx, index, _):
1649 """Get a NIC's bridge.
1651 @type ctx: L{InstanceQueryData}
1652 @type inst: L{objects.Instance}
1653 @param inst: Instance object
1654 @type nic: L{objects.NIC}
1655 @param nic: NIC object
1658 assert len(ctx.inst_nicparams) >= index
1659 return ctx.inst_nicparams[index][name]
1664 def _GetInstanceNetworkFields():
1665 """Get instance fields involving network interfaces.
1667 @return: Tuple containing list of field definitions used as input for
1668 L{_PrepareFieldList} and a list of aliases
1671 nic_mac_fn = lambda ctx, _, nic: nic.mac
1672 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1673 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1677 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1678 "Number of network interfaces"),
1679 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1680 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1681 "List containing each network interface's MAC address"),
1682 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1683 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1684 "List containing each network interface's IP address"),
1685 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1686 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1687 "List containing each network interface's mode"), IQ_CONFIG, 0,
1688 lambda ctx, inst: [nicp[constants.NIC_MODE]
1689 for nicp in ctx.inst_nicparams]),
1690 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1691 "List containing each network interface's link"), IQ_CONFIG, 0,
1692 lambda ctx, inst: [nicp[constants.NIC_LINK]
1693 for nicp in ctx.inst_nicparams]),
1694 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1695 "List containing each network interface's bridge"),
1696 IQ_CONFIG, 0, _GetInstAllNicBridges),
1697 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1698 "List containing each interface's network"), IQ_CONFIG, 0,
1699 lambda ctx, inst: [nic.network for nic in inst.nics]),
1703 for i in range(constants.MAX_NICS):
1704 numtext = utils.FormatOrdinal(i + 1)
1706 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1707 "IP address of %s network interface" % numtext),
1708 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1709 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1710 "MAC address of %s network interface" % numtext),
1711 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1712 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1713 "Mode of %s network interface" % numtext),
1714 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1715 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1716 "Link of %s network interface" % numtext),
1717 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1718 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1719 "Bridge of %s network interface" % numtext),
1720 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1721 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1722 "Network of %s network interface" % numtext),
1723 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1727 # Legacy fields for first NIC
1729 ("mac", "nic.mac/0"),
1730 ("bridge", "nic.bridge/0"),
1731 ("nic_mode", "nic.mode/0"),
1732 ("nic_link", "nic.link/0"),
1733 ("nic_network", "nic.network/0"),
1736 return (fields, aliases)
1739 def _GetInstDiskUsage(ctx, inst):
1740 """Get disk usage for an instance.
1742 @type ctx: L{InstanceQueryData}
1743 @type inst: L{objects.Instance}
1744 @param inst: Instance object
1747 usage = ctx.disk_usage[inst.name]
1755 def _GetInstanceConsole(ctx, inst):
1756 """Get console information for instance.
1758 @type ctx: L{InstanceQueryData}
1759 @type inst: L{objects.Instance}
1760 @param inst: Instance object
1763 consinfo = ctx.console[inst.name]
1765 if consinfo is None:
1771 def _GetInstanceDiskFields():
1772 """Get instance fields involving disks.
1774 @return: List of field definitions used as input for L{_PrepareFieldList}
1778 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1779 "Total disk space used by instance on each of its nodes;"
1780 " this is not the disk size visible to the instance, but"
1781 " the usage on the node"),
1782 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1783 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1784 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1785 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1786 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1791 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1792 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1793 IQ_CONFIG, 0, _GetInstDiskSize(i))
1794 for i in range(constants.MAX_DISKS)])
1799 def _GetInstanceParameterFields():
1800 """Get instance fields involving parameters.
1802 @return: List of field definitions used as input for L{_PrepareFieldList}
1807 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1808 "Hypervisor parameters (merged)"),
1809 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1810 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1811 "Backend parameters (merged)"),
1812 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1813 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1814 "Operating system parameters (merged)"),
1815 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1817 # Unfilled parameters
1818 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1819 "Custom hypervisor parameters"),
1820 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1821 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1822 "Custom backend parameters",),
1823 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1824 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1825 "Custom operating system parameters",),
1826 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1827 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1828 "Custom network interface parameters"),
1829 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1833 def _GetInstHvParam(name):
1834 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1837 (_MakeField("hv/%s" % name,
1838 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
1839 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1840 IQ_CONFIG, 0, _GetInstHvParam(name))
1841 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1842 if name not in constants.HVC_GLOBALS])
1845 def _GetInstBeParam(name):
1846 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1849 (_MakeField("be/%s" % name,
1850 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
1851 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1852 IQ_CONFIG, 0, _GetInstBeParam(name))
1853 for name, kind in constants.BES_PARAMETER_TYPES.items()])
1858 _INST_SIMPLE_FIELDS = {
1859 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1860 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1861 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1862 # Depending on the hypervisor, the port can be None
1863 "network_port": ("Network_port", QFT_OTHER, 0,
1864 "Instance network port if available (e.g. for VNC console)"),
1865 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1866 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1867 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1871 def _GetInstNodeGroup(ctx, default, node_name):
1872 """Gets group UUID of an instance node.
1874 @type ctx: L{InstanceQueryData}
1875 @param default: Default value
1876 @type node_name: string
1877 @param node_name: Node name
1881 node = ctx.nodes[node_name]
1888 def _GetInstNodeGroupName(ctx, default, node_name):
1889 """Gets group name of an instance node.
1891 @type ctx: L{InstanceQueryData}
1892 @param default: Default value
1893 @type node_name: string
1894 @param node_name: Node name
1898 node = ctx.nodes[node_name]
1903 group = ctx.groups[node.group]
1910 def _BuildInstanceFields():
1911 """Builds list of fields for instance queries.
1915 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1916 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1917 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1918 "Primary node's group"),
1920 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1921 inst.primary_node)),
1922 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1923 "Primary node's group UUID"),
1925 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1926 # TODO: Allow filtering by secondary node as hostname
1927 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1928 "Secondary nodes; usually this will just be one node"),
1929 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1930 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1931 "Node groups of secondary nodes"),
1933 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1934 inst.secondary_nodes)),
1935 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1936 "Node group UUIDs of secondary nodes"),
1938 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1939 inst.secondary_nodes)),
1940 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1941 "Desired state of instance"),
1942 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1943 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1944 "Desired state of instance"),
1945 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1946 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1947 lambda ctx, inst: list(inst.GetTags())),
1948 (_MakeField("console", "Console", QFT_OTHER,
1949 "Instance console information"), IQ_CONSOLE, 0,
1950 _GetInstanceConsole),
1955 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1956 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
1958 # Fields requiring talking to the node
1960 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1961 IQ_LIVE, 0, _GetInstOperState),
1962 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1963 "Actual memory usage as seen by hypervisor"),
1964 IQ_LIVE, 0, _GetInstLiveData("memory")),
1965 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1966 "Actual number of VCPUs as seen by hypervisor"),
1967 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1971 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1972 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1973 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1974 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1975 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1976 " and actually is, \"%s\" if instance is stopped and"
1977 " is not running, \"%s\" if instance running, but not on its"
1978 " designated primary node, \"%s\" if instance should be"
1979 " stopped, but is actually running, \"%s\" if instance should"
1980 " run, but doesn't, \"%s\" if instance's primary node is down,"
1981 " \"%s\" if instance's primary node is marked offline,"
1982 " \"%s\" if instance is offline and does not use dynamic"
1983 " resources" % status_values)
1984 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1985 IQ_LIVE, 0, _GetInstStatus))
1986 assert set(status_values) == constants.INSTST_ALL, \
1987 "Status documentation mismatch"
1989 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1991 fields.extend(network_fields)
1992 fields.extend(_GetInstanceParameterFields())
1993 fields.extend(_GetInstanceDiskFields())
1994 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1997 ("vcpus", "be/vcpus"),
1998 ("be/memory", "be/maxmem"),
1999 ("sda_size", "disk.size/0"),
2000 ("sdb_size", "disk.size/1"),
2003 return _PrepareFieldList(fields, aliases)
2006 class LockQueryData:
2007 """Data container for lock data queries.
2010 def __init__(self, lockdata):
2011 """Initializes this class.
2014 self.lockdata = lockdata
2017 """Iterate over all locks.
2020 return iter(self.lockdata)
2023 def _GetLockOwners(_, data):
2024 """Returns a sorted list of a lock's current owners.
2027 (_, _, owners, _) = data
2030 owners = utils.NiceSort(owners)
2035 def _GetLockPending(_, data):
2036 """Returns a sorted list of a lock's pending acquires.
2039 (_, _, _, pending) = data
2042 pending = [(mode, utils.NiceSort(names))
2043 for (mode, names) in pending]
2048 def _BuildLockFields():
2049 """Builds list of fields for lock queries.
2052 return _PrepareFieldList([
2053 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2054 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2055 lambda ctx, (name, mode, owners, pending): name),
2056 (_MakeField("mode", "Mode", QFT_OTHER,
2057 "Mode in which the lock is currently acquired"
2058 " (exclusive or shared)"),
2059 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2060 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2061 LQ_OWNER, 0, _GetLockOwners),
2062 (_MakeField("pending", "Pending", QFT_OTHER,
2063 "Threads waiting for the lock"),
2064 LQ_PENDING, 0, _GetLockPending),
2068 class GroupQueryData:
2069 """Data container for node group data queries.
2072 def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2074 """Initializes this class.
2076 @param cluster: Cluster object
2077 @param groups: List of node group objects
2078 @type group_to_nodes: dict; group UUID as key
2079 @param group_to_nodes: Per-group list of nodes
2080 @type group_to_instances: dict; group UUID as key
2081 @param group_to_instances: Per-group list of (primary) instances
2082 @type want_diskparams: bool
2083 @param want_diskparams: Whether diskparamters should be calculated
2086 self.groups = groups
2087 self.group_to_nodes = group_to_nodes
2088 self.group_to_instances = group_to_instances
2089 self.cluster = cluster
2090 self.want_diskparams = want_diskparams
2092 # Used for individual rows
2093 self.group_ipolicy = None
2094 self.ndparams = None
2095 self.group_dp = None
2098 """Iterate over all node groups.
2100 This function has side-effects and only one instance of the resulting
2101 generator should be used at a time.
2104 for group in self.groups:
2105 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2106 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2107 if self.want_diskparams:
2108 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2110 self.group_dp = None
2114 _GROUP_SIMPLE_FIELDS = {
2115 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2116 "name": ("Group", QFT_TEXT, "Group name"),
2117 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2118 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2122 def _BuildGroupFields():
2123 """Builds list of fields for node group queries.
2127 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2129 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2131 def _GetLength(getter):
2132 return lambda ctx, group: len(getter(ctx)[group.uuid])
2134 def _GetSortedList(getter):
2135 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2137 group_to_nodes = operator.attrgetter("group_to_nodes")
2138 group_to_instances = operator.attrgetter("group_to_instances")
2140 # Add fields for nodes
2142 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2143 GQ_NODE, 0, _GetLength(group_to_nodes)),
2144 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2145 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2148 # Add fields for instances
2150 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2151 "Number of primary instances"),
2152 GQ_INST, 0, _GetLength(group_to_instances)),
2153 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2154 "List of primary instances"),
2155 GQ_INST, 0, _GetSortedList(group_to_instances)),
2160 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2161 lambda ctx, group: list(group.GetTags())),
2162 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2163 "Instance policy limitations (merged)"),
2164 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2165 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2166 "Custom instance policy limitations"),
2167 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2168 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2169 "Custom node parameters"),
2170 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2171 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2173 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2174 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2175 "Disk parameters (merged)"),
2176 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2177 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2178 "Custom disk parameters"),
2179 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2183 fields.extend(_BuildNDFields(True))
2185 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2187 return _PrepareFieldList(fields, [])
2190 class OsInfo(objects.ConfigObject):
2203 def _BuildOsFields():
2204 """Builds list of fields for operating system queries.
2208 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2209 None, 0, _GetItemAttr("name")),
2210 (_MakeField("valid", "Valid", QFT_BOOL,
2211 "Whether operating system definition is valid"),
2212 None, 0, _GetItemAttr("valid")),
2213 (_MakeField("hidden", "Hidden", QFT_BOOL,
2214 "Whether operating system is hidden"),
2215 None, 0, _GetItemAttr("hidden")),
2216 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2217 "Whether operating system is blacklisted"),
2218 None, 0, _GetItemAttr("blacklisted")),
2219 (_MakeField("variants", "Variants", QFT_OTHER,
2220 "Operating system variants"),
2221 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2222 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2223 "Operating system API versions"),
2224 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2225 (_MakeField("parameters", "Parameters", QFT_OTHER,
2226 "Operating system parameters"),
2227 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2228 _GetItemAttr("parameters"))),
2229 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2230 "Status from node"),
2231 None, 0, _GetItemAttr("node_status")),
2234 return _PrepareFieldList(fields, [])
2237 class ExtStorageInfo(objects.ConfigObject):
2246 def _BuildExtStorageFields():
2247 """Builds list of fields for extstorage provider queries.
2251 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2252 None, 0, _GetItemAttr("name")),
2253 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2254 "Status from node"),
2255 None, 0, _GetItemAttr("node_status")),
2256 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2257 "Overall Nodegroup status"),
2258 None, 0, _GetItemAttr("nodegroup_status")),
2259 (_MakeField("parameters", "Parameters", QFT_OTHER,
2260 "ExtStorage provider parameters"),
2261 None, 0, _GetItemAttr("parameters")),
2264 return _PrepareFieldList(fields, [])
2267 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2268 """Return L{_FS_UNAVAIL} if job is None.
2270 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2271 found, in which case this function converts it to L{_FS_UNAVAIL}.
2280 def _JobUnavail(inner):
2281 """Wrapper for L{_JobUnavailInner}.
2284 return compat.partial(_JobUnavailInner, inner)
2287 def _PerJobOpInner(fn, job):
2288 """Executes a function per opcode in a job.
2291 return map(fn, job.ops)
2295 """Wrapper for L{_PerJobOpInner}.
2298 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2301 def _JobTimestampInner(fn, job):
2302 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2307 if timestamp is None:
2313 def _JobTimestamp(fn):
2314 """Wrapper for L{_JobTimestampInner}.
2317 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2320 def _BuildJobFields():
2321 """Builds list of fields for job queries.
2325 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2326 None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2327 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2328 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2329 (_MakeField("priority", "Priority", QFT_NUMBER,
2330 ("Current job priority (%s to %s)" %
2331 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2332 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2333 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2334 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2335 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2336 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2337 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2338 "List of opcodes results"),
2339 None, 0, _PerJobOp(operator.attrgetter("result"))),
2340 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2341 "List of opcodes status"),
2342 None, 0, _PerJobOp(operator.attrgetter("status"))),
2343 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2344 "List of opcode output logs"),
2345 None, 0, _PerJobOp(operator.attrgetter("log"))),
2346 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2347 "List of opcode start timestamps (before acquiring locks)"),
2348 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2349 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2350 "List of opcode execution start timestamps (after acquiring"
2352 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2353 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2354 "List of opcode execution end timestamps"),
2355 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2356 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2357 "List of opcode priorities"),
2358 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2359 (_MakeField("summary", "Summary", QFT_OTHER,
2360 "List of per-opcode summaries"),
2361 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2365 for (name, attr, title, desc) in [
2366 ("received_ts", "received_timestamp", "Received",
2367 "Timestamp of when job was received"),
2368 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2369 ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2371 getter = operator.attrgetter(attr)
2373 (_MakeField(name, title, QFT_OTHER,
2374 "%s (tuple containing seconds and microseconds)" % desc),
2375 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2378 return _PrepareFieldList(fields, [])
2381 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2382 """Returns an export name if available.
2391 def _BuildExportFields():
2392 """Builds list of fields for exports.
2396 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2397 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2398 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2399 None, 0, _GetExportName),
2402 return _PrepareFieldList(fields, [])
2405 _CLUSTER_VERSION_FIELDS = {
2406 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2407 "Software version"),
2408 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2409 constants.PROTOCOL_VERSION,
2410 "RPC protocol version"),
2411 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2412 "Configuration format version"),
2413 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2414 "API version for OS template scripts"),
2415 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2416 "Import/export file format version"),
2420 _CLUSTER_SIMPLE_FIELDS = {
2421 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2422 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2423 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2427 class ClusterQueryData:
2428 def __init__(self, cluster, drain_flag, watcher_pause):
2429 """Initializes this class.
2431 @type cluster: L{objects.Cluster}
2432 @param cluster: Instance of cluster object
2433 @type drain_flag: bool
2434 @param drain_flag: Whether job queue is drained
2435 @type watcher_pause: number
2436 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2439 self._cluster = cluster
2440 self.drain_flag = drain_flag
2441 self.watcher_pause = watcher_pause
2444 return iter([self._cluster])
2447 def _ClusterWatcherPause(ctx, _):
2448 """Returns until when watcher is paused (if available).
2451 if ctx.watcher_pause is None:
2454 return ctx.watcher_pause
2457 def _BuildClusterFields():
2458 """Builds list of fields for cluster information.
2462 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2463 lambda ctx, cluster: list(cluster.GetTags())),
2464 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2465 "Architecture information"), None, 0,
2466 lambda ctx, _: runtime.GetArchInfo()),
2467 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2468 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2469 lambda ctx, _: ctx.drain_flag),
2470 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2471 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2472 _ClusterWatcherPause),
2477 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2478 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2483 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2484 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
2487 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2489 return _PrepareFieldList(fields, [
2490 ("name", "cluster_name")])
2493 class NetworkQueryData:
2494 """Data container for network data queries.
2497 def __init__(self, networks, network_to_groups,
2498 network_to_instances, stats):
2499 """Initializes this class.
2501 @param networks: List of network objects
2502 @type network_to_groups: dict; network UUID as key
2503 @param network_to_groups: Per-network list of groups
2504 @type network_to_instances: dict; network UUID as key
2505 @param network_to_instances: Per-network list of instances
2506 @type stats: dict; network UUID as key
2507 @param stats: Per-network usage statistics
2510 self.networks = networks
2511 self.network_to_groups = network_to_groups
2512 self.network_to_instances = network_to_instances
2516 """Iterate over all networks.
2519 for net in self.networks:
2521 self.curstats = self.stats.get(net.uuid, None)
2523 self.curstats = None
2527 _NETWORK_SIMPLE_FIELDS = {
2528 "name": ("Network", QFT_TEXT, 0, "Name"),
2529 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"),
2530 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"),
2531 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"),
2532 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"),
2533 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"),
2534 "network_type": ("NetworkType", QFT_OTHER, 0, "Network type"),
2535 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
2536 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2540 _NETWORK_STATS_FIELDS = {
2541 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"),
2543 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"),
2544 "map": ("Map", QFT_TEXT, 0, "Actual mapping"),
2545 "external_reservations":
2546 ("ExternalReservations", QFT_TEXT, 0, "External reservations"),
2550 def _GetNetworkStatsField(field, kind, ctx, _):
2551 """Gets the value of a "stats" field from L{NetworkQueryData}.
2553 @param field: Field name
2554 @param kind: Data kind, one of L{constants.QFT_ALL}
2555 @type ctx: L{NetworkQueryData}
2558 return _GetStatsField(field, kind, ctx.curstats)
2561 def _BuildNetworkFields():
2562 """Builds list of fields for network queries.
2566 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2567 lambda ctx, inst: list(inst.GetTags())),
2572 (_MakeField(name, title, kind, doc),
2573 NETQ_CONFIG, 0, _GetItemAttr(name))
2574 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
2576 def _GetLength(getter):
2577 return lambda ctx, network: len(getter(ctx)[network.uuid])
2579 def _GetSortedList(getter):
2580 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2582 network_to_groups = operator.attrgetter("network_to_groups")
2583 network_to_instances = operator.attrgetter("network_to_instances")
2585 # Add fields for node groups
2587 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2588 NETQ_GROUP, 0, _GetLength(network_to_groups)),
2589 (_MakeField("group_list", "GroupList", QFT_OTHER,
2590 "List of nodegroups (group name, NIC mode, NIC link)"),
2591 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]),
2594 # Add fields for instances
2596 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2597 NETQ_INST, 0, _GetLength(network_to_instances)),
2598 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2599 NETQ_INST, 0, _GetSortedList(network_to_instances)),
2602 # Add fields for usage statistics
2604 (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2605 compat.partial(_GetNetworkStatsField, name, kind))
2606 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
2608 return _PrepareFieldList(fields, [])
2610 #: Fields for cluster information
2611 CLUSTER_FIELDS = _BuildClusterFields()
2613 #: Fields available for node queries
2614 NODE_FIELDS = _BuildNodeFields()
2616 #: Fields available for instance queries
2617 INSTANCE_FIELDS = _BuildInstanceFields()
2619 #: Fields available for lock queries
2620 LOCK_FIELDS = _BuildLockFields()
2622 #: Fields available for node group queries
2623 GROUP_FIELDS = _BuildGroupFields()
2625 #: Fields available for operating system queries
2626 OS_FIELDS = _BuildOsFields()
2628 #: Fields available for extstorage provider queries
2629 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2631 #: Fields available for job queries
2632 JOB_FIELDS = _BuildJobFields()
2634 #: Fields available for exports
2635 EXPORT_FIELDS = _BuildExportFields()
2637 #: Fields available for network queries
2638 NETWORK_FIELDS = _BuildNetworkFields()
2640 #: All available resources
2642 constants.QR_CLUSTER: CLUSTER_FIELDS,
2643 constants.QR_INSTANCE: INSTANCE_FIELDS,
2644 constants.QR_NODE: NODE_FIELDS,
2645 constants.QR_LOCK: LOCK_FIELDS,
2646 constants.QR_GROUP: GROUP_FIELDS,
2647 constants.QR_OS: OS_FIELDS,
2648 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2649 constants.QR_JOB: JOB_FIELDS,
2650 constants.QR_EXPORT: EXPORT_FIELDS,
2651 constants.QR_NETWORK: NETWORK_FIELDS,
2654 #: All available field lists
2655 ALL_FIELD_LISTS = ALL_FIELDS.values()