1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Module for query operations
23
24 How it works:
25
26 - Add field definitions
27 - See how L{NODE_FIELDS} is built
28 - Each field gets:
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
33 L{TITLE_RE}
34 - Value data type, e.g. L{constants.QFT_NUMBER}
35 - Human-readable description, must not end with punctuation or
36 contain newlines
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
41 checks
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
45 result
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
49
50 @attention: Retrieval functions must be idempotent. They can be called multiple
51 times, in any order and any number of times.
52
53 """
54
55 import logging
56 import operator
57 import re
58
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
64 from ganeti import ht
65 from ganeti import runtime
66 from ganeti import qlang
67
68 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
69 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
70 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
71 RS_UNAVAIL, RS_OFFLINE)
72
73
74
75
76
77
78 (NQ_CONFIG,
79 NQ_INST,
80 NQ_LIVE,
81 NQ_GROUP,
82 NQ_OOB) = range(1, 6)
83
84 (IQ_CONFIG,
85 IQ_LIVE,
86 IQ_DISKUSAGE,
87 IQ_CONSOLE,
88 IQ_NODES) = range(100, 105)
89
90 (LQ_MODE,
91 LQ_OWNER,
92 LQ_PENDING) = range(10, 13)
93
94 (GQ_CONFIG,
95 GQ_NODE,
96 GQ_INST,
97 GQ_DISKPARAMS) = range(200, 204)
98
99 (CQ_CONFIG,
100 CQ_QUEUE_DRAINED,
101 CQ_WATCHER_PAUSE) = range(300, 303)
102
103
104 QFF_HOSTNAME = 0x01
105 QFF_IP_ADDRESS = 0x02
106
107 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS)
108
109 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
110 TITLE_RE = re.compile(r"^[^\s]+$")
111 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
112
113
114 _VERIFY_FN = {
115 QFT_UNKNOWN: ht.TNone,
116 QFT_TEXT: ht.TString,
117 QFT_BOOL: ht.TBool,
118 QFT_NUMBER: ht.TInt,
119 QFT_UNIT: ht.TInt,
120 QFT_TIMESTAMP: ht.TNumber,
121 QFT_OTHER: lambda _: True,
122 }
123
124
125 _FS_UNKNOWN = object()
126 _FS_NODATA = object()
127 _FS_UNAVAIL = object()
128 _FS_OFFLINE = object()
129
130
131 _FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE])
132
133
134 _VTToQFT = {
135
136 constants.VTYPE_STRING: QFT_OTHER,
137 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
138 constants.VTYPE_BOOL: QFT_BOOL,
139 constants.VTYPE_SIZE: QFT_UNIT,
140 constants.VTYPE_INT: QFT_NUMBER,
141 }
142
143 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
144
145
146 NDP_TITLE = {
147 constants.ND_OOB_PROGRAM: "OutOfBandProgram",
148 constants.ND_SPINDLE_COUNT: "SpindleCount",
149 }
150
151
153 """Gets the contents of an unknown field.
154
155 """
156 return _FS_UNKNOWN
157
158
160 """Calculates the internal list of selected fields.
161
162 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
163
164 @type fielddefs: dict
165 @param fielddefs: Field definitions
166 @type selected: list of strings
167 @param selected: List of selected fields
168
169 """
170 result = []
171
172 for name in selected:
173 try:
174 fdef = fielddefs[name]
175 except KeyError:
176 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
177 None, 0, _GetUnknownField)
178
179 assert len(fdef) == 4
180
181 result.append(fdef)
182
183 return result
184
185
187 """Extract L{objects.QueryFieldDefinition} from field definitions.
188
189 @rtype: list of L{objects.QueryFieldDefinition}
190
191 """
192 return [fdef for (fdef, _, _, _) in fielddefs]
193
194
196 """Class for filter analytics.
197
198 When filters are used, the user of the L{Query} class usually doesn't know
199 exactly which items will be necessary for building the result. It therefore
200 has to prepare and compute the input data for potentially returning
201 everything.
202
203 There are two ways to optimize this. The first, and simpler, is to assign
204 each field a group of data, so that the caller can determine which
205 computations are necessary depending on the data groups requested. The list
206 of referenced groups must also be computed for fields referenced in the
207 filter.
208
209 The second is restricting the items based on a primary key. The primary key
210 is usually a unique name (e.g. a node name). This class extracts all
211 referenced names from a filter. If it encounters any filter condition which
212 disallows such a list to be determined (e.g. a non-equality filter), all
213 names will be requested.
214
215 The end-effect is that any operation other than L{qlang.OP_OR} and
216 L{qlang.OP_EQUAL} will make the query more expensive.
217
218 """
220 """Initializes this class.
221
222 @type namefield: string
223 @param namefield: Field caller is interested in
224
225 """
226 self._namefield = namefield
227
228
229
230 self._allnames = False
231
232
233 self._names = None
234
235
236 self._datakinds = set()
237
239 """Returns all requested values.
240
241 Returns C{None} if list of values can't be determined (e.g. encountered
242 non-equality operators).
243
244 @rtype: list
245
246 """
247 if self._allnames or self._names is None:
248 return None
249
250 return utils.UniqueSequence(self._names)
251
253 """Returns all kinds of data referenced by the filter.
254
255 """
256 return frozenset(self._datakinds)
257
259 """Changes internal state to request all names.
260
261 """
262 self._allnames = True
263 self._names = None
264
266 """Called when handling a logic operation.
267
268 @type op: string
269 @param op: Operator
270
271 """
272 if op != qlang.OP_OR:
273 self._NeedAllNames()
274
276 """Called when handling an unary operation.
277
278 @type op: string
279 @param op: Operator
280
281 """
282 self._NeedAllNames()
283
285 """Called when handling a binary operation.
286
287 @type op: string
288 @param op: Operator
289 @type name: string
290 @param name: Left-hand side of operator (field name)
291 @param value: Right-hand side of operator
292
293 """
294 if datakind is not None:
295 self._datakinds.add(datakind)
296
297 if self._allnames:
298 return
299
300
301
302 if op == qlang.OP_EQUAL and name == self._namefield:
303 if self._names is None:
304 self._names = []
305 self._names.append(value)
306 else:
307 self._NeedAllNames()
308
309
311 """Wrapper for logic operator functions.
312
313 """
314 return op_fn(fn(ctx, item) for fn in sentences)
315
316
318 """Wrapper for unary operator functions.
319
320 """
321 return op_fn(inner(ctx, item))
322
323
325 """Wrapper for binary operator functions.
326
327 """
328 return op_fn(retrieval_fn(ctx, item), value)
329
330
332 """Negates the result of a wrapped function.
333
334 """
335 return not fn(lhs, rhs)
336
337
339 """Compiles a regular expression.
340
341 """
342 try:
343 return re.compile(pattern)
344 except re.error, err:
345 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
346
347
349 """Converts a query filter to a callable usable for filtering.
350
351 """
352
353
354
355 _LEVELS_MAX = 10
356
357
358 (_OPTYPE_LOGIC,
359 _OPTYPE_UNARY,
360 _OPTYPE_BINARY) = range(1, 4)
361
362 """Functions for equality checks depending on field flags.
363
364 List of tuples containing flags and a callable receiving the left- and
365 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
366 (e.g. L{QFF_HOSTNAME}).
367
368 Order matters. The first item with flags will be used. Flags are checked
369 using binary AND.
370
371 """
372 _EQUALITY_CHECKS = [
373 (QFF_HOSTNAME,
374 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
375 case_sensitive=False),
376 None),
377 (None, operator.eq, None),
378 ]
379
380 """Known operators
381
382 Operator as key (C{qlang.OP_*}), value a tuple of operator group
383 (C{_OPTYPE_*}) and a group-specific value:
384
385 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
386 L{_HandleLogicOp}
387 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
388 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
389 right-hand side of the operator, used by L{_HandleBinaryOp}
390
391 """
392 _OPS = {
393
394 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
395 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
396
397
398 qlang.OP_NOT: (_OPTYPE_UNARY, None),
399 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
400
401
402 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
403 qlang.OP_NOT_EQUAL:
404 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
405 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
406 qlang.OP_LT: (_OPTYPE_BINARY, [
407 (None, operator.lt, None),
408 ]),
409 qlang.OP_GT: (_OPTYPE_BINARY, [
410 (None, operator.gt, None),
411 ]),
412 qlang.OP_LE: (_OPTYPE_BINARY, [
413 (None, operator.le, None),
414 ]),
415 qlang.OP_GE: (_OPTYPE_BINARY, [
416 (None, operator.ge, None),
417 ]),
418 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
419 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
420 ]),
421 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
422 (None, operator.contains, None),
423 ]),
424 }
425
427 """Initializes this class.
428
429 @param fields: Field definitions (return value of L{_PrepareFieldList})
430
431 """
432 self._fields = fields
433 self._hints = None
434 self._op_handler = None
435
437 """Converts a query filter into a callable function.
438
439 @type hints: L{_FilterHints} or None
440 @param hints: Callbacks doing analysis on filter
441 @type qfilter: list
442 @param qfilter: Filter structure
443 @rtype: callable
444 @return: Function receiving context and item as parameters, returning
445 boolean as to whether item matches filter
446
447 """
448 self._op_handler = {
449 self._OPTYPE_LOGIC:
450 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
451 self._OPTYPE_UNARY:
452 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
453 self._OPTYPE_BINARY:
454 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
455 }
456
457 try:
458 filter_fn = self._Compile(qfilter, 0)
459 finally:
460 self._op_handler = None
461
462 return filter_fn
463
465 """Inner function for converting filters.
466
467 Calls the correct handler functions for the top-level operator. This
468 function is called recursively (e.g. for logic operators).
469
470 """
471 if not (isinstance(qfilter, (list, tuple)) and qfilter):
472 raise errors.ParameterError("Invalid filter on level %s" % level)
473
474
475 if level >= self._LEVELS_MAX:
476 raise errors.ParameterError("Only up to %s levels are allowed (filter"
477 " nested too deep)" % self._LEVELS_MAX)
478
479
480 operands = qfilter[:]
481 op = operands.pop(0)
482
483 try:
484 (kind, op_data) = self._OPS[op]
485 except KeyError:
486 raise errors.ParameterError("Unknown operator '%s'" % op)
487
488 (handler, hints_cb) = self._op_handler[kind]
489
490 return handler(hints_cb, level, op, op_data, operands)
491
493 """Returns a field definition by name.
494
495 """
496 try:
497 return self._fields[name]
498 except KeyError:
499 raise errors.ParameterError("Unknown field '%s'" % name)
500
502 """Handles logic operators.
503
504 @type hints_fn: callable
505 @param hints_fn: Callback doing some analysis on the filter
506 @type level: integer
507 @param level: Current depth
508 @type op: string
509 @param op: Operator
510 @type op_fn: callable
511 @param op_fn: Function implementing operator
512 @type operands: list
513 @param operands: List of operands
514
515 """
516 if hints_fn:
517 hints_fn(op)
518
519 return compat.partial(_WrapLogicOp, op_fn,
520 [self._Compile(op, level + 1) for op in operands])
521
523 """Handles unary operators.
524
525 @type hints_fn: callable
526 @param hints_fn: Callback doing some analysis on the filter
527 @type level: integer
528 @param level: Current depth
529 @type op: string
530 @param op: Operator
531 @type op_fn: callable
532 @param op_fn: Function implementing operator
533 @type operands: list
534 @param operands: List of operands
535
536 """
537 assert op_fn is None
538
539 if hints_fn:
540 hints_fn(op)
541
542 if len(operands) != 1:
543 raise errors.ParameterError("Unary operator '%s' expects exactly one"
544 " operand" % op)
545
546 if op == qlang.OP_TRUE:
547 (_, _, _, retrieval_fn) = self._LookupField(operands[0])
548
549 op_fn = operator.truth
550 arg = retrieval_fn
551 elif op == qlang.OP_NOT:
552 op_fn = operator.not_
553 arg = self._Compile(operands[0], level + 1)
554 else:
555 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
556
557 return compat.partial(_WrapUnaryOp, op_fn, arg)
558
560 """Handles binary operators.
561
562 @type hints_fn: callable
563 @param hints_fn: Callback doing some analysis on the filter
564 @type level: integer
565 @param level: Current depth
566 @type op: string
567 @param op: Operator
568 @param op_data: Functions implementing operators
569 @type operands: list
570 @param operands: List of operands
571
572 """
573
574 try:
575 (name, value) = operands
576 except (ValueError, TypeError):
577 raise errors.ParameterError("Invalid binary operator, expected exactly"
578 " two operands")
579
580 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
581
582 assert fdef.kind != QFT_UNKNOWN
583
584
585
586 verify_fn = _VERIFY_FN[fdef.kind]
587 if not verify_fn(value):
588 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
589 " with '%s', expected %s" %
590 (name, fdef.kind, value.__class__.__name__,
591 verify_fn))
592
593 if hints_fn:
594 hints_fn(op, datakind, name, value)
595
596 for (fn_flags, fn, valprepfn) in op_data:
597 if fn_flags is None or fn_flags & field_flags:
598
599 if valprepfn:
600 value = valprepfn(value)
601
602 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
603
604 raise errors.ProgrammerError("Unable to find operator implementation"
605 " (op '%s', flags %s)" % (op, field_flags))
606
607
609 """Converts a query filter into a callable function.
610
611 See L{_FilterCompilerHelper} for details.
612
613 @rtype: callable
614
615 """
616 return _FilterCompilerHelper(fields)(hints, qfilter)
617
618
620 - def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
621 """Initializes this class.
622
623 The field definition is a dictionary with the field's name as a key and a
624 tuple containing, in order, the field definition object
625 (L{objects.QueryFieldDefinition}, the data kind to help calling code
626 collect data and a retrieval function. The retrieval function is called
627 with two parameters, in order, the data container and the item in container
628 (see L{Query.Query}).
629
630 Users of this class can call L{RequestedData} before preparing the data
631 container to determine what data is needed.
632
633 @type fieldlist: dictionary
634 @param fieldlist: Field definitions
635 @type selected: list of strings
636 @param selected: List of selected fields
637
638 """
639 assert namefield is None or namefield in fieldlist
640
641 self._fields = _GetQueryFields(fieldlist, selected)
642
643 self._filter_fn = None
644 self._requested_names = None
645 self._filter_datakinds = frozenset()
646
647 if qfilter is not None:
648
649 if namefield:
650 hints = _FilterHints(namefield)
651 else:
652 hints = None
653
654
655 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
656 if hints:
657 self._requested_names = hints.RequestedNames()
658 self._filter_datakinds = hints.ReferencedData()
659
660 if namefield is None:
661 self._name_fn = None
662 else:
663 (_, _, _, self._name_fn) = fieldlist[namefield]
664
666 """Returns all names referenced in the filter.
667
668 If there is no filter or operators are preventing determining the exact
669 names, C{None} is returned.
670
671 """
672 return self._requested_names
673
675 """Gets requested kinds of data.
676
677 @rtype: frozenset
678
679 """
680 return (self._filter_datakinds |
681 frozenset(datakind for (_, datakind, _, _) in self._fields
682 if datakind is not None))
683
685 """Returns the list of fields for this query.
686
687 Includes unknown fields.
688
689 @rtype: List of L{objects.QueryFieldDefinition}
690
691 """
692 return GetAllFields(self._fields)
693
694 - def Query(self, ctx, sort_by_name=True):
695 """Execute a query.
696
697 @param ctx: Data container passed to field retrieval functions, must
698 support iteration using C{__iter__}
699 @type sort_by_name: boolean
700 @param sort_by_name: Whether to sort by name or keep the input data's
701 ordering
702
703 """
704 sort = (self._name_fn and sort_by_name)
705
706 result = []
707
708 for idx, item in enumerate(ctx):
709 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
710 continue
711
712 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
713
714
715 if __debug__:
716 _VerifyResultRow(self._fields, row)
717
718 if sort:
719 (status, name) = _ProcessResult(self._name_fn(ctx, item))
720 assert status == constants.RS_NORMAL
721
722 result.append((utils.NiceSortKey(name), idx, row))
723 else:
724 result.append(row)
725
726 if not sort:
727 return result
728
729
730
731
732 result.sort()
733
734 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
735
736 return map(operator.itemgetter(2), result)
737
739 """Query with "old" query result format.
740
741 See L{Query.Query} for arguments.
742
743 """
744 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
745 if fdef.kind == QFT_UNKNOWN)
746 if unknown:
747 raise errors.OpPrereqError("Unknown output fields selected: %s" %
748 (utils.CommaJoin(unknown), ),
749 errors.ECODE_INVAL)
750
751 return [[value for (_, value) in row]
752 for row in self.Query(ctx, sort_by_name=sort_by_name)]
753
754
769
770
772 """Verifies the contents of a query result row.
773
774 @type fields: list
775 @param fields: Field definitions for result
776 @type row: list of tuples
777 @param row: Row data
778
779 """
780 assert len(row) == len(fields)
781 errs = []
782 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
783 if status == RS_NORMAL:
784 if not _VERIFY_FN[fdef.kind](value):
785 errs.append("normal field %s fails validation (value is %s)" %
786 (fdef.name, value))
787 elif value is not None:
788 errs.append("abnormal field %s has a non-None value" % fdef.name)
789 assert not errs, ("Failed validation: %s in row %s" %
790 (utils.CommaJoin(errs), row))
791
792
794 """Generates key for field dictionary.
795
796 """
797 assert fdef.name and fdef.title, "Name and title are required"
798 assert FIELD_NAME_RE.match(fdef.name)
799 assert TITLE_RE.match(fdef.title)
800 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
801 fdef.doc.strip() == fdef.doc), \
802 "Invalid description for field '%s'" % fdef.name
803 assert callable(fn)
804 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
805
806 return fdef.name
807
808
810 """Prepares field list for use by L{Query}.
811
812 Converts the list to a dictionary and does some verification.
813
814 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
815 kind, retrieval function)
816 @param fields: List of fields, see L{Query.__init__} for a better
817 description
818 @type aliases: list of tuples; (alias, target)
819 @param aliases: list of tuples containing aliases; for each
820 alias/target pair, a duplicate will be created in the field list
821 @rtype: dict
822 @return: Field dictionary for L{Query}
823
824 """
825 if __debug__:
826 duplicates = utils.FindDuplicates(fdef.title.lower()
827 for (fdef, _, _, _) in fields)
828 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
829
830 result = utils.SequenceToDict(fields, key=_FieldDictKey)
831
832 for alias, target in aliases:
833 assert alias not in result, "Alias %s overrides an existing field" % alias
834 assert target in result, "Missing target %s for alias %s" % (target, alias)
835 (fdef, k, flags, fn) = result[target]
836 fdef = fdef.Copy()
837 fdef.name = alias
838 result[alias] = (fdef, k, flags, fn)
839
840 assert len(result) == len(fields) + len(aliases)
841 assert compat.all(name == fdef.name
842 for (name, (fdef, _, _, _)) in result.items())
843
844 return result
845
846
848 """Prepares the response for a query.
849
850 @type query: L{Query}
851 @param ctx: Data container, see L{Query.Query}
852 @type sort_by_name: boolean
853 @param sort_by_name: Whether to sort by name or keep the input data's
854 ordering
855
856 """
857 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
858 fields=query.GetFields()).ToDict()
859
860
862 """Returns list of available fields.
863
864 @type fielddefs: dict
865 @param fielddefs: Field definitions
866 @type selected: list of strings
867 @param selected: List of selected fields
868 @return: List of L{objects.QueryFieldDefinition}
869
870 """
871 if selected is None:
872
873 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
874 key=operator.attrgetter("name"))
875 else:
876
877 fdefs = Query(fielddefs, selected).GetFields()
878
879 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
880
881
883 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
884
885 @param name: Field name as a regular expression
886 @param title: Human-readable title
887 @param kind: Field type
888 @param doc: Human-readable description
889
890 """
891 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
892 doc=doc)
893
894
896 """Returns a static value.
897
898 """
899 return value
900
901
907
908
928
929
931 """Returns a field function to return an attribute of the item.
932
933 @param attr: Attribute name
934
935 """
936 getter = operator.attrgetter(attr)
937 return lambda _, item: getter(item)
938
939
941 """Return a field function to return an ND parameter out of the context.
942
943 """
944 def _helper(ctx, _):
945 if ctx.ndparams is None:
946 return _FS_UNAVAIL
947 else:
948 return ctx.ndparams.get(name, None)
949 return _helper
950
951
966
967
969 """Wrapper for converting values.
970
971 @param convert: Conversion function receiving value as single parameter
972 @param fn: Retrieval function
973
974 """
975 value = fn(ctx, item)
976
977
978 if compat.any(value is fs for fs in _FS_ALL):
979
980 return value
981
982
983 return convert(value)
984
985
987 """Convenience wrapper for L{_ConvWrapInner}.
988
989 @param convert: Conversion function receiving value as single parameter
990 @param fn: Retrieval function
991
992 """
993 return compat.partial(_ConvWrapInner, convert, fn)
994
995
997 """Returns function for getting timestamp of item.
998
999 @type getter: callable
1000 @param getter: Function to retrieve timestamp attribute
1001
1002 """
1003 def fn(_, item):
1004 """Returns a timestamp of item.
1005
1006 """
1007 timestamp = getter(item)
1008 if timestamp is None:
1009
1010 return _FS_UNAVAIL
1011 else:
1012 return timestamp
1013
1014 return fn
1015
1016
1018 """Returns common timestamp fields.
1019
1020 @param datatype: Field data type for use by L{Query.RequestedData}
1021
1022 """
1023 return [
1024 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1025 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1026 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1027 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1028 ]
1029
1030
1032 """Data container for node data queries.
1033
1034 """
1035 - def __init__(self, nodes, live_data, master_name, node_to_primary,
1036 node_to_secondary, groups, oob_support, cluster):
1037 """Initializes this class.
1038
1039 """
1040 self.nodes = nodes
1041 self.live_data = live_data
1042 self.master_name = master_name
1043 self.node_to_primary = node_to_primary
1044 self.node_to_secondary = node_to_secondary
1045 self.groups = groups
1046 self.oob_support = oob_support
1047 self.cluster = cluster
1048
1049
1050 self.curlive_data = None
1051 self.ndparams = None
1052
1054 """Iterate over all nodes.
1055
1056 This function has side-effects and only one instance of the resulting
1057 generator should be used at a time.
1058
1059 """
1060 for node in self.nodes:
1061 group = self.groups.get(node.group, None)
1062 if group is None:
1063 self.ndparams = None
1064 else:
1065 self.ndparams = self.cluster.FillND(node, group)
1066 if self.live_data:
1067 self.curlive_data = self.live_data.get(node.name, None)
1068 else:
1069 self.curlive_data = None
1070 yield node
1071
1072
1073
1074 _NODE_SIMPLE_FIELDS = {
1075 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1076 "master_candidate": ("MasterC", QFT_BOOL, 0,
1077 "Whether node is a master candidate"),
1078 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1079 "Whether node can become a master candidate"),
1080 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1081 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1082 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1083 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1084 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1085 }
1086
1087
1088
1089
1090 _NODE_LIVE_FIELDS = {
1091 "bootid": ("BootID", QFT_TEXT, "bootid",
1092 "Random UUID renewed for each system reboot, can be used"
1093 " for detecting reboots by tracking changes"),
1094 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1095 "Number of NUMA domains on node (if exported by hypervisor)"),
1096 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1097 "Number of physical CPU sockets (if exported by hypervisor)"),
1098 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1099 "dfree": ("DFree", QFT_UNIT, "vg_free",
1100 "Available disk space in volume group"),
1101 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1102 "Total disk space in volume group used for instance disk"
1103 " allocation"),
1104 "mfree": ("MFree", QFT_UNIT, "memory_free",
1105 "Memory available for instance allocations"),
1106 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1107 "Amount of memory used by node (dom0 for Xen)"),
1108 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1109 "Total amount of memory of physical machine"),
1110 }
1111
1112
1114 """Build function for calling another function with an node group.
1115
1116 @param cb: The callback to be called with the nodegroup
1117
1118 """
1119 def fn(ctx, node):
1120 """Get group data for a node.
1121
1122 @type ctx: L{NodeQueryData}
1123 @type inst: L{objects.Node}
1124 @param inst: Node object
1125
1126 """
1127 ng = ctx.groups.get(node.group, None)
1128 if ng is None:
1129
1130 return _FS_UNAVAIL
1131
1132 return cb(ctx, node, ng)
1133
1134 return fn
1135
1136
1138 """Returns the name of a node's group.
1139
1140 @type ctx: L{NodeQueryData}
1141 @type node: L{objects.Node}
1142 @param node: Node object
1143 @type ng: L{objects.NodeGroup}
1144 @param ng: The node group this node belongs to
1145
1146 """
1147 return ng.name
1148
1149
1151 """Returns the node powered state
1152
1153 @type ctx: L{NodeQueryData}
1154 @type node: L{objects.Node}
1155 @param node: Node object
1156
1157 """
1158 if ctx.oob_support[node.name]:
1159 return node.powered
1160
1161 return _FS_UNAVAIL
1162
1163
1165 """Returns the ndparams for this node.
1166
1167 @type ctx: L{NodeQueryData}
1168 @type node: L{objects.Node}
1169 @param node: Node object
1170 @type ng: L{objects.NodeGroup}
1171 @param ng: The node group this node belongs to
1172
1173 """
1174 return ctx.cluster.SimpleFillND(ng.FillND(node))
1175
1176
1178 """Gets the value of a "live" field from L{NodeQueryData}.
1179
1180 @param field: Live field name
1181 @param kind: Data kind, one of L{constants.QFT_ALL}
1182 @type ctx: L{NodeQueryData}
1183 @type node: L{objects.Node}
1184 @param node: Node object
1185
1186 """
1187 if node.offline:
1188 return _FS_OFFLINE
1189
1190 if not node.vm_capable:
1191 return _FS_UNAVAIL
1192
1193 if not ctx.curlive_data:
1194 return _FS_NODATA
1195
1196 try:
1197 value = ctx.curlive_data[field]
1198 except KeyError:
1199 return _FS_UNAVAIL
1200
1201 if kind == QFT_TEXT:
1202 return value
1203
1204 assert kind in (QFT_NUMBER, QFT_UNIT)
1205
1206
1207 try:
1208 return int(value)
1209 except (ValueError, TypeError):
1210 logging.exception("Failed to convert node field '%s' (value %r) to int",
1211 value, field)
1212 return _FS_UNAVAIL
1213
1214
1216 """Converts node's hypervisor state for query result.
1217
1218 """
1219 hv_state = node.hv_state
1220
1221 if hv_state is None:
1222 return _FS_UNAVAIL
1223
1224 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1225
1226
1228 """Converts node's disk state for query result.
1229
1230 """
1231 disk_state = node.disk_state
1232
1233 if disk_state is None:
1234 return _FS_UNAVAIL
1235
1236 return dict((disk_kind, dict((name, value.ToDict())
1237 for (name, value) in kind_state.items()))
1238 for (disk_kind, kind_state) in disk_state.items())
1239
1240
1242 """Builds list of fields for node queries.
1243
1244 """
1245 fields = [
1246 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1247 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1248 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1249 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1250 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1251 lambda ctx, node: list(node.GetTags())),
1252 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1253 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1254 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1255 _GetGroup(_GetNodeGroup)),
1256 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1257 NQ_CONFIG, 0, _GetItemAttr("group")),
1258 (_MakeField("powered", "Powered", QFT_BOOL,
1259 "Whether node is thought to be powered on"),
1260 NQ_OOB, 0, _GetNodePower),
1261 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1262 "Merged node parameters"),
1263 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1264 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1265 "Custom node parameters"),
1266 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1267 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1268 NQ_CONFIG, 0, _GetNodeHvState),
1269 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1270 NQ_CONFIG, 0, _GetNodeDiskState),
1271 ]
1272
1273 fields.extend(_BuildNDFields(False))
1274
1275
1276 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1277 constants.NR_REGULAR, constants.NR_DRAINED,
1278 constants.NR_OFFLINE)
1279 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1280 " \"%s\" for regular, \"%s\" for a drained, \"%s\" for offline" %
1281 role_values)
1282 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1283 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1284 assert set(role_values) == constants.NR_ALL
1285
1286 def _GetLength(getter):
1287 return lambda ctx, node: len(getter(ctx)[node.name])
1288
1289 def _GetList(getter):
1290 return lambda ctx, node: list(getter(ctx)[node.name])
1291
1292
1293 for prefix, titleprefix, docword, getter in \
1294 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1295 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1296
1297 fields.extend([
1298 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1299 "Number of instances with this node as %s" % docword),
1300 NQ_INST, 0, _GetLength(getter)),
1301 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1302 QFT_OTHER,
1303 "List of instances with this node as %s" % docword),
1304 NQ_INST, 0, _GetList(getter)),
1305 ])
1306
1307
1308 fields.extend([
1309 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1310 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1311 ])
1312
1313
1314 fields.extend([
1315 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1316 compat.partial(_GetLiveNodeField, nfield, kind))
1317 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1318 ])
1319
1320
1321 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1322
1323 return _PrepareFieldList(fields, [])
1324
1325
1327 """Data container for instance data queries.
1328
1329 """
1330 - def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1331 live_data, wrongnode_inst, console, nodes, groups):
1332 """Initializes this class.
1333
1334 @param instances: List of instance objects
1335 @param cluster: Cluster object
1336 @type disk_usage: dict; instance name as key
1337 @param disk_usage: Per-instance disk usage
1338 @type offline_nodes: list of strings
1339 @param offline_nodes: List of offline nodes
1340 @type bad_nodes: list of strings
1341 @param bad_nodes: List of faulty nodes
1342 @type live_data: dict; instance name as key
1343 @param live_data: Per-instance live data
1344 @type wrongnode_inst: set
1345 @param wrongnode_inst: Set of instances running on wrong node(s)
1346 @type console: dict; instance name as key
1347 @param console: Per-instance console information
1348 @type nodes: dict; node name as key
1349 @param nodes: Node objects
1350
1351 """
1352 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1353 "Offline nodes not included in bad nodes"
1354 assert not (set(live_data.keys()) & set(bad_nodes)), \
1355 "Found live data for bad or offline nodes"
1356
1357 self.instances = instances
1358 self.cluster = cluster
1359 self.disk_usage = disk_usage
1360 self.offline_nodes = offline_nodes
1361 self.bad_nodes = bad_nodes
1362 self.live_data = live_data
1363 self.wrongnode_inst = wrongnode_inst
1364 self.console = console
1365 self.nodes = nodes
1366 self.groups = groups
1367
1368
1369 self.inst_hvparams = None
1370 self.inst_beparams = None
1371 self.inst_osparams = None
1372 self.inst_nicparams = None
1373
1375 """Iterate over all instances.
1376
1377 This function has side-effects and only one instance of the resulting
1378 generator should be used at a time.
1379
1380 """
1381 for inst in self.instances:
1382 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1383 self.inst_beparams = self.cluster.FillBE(inst)
1384 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1385 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1386 for nic in inst.nics]
1387
1388 yield inst
1389
1390
1392 """Get instance's operational status.
1393
1394 @type ctx: L{InstanceQueryData}
1395 @type inst: L{objects.Instance}
1396 @param inst: Instance object
1397
1398 """
1399
1400
1401 if inst.primary_node in ctx.bad_nodes:
1402 return _FS_NODATA
1403 else:
1404 return bool(ctx.live_data.get(inst.name))
1405
1406
1408 """Build function for retrieving live data.
1409
1410 @type name: string
1411 @param name: Live data field name
1412
1413 """
1414 def fn(ctx, inst):
1415 """Get live data for an instance.
1416
1417 @type ctx: L{InstanceQueryData}
1418 @type inst: L{objects.Instance}
1419 @param inst: Instance object
1420
1421 """
1422 if (inst.primary_node in ctx.bad_nodes or
1423 inst.primary_node in ctx.offline_nodes):
1424
1425
1426 return _FS_NODATA
1427
1428 if inst.name in ctx.live_data:
1429 data = ctx.live_data[inst.name]
1430 if name in data:
1431 return data[name]
1432
1433 return _FS_UNAVAIL
1434
1435 return fn
1436
1437
1466
1467
1469 """Build function for retrieving disk size.
1470
1471 @type index: int
1472 @param index: Disk index
1473
1474 """
1475 def fn(_, inst):
1476 """Get size of a disk.
1477
1478 @type inst: L{objects.Instance}
1479 @param inst: Instance object
1480
1481 """
1482 try:
1483 return inst.disks[index].size
1484 except IndexError:
1485 return _FS_UNAVAIL
1486
1487 return fn
1488
1489
1491 """Build function for calling another function with an instance NIC.
1492
1493 @type index: int
1494 @param index: NIC index
1495 @type cb: callable
1496 @param cb: Callback
1497
1498 """
1499 def fn(ctx, inst):
1500 """Call helper function with instance NIC.
1501
1502 @type ctx: L{InstanceQueryData}
1503 @type inst: L{objects.Instance}
1504 @param inst: Instance object
1505
1506 """
1507 try:
1508 nic = inst.nics[index]
1509 except IndexError:
1510 return _FS_UNAVAIL
1511
1512 return cb(ctx, index, nic)
1513
1514 return fn
1515
1516
1518 """Get a NIC's IP address.
1519
1520 @type ctx: L{InstanceQueryData}
1521 @type nic: L{objects.NIC}
1522 @param nic: NIC object
1523
1524 """
1525 if nic.ip is None:
1526 return _FS_UNAVAIL
1527 else:
1528 return nic.ip
1529
1530
1532 """Get a NIC's bridge.
1533
1534 @type ctx: L{InstanceQueryData}
1535 @type index: int
1536 @param index: NIC index
1537
1538 """
1539 assert len(ctx.inst_nicparams) >= index
1540
1541 nicparams = ctx.inst_nicparams[index]
1542
1543 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1544 return nicparams[constants.NIC_LINK]
1545 else:
1546 return _FS_UNAVAIL
1547
1548
1550 """Get all network bridges for an instance.
1551
1552 @type ctx: L{InstanceQueryData}
1553 @type inst: L{objects.Instance}
1554 @param inst: Instance object
1555
1556 """
1557 assert len(ctx.inst_nicparams) == len(inst.nics)
1558
1559 result = []
1560
1561 for nicp in ctx.inst_nicparams:
1562 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1563 result.append(nicp[constants.NIC_LINK])
1564 else:
1565 result.append(None)
1566
1567 assert len(result) == len(inst.nics)
1568
1569 return result
1570
1571
1573 """Build function for retrieving a NIC parameter.
1574
1575 @type name: string
1576 @param name: Parameter name
1577
1578 """
1579 def fn(ctx, index, _):
1580 """Get a NIC's bridge.
1581
1582 @type ctx: L{InstanceQueryData}
1583 @type inst: L{objects.Instance}
1584 @param inst: Instance object
1585 @type nic: L{objects.NIC}
1586 @param nic: NIC object
1587
1588 """
1589 assert len(ctx.inst_nicparams) >= index
1590 return ctx.inst_nicparams[index][name]
1591
1592 return fn
1593
1594
1596 """Get instance fields involving network interfaces.
1597
1598 @return: Tuple containing list of field definitions used as input for
1599 L{_PrepareFieldList} and a list of aliases
1600
1601 """
1602 nic_mac_fn = lambda ctx, _, nic: nic.mac
1603 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1604 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1605
1606 fields = [
1607
1608 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1609 "Number of network interfaces"),
1610 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1611 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1612 "List containing each network interface's MAC address"),
1613 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1614 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1615 "List containing each network interface's IP address"),
1616 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1617 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1618 "List containing each network interface's mode"), IQ_CONFIG, 0,
1619 lambda ctx, inst: [nicp[constants.NIC_MODE]
1620 for nicp in ctx.inst_nicparams]),
1621 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1622 "List containing each network interface's link"), IQ_CONFIG, 0,
1623 lambda ctx, inst: [nicp[constants.NIC_LINK]
1624 for nicp in ctx.inst_nicparams]),
1625 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1626 "List containing each network interface's bridge"),
1627 IQ_CONFIG, 0, _GetInstAllNicBridges),
1628 ]
1629
1630
1631 for i in range(constants.MAX_NICS):
1632 numtext = utils.FormatOrdinal(i + 1)
1633 fields.extend([
1634 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1635 "IP address of %s network interface" % numtext),
1636 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1637 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1638 "MAC address of %s network interface" % numtext),
1639 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1640 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1641 "Mode of %s network interface" % numtext),
1642 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1643 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1644 "Link of %s network interface" % numtext),
1645 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1646 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1647 "Bridge of %s network interface" % numtext),
1648 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1649 ])
1650
1651 aliases = [
1652
1653 ("ip", "nic.ip/0"),
1654 ("mac", "nic.mac/0"),
1655 ("bridge", "nic.bridge/0"),
1656 ("nic_mode", "nic.mode/0"),
1657 ("nic_link", "nic.link/0"),
1658 ]
1659
1660 return (fields, aliases)
1661
1662
1664 """Get disk usage for an instance.
1665
1666 @type ctx: L{InstanceQueryData}
1667 @type inst: L{objects.Instance}
1668 @param inst: Instance object
1669
1670 """
1671 usage = ctx.disk_usage[inst.name]
1672
1673 if usage is None:
1674 usage = 0
1675
1676 return usage
1677
1678
1680 """Get console information for instance.
1681
1682 @type ctx: L{InstanceQueryData}
1683 @type inst: L{objects.Instance}
1684 @param inst: Instance object
1685
1686 """
1687 consinfo = ctx.console[inst.name]
1688
1689 if consinfo is None:
1690 return _FS_UNAVAIL
1691
1692 return consinfo
1693
1694
1696 """Get instance fields involving disks.
1697
1698 @return: List of field definitions used as input for L{_PrepareFieldList}
1699
1700 """
1701 fields = [
1702 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1703 "Total disk space used by instance on each of its nodes;"
1704 " this is not the disk size visible to the instance, but"
1705 " the usage on the node"),
1706 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1707 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1708 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1709 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1710 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1711 ]
1712
1713
1714 fields.extend([
1715 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1716 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1717 IQ_CONFIG, 0, _GetInstDiskSize(i))
1718 for i in range(constants.MAX_DISKS)
1719 ])
1720
1721 return fields
1722
1723
1725 """Get instance fields involving parameters.
1726
1727 @return: List of field definitions used as input for L{_PrepareFieldList}
1728
1729 """
1730
1731 be_title = {
1732 constants.BE_AUTO_BALANCE: "Auto_balance",
1733 constants.BE_MAXMEM: "ConfigMaxMem",
1734 constants.BE_MINMEM: "ConfigMinMem",
1735 constants.BE_VCPUS: "ConfigVCPUs",
1736 }
1737
1738 hv_title = {
1739 constants.HV_ACPI: "ACPI",
1740 constants.HV_BOOT_ORDER: "Boot_order",
1741 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1742 constants.HV_DISK_TYPE: "Disk_type",
1743 constants.HV_INITRD_PATH: "Initrd_path",
1744 constants.HV_KERNEL_PATH: "Kernel_path",
1745 constants.HV_NIC_TYPE: "NIC_type",
1746 constants.HV_PAE: "PAE",
1747 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1748 }
1749
1750 fields = [
1751
1752 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1753 "Hypervisor parameters (merged)"),
1754 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1755 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1756 "Backend parameters (merged)"),
1757 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1758 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1759 "Operating system parameters (merged)"),
1760 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1761
1762
1763 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1764 "Custom hypervisor parameters"),
1765 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1766 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1767 "Custom backend parameters",),
1768 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1769 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1770 "Custom operating system parameters",),
1771 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1772 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1773 "Custom network interface parameters"),
1774 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1775 ]
1776
1777
1778 def _GetInstHvParam(name):
1779 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1780
1781 fields.extend([
1782 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1783 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1784 IQ_CONFIG, 0, _GetInstHvParam(name))
1785 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1786 if name not in constants.HVC_GLOBALS
1787 ])
1788
1789
1790 def _GetInstBeParam(name):
1791 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1792
1793 fields.extend([
1794 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1795 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1796 IQ_CONFIG, 0, _GetInstBeParam(name))
1797 for name, kind in constants.BES_PARAMETER_TYPES.items()
1798 ])
1799
1800 return fields
1801
1802
1803 _INST_SIMPLE_FIELDS = {
1804 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1805 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1806 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1807
1808 "network_port": ("Network_port", QFT_OTHER, 0,
1809 "Instance network port if available (e.g. for VNC console)"),
1810 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1811 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1812 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1813 }
1814
1815
1817 """Gets group UUID of an instance node.
1818
1819 @type ctx: L{InstanceQueryData}
1820 @param default: Default value
1821 @type node_name: string
1822 @param node_name: Node name
1823
1824 """
1825 try:
1826 node = ctx.nodes[node_name]
1827 except KeyError:
1828 return default
1829 else:
1830 return node.group
1831
1832
1834 """Gets group name of an instance node.
1835
1836 @type ctx: L{InstanceQueryData}
1837 @param default: Default value
1838 @type node_name: string
1839 @param node_name: Node name
1840
1841 """
1842 try:
1843 node = ctx.nodes[node_name]
1844 except KeyError:
1845 return default
1846
1847 try:
1848 group = ctx.groups[node.group]
1849 except KeyError:
1850 return default
1851
1852 return group.name
1853
1854
1856 """Builds list of fields for instance queries.
1857
1858 """
1859 fields = [
1860 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1861 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1862 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1863 "Primary node's group"),
1864 IQ_NODES, 0,
1865 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1866 inst.primary_node)),
1867 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1868 "Primary node's group UUID"),
1869 IQ_NODES, 0,
1870 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1871
1872 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1873 "Secondary nodes; usually this will just be one node"),
1874 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1875 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1876 "Node groups of secondary nodes"),
1877 IQ_NODES, 0,
1878 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1879 inst.secondary_nodes)),
1880 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1881 "Node group UUIDs of secondary nodes"),
1882 IQ_NODES, 0,
1883 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1884 inst.secondary_nodes)),
1885 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1886 "Desired state of instance"),
1887 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1888 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1889 "Desired state of instance"),
1890 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1891 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1892 lambda ctx, inst: list(inst.GetTags())),
1893 (_MakeField("console", "Console", QFT_OTHER,
1894 "Instance console information"), IQ_CONSOLE, 0,
1895 _GetInstanceConsole),
1896 ]
1897
1898
1899 fields.extend([
1900 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1901 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1902 ])
1903
1904
1905 fields.extend([
1906 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1907 IQ_LIVE, 0, _GetInstOperState),
1908 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1909 "Actual memory usage as seen by hypervisor"),
1910 IQ_LIVE, 0, _GetInstLiveData("memory")),
1911 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1912 "Actual number of VCPUs as seen by hypervisor"),
1913 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1914 ])
1915
1916
1917 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1918 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1919 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1920 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
1921 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1922 " and actually is, \"%s\" if instance is stopped and"
1923 " is not running, \"%s\" if instance running, but not on its"
1924 " designated primary node, \"%s\" if instance should be"
1925 " stopped, but is actually running, \"%s\" if instance should"
1926 " run, but doesn't, \"%s\" if instance's primary node is down,"
1927 " \"%s\" if instance's primary node is marked offline,"
1928 " \"%s\" if instance is offline and does not use dynamic"
1929 " resources" % status_values)
1930 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1931 IQ_LIVE, 0, _GetInstStatus))
1932 assert set(status_values) == constants.INSTST_ALL, \
1933 "Status documentation mismatch"
1934
1935 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1936
1937 fields.extend(network_fields)
1938 fields.extend(_GetInstanceParameterFields())
1939 fields.extend(_GetInstanceDiskFields())
1940 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1941
1942 aliases = [
1943 ("vcpus", "be/vcpus"),
1944 ("be/memory", "be/maxmem"),
1945 ("sda_size", "disk.size/0"),
1946 ("sdb_size", "disk.size/1"),
1947 ] + network_aliases
1948
1949 return _PrepareFieldList(fields, aliases)
1950
1951
1953 """Data container for lock data queries.
1954
1955 """
1957 """Initializes this class.
1958
1959 """
1960 self.lockdata = lockdata
1961
1963 """Iterate over all locks.
1964
1965 """
1966 return iter(self.lockdata)
1967
1968
1970 """Returns a sorted list of a lock's current owners.
1971
1972 """
1973 (_, _, owners, _) = data
1974
1975 if owners:
1976 owners = utils.NiceSort(owners)
1977
1978 return owners
1979
1980
1982 """Returns a sorted list of a lock's pending acquires.
1983
1984 """
1985 (_, _, _, pending) = data
1986
1987 if pending:
1988 pending = [(mode, utils.NiceSort(names))
1989 for (mode, names) in pending]
1990
1991 return pending
1992
1993
1995 """Builds list of fields for lock queries.
1996
1997 """
1998 return _PrepareFieldList([
1999
2000 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2001 lambda ctx, (name, mode, owners, pending): name),
2002 (_MakeField("mode", "Mode", QFT_OTHER,
2003 "Mode in which the lock is currently acquired"
2004 " (exclusive or shared)"),
2005 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2006 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2007 LQ_OWNER, 0, _GetLockOwners),
2008 (_MakeField("pending", "Pending", QFT_OTHER,
2009 "Threads waiting for the lock"),
2010 LQ_PENDING, 0, _GetLockPending),
2011 ], [])
2012
2013
2015 """Data container for node group data queries.
2016
2017 """
2018 - def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2019 want_diskparams):
2020 """Initializes this class.
2021
2022 @param cluster: Cluster object
2023 @param groups: List of node group objects
2024 @type group_to_nodes: dict; group UUID as key
2025 @param group_to_nodes: Per-group list of nodes
2026 @type group_to_instances: dict; group UUID as key
2027 @param group_to_instances: Per-group list of (primary) instances
2028 @type want_diskparams: bool
2029 @param want_diskparams: Whether diskparamters should be calculated
2030
2031 """
2032 self.groups = groups
2033 self.group_to_nodes = group_to_nodes
2034 self.group_to_instances = group_to_instances
2035 self.cluster = cluster
2036 self.want_diskparams = want_diskparams
2037
2038
2039 self.group_ipolicy = None
2040 self.ndparams = None
2041 self.group_dp = None
2042
2044 """Iterate over all node groups.
2045
2046 This function has side-effects and only one instance of the resulting
2047 generator should be used at a time.
2048
2049 """
2050 for group in self.groups:
2051 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2052 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2053 if self.want_diskparams:
2054 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2055 else:
2056 self.group_dp = None
2057 yield group
2058
2059
2060 _GROUP_SIMPLE_FIELDS = {
2061 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2062 "name": ("Group", QFT_TEXT, "Group name"),
2063 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2064 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2065 }
2066
2067
2069 """Builds list of fields for node group queries.
2070
2071 """
2072
2073 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2074 _GetItemAttr(name))
2075 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2076
2077 def _GetLength(getter):
2078 return lambda ctx, group: len(getter(ctx)[group.uuid])
2079
2080 def _GetSortedList(getter):
2081 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2082
2083 group_to_nodes = operator.attrgetter("group_to_nodes")
2084 group_to_instances = operator.attrgetter("group_to_instances")
2085
2086
2087 fields.extend([
2088 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2089 GQ_NODE, 0, _GetLength(group_to_nodes)),
2090 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2091 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2092 ])
2093
2094
2095 fields.extend([
2096 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2097 "Number of primary instances"),
2098 GQ_INST, 0, _GetLength(group_to_instances)),
2099 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2100 "List of primary instances"),
2101 GQ_INST, 0, _GetSortedList(group_to_instances)),
2102 ])
2103
2104
2105 fields.extend([
2106 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2107 lambda ctx, group: list(group.GetTags())),
2108 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2109 "Instance policy limitations (merged)"),
2110 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2111 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2112 "Custom instance policy limitations"),
2113 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2114 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2115 "Custom node parameters"),
2116 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2117 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2118 "Node parameters"),
2119 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2120 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2121 "Disk parameters (merged)"),
2122 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2123 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2124 "Custom disk parameters"),
2125 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2126 ])
2127
2128
2129 fields.extend(_BuildNDFields(True))
2130
2131 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2132
2133 return _PrepareFieldList(fields, [])
2134
2135
2136 -class OsInfo(objects.ConfigObject):
2137 __slots__ = [
2138 "name",
2139 "valid",
2140 "hidden",
2141 "blacklisted",
2142 "variants",
2143 "api_versions",
2144 "parameters",
2145 "node_status",
2146 ]
2147
2148
2150 """Builds list of fields for operating system queries.
2151
2152 """
2153 fields = [
2154 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2155 None, 0, _GetItemAttr("name")),
2156 (_MakeField("valid", "Valid", QFT_BOOL,
2157 "Whether operating system definition is valid"),
2158 None, 0, _GetItemAttr("valid")),
2159 (_MakeField("hidden", "Hidden", QFT_BOOL,
2160 "Whether operating system is hidden"),
2161 None, 0, _GetItemAttr("hidden")),
2162 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2163 "Whether operating system is blacklisted"),
2164 None, 0, _GetItemAttr("blacklisted")),
2165 (_MakeField("variants", "Variants", QFT_OTHER,
2166 "Operating system variants"),
2167 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2168 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2169 "Operating system API versions"),
2170 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2171 (_MakeField("parameters", "Parameters", QFT_OTHER,
2172 "Operating system parameters"),
2173 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2174 _GetItemAttr("parameters"))),
2175 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2176 "Status from node"),
2177 None, 0, _GetItemAttr("node_status")),
2178 ]
2179
2180 return _PrepareFieldList(fields, [])
2181
2182
2184 """Return L{_FS_UNAVAIL} if job is None.
2185
2186 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2187 found, in which case this function converts it to L{_FS_UNAVAIL}.
2188
2189 """
2190 if job is None:
2191 return _FS_UNAVAIL
2192 else:
2193 return fn(job)
2194
2195
2201
2202
2204 """Executes a function per opcode in a job.
2205
2206 """
2207 return map(fn, job.ops)
2208
2209
2215
2216
2218 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2219
2220 """
2221 timestamp = fn(job)
2222
2223 if timestamp is None:
2224 return _FS_UNAVAIL
2225 else:
2226 return timestamp
2227
2228
2234
2235
2237 """Builds list of fields for job queries.
2238
2239 """
2240 fields = [
2241 (_MakeField("id", "ID", QFT_TEXT, "Job ID"),
2242 None, 0, lambda _, (job_id, job): job_id),
2243 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2244 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2245 (_MakeField("priority", "Priority", QFT_NUMBER,
2246 ("Current job priority (%s to %s)" %
2247 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2248 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2249 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2250 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2251 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2252 "List of opcodes results"),
2253 None, 0, _PerJobOp(operator.attrgetter("result"))),
2254 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2255 "List of opcodes status"),
2256 None, 0, _PerJobOp(operator.attrgetter("status"))),
2257 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2258 "List of opcode output logs"),
2259 None, 0, _PerJobOp(operator.attrgetter("log"))),
2260 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2261 "List of opcode start timestamps (before acquiring locks)"),
2262 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2263 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2264 "List of opcode execution start timestamps (after acquiring"
2265 " locks)"),
2266 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2267 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2268 "List of opcode execution end timestamps"),
2269 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2270 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2271 "List of opcode priorities"),
2272 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2273 (_MakeField("received_ts", "Received", QFT_OTHER,
2274 "Timestamp of when job was received"),
2275 None, 0, _JobTimestamp(operator.attrgetter("received_timestamp"))),
2276 (_MakeField("start_ts", "Start", QFT_OTHER,
2277 "Timestamp of job start"),
2278 None, 0, _JobTimestamp(operator.attrgetter("start_timestamp"))),
2279 (_MakeField("end_ts", "End", QFT_OTHER,
2280 "Timestamp of job end"),
2281 None, 0, _JobTimestamp(operator.attrgetter("end_timestamp"))),
2282 (_MakeField("summary", "Summary", QFT_OTHER,
2283 "List of per-opcode summaries"),
2284 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2285 ]
2286
2287 return _PrepareFieldList(fields, [])
2288
2289
2291 """Returns an export name if available.
2292
2293 """
2294 if expname is None:
2295 return _FS_UNAVAIL
2296 else:
2297 return expname
2298
2299
2301 """Builds list of fields for exports.
2302
2303 """
2304 fields = [
2305 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2306 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2307 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2308 None, 0, _GetExportName),
2309 ]
2310
2311 return _PrepareFieldList(fields, [])
2312
2313
2314 _CLUSTER_VERSION_FIELDS = {
2315 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2316 "Software version"),
2317 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2318 constants.PROTOCOL_VERSION,
2319 "RPC protocol version"),
2320 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2321 "Configuration format version"),
2322 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2323 "API version for OS template scripts"),
2324 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2325 "Import/export file format version"),
2326 }
2327
2328
2329 _CLUSTER_SIMPLE_FIELDS = {
2330 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2331 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2332 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2333 }
2334
2335
2337 - def __init__(self, cluster, drain_flag, watcher_pause):
2338 """Initializes this class.
2339
2340 @type cluster: L{objects.Cluster}
2341 @param cluster: Instance of cluster object
2342 @type drain_flag: bool
2343 @param drain_flag: Whether job queue is drained
2344 @type watcher_pause: number
2345 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2346
2347 """
2348 self._cluster = cluster
2349 self.drain_flag = drain_flag
2350 self.watcher_pause = watcher_pause
2351
2353 return iter([self._cluster])
2354
2355
2357 """Returns until when watcher is paused (if available).
2358
2359 """
2360 if ctx.watcher_pause is None:
2361 return _FS_UNAVAIL
2362 else:
2363 return ctx.watcher_pause
2364
2365
2367 """Builds list of fields for cluster information.
2368
2369 """
2370 fields = [
2371 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2372 lambda ctx, cluster: list(cluster.GetTags())),
2373 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2374 "Architecture information"), None, 0,
2375 lambda ctx, _: runtime.GetArchInfo()),
2376 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2377 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2378 lambda ctx, _: ctx.drain_flag),
2379 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2380 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2381 _ClusterWatcherPause),
2382 ]
2383
2384
2385 fields.extend([
2386 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2387 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2388 ])
2389
2390
2391 fields.extend([
2392 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2393 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
2394 ])
2395
2396
2397 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2398
2399 return _PrepareFieldList(fields, [
2400 ("name", "cluster_name"),
2401 ])
2402
2403
2404
2405 CLUSTER_FIELDS = _BuildClusterFields()
2406
2407
2408 NODE_FIELDS = _BuildNodeFields()
2409
2410
2411 INSTANCE_FIELDS = _BuildInstanceFields()
2412
2413
2414 LOCK_FIELDS = _BuildLockFields()
2415
2416
2417 GROUP_FIELDS = _BuildGroupFields()
2418
2419
2420 OS_FIELDS = _BuildOsFields()
2421
2422
2423 JOB_FIELDS = _BuildJobFields()
2424
2425
2426 EXPORT_FIELDS = _BuildExportFields()
2427
2428
2429 ALL_FIELDS = {
2430 constants.QR_CLUSTER: CLUSTER_FIELDS,
2431 constants.QR_INSTANCE: INSTANCE_FIELDS,
2432 constants.QR_NODE: NODE_FIELDS,
2433 constants.QR_LOCK: LOCK_FIELDS,
2434 constants.QR_GROUP: GROUP_FIELDS,
2435 constants.QR_OS: OS_FIELDS,
2436 constants.QR_JOB: JOB_FIELDS,
2437 constants.QR_EXPORT: EXPORT_FIELDS,
2438 }
2439
2440
2441 ALL_FIELD_LISTS = ALL_FIELDS.values()
2442