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