1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """Module for query operations
32
33 How it works:
34
35 - Add field definitions
36 - See how L{NODE_FIELDS} is built
37 - Each field gets:
38 - Query field definition (L{objects.QueryFieldDefinition}, use
39 L{_MakeField} for creating), containing:
40 - Name, must be lowercase and match L{FIELD_NAME_RE}
41 - Title for tables, must not contain whitespace and match
42 L{TITLE_RE}
43 - Value data type, e.g. L{constants.QFT_NUMBER}
44 - Human-readable description, must not end with punctuation or
45 contain newlines
46 - Data request type, see e.g. C{NQ_*}
47 - OR-ed flags, see C{QFF_*}
48 - A retrieval function, see L{Query.__init__} for description
49 - Pass list of fields through L{_PrepareFieldList} for preparation and
50 checks
51 - Instantiate L{Query} with prepared field list definition and selected fields
52 - Call L{Query.RequestedData} to determine what data to collect/compute
53 - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use
54 result
55 - Data container must support iteration using C{__iter__}
56 - Items are passed to retrieval functions and can have any format
57 - Call L{Query.GetFields} to get list of definitions for selected fields
58
59 @attention: Retrieval functions must be idempotent. They can be called multiple
60 times, in any order and any number of times.
61
62 """
63
64 import logging
65 import operator
66 import re
67
68 from ganeti import constants
69 from ganeti import errors
70 from ganeti import utils
71 from ganeti import compat
72 from ganeti import objects
73 from ganeti import ht
74 from ganeti import runtime
75 from ganeti import qlang
76 from ganeti import jstore
77 from ganeti.hypervisor import hv_base
78
79 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
80 QFT_NUMBER_FLOAT, QFT_UNIT, QFT_TIMESTAMP,
81 QFT_OTHER, RS_NORMAL, RS_UNKNOWN, RS_NODATA,
82 RS_UNAVAIL, RS_OFFLINE)
83
84 (NETQ_CONFIG,
85 NETQ_GROUP,
86 NETQ_STATS,
87 NETQ_INST) = range(300, 304)
88
89
90
91
92
93 (NQ_CONFIG,
94 NQ_INST,
95 NQ_LIVE,
96 NQ_GROUP,
97 NQ_OOB) = range(1, 6)
98
99 (IQ_CONFIG,
100 IQ_LIVE,
101 IQ_DISKUSAGE,
102 IQ_CONSOLE,
103 IQ_NODES,
104 IQ_NETWORKS) = range(100, 106)
105
106 (LQ_MODE,
107 LQ_OWNER,
108 LQ_PENDING) = range(10, 13)
109
110 (GQ_CONFIG,
111 GQ_NODE,
112 GQ_INST,
113 GQ_DISKPARAMS) = range(200, 204)
114
115 (CQ_CONFIG,
116 CQ_QUEUE_DRAINED,
117 CQ_WATCHER_PAUSE) = range(300, 303)
118
119 (JQ_ARCHIVED, ) = range(400, 401)
120
121
122 QFF_HOSTNAME = 0x01
123 QFF_IP_ADDRESS = 0x02
124 QFF_JOB_ID = 0x04
125 QFF_SPLIT_TIMESTAMP = 0x08
126
127 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP)
128
129 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
130 TITLE_RE = re.compile(r"^[^\s]+$")
131 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
132
133
134 _VERIFY_FN = {
135 QFT_UNKNOWN: ht.TNone,
136 QFT_TEXT: ht.TString,
137 QFT_BOOL: ht.TBool,
138 QFT_NUMBER: ht.TInt,
139 QFT_NUMBER_FLOAT: ht.TFloat,
140 QFT_UNIT: ht.TInt,
141 QFT_TIMESTAMP: ht.TNumber,
142 QFT_OTHER: lambda _: True,
143 }
144
145
146 _FS_UNKNOWN = object()
147 _FS_NODATA = object()
148 _FS_UNAVAIL = object()
149 _FS_OFFLINE = object()
150
151
152 _FS_ALL = compat.UniqueFrozenset([
153 _FS_UNKNOWN,
154 _FS_NODATA,
155 _FS_UNAVAIL,
156 _FS_OFFLINE,
157 ])
158
159
160 _VTToQFT = {
161
162 constants.VTYPE_STRING: QFT_OTHER,
163 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
164 constants.VTYPE_BOOL: QFT_BOOL,
165 constants.VTYPE_SIZE: QFT_UNIT,
166 constants.VTYPE_INT: QFT_NUMBER,
167 constants.VTYPE_FLOAT: QFT_NUMBER_FLOAT,
168 }
169
170 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
171
172
174 """Gets the contents of an unknown field.
175
176 """
177 return _FS_UNKNOWN
178
179
181 """Calculates the internal list of selected fields.
182
183 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
184
185 @type fielddefs: dict
186 @param fielddefs: Field definitions
187 @type selected: list of strings
188 @param selected: List of selected fields
189
190 """
191 result = []
192
193 for name in selected:
194 try:
195 fdef = fielddefs[name]
196 except KeyError:
197 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
198 None, 0, _GetUnknownField)
199
200 assert len(fdef) == 4
201
202 result.append(fdef)
203
204 return result
205
206
208 """Extract L{objects.QueryFieldDefinition} from field definitions.
209
210 @rtype: list of L{objects.QueryFieldDefinition}
211
212 """
213 return [fdef for (fdef, _, _, _) in fielddefs]
214
215
217 """Class for filter analytics.
218
219 When filters are used, the user of the L{Query} class usually doesn't know
220 exactly which items will be necessary for building the result. It therefore
221 has to prepare and compute the input data for potentially returning
222 everything.
223
224 There are two ways to optimize this. The first, and simpler, is to assign
225 each field a group of data, so that the caller can determine which
226 computations are necessary depending on the data groups requested. The list
227 of referenced groups must also be computed for fields referenced in the
228 filter.
229
230 The second is restricting the items based on a primary key. The primary key
231 is usually a unique name (e.g. a node name). This class extracts all
232 referenced names from a filter. If it encounters any filter condition which
233 disallows such a list to be determined (e.g. a non-equality filter), all
234 names will be requested.
235
236 The end-effect is that any operation other than L{qlang.OP_OR} and
237 L{qlang.OP_EQUAL} will make the query more expensive.
238
239 """
241 """Initializes this class.
242
243 @type namefield: string
244 @param namefield: Field caller is interested in
245
246 """
247 self._namefield = namefield
248
249
250
251 self._allnames = False
252
253
254 self._names = None
255
256
257 self._datakinds = set()
258
260 """Returns all requested values.
261
262 Returns C{None} if list of values can't be determined (e.g. encountered
263 non-equality operators).
264
265 @rtype: list
266
267 """
268 if self._allnames or self._names is None:
269 return None
270
271 return utils.UniqueSequence(self._names)
272
274 """Returns all kinds of data referenced by the filter.
275
276 """
277 return frozenset(self._datakinds)
278
280 """Changes internal state to request all names.
281
282 """
283 self._allnames = True
284 self._names = None
285
287 """Called when handling a logic operation.
288
289 @type op: string
290 @param op: Operator
291
292 """
293 if op != qlang.OP_OR:
294 self._NeedAllNames()
295
297 """Called when handling an unary operation.
298
299 @type op: string
300 @param op: Operator
301
302 """
303 if datakind is not None:
304 self._datakinds.add(datakind)
305
306 self._NeedAllNames()
307
309 """Called when handling a binary operation.
310
311 @type op: string
312 @param op: Operator
313 @type name: string
314 @param name: Left-hand side of operator (field name)
315 @param value: Right-hand side of operator
316
317 """
318 if datakind is not None:
319 self._datakinds.add(datakind)
320
321 if self._allnames:
322 return
323
324
325
326 EQ_OPS = [qlang.OP_EQUAL, qlang.OP_EQUAL_LEGACY]
327 if op in EQ_OPS and name == self._namefield:
328 if self._names is None:
329 self._names = []
330 self._names.append(value)
331 else:
332 self._NeedAllNames()
333
334
336 """Wrapper for logic operator functions.
337
338 """
339 return op_fn(fn(ctx, item) for fn in sentences)
340
341
343 """Wrapper for unary operator functions.
344
345 """
346 return op_fn(inner(ctx, item))
347
348
350 """Wrapper for binary operator functions.
351
352 """
353 return op_fn(retrieval_fn(ctx, item), value)
354
355
357 """Negates the result of a wrapped function.
358
359 """
360 return not fn(lhs, rhs)
361
362
364 """Compiles a regular expression.
365
366 """
367 try:
368 return re.compile(pattern)
369 except re.error, err:
370 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
371
372
374 """Prepares a value for comparison by L{_MakeSplitTimestampComparison}.
375
376 """
377 if ht.TNumber(value):
378 return value
379 else:
380 return utils.MergeTime(value)
381
382
384 """Compares split timestamp values after converting to float.
385
386 """
387 return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
388
389
391 """Prepares flag-specific comparisons using a comparison function.
392
393 """
394 return [
395 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn),
396 _PrepareSplitTimestamp),
397 (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs),
398 jstore.ParseJobId),
399 (None, fn, None),
400 ]
401
402
404 """Converts a query filter to a callable usable for filtering.
405
406 """
407
408
409
410 _LEVELS_MAX = 10
411
412
413 (_OPTYPE_LOGIC,
414 _OPTYPE_UNARY,
415 _OPTYPE_BINARY) = range(1, 4)
416
417 """Functions for equality checks depending on field flags.
418
419 List of tuples containing flags and a callable receiving the left- and
420 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
421 (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
422
423 Order matters. The first item with flags will be used. Flags are checked
424 using binary AND.
425
426 """
427 _EQUALITY_CHECKS = [
428 (QFF_HOSTNAME,
429 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
430 case_sensitive=False),
431 None),
432 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq),
433 _PrepareSplitTimestamp),
434 (None, operator.eq, None),
435 ]
436
437 """Known operators
438
439 Operator as key (C{qlang.OP_*}), value a tuple of operator group
440 (C{_OPTYPE_*}) and a group-specific value:
441
442 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
443 L{_HandleLogicOp}
444 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
445 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
446 right-hand side of the operator, used by L{_HandleBinaryOp}
447
448 """
449 _OPS = {
450
451 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
452 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
453
454
455 qlang.OP_NOT: (_OPTYPE_UNARY, None),
456 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
457
458
459 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
460 qlang.OP_EQUAL_LEGACY: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
461 qlang.OP_NOT_EQUAL:
462 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
463 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
464 qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)),
465 qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)),
466 qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)),
467 qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)),
468 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
469 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
470 ]),
471 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
472 (None, operator.contains, None),
473 ]),
474 }
475
477 """Initializes this class.
478
479 @param fields: Field definitions (return value of L{_PrepareFieldList})
480
481 """
482 self._fields = fields
483 self._hints = None
484 self._op_handler = None
485
487 """Converts a query filter into a callable function.
488
489 @type hints: L{_FilterHints} or None
490 @param hints: Callbacks doing analysis on filter
491 @type qfilter: list
492 @param qfilter: Filter structure
493 @rtype: callable
494 @return: Function receiving context and item as parameters, returning
495 boolean as to whether item matches filter
496
497 """
498 self._op_handler = {
499 self._OPTYPE_LOGIC:
500 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
501 self._OPTYPE_UNARY:
502 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
503 self._OPTYPE_BINARY:
504 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
505 }
506
507 try:
508 filter_fn = self._Compile(qfilter, 0)
509 finally:
510 self._op_handler = None
511
512 return filter_fn
513
515 """Inner function for converting filters.
516
517 Calls the correct handler functions for the top-level operator. This
518 function is called recursively (e.g. for logic operators).
519
520 """
521 if not (isinstance(qfilter, (list, tuple)) and qfilter):
522 raise errors.ParameterError("Invalid filter on level %s" % level)
523
524
525 if level >= self._LEVELS_MAX:
526 raise errors.ParameterError("Only up to %s levels are allowed (filter"
527 " nested too deep)" % self._LEVELS_MAX)
528
529
530 operands = qfilter[:]
531 op = operands.pop(0)
532
533 try:
534 (kind, op_data) = self._OPS[op]
535 except KeyError:
536 raise errors.ParameterError("Unknown operator '%s'" % op)
537
538 (handler, hints_cb) = self._op_handler[kind]
539
540 return handler(hints_cb, level, op, op_data, operands)
541
543 """Returns a field definition by name.
544
545 """
546 try:
547 return self._fields[name]
548 except KeyError:
549 raise errors.ParameterError("Unknown field '%s'" % name)
550
552 """Handles logic operators.
553
554 @type hints_fn: callable
555 @param hints_fn: Callback doing some analysis on the filter
556 @type level: integer
557 @param level: Current depth
558 @type op: string
559 @param op: Operator
560 @type op_fn: callable
561 @param op_fn: Function implementing operator
562 @type operands: list
563 @param operands: List of operands
564
565 """
566 if hints_fn:
567 hints_fn(op)
568
569 return compat.partial(_WrapLogicOp, op_fn,
570 [self._Compile(op, level + 1) for op in operands])
571
573 """Handles unary operators.
574
575 @type hints_fn: callable
576 @param hints_fn: Callback doing some analysis on the filter
577 @type level: integer
578 @param level: Current depth
579 @type op: string
580 @param op: Operator
581 @type op_fn: callable
582 @param op_fn: Function implementing operator
583 @type operands: list
584 @param operands: List of operands
585
586 """
587 assert op_fn is None
588
589 if len(operands) != 1:
590 raise errors.ParameterError("Unary operator '%s' expects exactly one"
591 " operand" % op)
592
593 if op == qlang.OP_TRUE:
594 (_, datakind, _, retrieval_fn) = self._LookupField(operands[0])
595
596 if hints_fn:
597 hints_fn(op, datakind)
598
599 op_fn = operator.truth
600 arg = retrieval_fn
601 elif op == qlang.OP_NOT:
602 if hints_fn:
603 hints_fn(op, None)
604
605 op_fn = operator.not_
606 arg = self._Compile(operands[0], level + 1)
607 else:
608 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
609
610 return compat.partial(_WrapUnaryOp, op_fn, arg)
611
613 """Handles binary operators.
614
615 @type hints_fn: callable
616 @param hints_fn: Callback doing some analysis on the filter
617 @type level: integer
618 @param level: Current depth
619 @type op: string
620 @param op: Operator
621 @param op_data: Functions implementing operators
622 @type operands: list
623 @param operands: List of operands
624
625 """
626
627 try:
628 (name, value) = operands
629 except (ValueError, TypeError):
630 raise errors.ParameterError("Invalid binary operator, expected exactly"
631 " two operands")
632
633 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
634
635 assert fdef.kind != QFT_UNKNOWN
636
637
638
639 verify_fn = _VERIFY_FN[fdef.kind]
640 if not verify_fn(value):
641 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
642 " with '%s', expected %s" %
643 (name, fdef.kind, value.__class__.__name__,
644 verify_fn))
645
646 if hints_fn:
647 hints_fn(op, datakind, name, value)
648
649 for (fn_flags, fn, valprepfn) in op_data:
650 if fn_flags is None or fn_flags & field_flags:
651
652 if valprepfn:
653 value = valprepfn(value)
654
655 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
656
657 raise errors.ProgrammerError("Unable to find operator implementation"
658 " (op '%s', flags %s)" % (op, field_flags))
659
660
662 """Converts a query filter into a callable function.
663
664 See L{_FilterCompilerHelper} for details.
665
666 @rtype: callable
667
668 """
669 return _FilterCompilerHelper(fields)(hints, qfilter)
670
671
673 - def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
674 """Initializes this class.
675
676 The field definition is a dictionary with the field's name as a key and a
677 tuple containing, in order, the field definition object
678 (L{objects.QueryFieldDefinition}, the data kind to help calling code
679 collect data and a retrieval function. The retrieval function is called
680 with two parameters, in order, the data container and the item in container
681 (see L{Query.Query}).
682
683 Users of this class can call L{RequestedData} before preparing the data
684 container to determine what data is needed.
685
686 @type fieldlist: dictionary
687 @param fieldlist: Field definitions
688 @type selected: list of strings
689 @param selected: List of selected fields
690
691 """
692 assert namefield is None or namefield in fieldlist
693
694 self._fields = _GetQueryFields(fieldlist, selected)
695
696 self._filter_fn = None
697 self._requested_names = None
698 self._filter_datakinds = frozenset()
699
700 if qfilter is not None:
701
702 if namefield:
703 hints = _FilterHints(namefield)
704 else:
705 hints = None
706
707
708 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
709 if hints:
710 self._requested_names = hints.RequestedNames()
711 self._filter_datakinds = hints.ReferencedData()
712
713 if namefield is None:
714 self._name_fn = None
715 else:
716 (_, _, _, self._name_fn) = fieldlist[namefield]
717
719 """Returns all names referenced in the filter.
720
721 If there is no filter or operators are preventing determining the exact
722 names, C{None} is returned.
723
724 """
725 return self._requested_names
726
728 """Gets requested kinds of data.
729
730 @rtype: frozenset
731
732 """
733 return (self._filter_datakinds |
734 frozenset(datakind for (_, datakind, _, _) in self._fields
735 if datakind is not None))
736
738 """Returns the list of fields for this query.
739
740 Includes unknown fields.
741
742 @rtype: List of L{objects.QueryFieldDefinition}
743
744 """
745 return GetAllFields(self._fields)
746
747 - def Query(self, ctx, sort_by_name=True):
748 """Execute a query.
749
750 @param ctx: Data container passed to field retrieval functions, must
751 support iteration using C{__iter__}
752 @type sort_by_name: boolean
753 @param sort_by_name: Whether to sort by name or keep the input data's
754 ordering
755
756 """
757 sort = (self._name_fn and sort_by_name)
758
759 result = []
760
761 for idx, item in enumerate(ctx):
762 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
763 continue
764
765 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
766
767
768 if __debug__:
769 _VerifyResultRow(self._fields, row)
770
771 if sort:
772 (status, name) = _ProcessResult(self._name_fn(ctx, item))
773 assert status == constants.RS_NORMAL
774
775
776 result.append((utils.NiceSortKey(name), idx, row))
777 else:
778 result.append(row)
779
780 if not sort:
781 return result
782
783
784
785
786 result.sort()
787
788 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
789
790 return map(operator.itemgetter(2), result)
791
793 """Query with "old" query result format.
794
795 See L{Query.Query} for arguments.
796
797 """
798 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
799 if fdef.kind == QFT_UNKNOWN)
800 if unknown:
801 raise errors.OpPrereqError("Unknown output fields selected: %s" %
802 (utils.CommaJoin(unknown), ),
803 errors.ECODE_INVAL)
804
805 return [[value for (_, value) in row]
806 for row in self.Query(ctx, sort_by_name=sort_by_name)]
807
808
823
824
826 """Verifies the contents of a query result row.
827
828 @type fields: list
829 @param fields: Field definitions for result
830 @type row: list of tuples
831 @param row: Row data
832
833 """
834 assert len(row) == len(fields)
835 errs = []
836 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
837 if status == RS_NORMAL:
838 if not _VERIFY_FN[fdef.kind](value):
839 errs.append("normal field %s fails validation (value is %s)" %
840 (fdef.name, value))
841 elif value is not None:
842 errs.append("abnormal field %s has a non-None value" % fdef.name)
843 assert not errs, ("Failed validation: %s in row %s" %
844 (utils.CommaJoin(errs), row))
845
846
848 """Generates key for field dictionary.
849
850 """
851 assert fdef.name and fdef.title, "Name and title are required"
852 assert FIELD_NAME_RE.match(fdef.name)
853 assert TITLE_RE.match(fdef.title)
854 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
855 fdef.doc.strip() == fdef.doc), \
856 "Invalid description for field '%s'" % fdef.name
857 assert callable(fn)
858 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
859
860 return fdef.name
861
862
864 """Prepares field list for use by L{Query}.
865
866 Converts the list to a dictionary and does some verification.
867
868 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
869 kind, retrieval function)
870 @param fields: List of fields, see L{Query.__init__} for a better
871 description
872 @type aliases: list of tuples; (alias, target)
873 @param aliases: list of tuples containing aliases; for each
874 alias/target pair, a duplicate will be created in the field list
875 @rtype: dict
876 @return: Field dictionary for L{Query}
877
878 """
879 if __debug__:
880 duplicates = utils.FindDuplicates(fdef.title.lower()
881 for (fdef, _, _, _) in fields)
882 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
883
884 result = utils.SequenceToDict(fields, key=_FieldDictKey)
885
886 for alias, target in aliases:
887 assert alias not in result, "Alias %s overrides an existing field" % alias
888 assert target in result, "Missing target %s for alias %s" % (target, alias)
889 (fdef, k, flags, fn) = result[target]
890 fdef = fdef.Copy()
891 fdef.name = alias
892 result[alias] = (fdef, k, flags, fn)
893
894 assert len(result) == len(fields) + len(aliases)
895 assert compat.all(name == fdef.name
896 for (name, (fdef, _, _, _)) in result.items())
897
898 return result
899
900
902 """Prepares the response for a query.
903
904 @type query: L{Query}
905 @param ctx: Data container, see L{Query.Query}
906 @type sort_by_name: boolean
907 @param sort_by_name: Whether to sort by name or keep the input data's
908 ordering
909
910 """
911 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
912 fields=query.GetFields()).ToDict()
913
914
916 """Returns list of available fields.
917
918 @type fielddefs: dict
919 @param fielddefs: Field definitions
920 @type selected: list of strings
921 @param selected: List of selected fields
922 @return: List of L{objects.QueryFieldDefinition}
923
924 """
925 if selected is None:
926
927 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
928 key=operator.attrgetter("name"))
929 else:
930
931 fdefs = Query(fielddefs, selected).GetFields()
932
933 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
934
935
937 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
938
939 @param name: Field name as a regular expression
940 @param title: Human-readable title
941 @param kind: Field type
942 @param doc: Human-readable description
943
944 """
945 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
946 doc=doc)
947
948
950 """Returns a static value.
951
952 """
953 return value
954
955
961
962
982
983
985 """Returns a field function to return an attribute of the item.
986
987 @param attr: Attribute name
988
989 """
990 getter = operator.attrgetter(attr)
991 return lambda _, item: getter(item)
992
993
995 """Returns a field function to return a not-None attribute of the item.
996
997 If the value is None, then C{_FS_UNAVAIL} will be returned instead.
998
999 @param attr: Attribute name
1000
1001 """
1002 def _helper(_, obj):
1003 val = getattr(obj, attr)
1004 if val is None:
1005 return _FS_UNAVAIL
1006 else:
1007 return val
1008 return _helper
1009
1010
1012 """Return a field function to return an ND parameter out of the context.
1013
1014 """
1015 def _helper(ctx, _):
1016 if ctx.ndparams is None:
1017 return _FS_UNAVAIL
1018 else:
1019 return ctx.ndparams.get(name, None)
1020 return _helper
1021
1022
1039
1040
1042 """Wrapper for converting values.
1043
1044 @param convert: Conversion function receiving value as single parameter
1045 @param fn: Retrieval function
1046
1047 """
1048 value = fn(ctx, item)
1049
1050
1051 if compat.any(value is fs for fs in _FS_ALL):
1052
1053 return value
1054
1055
1056 return convert(value)
1057
1058
1060 """Convenience wrapper for L{_ConvWrapInner}.
1061
1062 @param convert: Conversion function receiving value as single parameter
1063 @param fn: Retrieval function
1064
1065 """
1066 return compat.partial(_ConvWrapInner, convert, fn)
1067
1068
1070 """Returns function for getting timestamp of item.
1071
1072 @type getter: callable
1073 @param getter: Function to retrieve timestamp attribute
1074
1075 """
1076 def fn(_, item):
1077 """Returns a timestamp of item.
1078
1079 """
1080 timestamp = getter(item)
1081 if timestamp is None:
1082
1083 return _FS_UNAVAIL
1084 else:
1085 return timestamp
1086
1087 return fn
1088
1089
1091 """Returns common timestamp fields.
1092
1093 @param datatype: Field data type for use by L{Query.RequestedData}
1094
1095 """
1096 return [
1097 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1098 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1099 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1100 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1101 ]
1102
1103
1105 """Data container for node data queries.
1106
1107 """
1108 - def __init__(self, nodes, live_data, master_uuid, node_to_primary,
1109 node_to_secondary, inst_uuid_to_inst_name, groups, oob_support,
1110 cluster):
1111 """Initializes this class.
1112
1113 """
1114 self.nodes = nodes
1115 self.live_data = live_data
1116 self.master_uuid = master_uuid
1117 self.node_to_primary = node_to_primary
1118 self.node_to_secondary = node_to_secondary
1119 self.inst_uuid_to_inst_name = inst_uuid_to_inst_name
1120 self.groups = groups
1121 self.oob_support = oob_support
1122 self.cluster = cluster
1123
1124
1125 self.curlive_data = None
1126 self.ndparams = None
1127
1129 """Iterate over all nodes.
1130
1131 This function has side-effects and only one instance of the resulting
1132 generator should be used at a time.
1133
1134 """
1135 for node in self.nodes:
1136 group = self.groups.get(node.group, None)
1137 if group is None:
1138 self.ndparams = None
1139 else:
1140 self.ndparams = self.cluster.FillND(node, group)
1141 if self.live_data:
1142 self.curlive_data = self.live_data.get(node.uuid, None)
1143 else:
1144 self.curlive_data = None
1145 yield node
1146
1147
1148
1149 _NODE_SIMPLE_FIELDS = {
1150 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1151 "master_candidate": ("MasterC", QFT_BOOL, 0,
1152 "Whether node is a master candidate"),
1153 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1154 "Whether node can become a master candidate"),
1155 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1156 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1157 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1158 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1159 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1160 }
1161
1162
1163
1164
1165 _NODE_LIVE_FIELDS = {
1166 "bootid": ("BootID", QFT_TEXT, "bootid",
1167 "Random UUID renewed for each system reboot, can be used"
1168 " for detecting reboots by tracking changes"),
1169 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1170 "Number of NUMA domains on node (if exported by hypervisor)"),
1171 "cnos": ("CNOs", QFT_NUMBER, "cpu_dom0",
1172 "Number of logical processors used by the node OS (dom0 for Xen)"),
1173 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1174 "Number of physical CPU sockets (if exported by hypervisor)"),
1175 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1176 "dfree": ("DFree", QFT_UNIT, "storage_free",
1177 "Available storage space in storage unit"),
1178 "dtotal": ("DTotal", QFT_UNIT, "storage_size",
1179 "Total storage space in storage unit used for instance disk"
1180 " allocation"),
1181 "spfree": ("SpFree", QFT_NUMBER, "spindles_free",
1182 "Available spindles in volume group (exclusive storage only)"),
1183 "sptotal": ("SpTotal", QFT_NUMBER, "spindles_total",
1184 "Total spindles in volume group (exclusive storage only)"),
1185 "mfree": ("MFree", QFT_UNIT, "memory_free",
1186 "Memory available for instance allocations"),
1187 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1188 "Amount of memory used by node (dom0 for Xen)"),
1189 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1190 "Total amount of memory of physical machine"),
1191 }
1192
1193
1195 """Build function for calling another function with an node group.
1196
1197 @param cb: The callback to be called with the nodegroup
1198
1199 """
1200 def fn(ctx, node):
1201 """Get group data for a node.
1202
1203 @type ctx: L{NodeQueryData}
1204 @type inst: L{objects.Node}
1205 @param inst: Node object
1206
1207 """
1208 ng = ctx.groups.get(node.group, None)
1209 if ng is None:
1210
1211 return _FS_UNAVAIL
1212
1213 return cb(ctx, node, ng)
1214
1215 return fn
1216
1217
1219 """Returns the name of a node's group.
1220
1221 @type ctx: L{NodeQueryData}
1222 @type node: L{objects.Node}
1223 @param node: Node object
1224 @type ng: L{objects.NodeGroup}
1225 @param ng: The node group this node belongs to
1226
1227 """
1228 return ng.name
1229
1230
1232 """Returns the node powered state
1233
1234 @type ctx: L{NodeQueryData}
1235 @type node: L{objects.Node}
1236 @param node: Node object
1237
1238 """
1239 if ctx.oob_support[node.uuid]:
1240 return node.powered
1241
1242 return _FS_UNAVAIL
1243
1244
1246 """Returns the ndparams for this node.
1247
1248 @type ctx: L{NodeQueryData}
1249 @type node: L{objects.Node}
1250 @param node: Node object
1251 @type ng: L{objects.NodeGroup}
1252 @param ng: The node group this node belongs to
1253
1254 """
1255 return ctx.cluster.SimpleFillND(ng.FillND(node))
1256
1257
1259 """Gets the value of a "live" field from L{NodeQueryData}.
1260
1261 @param field: Live field name
1262 @param kind: Data kind, one of L{constants.QFT_ALL}
1263 @type ctx: L{NodeQueryData}
1264 @type node: L{objects.Node}
1265 @param node: Node object
1266
1267 """
1268 if node.offline:
1269 return _FS_OFFLINE
1270
1271 if not node.vm_capable:
1272 return _FS_UNAVAIL
1273
1274 if not ctx.curlive_data:
1275 return _FS_NODATA
1276
1277 return _GetStatsField(field, kind, ctx.curlive_data)
1278
1279
1281 """Gets a value from live statistics.
1282
1283 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is
1284 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL}
1285 is returned.
1286
1287 @param field: Live field name
1288 @param kind: Data kind, one of L{constants.QFT_ALL}
1289 @type data: dict
1290 @param data: Statistics
1291
1292 """
1293 try:
1294 value = data[field]
1295 except KeyError:
1296 return _FS_UNAVAIL
1297
1298 if kind == QFT_TEXT:
1299 return value
1300
1301 assert kind in (QFT_NUMBER, QFT_UNIT)
1302
1303
1304 try:
1305 return int(value)
1306 except (ValueError, TypeError):
1307 logging.exception("Failed to convert node field '%s' (value %r) to int",
1308 field, value)
1309 return _FS_UNAVAIL
1310
1311
1313 """Converts node's hypervisor state for query result.
1314
1315 """
1316 hv_state = node.hv_state
1317
1318 if hv_state is None:
1319 return _FS_UNAVAIL
1320
1321 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1322
1323
1325 """Converts node's disk state for query result.
1326
1327 """
1328 disk_state = node.disk_state
1329
1330 if disk_state is None:
1331 return _FS_UNAVAIL
1332
1333 return dict((disk_kind, dict((name, value.ToDict())
1334 for (name, value) in kind_state.items()))
1335 for (disk_kind, kind_state) in disk_state.items())
1336
1337
1339 """Builds list of fields for node queries.
1340
1341 """
1342 fields = [
1343 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1344 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1345 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1346 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1347 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1348 lambda ctx, node: list(node.GetTags())),
1349 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1350 NQ_CONFIG, 0, lambda ctx, node: node.uuid == ctx.master_uuid),
1351 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1352 _GetGroup(_GetNodeGroup)),
1353 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1354 NQ_CONFIG, 0, _GetItemAttr("group")),
1355 (_MakeField("powered", "Powered", QFT_BOOL,
1356 "Whether node is thought to be powered on"),
1357 NQ_OOB, 0, _GetNodePower),
1358 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1359 "Merged node parameters"),
1360 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1361 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1362 "Custom node parameters"),
1363 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1364 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1365 NQ_CONFIG, 0, _GetNodeHvState),
1366 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1367 NQ_CONFIG, 0, _GetNodeDiskState),
1368 ]
1369
1370 fields.extend(_BuildNDFields(False))
1371
1372
1373 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1374 constants.NR_REGULAR, constants.NR_DRAINED,
1375 constants.NR_OFFLINE)
1376 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1377 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1378 role_values)
1379 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1380 lambda ctx, node: _GetNodeRole(node, ctx.master_uuid)))
1381 assert set(role_values) == constants.NR_ALL
1382
1383 def _GetLength(getter):
1384 return lambda ctx, node: len(getter(ctx)[node.uuid])
1385
1386 def _GetList(getter):
1387 return lambda ctx, node: utils.NiceSort(
1388 [ctx.inst_uuid_to_inst_name[uuid]
1389 for uuid in getter(ctx)[node.uuid]])
1390
1391
1392 for prefix, titleprefix, docword, getter in \
1393 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1394 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1395
1396 fields.extend([
1397 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1398 "Number of instances with this node as %s" % docword),
1399 NQ_INST, 0, _GetLength(getter)),
1400 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1401 QFT_OTHER,
1402 "List of instances with this node as %s" % docword),
1403 NQ_INST, 0, _GetList(getter)),
1404 ])
1405
1406
1407 fields.extend([
1408 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1409 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
1410
1411
1412 fields.extend([
1413 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1414 compat.partial(_GetLiveNodeField, nfield, kind))
1415 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
1416
1417
1418 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1419
1420 return _PrepareFieldList(fields, [])
1421
1422
1424 """Data container for instance data queries.
1425
1426 """
1427 - def __init__(self, instances, cluster, disk_usage, offline_node_uuids,
1428 bad_node_uuids, live_data, wrongnode_inst, console, nodes,
1429 groups, networks):
1430 """Initializes this class.
1431
1432 @param instances: List of instance objects
1433 @param cluster: Cluster object
1434 @type disk_usage: dict; instance UUID as key
1435 @param disk_usage: Per-instance disk usage
1436 @type offline_node_uuids: list of strings
1437 @param offline_node_uuids: List of offline nodes
1438 @type bad_node_uuids: list of strings
1439 @param bad_node_uuids: List of faulty nodes
1440 @type live_data: dict; instance UUID as key
1441 @param live_data: Per-instance live data
1442 @type wrongnode_inst: set
1443 @param wrongnode_inst: Set of instances running on wrong node(s)
1444 @type console: dict; instance UUID as key
1445 @param console: Per-instance console information
1446 @type nodes: dict; node UUID as key
1447 @param nodes: Node objects
1448 @type networks: dict; net_uuid as key
1449 @param networks: Network objects
1450
1451 """
1452 assert len(set(bad_node_uuids) & set(offline_node_uuids)) == \
1453 len(offline_node_uuids), \
1454 "Offline nodes not included in bad nodes"
1455 assert not (set(live_data.keys()) & set(bad_node_uuids)), \
1456 "Found live data for bad or offline nodes"
1457
1458 self.instances = instances
1459 self.cluster = cluster
1460 self.disk_usage = disk_usage
1461 self.offline_nodes = offline_node_uuids
1462 self.bad_nodes = bad_node_uuids
1463 self.live_data = live_data
1464 self.wrongnode_inst = wrongnode_inst
1465 self.console = console
1466 self.nodes = nodes
1467 self.groups = groups
1468 self.networks = networks
1469
1470
1471 self.inst_hvparams = None
1472 self.inst_beparams = None
1473 self.inst_osparams = None
1474 self.inst_nicparams = None
1475
1477 """Iterate over all instances.
1478
1479 This function has side-effects and only one instance of the resulting
1480 generator should be used at a time.
1481
1482 """
1483 for inst in self.instances:
1484 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1485 self.inst_beparams = self.cluster.FillBE(inst)
1486 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1487 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1488 for nic in inst.nics]
1489
1490 yield inst
1491
1492
1494 """Get instance's operational status.
1495
1496 @type ctx: L{InstanceQueryData}
1497 @type inst: L{objects.Instance}
1498 @param inst: Instance object
1499
1500 """
1501
1502
1503 if inst.primary_node in ctx.bad_nodes:
1504 return _FS_NODATA
1505 else:
1506 return bool(ctx.live_data.get(inst.uuid))
1507
1508
1510 """Build function for retrieving live data.
1511
1512 @type name: string
1513 @param name: Live data field name
1514
1515 """
1516 def fn(ctx, inst):
1517 """Get live data for an instance.
1518
1519 @type ctx: L{InstanceQueryData}
1520 @type inst: L{objects.Instance}
1521 @param inst: Instance object
1522
1523 """
1524 if (inst.primary_node in ctx.bad_nodes or
1525 inst.primary_node in ctx.offline_nodes):
1526
1527
1528 return _FS_NODATA
1529
1530 if inst.uuid in ctx.live_data:
1531 data = ctx.live_data[inst.uuid]
1532 if name in data:
1533 return data[name]
1534
1535 return _FS_UNAVAIL
1536
1537 return fn
1538
1539
1563
1564
1575
1576
1578 """Get instance status.
1579
1580 @type ctx: L{InstanceQueryData}
1581 @type inst: L{objects.Instance}
1582 @param inst: Instance object
1583
1584 """
1585 if inst.primary_node in ctx.offline_nodes:
1586 return constants.INSTST_NODEOFFLINE
1587
1588 if inst.primary_node in ctx.bad_nodes:
1589 return constants.INSTST_NODEDOWN
1590
1591 instance_live_data = ctx.live_data.get(inst.uuid)
1592
1593 if bool(instance_live_data):
1594 return _GetLiveInstStatus(ctx, inst, instance_live_data["state"])
1595 else:
1596 return _GetDeadInstStatus(inst)
1597
1598
1600 """Build function for calling another function with an instance Disk.
1601
1602 @type index: int
1603 @param index: Disk index
1604 @type cb: callable
1605 @param cb: Callback
1606
1607 """
1608 def fn(ctx, inst):
1609 """Call helper function with instance Disk.
1610
1611 @type ctx: L{InstanceQueryData}
1612 @type inst: L{objects.Instance}
1613 @param inst: Instance object
1614
1615 """
1616 try:
1617 nic = inst.disks[index]
1618 except IndexError:
1619 return _FS_UNAVAIL
1620
1621 return cb(ctx, index, nic)
1622
1623 return fn
1624
1625
1627 """Get a Disk's size.
1628
1629 @type ctx: L{InstanceQueryData}
1630 @type disk: L{objects.Disk}
1631 @param disk: The Disk object
1632
1633 """
1634 if disk.size is None:
1635 return _FS_UNAVAIL
1636 else:
1637 return disk.size
1638
1639
1641 """Get a Disk's spindles.
1642
1643 @type disk: L{objects.Disk}
1644 @param disk: The Disk object
1645
1646 """
1647 if disk.spindles is None:
1648 return _FS_UNAVAIL
1649 else:
1650 return disk.spindles
1651
1652
1654 """Get a Device's Name.
1655
1656 @type ctx: L{InstanceQueryData}
1657 @type device: L{objects.NIC} or L{objects.Disk}
1658 @param device: The NIC or Disk object
1659
1660 """
1661 if device.name is None:
1662 return _FS_UNAVAIL
1663 else:
1664 return device.name
1665
1666
1668 """Get a Device's UUID.
1669
1670 @type ctx: L{InstanceQueryData}
1671 @type device: L{objects.NIC} or L{objects.Disk}
1672 @param device: The NIC or Disk object
1673
1674 """
1675 if device.uuid is None:
1676 return _FS_UNAVAIL
1677 else:
1678 return device.uuid
1679
1680
1682 """Build function for calling another function with an instance NIC.
1683
1684 @type index: int
1685 @param index: NIC index
1686 @type cb: callable
1687 @param cb: Callback
1688
1689 """
1690 def fn(ctx, inst):
1691 """Call helper function with instance NIC.
1692
1693 @type ctx: L{InstanceQueryData}
1694 @type inst: L{objects.Instance}
1695 @param inst: Instance object
1696
1697 """
1698 try:
1699 nic = inst.nics[index]
1700 except IndexError:
1701 return _FS_UNAVAIL
1702
1703 return cb(ctx, index, nic)
1704
1705 return fn
1706
1707
1709 """Get a NIC's Network.
1710
1711 @type ctx: L{InstanceQueryData}
1712 @type nic: L{objects.NIC}
1713 @param nic: NIC object
1714
1715 """
1716 if nic.network is None:
1717 return _FS_UNAVAIL
1718 else:
1719 return ctx.networks[nic.network].name
1720
1721
1723 """Get a NIC's Network.
1724
1725 @type ctx: L{InstanceQueryData}
1726 @type nic: L{objects.NIC}
1727 @param nic: NIC object
1728
1729 """
1730 if nic.network is None:
1731 return _FS_UNAVAIL
1732 else:
1733 return nic.network
1734
1735
1737 """Get a NIC's IP address.
1738
1739 @type ctx: L{InstanceQueryData}
1740 @type nic: L{objects.NIC}
1741 @param nic: NIC object
1742
1743 """
1744 if nic.ip is None:
1745 return _FS_UNAVAIL
1746 else:
1747 return nic.ip
1748
1749
1751 """Get a NIC's bridge.
1752
1753 @type ctx: L{InstanceQueryData}
1754 @type index: int
1755 @param index: NIC index
1756
1757 """
1758 assert len(ctx.inst_nicparams) >= index
1759
1760 nicparams = ctx.inst_nicparams[index]
1761
1762 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1763 return nicparams[constants.NIC_LINK]
1764 else:
1765 return _FS_UNAVAIL
1766
1767
1769 """Get a NIC's VLAN.
1770
1771 @type ctx: L{InstanceQueryData}
1772 @type index: int
1773 @param index: NIC index
1774
1775 """
1776 assert len(ctx.inst_nicparams) >= index
1777
1778 nicparams = ctx.inst_nicparams[index]
1779
1780 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1781 return nicparams[constants.NIC_VLAN]
1782 else:
1783 return _FS_UNAVAIL
1784
1785
1787 """Get all network names for an instance.
1788
1789 @type ctx: L{InstanceQueryData}
1790 @type inst: L{objects.Instance}
1791 @param inst: Instance object
1792
1793 """
1794 result = []
1795
1796 for nic in inst.nics:
1797 name = None
1798 if nic.network:
1799 name = ctx.networks[nic.network].name
1800 result.append(name)
1801
1802 assert len(result) == len(inst.nics)
1803
1804 return result
1805
1806
1808 """Get all network VLANs for an instance.
1809
1810 @type ctx: L{InstanceQueryData}
1811 @type inst: L{objects.Instance}
1812 @param inst: Instance object
1813
1814 """
1815 assert len(ctx.inst_nicparams) == len(inst.nics)
1816
1817 result = []
1818
1819 for nicp in ctx.inst_nicparams:
1820 if nicp[constants.NIC_MODE] in \
1821 [constants.NIC_MODE_BRIDGED, constants.NIC_MODE_OVS]:
1822 result.append(nicp[constants.NIC_VLAN])
1823 else:
1824 result.append(None)
1825
1826 assert len(result) == len(inst.nics)
1827
1828 return result
1829
1830
1832 """Get all network bridges for an instance.
1833
1834 @type ctx: L{InstanceQueryData}
1835 @type inst: L{objects.Instance}
1836 @param inst: Instance object
1837
1838 """
1839 assert len(ctx.inst_nicparams) == len(inst.nics)
1840
1841 result = []
1842
1843 for nicp in ctx.inst_nicparams:
1844 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1845 result.append(nicp[constants.NIC_LINK])
1846 else:
1847 result.append(None)
1848
1849 assert len(result) == len(inst.nics)
1850
1851 return result
1852
1853
1855 """Build function for retrieving a NIC parameter.
1856
1857 @type name: string
1858 @param name: Parameter name
1859
1860 """
1861 def fn(ctx, index, _):
1862 """Get a NIC's bridge.
1863
1864 @type ctx: L{InstanceQueryData}
1865 @type inst: L{objects.Instance}
1866 @param inst: Instance object
1867 @type nic: L{objects.NIC}
1868 @param nic: NIC object
1869
1870 """
1871 assert len(ctx.inst_nicparams) >= index
1872 return ctx.inst_nicparams[index][name]
1873
1874 return fn
1875
1876
1878 """Get instance fields involving network interfaces.
1879
1880 @return: Tuple containing list of field definitions used as input for
1881 L{_PrepareFieldList} and a list of aliases
1882
1883 """
1884 nic_mac_fn = lambda ctx, _, nic: nic.mac
1885 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1886 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1887
1888 fields = [
1889
1890 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1891 "Number of network interfaces"),
1892 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1893 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1894 "List containing each network interface's MAC address"),
1895 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1896 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1897 "List containing each network interface's IP address"),
1898 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1899 (_MakeField("nic.names", "NIC_Names", QFT_OTHER,
1900 "List containing each network interface's name"),
1901 IQ_CONFIG, 0, lambda ctx, inst: [nic.name for nic in inst.nics]),
1902 (_MakeField("nic.uuids", "NIC_UUIDs", QFT_OTHER,
1903 "List containing each network interface's UUID"),
1904 IQ_CONFIG, 0, lambda ctx, inst: [nic.uuid for nic in inst.nics]),
1905 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1906 "List containing each network interface's mode"), IQ_CONFIG, 0,
1907 lambda ctx, inst: [nicp[constants.NIC_MODE]
1908 for nicp in ctx.inst_nicparams]),
1909 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1910 "List containing each network interface's link"), IQ_CONFIG, 0,
1911 lambda ctx, inst: [nicp[constants.NIC_LINK]
1912 for nicp in ctx.inst_nicparams]),
1913 (_MakeField("nic.vlans", "NIC_VLANs", QFT_OTHER,
1914 "List containing each network interface's VLAN"),
1915 IQ_CONFIG, 0, _GetInstAllNicVlans),
1916 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1917 "List containing each network interface's bridge"),
1918 IQ_CONFIG, 0, _GetInstAllNicBridges),
1919 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1920 "List containing each interface's network"), IQ_CONFIG, 0,
1921 lambda ctx, inst: [nic.network for nic in inst.nics]),
1922 (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER,
1923 "List containing each interface's network"),
1924 IQ_NETWORKS, 0, _GetInstAllNicNetworkNames)
1925 ]
1926
1927
1928 for i in range(constants.MAX_NICS):
1929 numtext = utils.FormatOrdinal(i + 1)
1930 fields.extend([
1931 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1932 "IP address of %s network interface" % numtext),
1933 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1934 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1935 "MAC address of %s network interface" % numtext),
1936 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1937 (_MakeField("nic.name/%s" % i, "NicName/%s" % i, QFT_TEXT,
1938 "Name address of %s network interface" % numtext),
1939 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceName)),
1940 (_MakeField("nic.uuid/%s" % i, "NicUUID/%s" % i, QFT_TEXT,
1941 "UUID address of %s network interface" % numtext),
1942 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceUUID)),
1943 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1944 "Mode of %s network interface" % numtext),
1945 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1946 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1947 "Link of %s network interface" % numtext),
1948 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1949 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1950 "Bridge of %s network interface" % numtext),
1951 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1952 (_MakeField("nic.vlan/%s" % i, "NicVLAN/%s" % i, QFT_TEXT,
1953 "VLAN of %s network interface" % numtext),
1954 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicVLan)),
1955 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1956 "Network of %s network interface" % numtext),
1957 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1958 (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT,
1959 "Network name of %s network interface" % numtext),
1960 IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)),
1961 ])
1962
1963 aliases = [
1964
1965 ("ip", "nic.ip/0"),
1966 ("mac", "nic.mac/0"),
1967 ("bridge", "nic.bridge/0"),
1968 ("nic_mode", "nic.mode/0"),
1969 ("nic_link", "nic.link/0"),
1970 ("nic_network", "nic.network/0"),
1971 ]
1972
1973 return (fields, aliases)
1974
1975
1977 """Get disk usage for an instance.
1978
1979 @type ctx: L{InstanceQueryData}
1980 @type inst: L{objects.Instance}
1981 @param inst: Instance object
1982
1983 """
1984 usage = ctx.disk_usage[inst.uuid]
1985
1986 if usage is None:
1987 usage = 0
1988
1989 return usage
1990
1991
1993 """Get console information for instance.
1994
1995 @type ctx: L{InstanceQueryData}
1996 @type inst: L{objects.Instance}
1997 @param inst: Instance object
1998
1999 """
2000 consinfo = ctx.console[inst.uuid]
2001
2002 if consinfo is None:
2003 return _FS_UNAVAIL
2004
2005 return consinfo
2006
2007
2009 """Get instance fields involving disks.
2010
2011 @return: List of field definitions used as input for L{_PrepareFieldList}
2012
2013 """
2014 fields = [
2015 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
2016 "Total disk space used by instance on each of its nodes;"
2017 " this is not the disk size visible to the instance, but"
2018 " the usage on the node"),
2019 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
2020 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
2021 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
2022 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
2023 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
2024 (_MakeField("disk.spindles", "Disk_spindles", QFT_OTHER,
2025 "List of disk spindles"),
2026 IQ_CONFIG, 0, lambda ctx, inst: [disk.spindles for disk in inst.disks]),
2027 (_MakeField("disk.names", "Disk_names", QFT_OTHER, "List of disk names"),
2028 IQ_CONFIG, 0, lambda ctx, inst: [disk.name for disk in inst.disks]),
2029 (_MakeField("disk.uuids", "Disk_UUIDs", QFT_OTHER, "List of disk UUIDs"),
2030 IQ_CONFIG, 0, lambda ctx, inst: [disk.uuid for disk in inst.disks]),
2031 ]
2032
2033
2034 for i in range(constants.MAX_DISKS):
2035 numtext = utils.FormatOrdinal(i + 1)
2036 fields.extend([
2037 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
2038 "Disk size of %s disk" % numtext),
2039 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSize)),
2040 (_MakeField("disk.spindles/%s" % i, "DiskSpindles/%s" % i, QFT_NUMBER,
2041 "Spindles of %s disk" % numtext),
2042 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSpindles)),
2043 (_MakeField("disk.name/%s" % i, "DiskName/%s" % i, QFT_TEXT,
2044 "Name of %s disk" % numtext),
2045 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceName)),
2046 (_MakeField("disk.uuid/%s" % i, "DiskUUID/%s" % i, QFT_TEXT,
2047 "UUID of %s disk" % numtext),
2048 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceUUID))])
2049
2050 return fields
2051
2052
2054 """Get instance fields involving parameters.
2055
2056 @return: List of field definitions used as input for L{_PrepareFieldList}
2057
2058 """
2059 fields = [
2060
2061 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
2062 "Hypervisor parameters (merged)"),
2063 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
2064 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
2065 "Backend parameters (merged)"),
2066 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
2067 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
2068 "Operating system parameters (merged)"),
2069 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
2070
2071
2072 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
2073 "Custom hypervisor parameters"),
2074 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
2075 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
2076 "Custom backend parameters",),
2077 IQ_CONFIG, 0, _GetItemAttr("beparams")),
2078 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
2079 "Custom operating system parameters",),
2080 IQ_CONFIG, 0, _GetItemAttr("osparams")),
2081 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
2082 "Custom network interface parameters"),
2083 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
2084 ]
2085
2086
2087 def _GetInstHvParam(name):
2088 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
2089
2090 fields.extend([
2091 (_MakeField("hv/%s" % name,
2092 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
2093 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
2094 IQ_CONFIG, 0, _GetInstHvParam(name))
2095 for name, kind in constants.HVS_PARAMETER_TYPES.items()
2096 if name not in constants.HVC_GLOBALS])
2097
2098
2099 def _GetInstBeParam(name):
2100 return lambda ctx, _: ctx.inst_beparams.get(name, None)
2101
2102 fields.extend([
2103 (_MakeField("be/%s" % name,
2104 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
2105 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
2106 IQ_CONFIG, 0, _GetInstBeParam(name))
2107 for name, kind in constants.BES_PARAMETER_TYPES.items()])
2108
2109 return fields
2110
2111
2112 _INST_SIMPLE_FIELDS = {
2113 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
2114 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
2115 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
2116
2117 "network_port": ("Network_port", QFT_OTHER, 0,
2118 "Instance network port if available (e.g. for VNC console)"),
2119 "os": ("OS", QFT_TEXT, 0, "Operating system"),
2120 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
2121 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
2122 }
2123
2124
2126 """Gets node name of a node.
2127
2128 @type ctx: L{InstanceQueryData}
2129 @param default: Default value
2130 @type node_uuid: string
2131 @param node_uuid: Node UUID
2132
2133 """
2134 try:
2135 node = ctx.nodes[node_uuid]
2136 except KeyError:
2137 return default
2138 else:
2139 return node.name
2140
2141
2143 """Gets group UUID of an instance node.
2144
2145 @type ctx: L{InstanceQueryData}
2146 @param default: Default value
2147 @type node_uuid: string
2148 @param node_uuid: Node UUID
2149
2150 """
2151 try:
2152 node = ctx.nodes[node_uuid]
2153 except KeyError:
2154 return default
2155 else:
2156 return node.group
2157
2158
2160 """Gets group name of an instance node.
2161
2162 @type ctx: L{InstanceQueryData}
2163 @param default: Default value
2164 @type node_uuid: string
2165 @param node_uuid: Node UUID
2166
2167 """
2168 try:
2169 node = ctx.nodes[node_uuid]
2170 except KeyError:
2171 return default
2172
2173 try:
2174 group = ctx.groups[node.group]
2175 except KeyError:
2176 return default
2177
2178 return group.name
2179
2180
2182 """Builds list of fields for instance queries.
2183
2184 """
2185 fields = [
2186 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
2187 IQ_NODES, QFF_HOSTNAME,
2188 lambda ctx, inst: _GetNodeName(ctx, None, inst.primary_node)),
2189 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
2190 "Primary node's group"),
2191 IQ_NODES, 0,
2192 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
2193 inst.primary_node)),
2194 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
2195 "Primary node's group UUID"),
2196 IQ_NODES, 0,
2197 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
2198
2199 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
2200 "Secondary nodes; usually this will just be one node"),
2201 IQ_NODES, 0,
2202 lambda ctx, inst: map(compat.partial(_GetNodeName, ctx, None),
2203 inst.secondary_nodes)),
2204 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
2205 "Node groups of secondary nodes"),
2206 IQ_NODES, 0,
2207 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
2208 inst.secondary_nodes)),
2209 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
2210 "Node group UUIDs of secondary nodes"),
2211 IQ_NODES, 0,
2212 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
2213 inst.secondary_nodes)),
2214 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
2215 "Desired state of the instance"),
2216 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
2217 (_MakeField("admin_up", "Autostart", QFT_BOOL,
2218 "Desired state of the instance"),
2219 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
2220 (_MakeField("admin_state_source", "InstanceStateSource", QFT_TEXT,
2221 "Who last changed the desired state of the instance"),
2222 IQ_CONFIG, 0, _GetItemAttr("admin_state_source")),
2223 (_MakeField("disks_active", "DisksActive", QFT_BOOL,
2224 "Desired state of the instance disks"),
2225 IQ_CONFIG, 0, _GetItemAttr("disks_active")),
2226 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2227 lambda ctx, inst: list(inst.GetTags())),
2228 (_MakeField("console", "Console", QFT_OTHER,
2229 "Instance console information"), IQ_CONSOLE, 0,
2230 _GetInstanceConsole),
2231 (_MakeField("forthcoming", "Forthcoming", QFT_BOOL,
2232 "Whether the Instance is forthcoming"), IQ_CONFIG, 0,
2233 lambda _, inst: bool(inst.forthcoming)),
2234 ]
2235
2236
2237 fields.extend([
2238 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
2239 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
2240
2241
2242 fields.extend([
2243 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
2244 IQ_LIVE, 0, _GetInstOperState),
2245 (_MakeField("oper_ram", "Memory", QFT_UNIT,
2246 "Actual memory usage as seen by hypervisor"),
2247 IQ_LIVE, 0, _GetInstLiveData("memory")),
2248 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
2249 "Actual number of VCPUs as seen by hypervisor"),
2250 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
2251 ])
2252
2253
2254 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
2255 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
2256 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
2257 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE,
2258 constants.INSTST_USERDOWN)
2259 status_doc = ("Instance status; \"%s\" if instance is set to be running"
2260 " and actually is, \"%s\" if instance is stopped and"
2261 " is not running, \"%s\" if instance running, but not on its"
2262 " designated primary node, \"%s\" if instance should be"
2263 " stopped, but is actually running, \"%s\" if instance should"
2264 " run, but doesn't, \"%s\" if instance's primary node is down,"
2265 " \"%s\" if instance's primary node is marked offline,"
2266 " \"%s\" if instance is offline and does not use dynamic,"
2267 " \"%s\" if the user shutdown the instance"
2268 " resources" % status_values)
2269 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
2270 IQ_LIVE, 0, _GetInstStatus))
2271
2272 assert set(status_values) == constants.INSTST_ALL, \
2273 "Status documentation mismatch"
2274
2275 (network_fields, network_aliases) = _GetInstanceNetworkFields()
2276
2277 fields.extend(network_fields)
2278 fields.extend(_GetInstanceParameterFields())
2279 fields.extend(_GetInstanceDiskFields())
2280 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
2281
2282 aliases = [
2283 ("vcpus", "be/vcpus"),
2284 ("be/memory", "be/maxmem"),
2285 ("sda_size", "disk.size/0"),
2286 ("sdb_size", "disk.size/1"),
2287 ] + network_aliases
2288
2289 return _PrepareFieldList(fields, aliases)
2290
2291
2293 """Data container for lock data queries.
2294
2295 """
2297 """Initializes this class.
2298
2299 """
2300 self.lockdata = lockdata
2301
2303 """Iterate over all locks.
2304
2305 """
2306 return iter(self.lockdata)
2307
2308
2310 """Returns a sorted list of a lock's current owners.
2311
2312 """
2313 (_, _, owners, _) = data
2314
2315 if owners:
2316 owners = utils.NiceSort(owners)
2317
2318 return owners
2319
2320
2322 """Returns a sorted list of a lock's pending acquires.
2323
2324 """
2325 (_, _, _, pending) = data
2326
2327 if pending:
2328 pending = [(mode, utils.NiceSort(names))
2329 for (mode, names) in pending]
2330
2331 return pending
2332
2333
2335 """Builds list of fields for lock queries.
2336
2337 """
2338 return _PrepareFieldList([
2339
2340 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2341 lambda ctx, (name, mode, owners, pending): name),
2342 (_MakeField("mode", "Mode", QFT_OTHER,
2343 "Mode in which the lock is currently acquired"
2344 " (exclusive or shared)"),
2345 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2346 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2347 LQ_OWNER, 0, _GetLockOwners),
2348 (_MakeField("pending", "Pending", QFT_OTHER,
2349 "Threads waiting for the lock"),
2350 LQ_PENDING, 0, _GetLockPending),
2351 ], [])
2352
2353
2355 """Data container for node group data queries.
2356
2357 """
2358 - def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2359 want_diskparams):
2360 """Initializes this class.
2361
2362 @param cluster: Cluster object
2363 @param groups: List of node group objects
2364 @type group_to_nodes: dict; group UUID as key
2365 @param group_to_nodes: Per-group list of nodes
2366 @type group_to_instances: dict; group UUID as key
2367 @param group_to_instances: Per-group list of (primary) instances
2368 @type want_diskparams: bool
2369 @param want_diskparams: Whether diskparamters should be calculated
2370
2371 """
2372 self.groups = groups
2373 self.group_to_nodes = group_to_nodes
2374 self.group_to_instances = group_to_instances
2375 self.cluster = cluster
2376 self.want_diskparams = want_diskparams
2377
2378
2379 self.group_ipolicy = None
2380 self.ndparams = None
2381 self.group_dp = None
2382
2384 """Iterate over all node groups.
2385
2386 This function has side-effects and only one instance of the resulting
2387 generator should be used at a time.
2388
2389 """
2390 for group in self.groups:
2391 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2392 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2393 if self.want_diskparams:
2394 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2395 else:
2396 self.group_dp = None
2397 yield group
2398
2399
2400 _GROUP_SIMPLE_FIELDS = {
2401 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2402 "name": ("Group", QFT_TEXT, "Group name"),
2403 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2404 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2405 }
2406
2407
2409 """Builds list of fields for node group queries.
2410
2411 """
2412
2413 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2414 _GetItemAttr(name))
2415 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2416
2417 def _GetLength(getter):
2418 return lambda ctx, group: len(getter(ctx)[group.uuid])
2419
2420 def _GetSortedList(getter):
2421 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2422
2423 group_to_nodes = operator.attrgetter("group_to_nodes")
2424 group_to_instances = operator.attrgetter("group_to_instances")
2425
2426
2427 fields.extend([
2428 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2429 GQ_NODE, 0, _GetLength(group_to_nodes)),
2430 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2431 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2432 ])
2433
2434
2435 fields.extend([
2436 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2437 "Number of primary instances"),
2438 GQ_INST, 0, _GetLength(group_to_instances)),
2439 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2440 "List of primary instances"),
2441 GQ_INST, 0, _GetSortedList(group_to_instances)),
2442 ])
2443
2444
2445 fields.extend([
2446 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2447 lambda ctx, group: list(group.GetTags())),
2448 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2449 "Instance policy limitations (merged)"),
2450 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2451 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2452 "Custom instance policy limitations"),
2453 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2454 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2455 "Custom node parameters"),
2456 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2457 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2458 "Node parameters"),
2459 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2460 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2461 "Disk parameters (merged)"),
2462 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2463 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2464 "Custom disk parameters"),
2465 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2466 ])
2467
2468
2469 fields.extend(_BuildNDFields(True))
2470
2471 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2472
2473 return _PrepareFieldList(fields, [])
2474
2475
2476 -class OsInfo(objects.ConfigObject):
2477 __slots__ = [
2478 "name",
2479 "valid",
2480 "hidden",
2481 "blacklisted",
2482 "variants",
2483 "api_versions",
2484 "parameters",
2485 "node_status",
2486 "os_hvp",
2487 "osparams",
2488 "trusted"
2489 ]
2490
2491
2493 """Builds list of fields for operating system queries.
2494
2495 """
2496 fields = [
2497 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2498 None, 0, _GetItemAttr("name")),
2499 (_MakeField("valid", "Valid", QFT_BOOL,
2500 "Whether operating system definition is valid"),
2501 None, 0, _GetItemAttr("valid")),
2502 (_MakeField("hidden", "Hidden", QFT_BOOL,
2503 "Whether operating system is hidden"),
2504 None, 0, _GetItemAttr("hidden")),
2505 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2506 "Whether operating system is blacklisted"),
2507 None, 0, _GetItemAttr("blacklisted")),
2508 (_MakeField("variants", "Variants", QFT_OTHER,
2509 "Operating system variants"),
2510 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2511 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2512 "Operating system API versions"),
2513 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2514 (_MakeField("parameters", "Parameters", QFT_OTHER,
2515 "Operating system parameters"),
2516 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2517 _GetItemAttr("parameters"))),
2518 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2519 "Status from node"),
2520 None, 0, _GetItemAttr("node_status")),
2521 (_MakeField("os_hvp", "OsHypervisorParams", QFT_OTHER,
2522 "Operating system specific hypervisor parameters"),
2523 None, 0, _GetItemAttr("os_hvp")),
2524 (_MakeField("osparams", "OsParameters", QFT_OTHER,
2525 "Operating system specific parameters"),
2526 None, 0, _GetItemAttr("osparams")),
2527 (_MakeField("trusted", "Trusted", QFT_BOOL,
2528 "Whether this OS is trusted"),
2529 None, 0, _GetItemAttr("trusted")),
2530 ]
2531
2532 return _PrepareFieldList(fields, [])
2533
2534
2536 __slots__ = [
2537 "name",
2538 "node_status",
2539 "nodegroup_status",
2540 "parameters",
2541 ]
2542
2543
2545 """Builds list of fields for extstorage provider queries.
2546
2547 """
2548 fields = [
2549 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2550 None, 0, _GetItemAttr("name")),
2551 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2552 "Status from node"),
2553 None, 0, _GetItemAttr("node_status")),
2554 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2555 "Overall Nodegroup status"),
2556 None, 0, _GetItemAttr("nodegroup_status")),
2557 (_MakeField("parameters", "Parameters", QFT_OTHER,
2558 "ExtStorage provider parameters"),
2559 None, 0, _GetItemAttr("parameters")),
2560 ]
2561
2562 return _PrepareFieldList(fields, [])
2563
2564
2566 """Return L{_FS_UNAVAIL} if job is None.
2567
2568 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2569 found, in which case this function converts it to L{_FS_UNAVAIL}.
2570
2571 """
2572 if job is None:
2573 return _FS_UNAVAIL
2574 else:
2575 return fn(job)
2576
2577
2583
2584
2586 """Executes a function per opcode in a job.
2587
2588 """
2589 return map(fn, job.ops)
2590
2591
2597
2598
2600 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2601
2602 """
2603 timestamp = fn(job)
2604
2605 if timestamp is None:
2606 return _FS_UNAVAIL
2607 else:
2608 return timestamp
2609
2610
2616
2617
2619 """Builds list of fields for job queries.
2620
2621 """
2622 fields = [
2623 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2624 None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2625 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2626 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2627 (_MakeField("priority", "Priority", QFT_NUMBER,
2628 ("Current job priority (%s to %s)" %
2629 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2630 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2631 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2632 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2633 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2634 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2635 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2636 "List of opcodes results"),
2637 None, 0, _PerJobOp(operator.attrgetter("result"))),
2638 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2639 "List of opcodes status"),
2640 None, 0, _PerJobOp(operator.attrgetter("status"))),
2641 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2642 "List of opcode output logs"),
2643 None, 0, _PerJobOp(operator.attrgetter("log"))),
2644 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2645 "List of opcode start timestamps (before acquiring locks)"),
2646 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2647 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2648 "List of opcode execution start timestamps (after acquiring"
2649 " locks)"),
2650 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2651 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2652 "List of opcode execution end timestamps"),
2653 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2654 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2655 "List of opcode priorities"),
2656 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2657 (_MakeField("summary", "Summary", QFT_OTHER,
2658 "List of per-opcode summaries"),
2659 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2660 ]
2661
2662
2663 for (name, attr, title, desc) in [
2664 ("received_ts", "received_timestamp", "Received",
2665 "Timestamp of when job was received"),
2666 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2667 ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2668 ]:
2669 getter = operator.attrgetter(attr)
2670 fields.extend([
2671 (_MakeField(name, title, QFT_OTHER,
2672 "%s (tuple containing seconds and microseconds)" % desc),
2673 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2674 ])
2675
2676 return _PrepareFieldList(fields, [])
2677
2678
2680 """Returns an export name if available.
2681
2682 """
2683 if expname is None:
2684 return _FS_NODATA
2685 else:
2686 return expname
2687
2688
2690 """Builds list of fields for exports.
2691
2692 """
2693 fields = [
2694 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2695 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2696 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2697 None, 0, _GetExportName),
2698 ]
2699
2700 return _PrepareFieldList(fields, [])
2701
2702
2703 _CLUSTER_VERSION_FIELDS = {
2704 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2705 "Software version"),
2706 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2707 constants.PROTOCOL_VERSION,
2708 "RPC protocol version"),
2709 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2710 "Configuration format version"),
2711 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2712 "API version for OS template scripts"),
2713 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2714 "Import/export file format version"),
2715 "vcs_version": ("VCSVersion", QFT_TEXT, constants.VCS_VERSION,
2716 "VCS version"),
2717 }
2718
2719
2720 _CLUSTER_SIMPLE_FIELDS = {
2721 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2722 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2723 }
2724
2725
2727 - def __init__(self, cluster, nodes, drain_flag, watcher_pause):
2728 """Initializes this class.
2729
2730 @type cluster: L{objects.Cluster}
2731 @param cluster: Instance of cluster object
2732 @type nodes: dict; node UUID as key
2733 @param nodes: Node objects
2734 @type drain_flag: bool
2735 @param drain_flag: Whether job queue is drained
2736 @type watcher_pause: number
2737 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2738
2739 """
2740 self._cluster = cluster
2741 self.nodes = nodes
2742 self.drain_flag = drain_flag
2743 self.watcher_pause = watcher_pause
2744
2746 return iter([self._cluster])
2747
2748
2750 """Returns until when watcher is paused (if available).
2751
2752 """
2753 if ctx.watcher_pause is None:
2754 return _FS_UNAVAIL
2755 else:
2756 return ctx.watcher_pause
2757
2758
2760 """Builds list of fields for cluster information.
2761
2762 """
2763 fields = [
2764 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2765 lambda ctx, cluster: list(cluster.GetTags())),
2766 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2767 "Architecture information"), None, 0,
2768 lambda ctx, _: runtime.GetArchInfo()),
2769 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2770 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2771 lambda ctx, _: ctx.drain_flag),
2772 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2773 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2774 _ClusterWatcherPause),
2775 (_MakeField("master_node", "Master", QFT_TEXT, "Master node name"),
2776 CQ_CONFIG, QFF_HOSTNAME,
2777 lambda ctx, cluster: _GetNodeName(ctx, None, cluster.master_node)),
2778 ]
2779
2780
2781 fields.extend([
2782 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2783 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2784 ],)
2785
2786
2787 fields.extend([
2788 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2789 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
2790
2791
2792 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2793
2794 return _PrepareFieldList(fields, [
2795 ("name", "cluster_name")])
2796
2797
2799 """Data container for network data queries.
2800
2801 """
2802 - def __init__(self, networks, network_to_groups,
2803 network_to_instances, stats):
2804 """Initializes this class.
2805
2806 @param networks: List of network objects
2807 @type network_to_groups: dict; network UUID as key
2808 @param network_to_groups: Per-network list of groups
2809 @type network_to_instances: dict; network UUID as key
2810 @param network_to_instances: Per-network list of instances
2811 @type stats: dict; network UUID as key
2812 @param stats: Per-network usage statistics
2813
2814 """
2815 self.networks = networks
2816 self.network_to_groups = network_to_groups
2817 self.network_to_instances = network_to_instances
2818 self.stats = stats
2819
2821 """Iterate over all networks.
2822
2823 """
2824 for net in self.networks:
2825 if self.stats:
2826 self.curstats = self.stats.get(net.uuid, None)
2827 else:
2828 self.curstats = None
2829 yield net
2830
2831
2832 _NETWORK_SIMPLE_FIELDS = {
2833 "name": ("Network", QFT_TEXT, 0, "Name"),
2834 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"),
2835 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"),
2836 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"),
2837 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"),
2838 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"),
2839 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
2840 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2841 }
2842
2843
2844 _NETWORK_STATS_FIELDS = {
2845 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"),
2846 "reserved_count":
2847 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"),
2848 "map": ("Map", QFT_TEXT, 0, "Actual mapping"),
2849 "external_reservations":
2850 ("ExternalReservations", QFT_TEXT, 0, "External reservations"),
2851 }
2852
2853
2855 """Gets the value of a "stats" field from L{NetworkQueryData}.
2856
2857 @param field: Field name
2858 @param kind: Data kind, one of L{constants.QFT_ALL}
2859 @type ctx: L{NetworkQueryData}
2860
2861 """
2862 return _GetStatsField(field, kind, ctx.curstats)
2863
2864
2866 """Builds list of fields for network queries.
2867
2868 """
2869 fields = [
2870 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2871 lambda ctx, inst: list(inst.GetTags())),
2872 ]
2873
2874
2875 fields.extend([
2876 (_MakeField(name, title, kind, doc),
2877 NETQ_CONFIG, 0, _GetItemMaybeAttr(name))
2878 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
2879
2880 def _GetLength(getter):
2881 return lambda ctx, network: len(getter(ctx)[network.uuid])
2882
2883 def _GetSortedList(getter):
2884 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2885
2886 network_to_groups = operator.attrgetter("network_to_groups")
2887 network_to_instances = operator.attrgetter("network_to_instances")
2888
2889
2890 fields.extend([
2891 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2892 NETQ_GROUP, 0, _GetLength(network_to_groups)),
2893 (_MakeField("group_list", "GroupList", QFT_OTHER,
2894 "List of nodegroups (group name, NIC mode, NIC link)"),
2895 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]),
2896 ])
2897
2898
2899 fields.extend([
2900 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2901 NETQ_INST, 0, _GetLength(network_to_instances)),
2902 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2903 NETQ_INST, 0, _GetSortedList(network_to_instances)),
2904 ])
2905
2906
2907 fields.extend([
2908 (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2909 compat.partial(_GetNetworkStatsField, name, kind))
2910 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
2911
2912
2913 fields.extend(_GetItemTimestampFields(IQ_NETWORKS))
2914
2915 return _PrepareFieldList(fields, [])
2916
2917
2918 _FILTER_SIMPLE_FIELDS = {
2919 "watermark": ("Watermark", QFT_NUMBER, 0, "Watermark"),
2920 "priority": ("Priority", QFT_NUMBER, 0, "Priority"),
2921 "predicates": ("Predicates", QFT_OTHER, 0, "Predicates"),
2922 "action": ("Action", QFT_OTHER, 0, "Action"),
2923 "reason_trail": ("ReasonTrail", QFT_OTHER, 0, "Reason trail"),
2924 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2925 }
2926
2927
2929 """Builds list of fields for job filter queries.
2930
2931 """
2932 fields = [
2933 (_MakeField(name, title, kind, doc), None, 0, _GetItemMaybeAttr(name))
2934 for (name, (title, kind, _, doc)) in _FILTER_SIMPLE_FIELDS.items()
2935 ]
2936
2937 return _PrepareFieldList(fields, [])
2938
2939
2940 CLUSTER_FIELDS = _BuildClusterFields()
2941
2942
2943 NODE_FIELDS = _BuildNodeFields()
2944
2945
2946 INSTANCE_FIELDS = _BuildInstanceFields()
2947
2948
2949 LOCK_FIELDS = _BuildLockFields()
2950
2951
2952 GROUP_FIELDS = _BuildGroupFields()
2953
2954
2955 OS_FIELDS = _BuildOsFields()
2956
2957
2958 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2959
2960
2961 JOB_FIELDS = _BuildJobFields()
2962
2963
2964 EXPORT_FIELDS = _BuildExportFields()
2965
2966
2967 NETWORK_FIELDS = _BuildNetworkFields()
2968
2969
2970 FILTER_FIELDS = _BuildFilterFields()
2971
2972
2973 ALL_FIELDS = {
2974 constants.QR_CLUSTER: CLUSTER_FIELDS,
2975 constants.QR_INSTANCE: INSTANCE_FIELDS,
2976 constants.QR_NODE: NODE_FIELDS,
2977 constants.QR_LOCK: LOCK_FIELDS,
2978 constants.QR_GROUP: GROUP_FIELDS,
2979 constants.QR_OS: OS_FIELDS,
2980 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2981 constants.QR_JOB: JOB_FIELDS,
2982 constants.QR_EXPORT: EXPORT_FIELDS,
2983 constants.QR_NETWORK: NETWORK_FIELDS,
2984 constants.QR_FILTER: FILTER_FIELDS,
2985 }
2986
2987
2988 ALL_FIELD_LISTS = ALL_FIELDS.values()
2989