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