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 qlang
66
67 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
68 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
69 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
70 RS_UNAVAIL, RS_OFFLINE)
71
72
73
74
75
76
77 (NQ_CONFIG,
78 NQ_INST,
79 NQ_LIVE,
80 NQ_GROUP,
81 NQ_OOB) = range(1, 6)
82
83 (IQ_CONFIG,
84 IQ_LIVE,
85 IQ_DISKUSAGE,
86 IQ_CONSOLE,
87 IQ_NODES) = range(100, 105)
88
89 (LQ_MODE,
90 LQ_OWNER,
91 LQ_PENDING) = range(10, 13)
92
93 (GQ_CONFIG,
94 GQ_NODE,
95 GQ_INST) = range(200, 203)
96
97
98 QFF_HOSTNAME = 0x01
99 QFF_IP_ADDRESS = 0x02
100
101 QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS)
102
103 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
104 TITLE_RE = re.compile(r"^[^\s]+$")
105 DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
106
107
108 _VERIFY_FN = {
109 QFT_UNKNOWN: ht.TNone,
110 QFT_TEXT: ht.TString,
111 QFT_BOOL: ht.TBool,
112 QFT_NUMBER: ht.TInt,
113 QFT_UNIT: ht.TInt,
114 QFT_TIMESTAMP: ht.TNumber,
115 QFT_OTHER: lambda _: True,
116 }
117
118
119 _FS_UNKNOWN = object()
120 _FS_NODATA = object()
121 _FS_UNAVAIL = object()
122 _FS_OFFLINE = object()
123
124
125 _FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE])
126
127
128 _VTToQFT = {
129
130 constants.VTYPE_STRING: QFT_OTHER,
131 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
132 constants.VTYPE_BOOL: QFT_BOOL,
133 constants.VTYPE_SIZE: QFT_UNIT,
134 constants.VTYPE_INT: QFT_NUMBER,
135 }
136
137 _SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
138
139
141 """Gets the contents of an unknown field.
142
143 """
144 return _FS_UNKNOWN
145
146
148 """Calculates the internal list of selected fields.
149
150 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
151
152 @type fielddefs: dict
153 @param fielddefs: Field definitions
154 @type selected: list of strings
155 @param selected: List of selected fields
156
157 """
158 result = []
159
160 for name in selected:
161 try:
162 fdef = fielddefs[name]
163 except KeyError:
164 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
165 None, 0, _GetUnknownField)
166
167 assert len(fdef) == 4
168
169 result.append(fdef)
170
171 return result
172
173
175 """Extract L{objects.QueryFieldDefinition} from field definitions.
176
177 @rtype: list of L{objects.QueryFieldDefinition}
178
179 """
180 return [fdef for (fdef, _, _, _) in fielddefs]
181
182
184 """Class for filter analytics.
185
186 When filters are used, the user of the L{Query} class usually doesn't know
187 exactly which items will be necessary for building the result. It therefore
188 has to prepare and compute the input data for potentially returning
189 everything.
190
191 There are two ways to optimize this. The first, and simpler, is to assign
192 each field a group of data, so that the caller can determine which
193 computations are necessary depending on the data groups requested. The list
194 of referenced groups must also be computed for fields referenced in the
195 filter.
196
197 The second is restricting the items based on a primary key. The primary key
198 is usually a unique name (e.g. a node name). This class extracts all
199 referenced names from a filter. If it encounters any filter condition which
200 disallows such a list to be determined (e.g. a non-equality filter), all
201 names will be requested.
202
203 The end-effect is that any operation other than L{qlang.OP_OR} and
204 L{qlang.OP_EQUAL} will make the query more expensive.
205
206 """
208 """Initializes this class.
209
210 @type namefield: string
211 @param namefield: Field caller is interested in
212
213 """
214 self._namefield = namefield
215
216
217
218 self._allnames = False
219
220
221 self._names = None
222
223
224 self._datakinds = set()
225
227 """Returns all requested values.
228
229 Returns C{None} if list of values can't be determined (e.g. encountered
230 non-equality operators).
231
232 @rtype: list
233
234 """
235 if self._allnames or self._names is None:
236 return None
237
238 return utils.UniqueSequence(self._names)
239
241 """Returns all kinds of data referenced by the filter.
242
243 """
244 return frozenset(self._datakinds)
245
247 """Changes internal state to request all names.
248
249 """
250 self._allnames = True
251 self._names = None
252
254 """Called when handling a logic operation.
255
256 @type op: string
257 @param op: Operator
258
259 """
260 if op != qlang.OP_OR:
261 self._NeedAllNames()
262
264 """Called when handling an unary operation.
265
266 @type op: string
267 @param op: Operator
268
269 """
270 self._NeedAllNames()
271
273 """Called when handling a binary operation.
274
275 @type op: string
276 @param op: Operator
277 @type name: string
278 @param name: Left-hand side of operator (field name)
279 @param value: Right-hand side of operator
280
281 """
282 if datakind is not None:
283 self._datakinds.add(datakind)
284
285 if self._allnames:
286 return
287
288
289
290 if op == qlang.OP_EQUAL and name == self._namefield:
291 if self._names is None:
292 self._names = []
293 self._names.append(value)
294 else:
295 self._NeedAllNames()
296
297
299 """Wrapper for logic operator functions.
300
301 """
302 return op_fn(fn(ctx, item) for fn in sentences)
303
304
306 """Wrapper for unary operator functions.
307
308 """
309 return op_fn(inner(ctx, item))
310
311
313 """Wrapper for binary operator functions.
314
315 """
316 return op_fn(retrieval_fn(ctx, item), value)
317
318
320 """Negates the result of a wrapped function.
321
322 """
323 return not fn(lhs, rhs)
324
325
327 """Compiles a regular expression.
328
329 """
330 try:
331 return re.compile(pattern)
332 except re.error, err:
333 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
334
335
337 """Converts a query filter to a callable usable for filtering.
338
339 """
340
341
342
343 _LEVELS_MAX = 10
344
345
346 (_OPTYPE_LOGIC,
347 _OPTYPE_UNARY,
348 _OPTYPE_BINARY) = range(1, 4)
349
350 """Functions for equality checks depending on field flags.
351
352 List of tuples containing flags and a callable receiving the left- and
353 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
354 (e.g. L{QFF_HOSTNAME}).
355
356 Order matters. The first item with flags will be used. Flags are checked
357 using binary AND.
358
359 """
360 _EQUALITY_CHECKS = [
361 (QFF_HOSTNAME,
362 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
363 case_sensitive=False),
364 None),
365 (None, operator.eq, None),
366 ]
367
368 """Known operators
369
370 Operator as key (C{qlang.OP_*}), value a tuple of operator group
371 (C{_OPTYPE_*}) and a group-specific value:
372
373 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
374 L{_HandleLogicOp}
375 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
376 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
377 right-hand side of the operator, used by L{_HandleBinaryOp}
378
379 """
380 _OPS = {
381
382 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
383 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
384
385
386 qlang.OP_NOT: (_OPTYPE_UNARY, None),
387 qlang.OP_TRUE: (_OPTYPE_UNARY, None),
388
389
390 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
391 qlang.OP_NOT_EQUAL:
392 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
393 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
394 qlang.OP_REGEXP: (_OPTYPE_BINARY, [
395 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
396 ]),
397 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
398 (None, operator.contains, None),
399 ]),
400 }
401
403 """Initializes this class.
404
405 @param fields: Field definitions (return value of L{_PrepareFieldList})
406
407 """
408 self._fields = fields
409 self._hints = None
410 self._op_handler = None
411
413 """Converts a query filter into a callable function.
414
415 @type hints: L{_FilterHints} or None
416 @param hints: Callbacks doing analysis on filter
417 @type filter_: list
418 @param filter_: Filter structure
419 @rtype: callable
420 @return: Function receiving context and item as parameters, returning
421 boolean as to whether item matches filter
422
423 """
424 self._op_handler = {
425 self._OPTYPE_LOGIC:
426 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
427 self._OPTYPE_UNARY:
428 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
429 self._OPTYPE_BINARY:
430 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
431 }
432
433 try:
434 filter_fn = self._Compile(filter_, 0)
435 finally:
436 self._op_handler = None
437
438 return filter_fn
439
441 """Inner function for converting filters.
442
443 Calls the correct handler functions for the top-level operator. This
444 function is called recursively (e.g. for logic operators).
445
446 """
447 if not (isinstance(filter_, (list, tuple)) and filter_):
448 raise errors.ParameterError("Invalid filter on level %s" % level)
449
450
451 if level >= self._LEVELS_MAX:
452 raise errors.ParameterError("Only up to %s levels are allowed (filter"
453 " nested too deep)" % self._LEVELS_MAX)
454
455
456 operands = filter_[:]
457 op = operands.pop(0)
458
459 try:
460 (kind, op_data) = self._OPS[op]
461 except KeyError:
462 raise errors.ParameterError("Unknown operator '%s'" % op)
463
464 (handler, hints_cb) = self._op_handler[kind]
465
466 return handler(hints_cb, level, op, op_data, operands)
467
469 """Returns a field definition by name.
470
471 """
472 try:
473 return self._fields[name]
474 except KeyError:
475 raise errors.ParameterError("Unknown field '%s'" % name)
476
478 """Handles logic operators.
479
480 @type hints_fn: callable
481 @param hints_fn: Callback doing some analysis on the filter
482 @type level: integer
483 @param level: Current depth
484 @type op: string
485 @param op: Operator
486 @type op_fn: callable
487 @param op_fn: Function implementing operator
488 @type operands: list
489 @param operands: List of operands
490
491 """
492 if hints_fn:
493 hints_fn(op)
494
495 return compat.partial(_WrapLogicOp, op_fn,
496 [self._Compile(op, level + 1) for op in operands])
497
499 """Handles unary operators.
500
501 @type hints_fn: callable
502 @param hints_fn: Callback doing some analysis on the filter
503 @type level: integer
504 @param level: Current depth
505 @type op: string
506 @param op: Operator
507 @type op_fn: callable
508 @param op_fn: Function implementing operator
509 @type operands: list
510 @param operands: List of operands
511
512 """
513 assert op_fn is None
514
515 if hints_fn:
516 hints_fn(op)
517
518 if len(operands) != 1:
519 raise errors.ParameterError("Unary operator '%s' expects exactly one"
520 " operand" % op)
521
522 if op == qlang.OP_TRUE:
523 (_, _, _, retrieval_fn) = self._LookupField(operands[0])
524
525 op_fn = operator.truth
526 arg = retrieval_fn
527 elif op == qlang.OP_NOT:
528 op_fn = operator.not_
529 arg = self._Compile(operands[0], level + 1)
530 else:
531 raise errors.ProgrammerError("Can't handle operator '%s'" % op)
532
533 return compat.partial(_WrapUnaryOp, op_fn, arg)
534
536 """Handles binary operators.
537
538 @type hints_fn: callable
539 @param hints_fn: Callback doing some analysis on the filter
540 @type level: integer
541 @param level: Current depth
542 @type op: string
543 @param op: Operator
544 @param op_data: Functions implementing operators
545 @type operands: list
546 @param operands: List of operands
547
548 """
549
550 try:
551 (name, value) = operands
552 except (ValueError, TypeError):
553 raise errors.ParameterError("Invalid binary operator, expected exactly"
554 " two operands")
555
556 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
557
558 assert fdef.kind != QFT_UNKNOWN
559
560
561
562 verify_fn = _VERIFY_FN[fdef.kind]
563 if not verify_fn(value):
564 raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
565 " with '%s', expected %s" %
566 (name, fdef.kind, value.__class__.__name__,
567 verify_fn))
568
569 if hints_fn:
570 hints_fn(op, datakind, name, value)
571
572 for (fn_flags, fn, valprepfn) in op_data:
573 if fn_flags is None or fn_flags & field_flags:
574
575 if valprepfn:
576 value = valprepfn(value)
577
578 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
579
580 raise errors.ProgrammerError("Unable to find operator implementation"
581 " (op '%s', flags %s)" % (op, field_flags))
582
583
585 """Converts a query filter into a callable function.
586
587 See L{_FilterCompilerHelper} for details.
588
589 @rtype: callable
590
591 """
592 return _FilterCompilerHelper(fields)(hints, filter_)
593
594
596 - def __init__(self, fieldlist, selected, filter_=None, namefield=None):
597 """Initializes this class.
598
599 The field definition is a dictionary with the field's name as a key and a
600 tuple containing, in order, the field definition object
601 (L{objects.QueryFieldDefinition}, the data kind to help calling code
602 collect data and a retrieval function. The retrieval function is called
603 with two parameters, in order, the data container and the item in container
604 (see L{Query.Query}).
605
606 Users of this class can call L{RequestedData} before preparing the data
607 container to determine what data is needed.
608
609 @type fieldlist: dictionary
610 @param fieldlist: Field definitions
611 @type selected: list of strings
612 @param selected: List of selected fields
613
614 """
615 assert namefield is None or namefield in fieldlist
616
617 self._fields = _GetQueryFields(fieldlist, selected)
618
619 self._filter_fn = None
620 self._requested_names = None
621 self._filter_datakinds = frozenset()
622
623 if filter_ is not None:
624
625 if namefield:
626 hints = _FilterHints(namefield)
627 else:
628 hints = None
629
630
631 self._filter_fn = _CompileFilter(fieldlist, hints, filter_)
632 if hints:
633 self._requested_names = hints.RequestedNames()
634 self._filter_datakinds = hints.ReferencedData()
635
636 if namefield is None:
637 self._name_fn = None
638 else:
639 (_, _, _, self._name_fn) = fieldlist[namefield]
640
642 """Returns all names referenced in the filter.
643
644 If there is no filter or operators are preventing determining the exact
645 names, C{None} is returned.
646
647 """
648 return self._requested_names
649
651 """Gets requested kinds of data.
652
653 @rtype: frozenset
654
655 """
656 return (self._filter_datakinds |
657 frozenset(datakind for (_, datakind, _, _) in self._fields
658 if datakind is not None))
659
661 """Returns the list of fields for this query.
662
663 Includes unknown fields.
664
665 @rtype: List of L{objects.QueryFieldDefinition}
666
667 """
668 return GetAllFields(self._fields)
669
670 - def Query(self, ctx, sort_by_name=True):
671 """Execute a query.
672
673 @param ctx: Data container passed to field retrieval functions, must
674 support iteration using C{__iter__}
675 @type sort_by_name: boolean
676 @param sort_by_name: Whether to sort by name or keep the input data's
677 ordering
678
679 """
680 sort = (self._name_fn and sort_by_name)
681
682 result = []
683
684 for idx, item in enumerate(ctx):
685 if not (self._filter_fn is None or self._filter_fn(ctx, item)):
686 continue
687
688 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
689
690
691 if __debug__:
692 _VerifyResultRow(self._fields, row)
693
694 if sort:
695 (status, name) = _ProcessResult(self._name_fn(ctx, item))
696 assert status == constants.RS_NORMAL
697
698 result.append((utils.NiceSortKey(name), idx, row))
699 else:
700 result.append(row)
701
702 if not sort:
703 return result
704
705
706
707
708 result.sort()
709
710 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
711
712 return map(operator.itemgetter(2), result)
713
715 """Query with "old" query result format.
716
717 See L{Query.Query} for arguments.
718
719 """
720 unknown = set(fdef.name for (fdef, _, _, _) in self._fields
721 if fdef.kind == QFT_UNKNOWN)
722 if unknown:
723 raise errors.OpPrereqError("Unknown output fields selected: %s" %
724 (utils.CommaJoin(unknown), ),
725 errors.ECODE_INVAL)
726
727 return [[value for (_, value) in row]
728 for row in self.Query(ctx, sort_by_name=sort_by_name)]
729
730
745
746
748 """Verifies the contents of a query result row.
749
750 @type fields: list
751 @param fields: Field definitions for result
752 @type row: list of tuples
753 @param row: Row data
754
755 """
756 assert len(row) == len(fields)
757 errs = []
758 for ((status, value), (fdef, _, _, _)) in zip(row, fields):
759 if status == RS_NORMAL:
760 if not _VERIFY_FN[fdef.kind](value):
761 errs.append("normal field %s fails validation (value is %s)" %
762 (fdef.name, value))
763 elif value is not None:
764 errs.append("abnormal field %s has a non-None value" % fdef.name)
765 assert not errs, ("Failed validation: %s in row %s" %
766 (utils.CommaJoin(errors), row))
767
768
770 """Prepares field list for use by L{Query}.
771
772 Converts the list to a dictionary and does some verification.
773
774 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
775 kind, retrieval function)
776 @param fields: List of fields, see L{Query.__init__} for a better
777 description
778 @type aliases: list of tuples; (alias, target)
779 @param aliases: list of tuples containing aliases; for each
780 alias/target pair, a duplicate will be created in the field list
781 @rtype: dict
782 @return: Field dictionary for L{Query}
783
784 """
785 if __debug__:
786 duplicates = utils.FindDuplicates(fdef.title.lower()
787 for (fdef, _, _, _) in fields)
788 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
789
790 result = {}
791
792 for field in fields:
793 (fdef, _, flags, fn) = field
794
795 assert fdef.name and fdef.title, "Name and title are required"
796 assert FIELD_NAME_RE.match(fdef.name)
797 assert TITLE_RE.match(fdef.title)
798 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
799 fdef.doc.strip() == fdef.doc), \
800 "Invalid description for field '%s'" % fdef.name
801 assert callable(fn)
802 assert fdef.name not in result, \
803 "Duplicate field name '%s' found" % fdef.name
804 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
805
806 result[fdef.name] = field
807
808 for alias, target in aliases:
809 assert alias not in result, "Alias %s overrides an existing field" % alias
810 assert target in result, "Missing target %s for alias %s" % (target, alias)
811 (fdef, k, flags, fn) = result[target]
812 fdef = fdef.Copy()
813 fdef.name = alias
814 result[alias] = (fdef, k, flags, fn)
815
816 assert len(result) == len(fields) + len(aliases)
817 assert compat.all(name == fdef.name
818 for (name, (fdef, _, _, _)) in result.items())
819
820 return result
821
822
824 """Prepares the response for a query.
825
826 @type query: L{Query}
827 @param ctx: Data container, see L{Query.Query}
828 @type sort_by_name: boolean
829 @param sort_by_name: Whether to sort by name or keep the input data's
830 ordering
831
832 """
833 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
834 fields=query.GetFields()).ToDict()
835
836
838 """Returns list of available fields.
839
840 @type fielddefs: dict
841 @param fielddefs: Field definitions
842 @type selected: list of strings
843 @param selected: List of selected fields
844 @return: List of L{objects.QueryFieldDefinition}
845
846 """
847 if selected is None:
848
849 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
850 key=operator.attrgetter("name"))
851 else:
852
853 fdefs = Query(fielddefs, selected).GetFields()
854
855 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
856
857
859 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
860
861 @param name: Field name as a regular expression
862 @param title: Human-readable title
863 @param kind: Field type
864 @param doc: Human-readable description
865
866 """
867 return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
868 doc=doc)
869
870
890
891
893 """Returns a field function to return an attribute of the item.
894
895 @param attr: Attribute name
896
897 """
898 getter = operator.attrgetter(attr)
899 return lambda _, item: getter(item)
900
901
903 """Wrapper for converting values.
904
905 @param convert: Conversion function receiving value as single parameter
906 @param fn: Retrieval function
907
908 """
909 value = fn(ctx, item)
910
911
912 if compat.any(value is fs for fs in _FS_ALL):
913
914 return value
915
916
917 return convert(value)
918
919
921 """Convenience wrapper for L{_ConvWrapInner}.
922
923 @param convert: Conversion function receiving value as single parameter
924 @param fn: Retrieval function
925
926 """
927 return compat.partial(_ConvWrapInner, convert, fn)
928
929
931 """Returns function for getting timestamp of item.
932
933 @type getter: callable
934 @param getter: Function to retrieve timestamp attribute
935
936 """
937 def fn(_, item):
938 """Returns a timestamp of item.
939
940 """
941 timestamp = getter(item)
942 if timestamp is None:
943
944 return _FS_UNAVAIL
945 else:
946 return timestamp
947
948 return fn
949
950
952 """Returns common timestamp fields.
953
954 @param datatype: Field data type for use by L{Query.RequestedData}
955
956 """
957 return [
958 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
959 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
960 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
961 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
962 ]
963
964
966 """Data container for node data queries.
967
968 """
969 - def __init__(self, nodes, live_data, master_name, node_to_primary,
970 node_to_secondary, groups, oob_support, cluster):
971 """Initializes this class.
972
973 """
974 self.nodes = nodes
975 self.live_data = live_data
976 self.master_name = master_name
977 self.node_to_primary = node_to_primary
978 self.node_to_secondary = node_to_secondary
979 self.groups = groups
980 self.oob_support = oob_support
981 self.cluster = cluster
982
983
984 self.curlive_data = None
985
987 """Iterate over all nodes.
988
989 This function has side-effects and only one instance of the resulting
990 generator should be used at a time.
991
992 """
993 for node in self.nodes:
994 if self.live_data:
995 self.curlive_data = self.live_data.get(node.name, None)
996 else:
997 self.curlive_data = None
998 yield node
999
1000
1001
1002 _NODE_SIMPLE_FIELDS = {
1003 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1004 "master_candidate": ("MasterC", QFT_BOOL, 0,
1005 "Whether node is a master candidate"),
1006 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1007 "Whether node can become a master candidate"),
1008 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1009 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1010 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1011 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1012 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1013 }
1014
1015
1016
1017
1018 _NODE_LIVE_FIELDS = {
1019 "bootid": ("BootID", QFT_TEXT, "bootid",
1020 "Random UUID renewed for each system reboot, can be used"
1021 " for detecting reboots by tracking changes"),
1022 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1023 "Number of NUMA domains on node (if exported by hypervisor)"),
1024 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1025 "Number of physical CPU sockets (if exported by hypervisor)"),
1026 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1027 "dfree": ("DFree", QFT_UNIT, "vg_free",
1028 "Available disk space in volume group"),
1029 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1030 "Total disk space in volume group used for instance disk"
1031 " allocation"),
1032 "mfree": ("MFree", QFT_UNIT, "memory_free",
1033 "Memory available for instance allocations"),
1034 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1035 "Amount of memory used by node (dom0 for Xen)"),
1036 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1037 "Total amount of memory of physical machine"),
1038 }
1039
1040
1042 """Build function for calling another function with an node group.
1043
1044 @param cb: The callback to be called with the nodegroup
1045
1046 """
1047 def fn(ctx, node):
1048 """Get group data for a node.
1049
1050 @type ctx: L{NodeQueryData}
1051 @type inst: L{objects.Node}
1052 @param inst: Node object
1053
1054 """
1055 ng = ctx.groups.get(node.group, None)
1056 if ng is None:
1057
1058 return _FS_UNAVAIL
1059
1060 return cb(ctx, node, ng)
1061
1062 return fn
1063
1064
1066 """Returns the name of a node's group.
1067
1068 @type ctx: L{NodeQueryData}
1069 @type node: L{objects.Node}
1070 @param node: Node object
1071 @type ng: L{objects.NodeGroup}
1072 @param ng: The node group this node belongs to
1073
1074 """
1075 return ng.name
1076
1077
1079 """Returns the node powered state
1080
1081 @type ctx: L{NodeQueryData}
1082 @type node: L{objects.Node}
1083 @param node: Node object
1084
1085 """
1086 if ctx.oob_support[node.name]:
1087 return node.powered
1088
1089 return _FS_UNAVAIL
1090
1091
1093 """Returns the ndparams for this node.
1094
1095 @type ctx: L{NodeQueryData}
1096 @type node: L{objects.Node}
1097 @param node: Node object
1098 @type ng: L{objects.NodeGroup}
1099 @param ng: The node group this node belongs to
1100
1101 """
1102 return ctx.cluster.SimpleFillND(ng.FillND(node))
1103
1104
1106 """Gets the value of a "live" field from L{NodeQueryData}.
1107
1108 @param field: Live field name
1109 @param kind: Data kind, one of L{constants.QFT_ALL}
1110 @type ctx: L{NodeQueryData}
1111 @type node: L{objects.Node}
1112 @param node: Node object
1113
1114 """
1115 if node.offline:
1116 return _FS_OFFLINE
1117
1118 if not node.vm_capable:
1119 return _FS_UNAVAIL
1120
1121 if not ctx.curlive_data:
1122 return _FS_NODATA
1123
1124 try:
1125 value = ctx.curlive_data[field]
1126 except KeyError:
1127 return _FS_UNAVAIL
1128
1129 if kind == QFT_TEXT:
1130 return value
1131
1132 assert kind in (QFT_NUMBER, QFT_UNIT)
1133
1134
1135 try:
1136 return int(value)
1137 except (ValueError, TypeError):
1138 logging.exception("Failed to convert node field '%s' (value %r) to int",
1139 value, field)
1140 return _FS_UNAVAIL
1141
1142
1144 """Builds list of fields for node queries.
1145
1146 """
1147 fields = [
1148 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1149 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1150 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1151 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1152 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1153 lambda ctx, node: list(node.GetTags())),
1154 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1155 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1156 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1157 _GetGroup(_GetNodeGroup)),
1158 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1159 NQ_CONFIG, 0, _GetItemAttr("group")),
1160 (_MakeField("powered", "Powered", QFT_BOOL,
1161 "Whether node is thought to be powered on"),
1162 NQ_OOB, 0, _GetNodePower),
1163 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1164 "Merged node parameters"),
1165 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1166 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1167 "Custom node parameters"),
1168 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1169 ]
1170
1171
1172 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1173 constants.NR_REGULAR, constants.NR_DRAINED,
1174 constants.NR_OFFLINE)
1175 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1176 " \"%s\" for regular, \"%s\" for a drained, \"%s\" for offline" %
1177 role_values)
1178 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1179 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1180 assert set(role_values) == constants.NR_ALL
1181
1182 def _GetLength(getter):
1183 return lambda ctx, node: len(getter(ctx)[node.name])
1184
1185 def _GetList(getter):
1186 return lambda ctx, node: list(getter(ctx)[node.name])
1187
1188
1189 for prefix, titleprefix, docword, getter in \
1190 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1191 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1192
1193 fields.extend([
1194 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1195 "Number of instances with this node as %s" % docword),
1196 NQ_INST, 0, _GetLength(getter)),
1197 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1198 QFT_OTHER,
1199 "List of instances with this node as %s" % docword),
1200 NQ_INST, 0, _GetList(getter)),
1201 ])
1202
1203
1204 fields.extend([
1205 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1206 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
1207 ])
1208
1209
1210 fields.extend([
1211 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1212 compat.partial(_GetLiveNodeField, nfield, kind))
1213 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
1214 ])
1215
1216
1217 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1218
1219 return _PrepareFieldList(fields, [])
1220
1221
1223 """Data container for instance data queries.
1224
1225 """
1226 - def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1227 live_data, wrongnode_inst, console, nodes, groups):
1228 """Initializes this class.
1229
1230 @param instances: List of instance objects
1231 @param cluster: Cluster object
1232 @type disk_usage: dict; instance name as key
1233 @param disk_usage: Per-instance disk usage
1234 @type offline_nodes: list of strings
1235 @param offline_nodes: List of offline nodes
1236 @type bad_nodes: list of strings
1237 @param bad_nodes: List of faulty nodes
1238 @type live_data: dict; instance name as key
1239 @param live_data: Per-instance live data
1240 @type wrongnode_inst: set
1241 @param wrongnode_inst: Set of instances running on wrong node(s)
1242 @type console: dict; instance name as key
1243 @param console: Per-instance console information
1244 @type nodes: dict; node name as key
1245 @param nodes: Node objects
1246
1247 """
1248 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1249 "Offline nodes not included in bad nodes"
1250 assert not (set(live_data.keys()) & set(bad_nodes)), \
1251 "Found live data for bad or offline nodes"
1252
1253 self.instances = instances
1254 self.cluster = cluster
1255 self.disk_usage = disk_usage
1256 self.offline_nodes = offline_nodes
1257 self.bad_nodes = bad_nodes
1258 self.live_data = live_data
1259 self.wrongnode_inst = wrongnode_inst
1260 self.console = console
1261 self.nodes = nodes
1262 self.groups = groups
1263
1264
1265 self.inst_hvparams = None
1266 self.inst_beparams = None
1267 self.inst_osparams = None
1268 self.inst_nicparams = None
1269
1271 """Iterate over all instances.
1272
1273 This function has side-effects and only one instance of the resulting
1274 generator should be used at a time.
1275
1276 """
1277 for inst in self.instances:
1278 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1279 self.inst_beparams = self.cluster.FillBE(inst)
1280 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1281 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1282 for nic in inst.nics]
1283
1284 yield inst
1285
1286
1288 """Get instance's operational status.
1289
1290 @type ctx: L{InstanceQueryData}
1291 @type inst: L{objects.Instance}
1292 @param inst: Instance object
1293
1294 """
1295
1296
1297 if inst.primary_node in ctx.bad_nodes:
1298 return _FS_NODATA
1299 else:
1300 return bool(ctx.live_data.get(inst.name))
1301
1302
1304 """Build function for retrieving live data.
1305
1306 @type name: string
1307 @param name: Live data field name
1308
1309 """
1310 def fn(ctx, inst):
1311 """Get live data for an instance.
1312
1313 @type ctx: L{InstanceQueryData}
1314 @type inst: L{objects.Instance}
1315 @param inst: Instance object
1316
1317 """
1318 if (inst.primary_node in ctx.bad_nodes or
1319 inst.primary_node in ctx.offline_nodes):
1320
1321
1322 return _FS_NODATA
1323
1324 if inst.name in ctx.live_data:
1325 data = ctx.live_data[inst.name]
1326 if name in data:
1327 return data[name]
1328
1329 return _FS_UNAVAIL
1330
1331 return fn
1332
1333
1360
1361
1363 """Build function for retrieving disk size.
1364
1365 @type index: int
1366 @param index: Disk index
1367
1368 """
1369 def fn(_, inst):
1370 """Get size of a disk.
1371
1372 @type inst: L{objects.Instance}
1373 @param inst: Instance object
1374
1375 """
1376 try:
1377 return inst.disks[index].size
1378 except IndexError:
1379 return _FS_UNAVAIL
1380
1381 return fn
1382
1383
1385 """Build function for calling another function with an instance NIC.
1386
1387 @type index: int
1388 @param index: NIC index
1389 @type cb: callable
1390 @param cb: Callback
1391
1392 """
1393 def fn(ctx, inst):
1394 """Call helper function with instance NIC.
1395
1396 @type ctx: L{InstanceQueryData}
1397 @type inst: L{objects.Instance}
1398 @param inst: Instance object
1399
1400 """
1401 try:
1402 nic = inst.nics[index]
1403 except IndexError:
1404 return _FS_UNAVAIL
1405
1406 return cb(ctx, index, nic)
1407
1408 return fn
1409
1410
1412 """Get a NIC's IP address.
1413
1414 @type ctx: L{InstanceQueryData}
1415 @type nic: L{objects.NIC}
1416 @param nic: NIC object
1417
1418 """
1419 if nic.ip is None:
1420 return _FS_UNAVAIL
1421 else:
1422 return nic.ip
1423
1424
1426 """Get a NIC's bridge.
1427
1428 @type ctx: L{InstanceQueryData}
1429 @type index: int
1430 @param index: NIC index
1431
1432 """
1433 assert len(ctx.inst_nicparams) >= index
1434
1435 nicparams = ctx.inst_nicparams[index]
1436
1437 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1438 return nicparams[constants.NIC_LINK]
1439 else:
1440 return _FS_UNAVAIL
1441
1442
1444 """Get all network bridges for an instance.
1445
1446 @type ctx: L{InstanceQueryData}
1447 @type inst: L{objects.Instance}
1448 @param inst: Instance object
1449
1450 """
1451 assert len(ctx.inst_nicparams) == len(inst.nics)
1452
1453 result = []
1454
1455 for nicp in ctx.inst_nicparams:
1456 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1457 result.append(nicp[constants.NIC_LINK])
1458 else:
1459 result.append(None)
1460
1461 assert len(result) == len(inst.nics)
1462
1463 return result
1464
1465
1467 """Build function for retrieving a NIC parameter.
1468
1469 @type name: string
1470 @param name: Parameter name
1471
1472 """
1473 def fn(ctx, index, _):
1474 """Get a NIC's bridge.
1475
1476 @type ctx: L{InstanceQueryData}
1477 @type inst: L{objects.Instance}
1478 @param inst: Instance object
1479 @type nic: L{objects.NIC}
1480 @param nic: NIC object
1481
1482 """
1483 assert len(ctx.inst_nicparams) >= index
1484 return ctx.inst_nicparams[index][name]
1485
1486 return fn
1487
1488
1490 """Get instance fields involving network interfaces.
1491
1492 @return: Tuple containing list of field definitions used as input for
1493 L{_PrepareFieldList} and a list of aliases
1494
1495 """
1496 nic_mac_fn = lambda ctx, _, nic: nic.mac
1497 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1498 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1499
1500 fields = [
1501
1502 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1503 "Number of network interfaces"),
1504 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1505 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1506 "List containing each network interface's MAC address"),
1507 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1508 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1509 "List containing each network interface's IP address"),
1510 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1511 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1512 "List containing each network interface's mode"), IQ_CONFIG, 0,
1513 lambda ctx, inst: [nicp[constants.NIC_MODE]
1514 for nicp in ctx.inst_nicparams]),
1515 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1516 "List containing each network interface's link"), IQ_CONFIG, 0,
1517 lambda ctx, inst: [nicp[constants.NIC_LINK]
1518 for nicp in ctx.inst_nicparams]),
1519 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1520 "List containing each network interface's bridge"),
1521 IQ_CONFIG, 0, _GetInstAllNicBridges),
1522 ]
1523
1524
1525 for i in range(constants.MAX_NICS):
1526 numtext = utils.FormatOrdinal(i + 1)
1527 fields.extend([
1528 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1529 "IP address of %s network interface" % numtext),
1530 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1531 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1532 "MAC address of %s network interface" % numtext),
1533 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1534 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1535 "Mode of %s network interface" % numtext),
1536 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1537 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1538 "Link of %s network interface" % numtext),
1539 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1540 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1541 "Bridge of %s network interface" % numtext),
1542 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1543 ])
1544
1545 aliases = [
1546
1547 ("ip", "nic.ip/0"),
1548 ("mac", "nic.mac/0"),
1549 ("bridge", "nic.bridge/0"),
1550 ("nic_mode", "nic.mode/0"),
1551 ("nic_link", "nic.link/0"),
1552 ]
1553
1554 return (fields, aliases)
1555
1556
1558 """Get disk usage for an instance.
1559
1560 @type ctx: L{InstanceQueryData}
1561 @type inst: L{objects.Instance}
1562 @param inst: Instance object
1563
1564 """
1565 usage = ctx.disk_usage[inst.name]
1566
1567 if usage is None:
1568 usage = 0
1569
1570 return usage
1571
1572
1574 """Get console information for instance.
1575
1576 @type ctx: L{InstanceQueryData}
1577 @type inst: L{objects.Instance}
1578 @param inst: Instance object
1579
1580 """
1581 consinfo = ctx.console[inst.name]
1582
1583 if consinfo is None:
1584 return _FS_UNAVAIL
1585
1586 return consinfo
1587
1588
1590 """Get instance fields involving disks.
1591
1592 @return: List of field definitions used as input for L{_PrepareFieldList}
1593
1594 """
1595 fields = [
1596 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1597 "Total disk space used by instance on each of its nodes;"
1598 " this is not the disk size visible to the instance, but"
1599 " the usage on the node"),
1600 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1601 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1602 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1603 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1604 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1605 ]
1606
1607
1608 fields.extend([
1609 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1610 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1611 IQ_CONFIG, 0, _GetInstDiskSize(i))
1612 for i in range(constants.MAX_DISKS)
1613 ])
1614
1615 return fields
1616
1617
1619 """Get instance fields involving parameters.
1620
1621 @return: List of field definitions used as input for L{_PrepareFieldList}
1622
1623 """
1624
1625 be_title = {
1626 constants.BE_AUTO_BALANCE: "Auto_balance",
1627 constants.BE_MEMORY: "ConfigMemory",
1628 constants.BE_VCPUS: "ConfigVCPUs",
1629 }
1630
1631 hv_title = {
1632 constants.HV_ACPI: "ACPI",
1633 constants.HV_BOOT_ORDER: "Boot_order",
1634 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1635 constants.HV_DISK_TYPE: "Disk_type",
1636 constants.HV_INITRD_PATH: "Initrd_path",
1637 constants.HV_KERNEL_PATH: "Kernel_path",
1638 constants.HV_NIC_TYPE: "NIC_type",
1639 constants.HV_PAE: "PAE",
1640 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1641 }
1642
1643 fields = [
1644
1645 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1646 "Hypervisor parameters (merged)"),
1647 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1648 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1649 "Backend parameters (merged)"),
1650 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1651 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1652 "Operating system parameters (merged)"),
1653 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1654
1655
1656 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1657 "Custom hypervisor parameters"),
1658 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1659 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1660 "Custom backend parameters",),
1661 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1662 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1663 "Custom operating system parameters",),
1664 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1665 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1666 "Custom network interface parameters"),
1667 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1668 ]
1669
1670
1671 def _GetInstHvParam(name):
1672 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1673
1674 fields.extend([
1675 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1676 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1677 IQ_CONFIG, 0, _GetInstHvParam(name))
1678 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1679 if name not in constants.HVC_GLOBALS
1680 ])
1681
1682
1683 def _GetInstBeParam(name):
1684 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1685
1686 fields.extend([
1687 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1688 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1689 IQ_CONFIG, 0, _GetInstBeParam(name))
1690 for name, kind in constants.BES_PARAMETER_TYPES.items()
1691 ])
1692
1693 return fields
1694
1695
1696 _INST_SIMPLE_FIELDS = {
1697 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1698 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1699 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1700
1701 "network_port": ("Network_port", QFT_OTHER, 0,
1702 "Instance network port if available (e.g. for VNC console)"),
1703 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1704 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1705 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1706 }
1707
1708
1710 """Gets group UUID of an instance node.
1711
1712 @type ctx: L{InstanceQueryData}
1713 @param default: Default value
1714 @type node_name: string
1715 @param node_name: Node name
1716
1717 """
1718 try:
1719 node = ctx.nodes[node_name]
1720 except KeyError:
1721 return default
1722 else:
1723 return node.group
1724
1725
1727 """Gets group name of an instance node.
1728
1729 @type ctx: L{InstanceQueryData}
1730 @param default: Default value
1731 @type node_name: string
1732 @param node_name: Node name
1733
1734 """
1735 try:
1736 node = ctx.nodes[node_name]
1737 except KeyError:
1738 return default
1739
1740 try:
1741 group = ctx.groups[node.group]
1742 except KeyError:
1743 return default
1744
1745 return group.name
1746
1747
1749 """Builds list of fields for instance queries.
1750
1751 """
1752 fields = [
1753 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1754 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1755 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1756 "Primary node's group"),
1757 IQ_NODES, 0,
1758 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1759 inst.primary_node)),
1760 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1761 "Primary node's group UUID"),
1762 IQ_NODES, 0,
1763 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1764
1765 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1766 "Secondary nodes; usually this will just be one node"),
1767 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1768 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1769 "Node groups of secondary nodes"),
1770 IQ_NODES, 0,
1771 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1772 inst.secondary_nodes)),
1773 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1774 "Node group UUIDs of secondary nodes"),
1775 IQ_NODES, 0,
1776 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1777 inst.secondary_nodes)),
1778 (_MakeField("admin_state", "Autostart", QFT_BOOL,
1779 "Desired state of instance (if set, the instance should be"
1780 " up)"),
1781 IQ_CONFIG, 0, _GetItemAttr("admin_up")),
1782 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1783 lambda ctx, inst: list(inst.GetTags())),
1784 (_MakeField("console", "Console", QFT_OTHER,
1785 "Instance console information"), IQ_CONSOLE, 0,
1786 _GetInstanceConsole),
1787 ]
1788
1789
1790 fields.extend([
1791 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
1792 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
1793 ])
1794
1795
1796 fields.extend([
1797 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
1798 IQ_LIVE, 0, _GetInstOperState),
1799 (_MakeField("oper_ram", "Memory", QFT_UNIT,
1800 "Actual memory usage as seen by hypervisor"),
1801 IQ_LIVE, 0, _GetInstLiveData("memory")),
1802 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
1803 "Actual number of VCPUs as seen by hypervisor"),
1804 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
1805 ])
1806
1807
1808 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
1809 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
1810 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
1811 constants.INSTST_NODEOFFLINE)
1812 status_doc = ("Instance status; \"%s\" if instance is set to be running"
1813 " and actually is, \"%s\" if instance is stopped and"
1814 " is not running, \"%s\" if instance running, but not on its"
1815 " designated primary node, \"%s\" if instance should be"
1816 " stopped, but is actually running, \"%s\" if instance should"
1817 " run, but doesn't, \"%s\" if instance's primary node is down,"
1818 " \"%s\" if instance's primary node is marked offline" %
1819 status_values)
1820 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
1821 IQ_LIVE, 0, _GetInstStatus))
1822 assert set(status_values) == constants.INSTST_ALL, \
1823 "Status documentation mismatch"
1824
1825 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1826
1827 fields.extend(network_fields)
1828 fields.extend(_GetInstanceParameterFields())
1829 fields.extend(_GetInstanceDiskFields())
1830 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1831
1832 aliases = [
1833 ("vcpus", "be/vcpus"),
1834 ("sda_size", "disk.size/0"),
1835 ("sdb_size", "disk.size/1"),
1836 ] + network_aliases
1837
1838 return _PrepareFieldList(fields, aliases)
1839
1840
1842 """Data container for lock data queries.
1843
1844 """
1846 """Initializes this class.
1847
1848 """
1849 self.lockdata = lockdata
1850
1852 """Iterate over all locks.
1853
1854 """
1855 return iter(self.lockdata)
1856
1857
1859 """Returns a sorted list of a lock's current owners.
1860
1861 """
1862 (_, _, owners, _) = data
1863
1864 if owners:
1865 owners = utils.NiceSort(owners)
1866
1867 return owners
1868
1869
1871 """Returns a sorted list of a lock's pending acquires.
1872
1873 """
1874 (_, _, _, pending) = data
1875
1876 if pending:
1877 pending = [(mode, utils.NiceSort(names))
1878 for (mode, names) in pending]
1879
1880 return pending
1881
1882
1884 """Builds list of fields for lock queries.
1885
1886 """
1887 return _PrepareFieldList([
1888
1889 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
1890 lambda ctx, (name, mode, owners, pending): name),
1891 (_MakeField("mode", "Mode", QFT_OTHER,
1892 "Mode in which the lock is currently acquired"
1893 " (exclusive or shared)"),
1894 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
1895 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
1896 LQ_OWNER, 0, _GetLockOwners),
1897 (_MakeField("pending", "Pending", QFT_OTHER,
1898 "Threads waiting for the lock"),
1899 LQ_PENDING, 0, _GetLockPending),
1900 ], [])
1901
1902
1904 """Data container for node group data queries.
1905
1906 """
1907 - def __init__(self, groups, group_to_nodes, group_to_instances):
1908 """Initializes this class.
1909
1910 @param groups: List of node group objects
1911 @type group_to_nodes: dict; group UUID as key
1912 @param group_to_nodes: Per-group list of nodes
1913 @type group_to_instances: dict; group UUID as key
1914 @param group_to_instances: Per-group list of (primary) instances
1915
1916 """
1917 self.groups = groups
1918 self.group_to_nodes = group_to_nodes
1919 self.group_to_instances = group_to_instances
1920
1922 """Iterate over all node groups.
1923
1924 """
1925 return iter(self.groups)
1926
1927
1928 _GROUP_SIMPLE_FIELDS = {
1929 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
1930 "name": ("Group", QFT_TEXT, "Group name"),
1931 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
1932 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
1933 "ndparams": ("NDParams", QFT_OTHER, "Node parameters"),
1934 }
1935
1936
1938 """Builds list of fields for node group queries.
1939
1940 """
1941
1942 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
1943 _GetItemAttr(name))
1944 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
1945
1946 def _GetLength(getter):
1947 return lambda ctx, group: len(getter(ctx)[group.uuid])
1948
1949 def _GetSortedList(getter):
1950 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
1951
1952 group_to_nodes = operator.attrgetter("group_to_nodes")
1953 group_to_instances = operator.attrgetter("group_to_instances")
1954
1955
1956 fields.extend([
1957 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
1958 GQ_NODE, 0, _GetLength(group_to_nodes)),
1959 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
1960 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
1961 ])
1962
1963
1964 fields.extend([
1965 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
1966 "Number of primary instances"),
1967 GQ_INST, 0, _GetLength(group_to_instances)),
1968 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
1969 "List of primary instances"),
1970 GQ_INST, 0, _GetSortedList(group_to_instances)),
1971 ])
1972
1973
1974 fields.extend([
1975 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
1976 lambda ctx, group: list(group.GetTags())),
1977 ])
1978
1979 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1980
1981 return _PrepareFieldList(fields, [])
1982
1983
1984 -class OsInfo(objects.ConfigObject):
1985 __slots__ = [
1986 "name",
1987 "valid",
1988 "hidden",
1989 "blacklisted",
1990 "variants",
1991 "api_versions",
1992 "parameters",
1993 "node_status",
1994 ]
1995
1996
1998 """Builds list of fields for operating system queries.
1999
2000 """
2001 fields = [
2002 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2003 None, 0, _GetItemAttr("name")),
2004 (_MakeField("valid", "Valid", QFT_BOOL,
2005 "Whether operating system definition is valid"),
2006 None, 0, _GetItemAttr("valid")),
2007 (_MakeField("hidden", "Hidden", QFT_BOOL,
2008 "Whether operating system is hidden"),
2009 None, 0, _GetItemAttr("hidden")),
2010 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2011 "Whether operating system is blacklisted"),
2012 None, 0, _GetItemAttr("blacklisted")),
2013 (_MakeField("variants", "Variants", QFT_OTHER,
2014 "Operating system variants"),
2015 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2016 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2017 "Operating system API versions"),
2018 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2019 (_MakeField("parameters", "Parameters", QFT_OTHER,
2020 "Operating system parameters"),
2021 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2022 _GetItemAttr("parameters"))),
2023 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2024 "Status from node"),
2025 None, 0, _GetItemAttr("node_status")),
2026 ]
2027
2028 return _PrepareFieldList(fields, [])
2029
2030
2031
2032 NODE_FIELDS = _BuildNodeFields()
2033
2034
2035 INSTANCE_FIELDS = _BuildInstanceFields()
2036
2037
2038 LOCK_FIELDS = _BuildLockFields()
2039
2040
2041 GROUP_FIELDS = _BuildGroupFields()
2042
2043
2044 OS_FIELDS = _BuildOsFields()
2045
2046
2047 ALL_FIELDS = {
2048 constants.QR_INSTANCE: INSTANCE_FIELDS,
2049 constants.QR_NODE: NODE_FIELDS,
2050 constants.QR_LOCK: LOCK_FIELDS,
2051 constants.QR_GROUP: GROUP_FIELDS,
2052 constants.QR_OS: OS_FIELDS,
2053 }
2054
2055
2056 ALL_FIELD_LISTS = ALL_FIELDS.values()
2057