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