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