List node parameters (if any) in gnt-node info
[ganeti-local] / lib / query.py
1 #
2 #
3
4 # Copyright (C) 2010 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 import logging
25 import operator
26 import re
27
28 from ganeti import constants
29 from ganeti import errors
30 from ganeti import utils
31 from ganeti import compat
32 from ganeti import objects
33 from ganeti import ht
34
35
36 (NQ_CONFIG,
37  NQ_INST,
38  NQ_LIVE,
39  NQ_GROUP,
40  NQ_OOB) = range(1, 6)
41
42 (IQ_CONFIG,
43  IQ_LIVE,
44  IQ_DISKUSAGE) = range(100, 103)
45
46 (LQ_MODE,
47  LQ_OWNER,
48  LQ_PENDING) = range(10, 13)
49
50 (GQ_CONFIG,
51  GQ_NODE,
52  GQ_INST) = range(200, 203)
53
54
55 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
56 TITLE_RE = re.compile(r"^[^\s]+$")
57
58 #: Verification function for each field type
59 _VERIFY_FN = {
60   constants.QFT_UNKNOWN: ht.TNone,
61   constants.QFT_TEXT: ht.TString,
62   constants.QFT_BOOL: ht.TBool,
63   constants.QFT_NUMBER: ht.TInt,
64   constants.QFT_UNIT: ht.TInt,
65   constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
66   constants.QFT_OTHER: lambda _: True,
67   }
68
69
70 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
71   """Gets the contents of an unknown field.
72
73   """
74   return (constants.QRFS_UNKNOWN, None)
75
76
77 def _GetQueryFields(fielddefs, selected):
78   """Calculates the internal list of selected fields.
79
80   Unknown fields are returned as L{constants.QFT_UNKNOWN}.
81
82   @type fielddefs: dict
83   @param fielddefs: Field definitions
84   @type selected: list of strings
85   @param selected: List of selected fields
86
87   """
88   result = []
89
90   for name in selected:
91     try:
92       fdef = fielddefs[name]
93     except KeyError:
94       fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
95               None, _GetUnknownField)
96
97     assert len(fdef) == 3
98
99     result.append(fdef)
100
101   return result
102
103
104 def GetAllFields(fielddefs):
105   """Extract L{objects.QueryFieldDefinition} from field definitions.
106
107   @rtype: list of L{objects.QueryFieldDefinition}
108
109   """
110   return [fdef for (fdef, _, _) in fielddefs]
111
112
113 class Query:
114   def __init__(self, fieldlist, selected):
115     """Initializes this class.
116
117     The field definition is a dictionary with the field's name as a key and a
118     tuple containing, in order, the field definition object
119     (L{objects.QueryFieldDefinition}, the data kind to help calling code
120     collect data and a retrieval function. The retrieval function is called
121     with two parameters, in order, the data container and the item in container
122     (see L{Query.Query}).
123
124     Users of this class can call L{RequestedData} before preparing the data
125     container to determine what data is needed.
126
127     @type fieldlist: dictionary
128     @param fieldlist: Field definitions
129     @type selected: list of strings
130     @param selected: List of selected fields
131
132     """
133     self._fields = _GetQueryFields(fieldlist, selected)
134
135   def RequestedData(self):
136     """Gets requested kinds of data.
137
138     @rtype: frozenset
139
140     """
141     return frozenset(datakind
142                      for (_, datakind, _) in self._fields
143                      if datakind is not None)
144
145   def GetFields(self):
146     """Returns the list of fields for this query.
147
148     Includes unknown fields.
149
150     @rtype: List of L{objects.QueryFieldDefinition}
151
152     """
153     return GetAllFields(self._fields)
154
155   def Query(self, ctx):
156     """Execute a query.
157
158     @param ctx: Data container passed to field retrieval functions, must
159       support iteration using C{__iter__}
160
161     """
162     result = [[fn(ctx, item) for (_, _, fn) in self._fields]
163               for item in ctx]
164
165     # Verify result
166     if __debug__:
167       for (idx, row) in enumerate(result):
168         assert _VerifyResultRow(self._fields, row), \
169                ("Inconsistent result for fields %s in row %s: %r" %
170                 (GetAllFields(self._fields), idx, row))
171
172     return result
173
174   def OldStyleQuery(self, ctx):
175     """Query with "old" query result format.
176
177     See L{Query.Query} for arguments.
178
179     """
180     unknown = set(fdef.name
181                   for (fdef, _, _) in self._fields
182                   if fdef.kind == constants.QFT_UNKNOWN)
183     if unknown:
184       raise errors.OpPrereqError("Unknown output fields selected: %s" %
185                                  (utils.CommaJoin(unknown), ),
186                                  errors.ECODE_INVAL)
187
188     return [[value for (_, value) in row]
189             for row in self.Query(ctx)]
190
191
192 def _VerifyResultRow(fields, row):
193   """Verifies the contents of a query result row.
194
195   @type fields: list
196   @param fields: Field definitions for result
197   @type row: list of tuples
198   @param row: Row data
199
200   """
201   return (len(row) == len(fields) and
202           compat.all((status == constants.QRFS_NORMAL and
203                       _VERIFY_FN[fdef.kind](value)) or
204                      # Value for an abnormal status must be None
205                      (status != constants.QRFS_NORMAL and value is None)
206                      for ((status, value), (fdef, _, _)) in zip(row, fields)))
207
208
209 def _PrepareFieldList(fields):
210   """Prepares field list for use by L{Query}.
211
212   Converts the list to a dictionary and does some verification.
213
214   @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
215     retrieval function)
216   @param fields: List of fields
217   @rtype: dict
218   @return: Field dictionary for L{Query}
219
220   """
221   if __debug__:
222     duplicates = utils.FindDuplicates(fdef.title.lower()
223                                       for (fdef, _, _) in fields)
224     assert not duplicates, "Duplicate title(s) found: %r" % duplicates
225
226   result = {}
227
228   for field in fields:
229     (fdef, _, fn) = field
230
231     assert fdef.name and fdef.title, "Name and title are required"
232     assert FIELD_NAME_RE.match(fdef.name)
233     assert TITLE_RE.match(fdef.title)
234     assert callable(fn)
235     assert fdef.name not in result, \
236            "Duplicate field name '%s' found" % fdef.name
237
238     result[fdef.name] = field
239
240   assert len(result) == len(fields)
241   assert compat.all(name == fdef.name
242                     for (name, (fdef, _, _)) in result.items())
243
244   return result
245
246
247 def GetQueryResponse(query, ctx):
248   """Prepares the response for a query.
249
250   @type query: L{Query}
251   @param ctx: Data container, see L{Query.Query}
252
253   """
254   return objects.QueryResponse(data=query.Query(ctx),
255                                fields=query.GetFields()).ToDict()
256
257
258 def QueryFields(fielddefs, selected):
259   """Returns list of available fields.
260
261   @type fielddefs: dict
262   @param fielddefs: Field definitions
263   @type selected: list of strings
264   @param selected: List of selected fields
265   @return: List of L{objects.QueryFieldDefinition}
266
267   """
268   if selected is None:
269     # Client requests all fields, sort by name
270     fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
271                            key=operator.attrgetter("name"))
272   else:
273     # Keep order as requested by client
274     fdefs = Query(fielddefs, selected).GetFields()
275
276   return objects.QueryFieldsResponse(fields=fdefs).ToDict()
277
278
279 def _MakeField(name, title, kind):
280   """Wrapper for creating L{objects.QueryFieldDefinition} instances.
281
282   @param name: Field name as a regular expression
283   @param title: Human-readable title
284   @param kind: Field type
285
286   """
287   return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
288
289
290 def _GetNodeRole(node, master_name):
291   """Determine node role.
292
293   @type node: L{objects.Node}
294   @param node: Node object
295   @type master_name: string
296   @param master_name: Master node name
297
298   """
299   if node.name == master_name:
300     return "M"
301   elif node.master_candidate:
302     return "C"
303   elif node.drained:
304     return "D"
305   elif node.offline:
306     return "O"
307   else:
308     return "R"
309
310
311 def _GetItemAttr(attr):
312   """Returns a field function to return an attribute of the item.
313
314   @param attr: Attribute name
315
316   """
317   getter = operator.attrgetter(attr)
318   return lambda _, item: (constants.QRFS_NORMAL, getter(item))
319
320
321 def _GetItemTimestamp(getter):
322   """Returns function for getting timestamp of item.
323
324   @type getter: callable
325   @param getter: Function to retrieve timestamp attribute
326
327   """
328   def fn(_, item):
329     """Returns a timestamp of item.
330
331     """
332     timestamp = getter(item)
333     if timestamp is None:
334       # Old configs might not have all timestamps
335       return (constants.QRFS_UNAVAIL, None)
336     else:
337       return (constants.QRFS_NORMAL, timestamp)
338
339   return fn
340
341
342 def _GetItemTimestampFields(datatype):
343   """Returns common timestamp fields.
344
345   @param datatype: Field data type for use by L{Query.RequestedData}
346
347   """
348   return [
349     (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
350      _GetItemTimestamp(operator.attrgetter("ctime"))),
351     (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
352      _GetItemTimestamp(operator.attrgetter("mtime"))),
353     ]
354
355
356 class NodeQueryData:
357   """Data container for node data queries.
358
359   """
360   def __init__(self, nodes, live_data, master_name, node_to_primary,
361                node_to_secondary, groups, oob_support, cluster):
362     """Initializes this class.
363
364     """
365     self.nodes = nodes
366     self.live_data = live_data
367     self.master_name = master_name
368     self.node_to_primary = node_to_primary
369     self.node_to_secondary = node_to_secondary
370     self.groups = groups
371     self.oob_support = oob_support
372     self.cluster = cluster
373
374     # Used for individual rows
375     self.curlive_data = None
376
377   def __iter__(self):
378     """Iterate over all nodes.
379
380     This function has side-effects and only one instance of the resulting
381     generator should be used at a time.
382
383     """
384     for node in self.nodes:
385       if self.live_data:
386         self.curlive_data = self.live_data.get(node.name, None)
387       else:
388         self.curlive_data = None
389       yield node
390
391
392 #: Fields that are direct attributes of an L{objects.Node} object
393 _NODE_SIMPLE_FIELDS = {
394   "drained": ("Drained", constants.QFT_BOOL),
395   "master_candidate": ("MasterC", constants.QFT_BOOL),
396   "master_capable": ("MasterCapable", constants.QFT_BOOL),
397   "name": ("Node", constants.QFT_TEXT),
398   "offline": ("Offline", constants.QFT_BOOL),
399   "serial_no": ("SerialNo", constants.QFT_NUMBER),
400   "uuid": ("UUID", constants.QFT_TEXT),
401   "vm_capable": ("VMCapable", constants.QFT_BOOL),
402   }
403
404
405 #: Fields requiring talking to the node
406 _NODE_LIVE_FIELDS = {
407   "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
408   "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
409   "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
410   "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
411   "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
412   "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
413   "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
414   "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
415   "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
416   }
417
418
419 def _GetGroup(cb):
420   """Build function for calling another function with an node group.
421
422   @param cb: The callback to be called with the nodegroup
423
424   """
425   def fn(ctx, node):
426     """Get group data for a node.
427
428     @type ctx: L{NodeQueryData}
429     @type inst: L{objects.Node}
430     @param inst: Node object
431
432     """
433     ng = ctx.groups.get(node.group, None)
434     if ng is None:
435       # Nodes always have a group, or the configuration is corrupt
436       return (constants.QRFS_UNAVAIL, None)
437
438     return cb(ctx, node, ng)
439
440   return fn
441
442
443 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
444   """Returns the name of a node's group.
445
446   @type ctx: L{NodeQueryData}
447   @type node: L{objects.Node}
448   @param node: Node object
449   @type ng: L{objects.NodeGroup}
450   @param ng: The node group this node belongs to
451
452   """
453   return (constants.QRFS_NORMAL, ng.name)
454
455
456 def _GetNodePower(ctx, node):
457   """Returns the node powered state
458
459   @type ctx: L{NodeQueryData}
460   @type node: L{objects.Node}
461   @param node: Node object
462
463   """
464   if ctx.oob_support[node.name]:
465     return (constants.QRFS_NORMAL, node.powered)
466
467   return (constants.QRFS_UNAVAIL, None)
468
469
470 def _GetNdParams(ctx, node, ng):
471   """Returns the ndparams for this node.
472
473   @type ctx: L{NodeQueryData}
474   @type node: L{objects.Node}
475   @param node: Node object
476   @type ng: L{objects.NodeGroup}
477   @param ng: The node group this node belongs to
478
479   """
480   return (constants.QRFS_NORMAL, ctx.cluster.SimpleFillND(ng.FillND(node)))
481
482
483 def _GetLiveNodeField(field, kind, ctx, node):
484   """Gets the value of a "live" field from L{NodeQueryData}.
485
486   @param field: Live field name
487   @param kind: Data kind, one of L{constants.QFT_ALL}
488   @type ctx: L{NodeQueryData}
489   @type node: L{objects.Node}
490   @param node: Node object
491
492   """
493   if node.offline:
494     return (constants.QRFS_OFFLINE, None)
495
496   if not ctx.curlive_data:
497     return (constants.QRFS_NODATA, None)
498
499   try:
500     value = ctx.curlive_data[field]
501   except KeyError:
502     return (constants.QRFS_UNAVAIL, None)
503
504   if kind == constants.QFT_TEXT:
505     return (constants.QRFS_NORMAL, value)
506
507   assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
508
509   # Try to convert into number
510   try:
511     return (constants.QRFS_NORMAL, int(value))
512   except (ValueError, TypeError):
513     logging.exception("Failed to convert node field '%s' (value %r) to int",
514                       value, field)
515     return (constants.QRFS_UNAVAIL, None)
516
517
518 def _BuildNodeFields():
519   """Builds list of fields for node queries.
520
521   """
522   fields = [
523     (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
524      lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
525     (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
526      lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
527     (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
528      lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
529     (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
530      lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
531     (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
532      lambda ctx, node: (constants.QRFS_NORMAL,
533                         _GetNodeRole(node, ctx.master_name))),
534     (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP,
535      _GetGroup(_GetNodeGroup)),
536     (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
537      NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
538     (_MakeField("powered", "Powered", constants.QFT_BOOL), NQ_OOB,
539       _GetNodePower),
540     (_MakeField("ndparams", "NodeParameters", constants.QFT_OTHER), NQ_GROUP,
541       _GetGroup(_GetNdParams)),
542     (_MakeField("custom_ndparams", "CustomNodeParameters", constants.QFT_OTHER),
543       NQ_GROUP, lambda ctx, node: (constants.QRFS_NORMAL, node.ndparams)),
544     ]
545
546   def _GetLength(getter):
547     return lambda ctx, node: (constants.QRFS_NORMAL,
548                               len(getter(ctx)[node.name]))
549
550   def _GetList(getter):
551     return lambda ctx, node: (constants.QRFS_NORMAL,
552                               list(getter(ctx)[node.name]))
553
554   # Add fields operating on instance lists
555   for prefix, titleprefix, getter in \
556       [("p", "Pri", operator.attrgetter("node_to_primary")),
557        ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
558     fields.extend([
559       (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
560                   constants.QFT_NUMBER),
561        NQ_INST, _GetLength(getter)),
562       (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
563                   constants.QFT_OTHER),
564        NQ_INST, _GetList(getter)),
565       ])
566
567   # Add simple fields
568   fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
569                  for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
570
571   # Add fields requiring live data
572   fields.extend([
573     (_MakeField(name, title, kind), NQ_LIVE,
574      compat.partial(_GetLiveNodeField, nfield, kind))
575     for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
576     ])
577
578   # Add timestamps
579   fields.extend(_GetItemTimestampFields(NQ_CONFIG))
580
581   return _PrepareFieldList(fields)
582
583
584 class InstanceQueryData:
585   """Data container for instance data queries.
586
587   """
588   def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
589                live_data):
590     """Initializes this class.
591
592     @param instances: List of instance objects
593     @param cluster: Cluster object
594     @type disk_usage: dict; instance name as key
595     @param disk_usage: Per-instance disk usage
596     @type offline_nodes: list of strings
597     @param offline_nodes: List of offline nodes
598     @type bad_nodes: list of strings
599     @param bad_nodes: List of faulty nodes
600     @type live_data: dict; instance name as key
601     @param live_data: Per-instance live data
602
603     """
604     assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
605            "Offline nodes not included in bad nodes"
606     assert not (set(live_data.keys()) & set(bad_nodes)), \
607            "Found live data for bad or offline nodes"
608
609     self.instances = instances
610     self.cluster = cluster
611     self.disk_usage = disk_usage
612     self.offline_nodes = offline_nodes
613     self.bad_nodes = bad_nodes
614     self.live_data = live_data
615
616     # Used for individual rows
617     self.inst_hvparams = None
618     self.inst_beparams = None
619     self.inst_nicparams = None
620
621   def __iter__(self):
622     """Iterate over all instances.
623
624     This function has side-effects and only one instance of the resulting
625     generator should be used at a time.
626
627     """
628     for inst in self.instances:
629       self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
630       self.inst_beparams = self.cluster.FillBE(inst)
631       self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
632                              for nic in inst.nics]
633
634       yield inst
635
636
637 def _GetInstOperState(ctx, inst):
638   """Get instance's operational status.
639
640   @type ctx: L{InstanceQueryData}
641   @type inst: L{objects.Instance}
642   @param inst: Instance object
643
644   """
645   # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
646   # when we actually don't know due to missing data
647   if inst.primary_node in ctx.bad_nodes:
648     return (constants.QRFS_NODATA, None)
649   else:
650     return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
651
652
653 def _GetInstLiveData(name):
654   """Build function for retrieving live data.
655
656   @type name: string
657   @param name: Live data field name
658
659   """
660   def fn(ctx, inst):
661     """Get live data for an instance.
662
663     @type ctx: L{InstanceQueryData}
664     @type inst: L{objects.Instance}
665     @param inst: Instance object
666
667     """
668     if (inst.primary_node in ctx.bad_nodes or
669         inst.primary_node in ctx.offline_nodes):
670       # Can't use QRFS_OFFLINE here as it would describe the instance to be
671       # offline when we actually don't know due to missing data
672       return (constants.QRFS_NODATA, None)
673
674     if inst.name in ctx.live_data:
675       data = ctx.live_data[inst.name]
676       if name in data:
677         return (constants.QRFS_NORMAL, data[name])
678
679     return (constants.QRFS_UNAVAIL, None)
680
681   return fn
682
683
684 def _GetInstStatus(ctx, inst):
685   """Get instance status.
686
687   @type ctx: L{InstanceQueryData}
688   @type inst: L{objects.Instance}
689   @param inst: Instance object
690
691   """
692   if inst.primary_node in ctx.offline_nodes:
693     return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
694
695   if inst.primary_node in ctx.bad_nodes:
696     return (constants.QRFS_NORMAL, "ERROR_nodedown")
697
698   if bool(ctx.live_data.get(inst.name)):
699     if inst.admin_up:
700       return (constants.QRFS_NORMAL, "running")
701     else:
702       return (constants.QRFS_NORMAL, "ERROR_up")
703
704   if inst.admin_up:
705     return (constants.QRFS_NORMAL, "ERROR_down")
706
707   return (constants.QRFS_NORMAL, "ADMIN_down")
708
709
710 def _GetInstDiskSize(index):
711   """Build function for retrieving disk size.
712
713   @type index: int
714   @param index: Disk index
715
716   """
717   def fn(_, inst):
718     """Get size of a disk.
719
720     @type inst: L{objects.Instance}
721     @param inst: Instance object
722
723     """
724     try:
725       return (constants.QRFS_NORMAL, inst.disks[index].size)
726     except IndexError:
727       return (constants.QRFS_UNAVAIL, None)
728
729   return fn
730
731
732 def _GetInstNic(index, cb):
733   """Build function for calling another function with an instance NIC.
734
735   @type index: int
736   @param index: NIC index
737   @type cb: callable
738   @param cb: Callback
739
740   """
741   def fn(ctx, inst):
742     """Call helper function with instance NIC.
743
744     @type ctx: L{InstanceQueryData}
745     @type inst: L{objects.Instance}
746     @param inst: Instance object
747
748     """
749     try:
750       nic = inst.nics[index]
751     except IndexError:
752       return (constants.QRFS_UNAVAIL, None)
753
754     return cb(ctx, index, nic)
755
756   return fn
757
758
759 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
760   """Get a NIC's IP address.
761
762   @type ctx: L{InstanceQueryData}
763   @type nic: L{objects.NIC}
764   @param nic: NIC object
765
766   """
767   if nic.ip is None:
768     return (constants.QRFS_UNAVAIL, None)
769   else:
770     return (constants.QRFS_NORMAL, nic.ip)
771
772
773 def _GetInstNicBridge(ctx, index, _):
774   """Get a NIC's bridge.
775
776   @type ctx: L{InstanceQueryData}
777   @type index: int
778   @param index: NIC index
779
780   """
781   assert len(ctx.inst_nicparams) >= index
782
783   nicparams = ctx.inst_nicparams[index]
784
785   if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
786     return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
787   else:
788     return (constants.QRFS_UNAVAIL, None)
789
790
791 def _GetInstAllNicBridges(ctx, inst):
792   """Get all network bridges for an instance.
793
794   @type ctx: L{InstanceQueryData}
795   @type inst: L{objects.Instance}
796   @param inst: Instance object
797
798   """
799   assert len(ctx.inst_nicparams) == len(inst.nics)
800
801   result = []
802
803   for nicp in ctx.inst_nicparams:
804     if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
805       result.append(nicp[constants.NIC_LINK])
806     else:
807       result.append(None)
808
809   assert len(result) == len(inst.nics)
810
811   return (constants.QRFS_NORMAL, result)
812
813
814 def _GetInstNicParam(name):
815   """Build function for retrieving a NIC parameter.
816
817   @type name: string
818   @param name: Parameter name
819
820   """
821   def fn(ctx, index, _):
822     """Get a NIC's bridge.
823
824     @type ctx: L{InstanceQueryData}
825     @type inst: L{objects.Instance}
826     @param inst: Instance object
827     @type nic: L{objects.NIC}
828     @param nic: NIC object
829
830     """
831     assert len(ctx.inst_nicparams) >= index
832     return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
833
834   return fn
835
836
837 def _GetInstanceNetworkFields():
838   """Get instance fields involving network interfaces.
839
840   @return: List of field definitions used as input for L{_PrepareFieldList}
841
842   """
843   nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
844   nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
845   nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
846
847   fields = [
848     # First NIC (legacy)
849     (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
850      _GetInstNic(0, _GetInstNicIp)),
851     (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
852      _GetInstNic(0, nic_mac_fn)),
853     (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
854      _GetInstNic(0, _GetInstNicBridge)),
855     (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
856      _GetInstNic(0, nic_mode_fn)),
857     (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
858      _GetInstNic(0, nic_link_fn)),
859
860     # All NICs
861     (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
862      lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
863     (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
864      lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
865     (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
866      lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
867     (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
868      lambda ctx, inst: (constants.QRFS_NORMAL,
869                         [nicp[constants.NIC_MODE]
870                          for nicp in ctx.inst_nicparams])),
871     (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
872      lambda ctx, inst: (constants.QRFS_NORMAL,
873                         [nicp[constants.NIC_LINK]
874                          for nicp in ctx.inst_nicparams])),
875     (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
876      _GetInstAllNicBridges),
877     ]
878
879   # NICs by number
880   for i in range(constants.MAX_NICS):
881     fields.extend([
882       (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
883        IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
884       (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
885        IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
886       (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
887        IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
888       (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
889        IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
890       (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
891        IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
892       ])
893
894   return fields
895
896
897 def _GetInstDiskUsage(ctx, inst):
898   """Get disk usage for an instance.
899
900   @type ctx: L{InstanceQueryData}
901   @type inst: L{objects.Instance}
902   @param inst: Instance object
903
904   """
905   usage = ctx.disk_usage[inst.name]
906
907   if usage is None:
908     usage = 0
909
910   return (constants.QRFS_NORMAL, usage)
911
912
913 def _GetInstanceDiskFields():
914   """Get instance fields involving disks.
915
916   @return: List of field definitions used as input for L{_PrepareFieldList}
917
918   """
919   fields = [
920     (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
921      _GetInstDiskUsage),
922     (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
923      _GetInstDiskSize(0)),
924     (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
925      _GetInstDiskSize(1)),
926     (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
927      lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
928     (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
929      lambda ctx, inst: (constants.QRFS_NORMAL,
930                         [disk.size for disk in inst.disks])),
931     ]
932
933   # Disks by number
934   fields.extend([
935     (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
936      IQ_CONFIG, _GetInstDiskSize(i))
937     for i in range(constants.MAX_DISKS)
938     ])
939
940   return fields
941
942
943 def _GetInstanceParameterFields():
944   """Get instance fields involving parameters.
945
946   @return: List of field definitions used as input for L{_PrepareFieldList}
947
948   """
949   # TODO: Consider moving titles closer to constants
950   be_title = {
951     constants.BE_AUTO_BALANCE: "Auto_balance",
952     constants.BE_MEMORY: "Configured_memory",
953     constants.BE_VCPUS: "VCPUs",
954     }
955
956   hv_title = {
957     constants.HV_ACPI: "ACPI",
958     constants.HV_BOOT_ORDER: "Boot_order",
959     constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
960     constants.HV_DISK_TYPE: "Disk_type",
961     constants.HV_INITRD_PATH: "Initrd_path",
962     constants.HV_KERNEL_PATH: "Kernel_path",
963     constants.HV_NIC_TYPE: "NIC_type",
964     constants.HV_PAE: "PAE",
965     constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
966     }
967
968   fields = [
969     # Filled parameters
970     (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
971      IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
972     (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
973      IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
974     (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
975      lambda ctx, _: (constants.QRFS_NORMAL,
976                      ctx.inst_beparams[constants.BE_VCPUS])),
977
978     # Unfilled parameters
979     (_MakeField("custom_hvparams", "CustomHypervisorParameters",
980                 constants.QFT_OTHER),
981      IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
982     (_MakeField("custom_beparams", "CustomBackendParameters",
983                 constants.QFT_OTHER),
984      IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
985     (_MakeField("custom_nicparams", "CustomNicParameters",
986                 constants.QFT_OTHER),
987      IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
988                                    [nic.nicparams for nic in inst.nics])),
989     ]
990
991   # HV params
992   def _GetInstHvParam(name):
993     return lambda ctx, _: (constants.QRFS_NORMAL,
994                            ctx.inst_hvparams.get(name, None))
995
996   fields.extend([
997     # For now all hypervisor parameters are exported as QFT_OTHER
998     (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
999                 constants.QFT_OTHER),
1000      IQ_CONFIG, _GetInstHvParam(name))
1001     for name in constants.HVS_PARAMETERS
1002     if name not in constants.HVC_GLOBALS
1003     ])
1004
1005   # BE params
1006   def _GetInstBeParam(name):
1007     return lambda ctx, _: (constants.QRFS_NORMAL,
1008                            ctx.inst_beparams.get(name, None))
1009
1010   fields.extend([
1011     # For now all backend parameters are exported as QFT_OTHER
1012     (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1013                 constants.QFT_OTHER),
1014      IQ_CONFIG, _GetInstBeParam(name))
1015     for name in constants.BES_PARAMETERS
1016     ])
1017
1018   return fields
1019
1020
1021 _INST_SIMPLE_FIELDS = {
1022   "disk_template": ("Disk_template", constants.QFT_TEXT),
1023   "hypervisor": ("Hypervisor", constants.QFT_TEXT),
1024   "name": ("Node", constants.QFT_TEXT),
1025   # Depending on the hypervisor, the port can be None
1026   "network_port": ("Network_port", constants.QFT_OTHER),
1027   "os": ("OS", constants.QFT_TEXT),
1028   "serial_no": ("SerialNo", constants.QFT_NUMBER),
1029   "uuid": ("UUID", constants.QFT_TEXT),
1030   }
1031
1032
1033 def _BuildInstanceFields():
1034   """Builds list of fields for instance queries.
1035
1036   """
1037   fields = [
1038     (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
1039      lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
1040     (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
1041      lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
1042     (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
1043      lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
1044     (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
1045      lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
1046     ]
1047
1048   # Add simple fields
1049   fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1050                  for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1051
1052   # Fields requiring talking to the node
1053   fields.extend([
1054     (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
1055      _GetInstOperState),
1056     (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
1057      _GetInstLiveData("memory")),
1058     (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
1059      _GetInstLiveData("vcpus")),
1060     (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
1061      _GetInstStatus),
1062     ])
1063
1064   fields.extend(_GetInstanceParameterFields())
1065   fields.extend(_GetInstanceDiskFields())
1066   fields.extend(_GetInstanceNetworkFields())
1067   fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1068
1069   return _PrepareFieldList(fields)
1070
1071
1072 class LockQueryData:
1073   """Data container for lock data queries.
1074
1075   """
1076   def __init__(self, lockdata):
1077     """Initializes this class.
1078
1079     """
1080     self.lockdata = lockdata
1081
1082   def __iter__(self):
1083     """Iterate over all locks.
1084
1085     """
1086     return iter(self.lockdata)
1087
1088
1089 def _GetLockOwners(_, data):
1090   """Returns a sorted list of a lock's current owners.
1091
1092   """
1093   (_, _, owners, _) = data
1094
1095   if owners:
1096     owners = utils.NiceSort(owners)
1097
1098   return (constants.QRFS_NORMAL, owners)
1099
1100
1101 def _GetLockPending(_, data):
1102   """Returns a sorted list of a lock's pending acquires.
1103
1104   """
1105   (_, _, _, pending) = data
1106
1107   if pending:
1108     pending = [(mode, utils.NiceSort(names))
1109                for (mode, names) in pending]
1110
1111   return (constants.QRFS_NORMAL, pending)
1112
1113
1114 def _BuildLockFields():
1115   """Builds list of fields for lock queries.
1116
1117   """
1118   return _PrepareFieldList([
1119     (_MakeField("name", "Name", constants.QFT_TEXT), None,
1120      lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1121     (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1122      lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1123     (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1124      _GetLockOwners),
1125     (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1126      _GetLockPending),
1127     ])
1128
1129
1130 class GroupQueryData:
1131   """Data container for node group data queries.
1132
1133   """
1134   def __init__(self, groups, group_to_nodes, group_to_instances):
1135     """Initializes this class.
1136
1137     @param groups: List of node group objects
1138     @type group_to_nodes: dict; group UUID as key
1139     @param group_to_nodes: Per-group list of nodes
1140     @type group_to_instances: dict; group UUID as key
1141     @param group_to_instances: Per-group list of (primary) instances
1142
1143     """
1144     self.groups = groups
1145     self.group_to_nodes = group_to_nodes
1146     self.group_to_instances = group_to_instances
1147
1148   def __iter__(self):
1149     """Iterate over all node groups.
1150
1151     """
1152     return iter(self.groups)
1153
1154
1155 _GROUP_SIMPLE_FIELDS = {
1156   "alloc_policy": ("AllocPolicy", constants.QFT_TEXT),
1157   "name": ("Group", constants.QFT_TEXT),
1158   "serial_no": ("SerialNo", constants.QFT_NUMBER),
1159   "uuid": ("UUID", constants.QFT_TEXT),
1160   }
1161
1162
1163 def _BuildGroupFields():
1164   """Builds list of fields for node group queries.
1165
1166   """
1167   # Add simple fields
1168   fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1169             for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1170
1171   def _GetLength(getter):
1172     return lambda ctx, group: (constants.QRFS_NORMAL,
1173                                len(getter(ctx)[group.uuid]))
1174
1175   def _GetSortedList(getter):
1176     return lambda ctx, group: (constants.QRFS_NORMAL,
1177                                utils.NiceSort(getter(ctx)[group.uuid]))
1178
1179   group_to_nodes = operator.attrgetter("group_to_nodes")
1180   group_to_instances = operator.attrgetter("group_to_instances")
1181
1182   # Add fields for nodes
1183   fields.extend([
1184     (_MakeField("node_cnt", "Nodes", constants.QFT_NUMBER),
1185      GQ_NODE, _GetLength(group_to_nodes)),
1186     (_MakeField("node_list", "NodeList", constants.QFT_OTHER),
1187      GQ_NODE, _GetSortedList(group_to_nodes)),
1188     ])
1189
1190   # Add fields for instances
1191   fields.extend([
1192     (_MakeField("pinst_cnt", "Instances", constants.QFT_NUMBER),
1193      GQ_INST, _GetLength(group_to_instances)),
1194     (_MakeField("pinst_list", "InstanceList", constants.QFT_OTHER),
1195      GQ_INST, _GetSortedList(group_to_instances)),
1196     ])
1197
1198   fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1199
1200   return _PrepareFieldList(fields)
1201
1202
1203 #: Fields available for node queries
1204 NODE_FIELDS = _BuildNodeFields()
1205
1206 #: Fields available for instance queries
1207 INSTANCE_FIELDS = _BuildInstanceFields()
1208
1209 #: Fields available for lock queries
1210 LOCK_FIELDS = _BuildLockFields()
1211
1212 #: Fields available for node group queries
1213 GROUP_FIELDS = _BuildGroupFields()
1214
1215 #: All available field lists
1216 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]