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 """Return a field function to return an ND parameter out of the context.
982
983 """
984 def _helper(ctx, _):
985 if ctx.ndparams is None:
986 return _FS_UNAVAIL
987 else:
988 return ctx.ndparams.get(name, None)
989 return _helper
990
991
1008
1009
1011 """Wrapper for converting values.
1012
1013 @param convert: Conversion function receiving value as single parameter
1014 @param fn: Retrieval function
1015
1016 """
1017 value = fn(ctx, item)
1018
1019
1020 if compat.any(value is fs for fs in _FS_ALL):
1021
1022 return value
1023
1024
1025 return convert(value)
1026
1027
1029 """Convenience wrapper for L{_ConvWrapInner}.
1030
1031 @param convert: Conversion function receiving value as single parameter
1032 @param fn: Retrieval function
1033
1034 """
1035 return compat.partial(_ConvWrapInner, convert, fn)
1036
1037
1039 """Returns function for getting timestamp of item.
1040
1041 @type getter: callable
1042 @param getter: Function to retrieve timestamp attribute
1043
1044 """
1045 def fn(_, item):
1046 """Returns a timestamp of item.
1047
1048 """
1049 timestamp = getter(item)
1050 if timestamp is None:
1051
1052 return _FS_UNAVAIL
1053 else:
1054 return timestamp
1055
1056 return fn
1057
1058
1060 """Returns common timestamp fields.
1061
1062 @param datatype: Field data type for use by L{Query.RequestedData}
1063
1064 """
1065 return [
1066 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1067 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1068 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1069 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1070 ]
1071
1072
1074 """Data container for node data queries.
1075
1076 """
1077 - def __init__(self, nodes, live_data, master_name, node_to_primary,
1078 node_to_secondary, groups, oob_support, cluster):
1079 """Initializes this class.
1080
1081 """
1082 self.nodes = nodes
1083 self.live_data = live_data
1084 self.master_name = master_name
1085 self.node_to_primary = node_to_primary
1086 self.node_to_secondary = node_to_secondary
1087 self.groups = groups
1088 self.oob_support = oob_support
1089 self.cluster = cluster
1090
1091
1092 self.curlive_data = None
1093 self.ndparams = None
1094
1096 """Iterate over all nodes.
1097
1098 This function has side-effects and only one instance of the resulting
1099 generator should be used at a time.
1100
1101 """
1102 for node in self.nodes:
1103 group = self.groups.get(node.group, None)
1104 if group is None:
1105 self.ndparams = None
1106 else:
1107 self.ndparams = self.cluster.FillND(node, group)
1108 if self.live_data:
1109 self.curlive_data = self.live_data.get(node.name, None)
1110 else:
1111 self.curlive_data = None
1112 yield node
1113
1114
1115
1116 _NODE_SIMPLE_FIELDS = {
1117 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1118 "master_candidate": ("MasterC", QFT_BOOL, 0,
1119 "Whether node is a master candidate"),
1120 "master_capable": ("MasterCapable", QFT_BOOL, 0,
1121 "Whether node can become a master candidate"),
1122 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1123 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1124 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1125 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1126 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1127 }
1128
1129
1130
1131
1132 _NODE_LIVE_FIELDS = {
1133 "bootid": ("BootID", QFT_TEXT, "bootid",
1134 "Random UUID renewed for each system reboot, can be used"
1135 " for detecting reboots by tracking changes"),
1136 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1137 "Number of NUMA domains on node (if exported by hypervisor)"),
1138 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1139 "Number of physical CPU sockets (if exported by hypervisor)"),
1140 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1141 "dfree": ("DFree", QFT_UNIT, "vg_free",
1142 "Available disk space in volume group"),
1143 "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1144 "Total disk space in volume group used for instance disk"
1145 " allocation"),
1146 "mfree": ("MFree", QFT_UNIT, "memory_free",
1147 "Memory available for instance allocations"),
1148 "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1149 "Amount of memory used by node (dom0 for Xen)"),
1150 "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1151 "Total amount of memory of physical machine"),
1152 }
1153
1154
1156 """Build function for calling another function with an node group.
1157
1158 @param cb: The callback to be called with the nodegroup
1159
1160 """
1161 def fn(ctx, node):
1162 """Get group data for a node.
1163
1164 @type ctx: L{NodeQueryData}
1165 @type inst: L{objects.Node}
1166 @param inst: Node object
1167
1168 """
1169 ng = ctx.groups.get(node.group, None)
1170 if ng is None:
1171
1172 return _FS_UNAVAIL
1173
1174 return cb(ctx, node, ng)
1175
1176 return fn
1177
1178
1180 """Returns the name of a node's group.
1181
1182 @type ctx: L{NodeQueryData}
1183 @type node: L{objects.Node}
1184 @param node: Node object
1185 @type ng: L{objects.NodeGroup}
1186 @param ng: The node group this node belongs to
1187
1188 """
1189 return ng.name
1190
1191
1193 """Returns the node powered state
1194
1195 @type ctx: L{NodeQueryData}
1196 @type node: L{objects.Node}
1197 @param node: Node object
1198
1199 """
1200 if ctx.oob_support[node.name]:
1201 return node.powered
1202
1203 return _FS_UNAVAIL
1204
1205
1207 """Returns the ndparams for this node.
1208
1209 @type ctx: L{NodeQueryData}
1210 @type node: L{objects.Node}
1211 @param node: Node object
1212 @type ng: L{objects.NodeGroup}
1213 @param ng: The node group this node belongs to
1214
1215 """
1216 return ctx.cluster.SimpleFillND(ng.FillND(node))
1217
1218
1220 """Gets the value of a "live" field from L{NodeQueryData}.
1221
1222 @param field: Live field name
1223 @param kind: Data kind, one of L{constants.QFT_ALL}
1224 @type ctx: L{NodeQueryData}
1225 @type node: L{objects.Node}
1226 @param node: Node object
1227
1228 """
1229 if node.offline:
1230 return _FS_OFFLINE
1231
1232 if not node.vm_capable:
1233 return _FS_UNAVAIL
1234
1235 if not ctx.curlive_data:
1236 return _FS_NODATA
1237
1238 return _GetStatsField(field, kind, ctx.curlive_data)
1239
1240
1242 """Gets a value from live statistics.
1243
1244 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is
1245 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL}
1246 is returned.
1247
1248 @param field: Live field name
1249 @param kind: Data kind, one of L{constants.QFT_ALL}
1250 @type data: dict
1251 @param data: Statistics
1252
1253 """
1254 try:
1255 value = data[field]
1256 except KeyError:
1257 return _FS_UNAVAIL
1258
1259 if kind == QFT_TEXT:
1260 return value
1261
1262 assert kind in (QFT_NUMBER, QFT_UNIT)
1263
1264
1265 try:
1266 return int(value)
1267 except (ValueError, TypeError):
1268 logging.exception("Failed to convert node field '%s' (value %r) to int",
1269 field, value)
1270 return _FS_UNAVAIL
1271
1272
1274 """Converts node's hypervisor state for query result.
1275
1276 """
1277 hv_state = node.hv_state
1278
1279 if hv_state is None:
1280 return _FS_UNAVAIL
1281
1282 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1283
1284
1286 """Converts node's disk state for query result.
1287
1288 """
1289 disk_state = node.disk_state
1290
1291 if disk_state is None:
1292 return _FS_UNAVAIL
1293
1294 return dict((disk_kind, dict((name, value.ToDict())
1295 for (name, value) in kind_state.items()))
1296 for (disk_kind, kind_state) in disk_state.items())
1297
1298
1300 """Builds list of fields for node queries.
1301
1302 """
1303 fields = [
1304 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1305 NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1306 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1307 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1308 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1309 lambda ctx, node: list(node.GetTags())),
1310 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1311 NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1312 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1313 _GetGroup(_GetNodeGroup)),
1314 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1315 NQ_CONFIG, 0, _GetItemAttr("group")),
1316 (_MakeField("powered", "Powered", QFT_BOOL,
1317 "Whether node is thought to be powered on"),
1318 NQ_OOB, 0, _GetNodePower),
1319 (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1320 "Merged node parameters"),
1321 NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1322 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1323 "Custom node parameters"),
1324 NQ_GROUP, 0, _GetItemAttr("ndparams")),
1325 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1326 NQ_CONFIG, 0, _GetNodeHvState),
1327 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1328 NQ_CONFIG, 0, _GetNodeDiskState),
1329 ]
1330
1331 fields.extend(_BuildNDFields(False))
1332
1333
1334 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1335 constants.NR_REGULAR, constants.NR_DRAINED,
1336 constants.NR_OFFLINE)
1337 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1338 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1339 role_values)
1340 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1341 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1342 assert set(role_values) == constants.NR_ALL
1343
1344 def _GetLength(getter):
1345 return lambda ctx, node: len(getter(ctx)[node.name])
1346
1347 def _GetList(getter):
1348 return lambda ctx, node: list(getter(ctx)[node.name])
1349
1350
1351 for prefix, titleprefix, docword, getter in \
1352 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1353 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1354
1355 fields.extend([
1356 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1357 "Number of instances with this node as %s" % docword),
1358 NQ_INST, 0, _GetLength(getter)),
1359 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1360 QFT_OTHER,
1361 "List of instances with this node as %s" % docword),
1362 NQ_INST, 0, _GetList(getter)),
1363 ])
1364
1365
1366 fields.extend([
1367 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1368 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
1369
1370
1371 fields.extend([
1372 (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1373 compat.partial(_GetLiveNodeField, nfield, kind))
1374 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
1375
1376
1377 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1378
1379 return _PrepareFieldList(fields, [])
1380
1381
1383 """Data container for instance data queries.
1384
1385 """
1386 - def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1387 live_data, wrongnode_inst, console, nodes, groups, networks):
1388 """Initializes this class.
1389
1390 @param instances: List of instance objects
1391 @param cluster: Cluster object
1392 @type disk_usage: dict; instance name as key
1393 @param disk_usage: Per-instance disk usage
1394 @type offline_nodes: list of strings
1395 @param offline_nodes: List of offline nodes
1396 @type bad_nodes: list of strings
1397 @param bad_nodes: List of faulty nodes
1398 @type live_data: dict; instance name as key
1399 @param live_data: Per-instance live data
1400 @type wrongnode_inst: set
1401 @param wrongnode_inst: Set of instances running on wrong node(s)
1402 @type console: dict; instance name as key
1403 @param console: Per-instance console information
1404 @type nodes: dict; node name as key
1405 @param nodes: Node objects
1406 @type networks: dict; net_uuid as key
1407 @param networks: Network objects
1408
1409 """
1410 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1411 "Offline nodes not included in bad nodes"
1412 assert not (set(live_data.keys()) & set(bad_nodes)), \
1413 "Found live data for bad or offline nodes"
1414
1415 self.instances = instances
1416 self.cluster = cluster
1417 self.disk_usage = disk_usage
1418 self.offline_nodes = offline_nodes
1419 self.bad_nodes = bad_nodes
1420 self.live_data = live_data
1421 self.wrongnode_inst = wrongnode_inst
1422 self.console = console
1423 self.nodes = nodes
1424 self.groups = groups
1425 self.networks = networks
1426
1427
1428 self.inst_hvparams = None
1429 self.inst_beparams = None
1430 self.inst_osparams = None
1431 self.inst_nicparams = None
1432
1434 """Iterate over all instances.
1435
1436 This function has side-effects and only one instance of the resulting
1437 generator should be used at a time.
1438
1439 """
1440 for inst in self.instances:
1441 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1442 self.inst_beparams = self.cluster.FillBE(inst)
1443 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1444 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1445 for nic in inst.nics]
1446
1447 yield inst
1448
1449
1451 """Get instance's operational status.
1452
1453 @type ctx: L{InstanceQueryData}
1454 @type inst: L{objects.Instance}
1455 @param inst: Instance object
1456
1457 """
1458
1459
1460 if inst.primary_node in ctx.bad_nodes:
1461 return _FS_NODATA
1462 else:
1463 return bool(ctx.live_data.get(inst.name))
1464
1465
1467 """Build function for retrieving live data.
1468
1469 @type name: string
1470 @param name: Live data field name
1471
1472 """
1473 def fn(ctx, inst):
1474 """Get live data for an instance.
1475
1476 @type ctx: L{InstanceQueryData}
1477 @type inst: L{objects.Instance}
1478 @param inst: Instance object
1479
1480 """
1481 if (inst.primary_node in ctx.bad_nodes or
1482 inst.primary_node in ctx.offline_nodes):
1483
1484
1485 return _FS_NODATA
1486
1487 if inst.name in ctx.live_data:
1488 data = ctx.live_data[inst.name]
1489 if name in data:
1490 return data[name]
1491
1492 return _FS_UNAVAIL
1493
1494 return fn
1495
1496
1525
1526
1528 """Build function for retrieving disk size.
1529
1530 @type index: int
1531 @param index: Disk index
1532
1533 """
1534 def fn(_, inst):
1535 """Get size of a disk.
1536
1537 @type inst: L{objects.Instance}
1538 @param inst: Instance object
1539
1540 """
1541 try:
1542 return inst.disks[index].size
1543 except IndexError:
1544 return _FS_UNAVAIL
1545
1546 return fn
1547
1548
1550 """Build function for calling another function with an instance NIC.
1551
1552 @type index: int
1553 @param index: NIC index
1554 @type cb: callable
1555 @param cb: Callback
1556
1557 """
1558 def fn(ctx, inst):
1559 """Call helper function with instance NIC.
1560
1561 @type ctx: L{InstanceQueryData}
1562 @type inst: L{objects.Instance}
1563 @param inst: Instance object
1564
1565 """
1566 try:
1567 nic = inst.nics[index]
1568 except IndexError:
1569 return _FS_UNAVAIL
1570
1571 return cb(ctx, index, nic)
1572
1573 return fn
1574
1575
1577 """Get a NIC's Network.
1578
1579 @type ctx: L{InstanceQueryData}
1580 @type nic: L{objects.NIC}
1581 @param nic: NIC object
1582
1583 """
1584 if nic.network is None:
1585 return _FS_UNAVAIL
1586 else:
1587 return ctx.networks[nic.network].name
1588
1589
1591 """Get a NIC's Network.
1592
1593 @type ctx: L{InstanceQueryData}
1594 @type nic: L{objects.NIC}
1595 @param nic: NIC object
1596
1597 """
1598 if nic.network is None:
1599 return _FS_UNAVAIL
1600 else:
1601 return nic.network
1602
1603
1605 """Get a NIC's IP address.
1606
1607 @type ctx: L{InstanceQueryData}
1608 @type nic: L{objects.NIC}
1609 @param nic: NIC object
1610
1611 """
1612 if nic.ip is None:
1613 return _FS_UNAVAIL
1614 else:
1615 return nic.ip
1616
1617
1619 """Get a NIC's bridge.
1620
1621 @type ctx: L{InstanceQueryData}
1622 @type index: int
1623 @param index: NIC index
1624
1625 """
1626 assert len(ctx.inst_nicparams) >= index
1627
1628 nicparams = ctx.inst_nicparams[index]
1629
1630 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1631 return nicparams[constants.NIC_LINK]
1632 else:
1633 return _FS_UNAVAIL
1634
1635
1637 """Get all network names for an instance.
1638
1639 @type ctx: L{InstanceQueryData}
1640 @type inst: L{objects.Instance}
1641 @param inst: Instance object
1642
1643 """
1644 result = []
1645
1646 for nic in inst.nics:
1647 name = None
1648 if nic.network:
1649 name = ctx.networks[nic.network].name
1650 result.append(name)
1651
1652 assert len(result) == len(inst.nics)
1653
1654 return result
1655
1656
1658 """Get all network bridges for an instance.
1659
1660 @type ctx: L{InstanceQueryData}
1661 @type inst: L{objects.Instance}
1662 @param inst: Instance object
1663
1664 """
1665 assert len(ctx.inst_nicparams) == len(inst.nics)
1666
1667 result = []
1668
1669 for nicp in ctx.inst_nicparams:
1670 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1671 result.append(nicp[constants.NIC_LINK])
1672 else:
1673 result.append(None)
1674
1675 assert len(result) == len(inst.nics)
1676
1677 return result
1678
1679
1681 """Build function for retrieving a NIC parameter.
1682
1683 @type name: string
1684 @param name: Parameter name
1685
1686 """
1687 def fn(ctx, index, _):
1688 """Get a NIC's bridge.
1689
1690 @type ctx: L{InstanceQueryData}
1691 @type inst: L{objects.Instance}
1692 @param inst: Instance object
1693 @type nic: L{objects.NIC}
1694 @param nic: NIC object
1695
1696 """
1697 assert len(ctx.inst_nicparams) >= index
1698 return ctx.inst_nicparams[index][name]
1699
1700 return fn
1701
1702
1704 """Get instance fields involving network interfaces.
1705
1706 @return: Tuple containing list of field definitions used as input for
1707 L{_PrepareFieldList} and a list of aliases
1708
1709 """
1710 nic_mac_fn = lambda ctx, _, nic: nic.mac
1711 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1712 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1713
1714 fields = [
1715
1716 (_MakeField("nic.count", "NICs", QFT_NUMBER,
1717 "Number of network interfaces"),
1718 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1719 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1720 "List containing each network interface's MAC address"),
1721 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1722 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1723 "List containing each network interface's IP address"),
1724 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1725 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1726 "List containing each network interface's mode"), IQ_CONFIG, 0,
1727 lambda ctx, inst: [nicp[constants.NIC_MODE]
1728 for nicp in ctx.inst_nicparams]),
1729 (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1730 "List containing each network interface's link"), IQ_CONFIG, 0,
1731 lambda ctx, inst: [nicp[constants.NIC_LINK]
1732 for nicp in ctx.inst_nicparams]),
1733 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1734 "List containing each network interface's bridge"),
1735 IQ_CONFIG, 0, _GetInstAllNicBridges),
1736 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1737 "List containing each interface's network"), IQ_CONFIG, 0,
1738 lambda ctx, inst: [nic.network for nic in inst.nics]),
1739 (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER,
1740 "List containing each interface's network"),
1741 IQ_NETWORKS, 0, _GetInstAllNicNetworkNames)
1742 ]
1743
1744
1745 for i in range(constants.MAX_NICS):
1746 numtext = utils.FormatOrdinal(i + 1)
1747 fields.extend([
1748 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1749 "IP address of %s network interface" % numtext),
1750 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1751 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1752 "MAC address of %s network interface" % numtext),
1753 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1754 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1755 "Mode of %s network interface" % numtext),
1756 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1757 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1758 "Link of %s network interface" % numtext),
1759 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1760 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1761 "Bridge of %s network interface" % numtext),
1762 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1763 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1764 "Network of %s network interface" % numtext),
1765 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1766 (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT,
1767 "Network name of %s network interface" % numtext),
1768 IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)),
1769 ])
1770
1771 aliases = [
1772
1773 ("ip", "nic.ip/0"),
1774 ("mac", "nic.mac/0"),
1775 ("bridge", "nic.bridge/0"),
1776 ("nic_mode", "nic.mode/0"),
1777 ("nic_link", "nic.link/0"),
1778 ("nic_network", "nic.network/0"),
1779 ]
1780
1781 return (fields, aliases)
1782
1783
1785 """Get disk usage for an instance.
1786
1787 @type ctx: L{InstanceQueryData}
1788 @type inst: L{objects.Instance}
1789 @param inst: Instance object
1790
1791 """
1792 usage = ctx.disk_usage[inst.name]
1793
1794 if usage is None:
1795 usage = 0
1796
1797 return usage
1798
1799
1801 """Get console information for instance.
1802
1803 @type ctx: L{InstanceQueryData}
1804 @type inst: L{objects.Instance}
1805 @param inst: Instance object
1806
1807 """
1808 consinfo = ctx.console[inst.name]
1809
1810 if consinfo is None:
1811 return _FS_UNAVAIL
1812
1813 return consinfo
1814
1815
1817 """Get instance fields involving disks.
1818
1819 @return: List of field definitions used as input for L{_PrepareFieldList}
1820
1821 """
1822 fields = [
1823 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1824 "Total disk space used by instance on each of its nodes;"
1825 " this is not the disk size visible to the instance, but"
1826 " the usage on the node"),
1827 IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1828 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1829 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1830 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1831 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1832 ]
1833
1834
1835 fields.extend([
1836 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1837 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
1838 IQ_CONFIG, 0, _GetInstDiskSize(i))
1839 for i in range(constants.MAX_DISKS)])
1840
1841 return fields
1842
1843
1845 """Get instance fields involving parameters.
1846
1847 @return: List of field definitions used as input for L{_PrepareFieldList}
1848
1849 """
1850 fields = [
1851
1852 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1853 "Hypervisor parameters (merged)"),
1854 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1855 (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1856 "Backend parameters (merged)"),
1857 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1858 (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1859 "Operating system parameters (merged)"),
1860 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1861
1862
1863 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1864 "Custom hypervisor parameters"),
1865 IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1866 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1867 "Custom backend parameters",),
1868 IQ_CONFIG, 0, _GetItemAttr("beparams")),
1869 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1870 "Custom operating system parameters",),
1871 IQ_CONFIG, 0, _GetItemAttr("osparams")),
1872 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1873 "Custom network interface parameters"),
1874 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1875 ]
1876
1877
1878 def _GetInstHvParam(name):
1879 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1880
1881 fields.extend([
1882 (_MakeField("hv/%s" % name,
1883 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
1884 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1885 IQ_CONFIG, 0, _GetInstHvParam(name))
1886 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1887 if name not in constants.HVC_GLOBALS])
1888
1889
1890 def _GetInstBeParam(name):
1891 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1892
1893 fields.extend([
1894 (_MakeField("be/%s" % name,
1895 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
1896 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1897 IQ_CONFIG, 0, _GetInstBeParam(name))
1898 for name, kind in constants.BES_PARAMETER_TYPES.items()])
1899
1900 return fields
1901
1902
1903 _INST_SIMPLE_FIELDS = {
1904 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1905 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1906 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1907
1908 "network_port": ("Network_port", QFT_OTHER, 0,
1909 "Instance network port if available (e.g. for VNC console)"),
1910 "os": ("OS", QFT_TEXT, 0, "Operating system"),
1911 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1912 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1913 }
1914
1915
1917 """Gets group UUID of an instance node.
1918
1919 @type ctx: L{InstanceQueryData}
1920 @param default: Default value
1921 @type node_name: string
1922 @param node_name: Node name
1923
1924 """
1925 try:
1926 node = ctx.nodes[node_name]
1927 except KeyError:
1928 return default
1929 else:
1930 return node.group
1931
1932
1934 """Gets group name of an instance node.
1935
1936 @type ctx: L{InstanceQueryData}
1937 @param default: Default value
1938 @type node_name: string
1939 @param node_name: Node name
1940
1941 """
1942 try:
1943 node = ctx.nodes[node_name]
1944 except KeyError:
1945 return default
1946
1947 try:
1948 group = ctx.groups[node.group]
1949 except KeyError:
1950 return default
1951
1952 return group.name
1953
1954
1956 """Builds list of fields for instance queries.
1957
1958 """
1959 fields = [
1960 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1961 IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1962 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1963 "Primary node's group"),
1964 IQ_NODES, 0,
1965 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1966 inst.primary_node)),
1967 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1968 "Primary node's group UUID"),
1969 IQ_NODES, 0,
1970 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1971
1972 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1973 "Secondary nodes; usually this will just be one node"),
1974 IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1975 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1976 "Node groups of secondary nodes"),
1977 IQ_NODES, 0,
1978 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1979 inst.secondary_nodes)),
1980 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1981 "Node group UUIDs of secondary nodes"),
1982 IQ_NODES, 0,
1983 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1984 inst.secondary_nodes)),
1985 (_MakeField("admin_state", "InstanceState", QFT_TEXT,
1986 "Desired state of instance"),
1987 IQ_CONFIG, 0, _GetItemAttr("admin_state")),
1988 (_MakeField("admin_up", "Autostart", QFT_BOOL,
1989 "Desired state of instance"),
1990 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
1991 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
1992 lambda ctx, inst: list(inst.GetTags())),
1993 (_MakeField("console", "Console", QFT_OTHER,
1994 "Instance console information"), IQ_CONSOLE, 0,
1995 _GetInstanceConsole),
1996 ]
1997
1998
1999 fields.extend([
2000 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
2001 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
2002
2003
2004 fields.extend([
2005 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
2006 IQ_LIVE, 0, _GetInstOperState),
2007 (_MakeField("oper_ram", "Memory", QFT_UNIT,
2008 "Actual memory usage as seen by hypervisor"),
2009 IQ_LIVE, 0, _GetInstLiveData("memory")),
2010 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
2011 "Actual number of VCPUs as seen by hypervisor"),
2012 IQ_LIVE, 0, _GetInstLiveData("vcpus")),
2013 ])
2014
2015
2016 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
2017 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
2018 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
2019 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
2020 status_doc = ("Instance status; \"%s\" if instance is set to be running"
2021 " and actually is, \"%s\" if instance is stopped and"
2022 " is not running, \"%s\" if instance running, but not on its"
2023 " designated primary node, \"%s\" if instance should be"
2024 " stopped, but is actually running, \"%s\" if instance should"
2025 " run, but doesn't, \"%s\" if instance's primary node is down,"
2026 " \"%s\" if instance's primary node is marked offline,"
2027 " \"%s\" if instance is offline and does not use dynamic"
2028 " resources" % status_values)
2029 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
2030 IQ_LIVE, 0, _GetInstStatus))
2031 assert set(status_values) == constants.INSTST_ALL, \
2032 "Status documentation mismatch"
2033
2034 (network_fields, network_aliases) = _GetInstanceNetworkFields()
2035
2036 fields.extend(network_fields)
2037 fields.extend(_GetInstanceParameterFields())
2038 fields.extend(_GetInstanceDiskFields())
2039 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
2040
2041 aliases = [
2042 ("vcpus", "be/vcpus"),
2043 ("be/memory", "be/maxmem"),
2044 ("sda_size", "disk.size/0"),
2045 ("sdb_size", "disk.size/1"),
2046 ] + network_aliases
2047
2048 return _PrepareFieldList(fields, aliases)
2049
2050
2052 """Data container for lock data queries.
2053
2054 """
2056 """Initializes this class.
2057
2058 """
2059 self.lockdata = lockdata
2060
2062 """Iterate over all locks.
2063
2064 """
2065 return iter(self.lockdata)
2066
2067
2069 """Returns a sorted list of a lock's current owners.
2070
2071 """
2072 (_, _, owners, _) = data
2073
2074 if owners:
2075 owners = utils.NiceSort(owners)
2076
2077 return owners
2078
2079
2081 """Returns a sorted list of a lock's pending acquires.
2082
2083 """
2084 (_, _, _, pending) = data
2085
2086 if pending:
2087 pending = [(mode, utils.NiceSort(names))
2088 for (mode, names) in pending]
2089
2090 return pending
2091
2092
2094 """Builds list of fields for lock queries.
2095
2096 """
2097 return _PrepareFieldList([
2098
2099 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2100 lambda ctx, (name, mode, owners, pending): name),
2101 (_MakeField("mode", "Mode", QFT_OTHER,
2102 "Mode in which the lock is currently acquired"
2103 " (exclusive or shared)"),
2104 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2105 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2106 LQ_OWNER, 0, _GetLockOwners),
2107 (_MakeField("pending", "Pending", QFT_OTHER,
2108 "Threads waiting for the lock"),
2109 LQ_PENDING, 0, _GetLockPending),
2110 ], [])
2111
2112
2114 """Data container for node group data queries.
2115
2116 """
2117 - def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2118 want_diskparams):
2119 """Initializes this class.
2120
2121 @param cluster: Cluster object
2122 @param groups: List of node group objects
2123 @type group_to_nodes: dict; group UUID as key
2124 @param group_to_nodes: Per-group list of nodes
2125 @type group_to_instances: dict; group UUID as key
2126 @param group_to_instances: Per-group list of (primary) instances
2127 @type want_diskparams: bool
2128 @param want_diskparams: Whether diskparamters should be calculated
2129
2130 """
2131 self.groups = groups
2132 self.group_to_nodes = group_to_nodes
2133 self.group_to_instances = group_to_instances
2134 self.cluster = cluster
2135 self.want_diskparams = want_diskparams
2136
2137
2138 self.group_ipolicy = None
2139 self.ndparams = None
2140 self.group_dp = None
2141
2143 """Iterate over all node groups.
2144
2145 This function has side-effects and only one instance of the resulting
2146 generator should be used at a time.
2147
2148 """
2149 for group in self.groups:
2150 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2151 self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2152 if self.want_diskparams:
2153 self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2154 else:
2155 self.group_dp = None
2156 yield group
2157
2158
2159 _GROUP_SIMPLE_FIELDS = {
2160 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2161 "name": ("Group", QFT_TEXT, "Group name"),
2162 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2163 "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2164 }
2165
2166
2168 """Builds list of fields for node group queries.
2169
2170 """
2171
2172 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2173 _GetItemAttr(name))
2174 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2175
2176 def _GetLength(getter):
2177 return lambda ctx, group: len(getter(ctx)[group.uuid])
2178
2179 def _GetSortedList(getter):
2180 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2181
2182 group_to_nodes = operator.attrgetter("group_to_nodes")
2183 group_to_instances = operator.attrgetter("group_to_instances")
2184
2185
2186 fields.extend([
2187 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2188 GQ_NODE, 0, _GetLength(group_to_nodes)),
2189 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2190 GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2191 ])
2192
2193
2194 fields.extend([
2195 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2196 "Number of primary instances"),
2197 GQ_INST, 0, _GetLength(group_to_instances)),
2198 (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2199 "List of primary instances"),
2200 GQ_INST, 0, _GetSortedList(group_to_instances)),
2201 ])
2202
2203
2204 fields.extend([
2205 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2206 lambda ctx, group: list(group.GetTags())),
2207 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2208 "Instance policy limitations (merged)"),
2209 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2210 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2211 "Custom instance policy limitations"),
2212 GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2213 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2214 "Custom node parameters"),
2215 GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2216 (_MakeField("ndparams", "NDParams", QFT_OTHER,
2217 "Node parameters"),
2218 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2219 (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2220 "Disk parameters (merged)"),
2221 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2222 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2223 "Custom disk parameters"),
2224 GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2225 ])
2226
2227
2228 fields.extend(_BuildNDFields(True))
2229
2230 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2231
2232 return _PrepareFieldList(fields, [])
2233
2234
2235 -class OsInfo(objects.ConfigObject):
2236 __slots__ = [
2237 "name",
2238 "valid",
2239 "hidden",
2240 "blacklisted",
2241 "variants",
2242 "api_versions",
2243 "parameters",
2244 "node_status",
2245 ]
2246
2247
2249 """Builds list of fields for operating system queries.
2250
2251 """
2252 fields = [
2253 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2254 None, 0, _GetItemAttr("name")),
2255 (_MakeField("valid", "Valid", QFT_BOOL,
2256 "Whether operating system definition is valid"),
2257 None, 0, _GetItemAttr("valid")),
2258 (_MakeField("hidden", "Hidden", QFT_BOOL,
2259 "Whether operating system is hidden"),
2260 None, 0, _GetItemAttr("hidden")),
2261 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2262 "Whether operating system is blacklisted"),
2263 None, 0, _GetItemAttr("blacklisted")),
2264 (_MakeField("variants", "Variants", QFT_OTHER,
2265 "Operating system variants"),
2266 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2267 (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2268 "Operating system API versions"),
2269 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2270 (_MakeField("parameters", "Parameters", QFT_OTHER,
2271 "Operating system parameters"),
2272 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2273 _GetItemAttr("parameters"))),
2274 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2275 "Status from node"),
2276 None, 0, _GetItemAttr("node_status")),
2277 ]
2278
2279 return _PrepareFieldList(fields, [])
2280
2281
2283 __slots__ = [
2284 "name",
2285 "node_status",
2286 "nodegroup_status",
2287 "parameters",
2288 ]
2289
2290
2292 """Builds list of fields for extstorage provider queries.
2293
2294 """
2295 fields = [
2296 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2297 None, 0, _GetItemAttr("name")),
2298 (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2299 "Status from node"),
2300 None, 0, _GetItemAttr("node_status")),
2301 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2302 "Overall Nodegroup status"),
2303 None, 0, _GetItemAttr("nodegroup_status")),
2304 (_MakeField("parameters", "Parameters", QFT_OTHER,
2305 "ExtStorage provider parameters"),
2306 None, 0, _GetItemAttr("parameters")),
2307 ]
2308
2309 return _PrepareFieldList(fields, [])
2310
2311
2313 """Return L{_FS_UNAVAIL} if job is None.
2314
2315 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2316 found, in which case this function converts it to L{_FS_UNAVAIL}.
2317
2318 """
2319 if job is None:
2320 return _FS_UNAVAIL
2321 else:
2322 return fn(job)
2323
2324
2330
2331
2333 """Executes a function per opcode in a job.
2334
2335 """
2336 return map(fn, job.ops)
2337
2338
2344
2345
2347 """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2348
2349 """
2350 timestamp = fn(job)
2351
2352 if timestamp is None:
2353 return _FS_UNAVAIL
2354 else:
2355 return timestamp
2356
2357
2363
2364
2366 """Builds list of fields for job queries.
2367
2368 """
2369 fields = [
2370 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2371 None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2372 (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2373 None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2374 (_MakeField("priority", "Priority", QFT_NUMBER,
2375 ("Current job priority (%s to %s)" %
2376 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2377 None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2378 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2379 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2380 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2381 None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2382 (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2383 "List of opcodes results"),
2384 None, 0, _PerJobOp(operator.attrgetter("result"))),
2385 (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2386 "List of opcodes status"),
2387 None, 0, _PerJobOp(operator.attrgetter("status"))),
2388 (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2389 "List of opcode output logs"),
2390 None, 0, _PerJobOp(operator.attrgetter("log"))),
2391 (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2392 "List of opcode start timestamps (before acquiring locks)"),
2393 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2394 (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2395 "List of opcode execution start timestamps (after acquiring"
2396 " locks)"),
2397 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2398 (_MakeField("opend", "OpCode_end", QFT_OTHER,
2399 "List of opcode execution end timestamps"),
2400 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2401 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2402 "List of opcode priorities"),
2403 None, 0, _PerJobOp(operator.attrgetter("priority"))),
2404 (_MakeField("summary", "Summary", QFT_OTHER,
2405 "List of per-opcode summaries"),
2406 None, 0, _PerJobOp(lambda op: op.input.Summary())),
2407 ]
2408
2409
2410 for (name, attr, title, desc) in [
2411 ("received_ts", "received_timestamp", "Received",
2412 "Timestamp of when job was received"),
2413 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2414 ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2415 ]:
2416 getter = operator.attrgetter(attr)
2417 fields.extend([
2418 (_MakeField(name, title, QFT_OTHER,
2419 "%s (tuple containing seconds and microseconds)" % desc),
2420 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2421 ])
2422
2423 return _PrepareFieldList(fields, [])
2424
2425
2427 """Returns an export name if available.
2428
2429 """
2430 if expname is None:
2431 return _FS_UNAVAIL
2432 else:
2433 return expname
2434
2435
2437 """Builds list of fields for exports.
2438
2439 """
2440 fields = [
2441 (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2442 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2443 (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2444 None, 0, _GetExportName),
2445 ]
2446
2447 return _PrepareFieldList(fields, [])
2448
2449
2450 _CLUSTER_VERSION_FIELDS = {
2451 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2452 "Software version"),
2453 "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2454 constants.PROTOCOL_VERSION,
2455 "RPC protocol version"),
2456 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2457 "Configuration format version"),
2458 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2459 "API version for OS template scripts"),
2460 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2461 "Import/export file format version"),
2462 }
2463
2464
2465 _CLUSTER_SIMPLE_FIELDS = {
2466 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2467 "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2468 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2469 }
2470
2471
2473 - def __init__(self, cluster, drain_flag, watcher_pause):
2474 """Initializes this class.
2475
2476 @type cluster: L{objects.Cluster}
2477 @param cluster: Instance of cluster object
2478 @type drain_flag: bool
2479 @param drain_flag: Whether job queue is drained
2480 @type watcher_pause: number
2481 @param watcher_pause: Until when watcher is paused (Unix timestamp)
2482
2483 """
2484 self._cluster = cluster
2485 self.drain_flag = drain_flag
2486 self.watcher_pause = watcher_pause
2487
2489 return iter([self._cluster])
2490
2491
2493 """Returns until when watcher is paused (if available).
2494
2495 """
2496 if ctx.watcher_pause is None:
2497 return _FS_UNAVAIL
2498 else:
2499 return ctx.watcher_pause
2500
2501
2503 """Builds list of fields for cluster information.
2504
2505 """
2506 fields = [
2507 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2508 lambda ctx, cluster: list(cluster.GetTags())),
2509 (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2510 "Architecture information"), None, 0,
2511 lambda ctx, _: runtime.GetArchInfo()),
2512 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2513 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2514 lambda ctx, _: ctx.drain_flag),
2515 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2516 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2517 _ClusterWatcherPause),
2518 ]
2519
2520
2521 fields.extend([
2522 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2523 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2524 ],)
2525
2526
2527 fields.extend([
2528 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2529 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
2530
2531
2532 fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2533
2534 return _PrepareFieldList(fields, [
2535 ("name", "cluster_name")])
2536
2537
2539 """Data container for network data queries.
2540
2541 """
2542 - def __init__(self, networks, network_to_groups,
2543 network_to_instances, stats):
2544 """Initializes this class.
2545
2546 @param networks: List of network objects
2547 @type network_to_groups: dict; network UUID as key
2548 @param network_to_groups: Per-network list of groups
2549 @type network_to_instances: dict; network UUID as key
2550 @param network_to_instances: Per-network list of instances
2551 @type stats: dict; network UUID as key
2552 @param stats: Per-network usage statistics
2553
2554 """
2555 self.networks = networks
2556 self.network_to_groups = network_to_groups
2557 self.network_to_instances = network_to_instances
2558 self.stats = stats
2559
2561 """Iterate over all networks.
2562
2563 """
2564 for net in self.networks:
2565 if self.stats:
2566 self.curstats = self.stats.get(net.uuid, None)
2567 else:
2568 self.curstats = None
2569 yield net
2570
2571
2572 _NETWORK_SIMPLE_FIELDS = {
2573 "name": ("Network", QFT_TEXT, 0, "Name"),
2574 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"),
2575 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"),
2576 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"),
2577 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"),
2578 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"),
2579 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
2580 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2581 }
2582
2583
2584 _NETWORK_STATS_FIELDS = {
2585 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"),
2586 "reserved_count":
2587 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"),
2588 "map": ("Map", QFT_TEXT, 0, "Actual mapping"),
2589 "external_reservations":
2590 ("ExternalReservations", QFT_TEXT, 0, "External reservations"),
2591 }
2592
2593
2595 """Gets the value of a "stats" field from L{NetworkQueryData}.
2596
2597 @param field: Field name
2598 @param kind: Data kind, one of L{constants.QFT_ALL}
2599 @type ctx: L{NetworkQueryData}
2600
2601 """
2602 return _GetStatsField(field, kind, ctx.curstats)
2603
2604
2606 """Builds list of fields for network queries.
2607
2608 """
2609 fields = [
2610 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2611 lambda ctx, inst: list(inst.GetTags())),
2612 ]
2613
2614
2615 fields.extend([
2616 (_MakeField(name, title, kind, doc),
2617 NETQ_CONFIG, 0, _GetItemAttr(name))
2618 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
2619
2620 def _GetLength(getter):
2621 return lambda ctx, network: len(getter(ctx)[network.uuid])
2622
2623 def _GetSortedList(getter):
2624 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2625
2626 network_to_groups = operator.attrgetter("network_to_groups")
2627 network_to_instances = operator.attrgetter("network_to_instances")
2628
2629
2630 fields.extend([
2631 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2632 NETQ_GROUP, 0, _GetLength(network_to_groups)),
2633 (_MakeField("group_list", "GroupList", QFT_OTHER,
2634 "List of nodegroups (group name, NIC mode, NIC link)"),
2635 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]),
2636 ])
2637
2638
2639 fields.extend([
2640 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2641 NETQ_INST, 0, _GetLength(network_to_instances)),
2642 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2643 NETQ_INST, 0, _GetSortedList(network_to_instances)),
2644 ])
2645
2646
2647 fields.extend([
2648 (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2649 compat.partial(_GetNetworkStatsField, name, kind))
2650 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
2651
2652
2653 fields.extend(_GetItemTimestampFields(IQ_NETWORKS))
2654
2655 return _PrepareFieldList(fields, [])
2656
2657
2658 CLUSTER_FIELDS = _BuildClusterFields()
2659
2660
2661 NODE_FIELDS = _BuildNodeFields()
2662
2663
2664 INSTANCE_FIELDS = _BuildInstanceFields()
2665
2666
2667 LOCK_FIELDS = _BuildLockFields()
2668
2669
2670 GROUP_FIELDS = _BuildGroupFields()
2671
2672
2673 OS_FIELDS = _BuildOsFields()
2674
2675
2676 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2677
2678
2679 JOB_FIELDS = _BuildJobFields()
2680
2681
2682 EXPORT_FIELDS = _BuildExportFields()
2683
2684
2685 NETWORK_FIELDS = _BuildNetworkFields()
2686
2687
2688 ALL_FIELDS = {
2689 constants.QR_CLUSTER: CLUSTER_FIELDS,
2690 constants.QR_INSTANCE: INSTANCE_FIELDS,
2691 constants.QR_NODE: NODE_FIELDS,
2692 constants.QR_LOCK: LOCK_FIELDS,
2693 constants.QR_GROUP: GROUP_FIELDS,
2694 constants.QR_OS: OS_FIELDS,
2695 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2696 constants.QR_JOB: JOB_FIELDS,
2697 constants.QR_EXPORT: EXPORT_FIELDS,
2698 constants.QR_NETWORK: NETWORK_FIELDS,
2699 }
2700
2701
2702 ALL_FIELD_LISTS = ALL_FIELDS.values()
2703