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