Merge branch 'devel-2.7'
[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 (NETQ_CONFIG,
75  NETQ_GROUP,
76  NETQ_STATS,
77  NETQ_INST) = range(300, 304)
78
79 # Constants for requesting data from the caller/data provider. Each property
80 # collected/computed separately by the data provider should have its own to
81 # only collect the requested data and not more.
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 # Query field flags
112 QFF_HOSTNAME = 0x01
113 QFF_IP_ADDRESS = 0x02
114 QFF_JOB_ID = 0x04
115 QFF_SPLIT_TIMESTAMP = 0x08
116 # Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
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 #: Verification function for each field type
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 # Unique objects for special field statuses
135 _FS_UNKNOWN = object()
136 _FS_NODATA = object()
137 _FS_UNAVAIL = object()
138 _FS_OFFLINE = object()
139
140 #: List of all special status
141 _FS_ALL = compat.UniqueFrozenset([
142   _FS_UNKNOWN,
143   _FS_NODATA,
144   _FS_UNAVAIL,
145   _FS_OFFLINE,
146   ])
147
148 #: VType to QFT mapping
149 _VTToQFT = {
150   # TODO: fix validation of empty strings
151   constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
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
161 def _GetUnknownField(ctx, item): # pylint: disable=W0613
162   """Gets the contents of an unknown field.
163
164   """
165   return _FS_UNKNOWN
166
167
168 def _GetQueryFields(fielddefs, selected):
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
195 def GetAllFields(fielddefs):
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
204 class _FilterHints:
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   """
228   def __init__(self, namefield):
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     #: Whether all names need to be requested (e.g. if a non-equality operator
238     #: has been used)
239     self._allnames = False
240
241     #: Which names to request
242     self._names = None
243
244     #: Data kinds referenced by the filter (used by L{Query.RequestedData})
245     self._datakinds = set()
246
247   def RequestedNames(self):
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
261   def ReferencedData(self):
262     """Returns all kinds of data referenced by the filter.
263
264     """
265     return frozenset(self._datakinds)
266
267   def _NeedAllNames(self):
268     """Changes internal state to request all names.
269
270     """
271     self._allnames = True
272     self._names = None
273
274   def NoteLogicOp(self, op):
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
284   def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
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
296   def NoteBinaryOp(self, op, datakind, name, value):
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     # If any operator other than equality was used, all names need to be
313     # retrieved
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
322 def _WrapLogicOp(op_fn, sentences, ctx, item):
323   """Wrapper for logic operator functions.
324
325   """
326   return op_fn(fn(ctx, item) for fn in sentences)
327
328
329 def _WrapUnaryOp(op_fn, inner, ctx, item):
330   """Wrapper for unary operator functions.
331
332   """
333   return op_fn(inner(ctx, item))
334
335
336 def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
337   """Wrapper for binary operator functions.
338
339   """
340   return op_fn(retrieval_fn(ctx, item), value)
341
342
343 def _WrapNot(fn, lhs, rhs):
344   """Negates the result of a wrapped function.
345
346   """
347   return not fn(lhs, rhs)
348
349
350 def _PrepareRegex(pattern):
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
360 def _PrepareSplitTimestamp(value):
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
370 def _MakeSplitTimestampComparison(fn):
371   """Compares split timestamp values after converting to float.
372
373   """
374   return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
375
376
377 def _MakeComparisonChecks(fn):
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
390 class _FilterCompilerHelper:
391   """Converts a query filter to a callable usable for filtering.
392
393   """
394   # String statement has no effect, pylint: disable=W0105
395
396   #: How deep filters can be nested
397   _LEVELS_MAX = 10
398
399   # Unique identifiers for operator groups
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     # Logic operators
438     qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
439     qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
440
441     # Unary operators
442     qlang.OP_NOT: (_OPTYPE_UNARY, None),
443     qlang.OP_TRUE: (_OPTYPE_UNARY, None),
444
445     # Binary operators
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
462   def __init__(self, fields):
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
472   def __call__(self, hints, qfilter):
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
500   def _Compile(self, qfilter, level):
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     # Limit recursion
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     # Create copy to be modified
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
528   def _LookupField(self, name):
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
537   def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
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
558   def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
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
598   def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
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     # Unused arguments, pylint: disable=W0613
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     # TODO: Type conversions?
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         # Prepare value if necessary (e.g. compile regular expression)
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
647 def _CompileFilter(fields, hints, qfilter):
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
658 class Query:
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       # Collect requested names if wanted
688       if namefield:
689         hints = _FilterHints(namefield)
690       else:
691         hints = None
692
693       # Build filter function
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
704   def RequestedNames(self):
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
713   def RequestedData(self):
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
723   def GetFields(self):
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       # Verify result
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         # TODO: Are there cases where we wouldn't want to use NiceSort?
761         # Answer: if the name field is non-string...
762         result.append((utils.NiceSortKey(name), idx, row))
763       else:
764         result.append(row)
765
766     if not sort:
767       return result
768
769     # TODO: Would "heapq" be more efficient than sorting?
770
771     # Sorting in-place instead of using "sorted()"
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
778   def OldStyleQuery(self, ctx, sort_by_name=True):
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
795 def _ProcessResult(value):
796   """Converts result values into externally-visible ones.
797
798   """
799   if value is _FS_UNKNOWN:
800     return (RS_UNKNOWN, None)
801   elif value is _FS_NODATA:
802     return (RS_NODATA, None)
803   elif value is _FS_UNAVAIL:
804     return (RS_UNAVAIL, None)
805   elif value is _FS_OFFLINE:
806     return (RS_OFFLINE, None)
807   else:
808     return (RS_NORMAL, value)
809
810
811 def _VerifyResultRow(fields, row):
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
833 def _FieldDictKey((fdef, _, flags, fn)):
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
849 def _PrepareFieldList(fields, aliases):
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
887 def GetQueryResponse(query, ctx, sort_by_name=True):
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
901 def QueryFields(fielddefs, selected):
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     # Client requests all fields, sort by name
913     fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
914                            key=operator.attrgetter("name"))
915   else:
916     # Keep order as requested by client
917     fdefs = Query(fielddefs, selected).GetFields()
918
919   return objects.QueryFieldsResponse(fields=fdefs).ToDict()
920
921
922 def _MakeField(name, title, kind, doc):
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
935 def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
936   """Returns a static value.
937
938   """
939   return value
940
941
942 def _StaticValue(value):
943   """Prepares a function to return a static value.
944
945   """
946   return compat.partial(_StaticValueInner, value)
947
948
949 def _GetNodeRole(node, master_name):
950   """Determine node role.
951
952   @type node: L{objects.Node}
953   @param node: Node object
954   @type master_name: string
955   @param master_name: Master node name
956
957   """
958   if node.name == master_name:
959     return constants.NR_MASTER
960   elif node.master_candidate:
961     return constants.NR_MCANDIDATE
962   elif node.drained:
963     return constants.NR_DRAINED
964   elif node.offline:
965     return constants.NR_OFFLINE
966   else:
967     return constants.NR_REGULAR
968
969
970 def _GetItemAttr(attr):
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
980 def _GetNDParam(name):
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
992 def _BuildNDFields(is_group):
993   """Builds all the ndparam fields.
994
995   @param is_group: whether this is called at group or node level
996
997   """
998   if is_group:
999     field_kind = GQ_CONFIG
1000   else:
1001     field_kind = NQ_GROUP
1002   return [(_MakeField("ndp/%s" % name,
1003                       constants.NDS_PARAMETER_TITLES.get(name,
1004                                                          "ndp/%s" % name),
1005                       _VTToQFT[kind], "The \"%s\" node parameter" % name),
1006            field_kind, 0, _GetNDParam(name))
1007           for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1008
1009
1010 def _ConvWrapInner(convert, fn, ctx, item):
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   # Is the value an abnormal status?
1020   if compat.any(value is fs for fs in _FS_ALL):
1021     # Return right away
1022     return value
1023
1024   # TODO: Should conversion function also receive context, item or both?
1025   return convert(value)
1026
1027
1028 def _ConvWrap(convert, fn):
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
1038 def _GetItemTimestamp(getter):
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       # Old configs might not have all timestamps
1052       return _FS_UNAVAIL
1053     else:
1054       return timestamp
1055
1056   return fn
1057
1058
1059 def _GetItemTimestampFields(datatype):
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
1073 class NodeQueryData:
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     # Used for individual rows
1092     self.curlive_data = None
1093     self.ndparams = None
1094
1095   def __iter__(self):
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 #: Fields that are direct attributes of an L{objects.Node} object
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 #: Fields requiring talking to the node
1131 # Note that none of these are available for non-vm_capable nodes
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
1155 def _GetGroup(cb):
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       # Nodes always have a group, or the configuration is corrupt
1172       return _FS_UNAVAIL
1173
1174     return cb(ctx, node, ng)
1175
1176   return fn
1177
1178
1179 def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
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
1192 def _GetNodePower(ctx, node):
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
1206 def _GetNdParams(ctx, node, ng):
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
1219 def _GetLiveNodeField(field, kind, ctx, node):
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
1241 def _GetStatsField(field, kind, data):
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   # Try to convert into number
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
1273 def _GetNodeHvState(_, node):
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
1285 def _GetNodeDiskState(_, node):
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
1299 def _BuildNodeFields():
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   # Node role
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   # Add fields operating on instance lists
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     # TODO: Allow filterting by hostname in list
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   # Add simple fields
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   # Add fields requiring live data
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   # Add timestamps
1377   fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1378
1379   return _PrepareFieldList(fields, [])
1380
1381
1382 class InstanceQueryData:
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     # Used for individual rows
1428     self.inst_hvparams = None
1429     self.inst_beparams = None
1430     self.inst_osparams = None
1431     self.inst_nicparams = None
1432
1433   def __iter__(self):
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
1450 def _GetInstOperState(ctx, inst):
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   # Can't use RS_OFFLINE here as it would describe the instance to
1459   # be offline when we actually don't know due to missing data
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
1466 def _GetInstLiveData(name):
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       # Can't use RS_OFFLINE here as it would describe the instance to be
1484       # offline when we actually don't know due to missing data
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
1497 def _GetInstStatus(ctx, inst):
1498   """Get instance status.
1499
1500   @type ctx: L{InstanceQueryData}
1501   @type inst: L{objects.Instance}
1502   @param inst: Instance object
1503
1504   """
1505   if inst.primary_node in ctx.offline_nodes:
1506     return constants.INSTST_NODEOFFLINE
1507
1508   if inst.primary_node in ctx.bad_nodes:
1509     return constants.INSTST_NODEDOWN
1510
1511   if bool(ctx.live_data.get(inst.name)):
1512     if inst.name in ctx.wrongnode_inst:
1513       return constants.INSTST_WRONGNODE
1514     elif inst.admin_state == constants.ADMINST_UP:
1515       return constants.INSTST_RUNNING
1516     else:
1517       return constants.INSTST_ERRORUP
1518
1519   if inst.admin_state == constants.ADMINST_UP:
1520     return constants.INSTST_ERRORDOWN
1521   elif inst.admin_state == constants.ADMINST_DOWN:
1522     return constants.INSTST_ADMINDOWN
1523
1524   return constants.INSTST_ADMINOFFLINE
1525
1526
1527 def _GetInstDiskSize(index):
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
1549 def _GetInstNic(index, cb):
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
1576 def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
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
1590 def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
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
1604 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
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
1618 def _GetInstNicBridge(ctx, index, _):
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
1636 def _GetInstAllNicNetworkNames(ctx, inst):
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
1657 def _GetInstAllNicBridges(ctx, inst):
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
1680 def _GetInstNicParam(name):
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
1703 def _GetInstanceNetworkFields():
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     # All NICs
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   # NICs by number
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     # Legacy fields for first NIC
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
1784 def _GetInstDiskUsage(ctx, inst):
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
1800 def _GetInstanceConsole(ctx, inst):
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
1816 def _GetInstanceDiskFields():
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   # Disks by number
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
1844 def _GetInstanceParameterFields():
1845   """Get instance fields involving parameters.
1846
1847   @return: List of field definitions used as input for L{_PrepareFieldList}
1848
1849   """
1850   fields = [
1851     # Filled parameters
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     # Unfilled parameters
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   # HV params
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   # BE params
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   # Depending on the hypervisor, the port can be None
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
1916 def _GetInstNodeGroup(ctx, default, node_name):
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
1933 def _GetInstNodeGroupName(ctx, default, node_name):
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
1955 def _BuildInstanceFields():
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     # TODO: Allow filtering by secondary node as hostname
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   # Add simple fields
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   # Fields requiring talking to the node
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   # Status field
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
2051 class LockQueryData:
2052   """Data container for lock data queries.
2053
2054   """
2055   def __init__(self, lockdata):
2056     """Initializes this class.
2057
2058     """
2059     self.lockdata = lockdata
2060
2061   def __iter__(self):
2062     """Iterate over all locks.
2063
2064     """
2065     return iter(self.lockdata)
2066
2067
2068 def _GetLockOwners(_, data):
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
2080 def _GetLockPending(_, data):
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
2093 def _BuildLockFields():
2094   """Builds list of fields for lock queries.
2095
2096   """
2097   return _PrepareFieldList([
2098     # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
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
2113 class GroupQueryData:
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     # Used for individual rows
2138     self.group_ipolicy = None
2139     self.ndparams = None
2140     self.group_dp = None
2141
2142   def __iter__(self):
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
2167 def _BuildGroupFields():
2168   """Builds list of fields for node group queries.
2169
2170   """
2171   # Add simple fields
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   # Add fields for nodes
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   # Add fields for instances
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   # Other fields
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   # ND parameters
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
2248 def _BuildOsFields():
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
2282 class ExtStorageInfo(objects.ConfigObject):
2283   __slots__ = [
2284     "name",
2285     "node_status",
2286     "nodegroup_status",
2287     "parameters",
2288     ]
2289
2290
2291 def _BuildExtStorageFields():
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
2312 def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
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
2325 def _JobUnavail(inner):
2326   """Wrapper for L{_JobUnavailInner}.
2327
2328   """
2329   return compat.partial(_JobUnavailInner, inner)
2330
2331
2332 def _PerJobOpInner(fn, job):
2333   """Executes a function per opcode in a job.
2334
2335   """
2336   return map(fn, job.ops)
2337
2338
2339 def _PerJobOp(fn):
2340   """Wrapper for L{_PerJobOpInner}.
2341
2342   """
2343   return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2344
2345
2346 def _JobTimestampInner(fn, job):
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
2358 def _JobTimestamp(fn):
2359   """Wrapper for L{_JobTimestampInner}.
2360
2361   """
2362   return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2363
2364
2365 def _BuildJobFields():
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   # Timestamp fields
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
2426 def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
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
2436 def _BuildExportFields():
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
2472 class ClusterQueryData:
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
2488   def __iter__(self):
2489     return iter([self._cluster])
2490
2491
2492 def _ClusterWatcherPause(ctx, _):
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
2502 def _BuildClusterFields():
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   # Simple fields
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   # Version fields
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   # Add timestamps
2532   fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2533
2534   return _PrepareFieldList(fields, [
2535     ("name", "cluster_name")])
2536
2537
2538 class NetworkQueryData:
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
2560   def __iter__(self):
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
2594 def _GetNetworkStatsField(field, kind, ctx, _):
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
2605 def _BuildNetworkFields():
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   # Add simple fields
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   # Add fields for node groups
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   # Add fields for instances
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   # Add fields for usage statistics
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   return _PrepareFieldList(fields, [])
2653
2654 #: Fields for cluster information
2655 CLUSTER_FIELDS = _BuildClusterFields()
2656
2657 #: Fields available for node queries
2658 NODE_FIELDS = _BuildNodeFields()
2659
2660 #: Fields available for instance queries
2661 INSTANCE_FIELDS = _BuildInstanceFields()
2662
2663 #: Fields available for lock queries
2664 LOCK_FIELDS = _BuildLockFields()
2665
2666 #: Fields available for node group queries
2667 GROUP_FIELDS = _BuildGroupFields()
2668
2669 #: Fields available for operating system queries
2670 OS_FIELDS = _BuildOsFields()
2671
2672 #: Fields available for extstorage provider queries
2673 EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2674
2675 #: Fields available for job queries
2676 JOB_FIELDS = _BuildJobFields()
2677
2678 #: Fields available for exports
2679 EXPORT_FIELDS = _BuildExportFields()
2680
2681 #: Fields available for network queries
2682 NETWORK_FIELDS = _BuildNetworkFields()
2683
2684 #: All available resources
2685 ALL_FIELDS = {
2686   constants.QR_CLUSTER: CLUSTER_FIELDS,
2687   constants.QR_INSTANCE: INSTANCE_FIELDS,
2688   constants.QR_NODE: NODE_FIELDS,
2689   constants.QR_LOCK: LOCK_FIELDS,
2690   constants.QR_GROUP: GROUP_FIELDS,
2691   constants.QR_OS: OS_FIELDS,
2692   constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2693   constants.QR_JOB: JOB_FIELDS,
2694   constants.QR_EXPORT: EXPORT_FIELDS,
2695   constants.QR_NETWORK: NETWORK_FIELDS,
2696   }
2697
2698 #: All available field lists
2699 ALL_FIELD_LISTS = ALL_FIELDS.values()