4 # Copyright (C) 2010, 2011 Google Inc.
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.
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.
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
22 """Module for query operations
26 - Add field definitions
27 - See how L{NODE_FIELDS} is built
29 - Query field definition (L{objects.QueryFieldDefinition}, use
30 L{_MakeField} for creating), containing:
31 - Name, must be lowercase and match L{FIELD_NAME_RE}
32 - Title for tables, must not contain whitespace and match
34 - Value data type, e.g. L{constants.QFT_NUMBER}
35 - Data request type, see e.g. C{NQ_*}
36 - A retrieval function, see L{Query.__init__} for description
37 - Pass list of fields through L{_PrepareFieldList} for preparation and
39 - Instantiate L{Query} with prepared field list definition and selected fields
40 - Call L{Query.RequestedData} to determine what data to collect/compute
41 - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use
43 - Data container must support iteration using C{__iter__}
44 - Items are passed to retrieval functions and can have any format
45 - Call L{Query.GetFields} to get list of definitions for selected fields
47 @attention: Retrieval functions must be idempotent. They can be called multiple
48 times, in any order and any number of times. This is important to keep in
49 mind for implementing filters in the future.
57 from ganeti import constants
58 from ganeti import errors
59 from ganeti import utils
60 from ganeti import compat
61 from ganeti import objects
64 from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
65 QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
66 QRFS_NORMAL, QRFS_UNKNOWN, QRFS_NODATA,
67 QRFS_UNAVAIL, QRFS_OFFLINE)
70 # Constants for requesting data from the caller/data provider. Each property
71 # collected/computed separately by the data provider should have its own to
72 # only collect the requested data and not more.
82 IQ_DISKUSAGE) = range(100, 103)
86 LQ_PENDING) = range(10, 13)
90 GQ_INST) = range(200, 203)
93 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
94 TITLE_RE = re.compile(r"^[^\s]+$")
96 #: Verification function for each field type
98 QFT_UNKNOWN: ht.TNone,
103 QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
104 QFT_OTHER: lambda _: True,
107 # Unique objects for special field statuses
108 _FS_UNKNOWN = object()
109 _FS_NODATA = object()
110 _FS_UNAVAIL = object()
111 _FS_OFFLINE = object()
114 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
115 """Gets the contents of an unknown field.
121 def _GetQueryFields(fielddefs, selected):
122 """Calculates the internal list of selected fields.
124 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
126 @type fielddefs: dict
127 @param fielddefs: Field definitions
128 @type selected: list of strings
129 @param selected: List of selected fields
134 for name in selected:
136 fdef = fielddefs[name]
138 fdef = (_MakeField(name, name, QFT_UNKNOWN), None, _GetUnknownField)
140 assert len(fdef) == 3
147 def GetAllFields(fielddefs):
148 """Extract L{objects.QueryFieldDefinition} from field definitions.
150 @rtype: list of L{objects.QueryFieldDefinition}
153 return [fdef for (fdef, _, _) in fielddefs]
157 def __init__(self, fieldlist, selected):
158 """Initializes this class.
160 The field definition is a dictionary with the field's name as a key and a
161 tuple containing, in order, the field definition object
162 (L{objects.QueryFieldDefinition}, the data kind to help calling code
163 collect data and a retrieval function. The retrieval function is called
164 with two parameters, in order, the data container and the item in container
165 (see L{Query.Query}).
167 Users of this class can call L{RequestedData} before preparing the data
168 container to determine what data is needed.
170 @type fieldlist: dictionary
171 @param fieldlist: Field definitions
172 @type selected: list of strings
173 @param selected: List of selected fields
176 self._fields = _GetQueryFields(fieldlist, selected)
178 def RequestedData(self):
179 """Gets requested kinds of data.
184 return frozenset(datakind
185 for (_, datakind, _) in self._fields
186 if datakind is not None)
189 """Returns the list of fields for this query.
191 Includes unknown fields.
193 @rtype: List of L{objects.QueryFieldDefinition}
196 return GetAllFields(self._fields)
198 def Query(self, ctx):
201 @param ctx: Data container passed to field retrieval functions, must
202 support iteration using C{__iter__}
205 result = [[_ProcessResult(fn(ctx, item)) for (_, _, fn) in self._fields]
210 for (idx, row) in enumerate(result):
211 assert _VerifyResultRow(self._fields, row), \
212 ("Inconsistent result for fields %s in row %s: %r" %
213 (GetAllFields(self._fields), idx, row))
217 def OldStyleQuery(self, ctx):
218 """Query with "old" query result format.
220 See L{Query.Query} for arguments.
223 unknown = set(fdef.name
224 for (fdef, _, _) in self._fields if fdef.kind == QFT_UNKNOWN)
226 raise errors.OpPrereqError("Unknown output fields selected: %s" %
227 (utils.CommaJoin(unknown), ),
230 return [[value for (_, value) in row]
231 for row in self.Query(ctx)]
234 def _ProcessResult(value):
235 """Converts result values into externally-visible ones.
238 if value is _FS_UNKNOWN:
239 return (QRFS_UNKNOWN, None)
240 elif value is _FS_NODATA:
241 return (QRFS_NODATA, None)
242 elif value is _FS_UNAVAIL:
243 return (QRFS_UNAVAIL, None)
244 elif value is _FS_OFFLINE:
245 return (QRFS_OFFLINE, None)
247 return (QRFS_NORMAL, value)
250 def _VerifyResultRow(fields, row):
251 """Verifies the contents of a query result row.
254 @param fields: Field definitions for result
255 @type row: list of tuples
259 return (len(row) == len(fields) and
260 compat.all((status == QRFS_NORMAL and _VERIFY_FN[fdef.kind](value)) or
261 # Value for an abnormal status must be None
262 (status != QRFS_NORMAL and value is None)
263 for ((status, value), (fdef, _, _)) in zip(row, fields)))
266 def _PrepareFieldList(fields):
267 """Prepares field list for use by L{Query}.
269 Converts the list to a dictionary and does some verification.
271 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
273 @param fields: List of fields, see L{Query.__init__} for a better description
275 @return: Field dictionary for L{Query}
279 duplicates = utils.FindDuplicates(fdef.title.lower()
280 for (fdef, _, _) in fields)
281 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
286 (fdef, _, fn) = field
288 assert fdef.name and fdef.title, "Name and title are required"
289 assert FIELD_NAME_RE.match(fdef.name)
290 assert TITLE_RE.match(fdef.title)
292 assert fdef.name not in result, \
293 "Duplicate field name '%s' found" % fdef.name
295 result[fdef.name] = field
297 assert len(result) == len(fields)
298 assert compat.all(name == fdef.name
299 for (name, (fdef, _, _)) in result.items())
304 def GetQueryResponse(query, ctx):
305 """Prepares the response for a query.
307 @type query: L{Query}
308 @param ctx: Data container, see L{Query.Query}
311 return objects.QueryResponse(data=query.Query(ctx),
312 fields=query.GetFields()).ToDict()
315 def QueryFields(fielddefs, selected):
316 """Returns list of available fields.
318 @type fielddefs: dict
319 @param fielddefs: Field definitions
320 @type selected: list of strings
321 @param selected: List of selected fields
322 @return: List of L{objects.QueryFieldDefinition}
326 # Client requests all fields, sort by name
327 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
328 key=operator.attrgetter("name"))
330 # Keep order as requested by client
331 fdefs = Query(fielddefs, selected).GetFields()
333 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
336 def _MakeField(name, title, kind):
337 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
339 @param name: Field name as a regular expression
340 @param title: Human-readable title
341 @param kind: Field type
344 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
347 def _GetNodeRole(node, master_name):
348 """Determine node role.
350 @type node: L{objects.Node}
351 @param node: Node object
352 @type master_name: string
353 @param master_name: Master node name
356 if node.name == master_name:
358 elif node.master_candidate:
368 def _GetItemAttr(attr):
369 """Returns a field function to return an attribute of the item.
371 @param attr: Attribute name
374 getter = operator.attrgetter(attr)
375 return lambda _, item: getter(item)
378 def _GetItemTimestamp(getter):
379 """Returns function for getting timestamp of item.
381 @type getter: callable
382 @param getter: Function to retrieve timestamp attribute
386 """Returns a timestamp of item.
389 timestamp = getter(item)
390 if timestamp is None:
391 # Old configs might not have all timestamps
399 def _GetItemTimestampFields(datatype):
400 """Returns common timestamp fields.
402 @param datatype: Field data type for use by L{Query.RequestedData}
406 (_MakeField("ctime", "CTime", QFT_TIMESTAMP), datatype,
407 _GetItemTimestamp(operator.attrgetter("ctime"))),
408 (_MakeField("mtime", "MTime", QFT_TIMESTAMP), datatype,
409 _GetItemTimestamp(operator.attrgetter("mtime"))),
414 """Data container for node data queries.
417 def __init__(self, nodes, live_data, master_name, node_to_primary,
418 node_to_secondary, groups, oob_support, cluster):
419 """Initializes this class.
423 self.live_data = live_data
424 self.master_name = master_name
425 self.node_to_primary = node_to_primary
426 self.node_to_secondary = node_to_secondary
428 self.oob_support = oob_support
429 self.cluster = cluster
431 # Used for individual rows
432 self.curlive_data = None
435 """Iterate over all nodes.
437 This function has side-effects and only one instance of the resulting
438 generator should be used at a time.
441 for node in self.nodes:
443 self.curlive_data = self.live_data.get(node.name, None)
445 self.curlive_data = None
449 #: Fields that are direct attributes of an L{objects.Node} object
450 _NODE_SIMPLE_FIELDS = {
451 "drained": ("Drained", QFT_BOOL),
452 "master_candidate": ("MasterC", QFT_BOOL),
453 "master_capable": ("MasterCapable", QFT_BOOL),
454 "name": ("Node", QFT_TEXT),
455 "offline": ("Offline", QFT_BOOL),
456 "serial_no": ("SerialNo", QFT_NUMBER),
457 "uuid": ("UUID", QFT_TEXT),
458 "vm_capable": ("VMCapable", QFT_BOOL),
462 #: Fields requiring talking to the node
463 _NODE_LIVE_FIELDS = {
464 "bootid": ("BootID", QFT_TEXT, "bootid"),
465 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes"),
466 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets"),
467 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total"),
468 "dfree": ("DFree", QFT_UNIT, "vg_free"),
469 "dtotal": ("DTotal", QFT_UNIT, "vg_size"),
470 "mfree": ("MFree", QFT_UNIT, "memory_free"),
471 "mnode": ("MNode", QFT_UNIT, "memory_dom0"),
472 "mtotal": ("MTotal", QFT_UNIT, "memory_total"),
477 """Build function for calling another function with an node group.
479 @param cb: The callback to be called with the nodegroup
483 """Get group data for a node.
485 @type ctx: L{NodeQueryData}
486 @type inst: L{objects.Node}
487 @param inst: Node object
490 ng = ctx.groups.get(node.group, None)
492 # Nodes always have a group, or the configuration is corrupt
495 return cb(ctx, node, ng)
500 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
501 """Returns the name of a node's group.
503 @type ctx: L{NodeQueryData}
504 @type node: L{objects.Node}
505 @param node: Node object
506 @type ng: L{objects.NodeGroup}
507 @param ng: The node group this node belongs to
513 def _GetNodePower(ctx, node):
514 """Returns the node powered state
516 @type ctx: L{NodeQueryData}
517 @type node: L{objects.Node}
518 @param node: Node object
521 if ctx.oob_support[node.name]:
527 def _GetNdParams(ctx, node, ng):
528 """Returns the ndparams for this node.
530 @type ctx: L{NodeQueryData}
531 @type node: L{objects.Node}
532 @param node: Node object
533 @type ng: L{objects.NodeGroup}
534 @param ng: The node group this node belongs to
537 return ctx.cluster.SimpleFillND(ng.FillND(node))
540 def _GetLiveNodeField(field, kind, ctx, node):
541 """Gets the value of a "live" field from L{NodeQueryData}.
543 @param field: Live field name
544 @param kind: Data kind, one of L{constants.QFT_ALL}
545 @type ctx: L{NodeQueryData}
546 @type node: L{objects.Node}
547 @param node: Node object
553 if not ctx.curlive_data:
557 value = ctx.curlive_data[field]
564 assert kind in (QFT_NUMBER, QFT_UNIT)
566 # Try to convert into number
569 except (ValueError, TypeError):
570 logging.exception("Failed to convert node field '%s' (value %r) to int",
575 def _BuildNodeFields():
576 """Builds list of fields for node queries.
580 (_MakeField("pip", "PrimaryIP", QFT_TEXT), NQ_CONFIG,
581 _GetItemAttr("primary_ip")),
582 (_MakeField("sip", "SecondaryIP", QFT_TEXT), NQ_CONFIG,
583 _GetItemAttr("secondary_ip")),
584 (_MakeField("tags", "Tags", QFT_OTHER), NQ_CONFIG,
585 lambda ctx, node: list(node.GetTags())),
586 (_MakeField("master", "IsMaster", QFT_BOOL), NQ_CONFIG,
587 lambda ctx, node: node.name == ctx.master_name),
588 (_MakeField("role", "Role", QFT_TEXT), NQ_CONFIG,
589 lambda ctx, node: _GetNodeRole(node, ctx.master_name)),
590 (_MakeField("group", "Group", QFT_TEXT), NQ_GROUP,
591 _GetGroup(_GetNodeGroup)),
592 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT),
593 NQ_CONFIG, _GetItemAttr("group")),
594 (_MakeField("powered", "Powered", QFT_BOOL), NQ_OOB, _GetNodePower),
595 (_MakeField("ndparams", "NodeParameters", QFT_OTHER), NQ_GROUP,
596 _GetGroup(_GetNdParams)),
597 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER),
598 NQ_GROUP, _GetItemAttr("ndparams")),
601 def _GetLength(getter):
602 return lambda ctx, node: len(getter(ctx)[node.name])
604 def _GetList(getter):
605 return lambda ctx, node: list(getter(ctx)[node.name])
607 # Add fields operating on instance lists
608 for prefix, titleprefix, getter in \
609 [("p", "Pri", operator.attrgetter("node_to_primary")),
610 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
612 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER),
613 NQ_INST, _GetLength(getter)),
614 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
616 NQ_INST, _GetList(getter)),
620 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
621 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
623 # Add fields requiring live data
625 (_MakeField(name, title, kind), NQ_LIVE,
626 compat.partial(_GetLiveNodeField, nfield, kind))
627 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
631 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
633 return _PrepareFieldList(fields)
636 class InstanceQueryData:
637 """Data container for instance data queries.
640 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
642 """Initializes this class.
644 @param instances: List of instance objects
645 @param cluster: Cluster object
646 @type disk_usage: dict; instance name as key
647 @param disk_usage: Per-instance disk usage
648 @type offline_nodes: list of strings
649 @param offline_nodes: List of offline nodes
650 @type bad_nodes: list of strings
651 @param bad_nodes: List of faulty nodes
652 @type live_data: dict; instance name as key
653 @param live_data: Per-instance live data
656 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
657 "Offline nodes not included in bad nodes"
658 assert not (set(live_data.keys()) & set(bad_nodes)), \
659 "Found live data for bad or offline nodes"
661 self.instances = instances
662 self.cluster = cluster
663 self.disk_usage = disk_usage
664 self.offline_nodes = offline_nodes
665 self.bad_nodes = bad_nodes
666 self.live_data = live_data
668 # Used for individual rows
669 self.inst_hvparams = None
670 self.inst_beparams = None
671 self.inst_nicparams = None
674 """Iterate over all instances.
676 This function has side-effects and only one instance of the resulting
677 generator should be used at a time.
680 for inst in self.instances:
681 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
682 self.inst_beparams = self.cluster.FillBE(inst)
683 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
684 for nic in inst.nics]
689 def _GetInstOperState(ctx, inst):
690 """Get instance's operational status.
692 @type ctx: L{InstanceQueryData}
693 @type inst: L{objects.Instance}
694 @param inst: Instance object
697 # Can't use QRFS_OFFLINE here as it would describe the instance to
698 # be offline when we actually don't know due to missing data
699 if inst.primary_node in ctx.bad_nodes:
702 return bool(ctx.live_data.get(inst.name))
705 def _GetInstLiveData(name):
706 """Build function for retrieving live data.
709 @param name: Live data field name
713 """Get live data for an instance.
715 @type ctx: L{InstanceQueryData}
716 @type inst: L{objects.Instance}
717 @param inst: Instance object
720 if (inst.primary_node in ctx.bad_nodes or
721 inst.primary_node in ctx.offline_nodes):
722 # Can't use QRFS_OFFLINE here as it would describe the instance to be
723 # offline when we actually don't know due to missing data
726 if inst.name in ctx.live_data:
727 data = ctx.live_data[inst.name]
736 def _GetInstStatus(ctx, inst):
737 """Get instance status.
739 @type ctx: L{InstanceQueryData}
740 @type inst: L{objects.Instance}
741 @param inst: Instance object
744 if inst.primary_node in ctx.offline_nodes:
745 return "ERROR_nodeoffline"
747 if inst.primary_node in ctx.bad_nodes:
748 return "ERROR_nodedown"
750 if bool(ctx.live_data.get(inst.name)):
762 def _GetInstDiskSize(index):
763 """Build function for retrieving disk size.
766 @param index: Disk index
770 """Get size of a disk.
772 @type inst: L{objects.Instance}
773 @param inst: Instance object
777 return inst.disks[index].size
784 def _GetInstNic(index, cb):
785 """Build function for calling another function with an instance NIC.
788 @param index: NIC index
794 """Call helper function with instance NIC.
796 @type ctx: L{InstanceQueryData}
797 @type inst: L{objects.Instance}
798 @param inst: Instance object
802 nic = inst.nics[index]
806 return cb(ctx, index, nic)
811 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
812 """Get a NIC's IP address.
814 @type ctx: L{InstanceQueryData}
815 @type nic: L{objects.NIC}
816 @param nic: NIC object
825 def _GetInstNicBridge(ctx, index, _):
826 """Get a NIC's bridge.
828 @type ctx: L{InstanceQueryData}
830 @param index: NIC index
833 assert len(ctx.inst_nicparams) >= index
835 nicparams = ctx.inst_nicparams[index]
837 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
838 return nicparams[constants.NIC_LINK]
843 def _GetInstAllNicBridges(ctx, inst):
844 """Get all network bridges for an instance.
846 @type ctx: L{InstanceQueryData}
847 @type inst: L{objects.Instance}
848 @param inst: Instance object
851 assert len(ctx.inst_nicparams) == len(inst.nics)
855 for nicp in ctx.inst_nicparams:
856 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
857 result.append(nicp[constants.NIC_LINK])
861 assert len(result) == len(inst.nics)
866 def _GetInstNicParam(name):
867 """Build function for retrieving a NIC parameter.
870 @param name: Parameter name
873 def fn(ctx, index, _):
874 """Get a NIC's bridge.
876 @type ctx: L{InstanceQueryData}
877 @type inst: L{objects.Instance}
878 @param inst: Instance object
879 @type nic: L{objects.NIC}
880 @param nic: NIC object
883 assert len(ctx.inst_nicparams) >= index
884 return ctx.inst_nicparams[index][name]
889 def _GetInstanceNetworkFields():
890 """Get instance fields involving network interfaces.
892 @return: List of field definitions used as input for L{_PrepareFieldList}
895 nic_mac_fn = lambda ctx, _, nic: nic.mac
896 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
897 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
901 (_MakeField("ip", "IP_address", QFT_TEXT), IQ_CONFIG,
902 _GetInstNic(0, _GetInstNicIp)),
903 (_MakeField("mac", "MAC_address", QFT_TEXT), IQ_CONFIG,
904 _GetInstNic(0, nic_mac_fn)),
905 (_MakeField("bridge", "Bridge", QFT_TEXT), IQ_CONFIG,
906 _GetInstNic(0, _GetInstNicBridge)),
907 (_MakeField("nic_mode", "NIC_Mode", QFT_TEXT), IQ_CONFIG,
908 _GetInstNic(0, nic_mode_fn)),
909 (_MakeField("nic_link", "NIC_Link", QFT_TEXT), IQ_CONFIG,
910 _GetInstNic(0, nic_link_fn)),
913 (_MakeField("nic.count", "NICs", QFT_NUMBER), IQ_CONFIG,
914 lambda ctx, inst: len(inst.nics)),
915 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER), IQ_CONFIG,
916 lambda ctx, inst: [nic.mac for nic in inst.nics]),
917 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER), IQ_CONFIG,
918 lambda ctx, inst: [nic.ip for nic in inst.nics]),
919 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER), IQ_CONFIG,
920 lambda ctx, inst: [nicp[constants.NIC_MODE]
921 for nicp in ctx.inst_nicparams]),
922 (_MakeField("nic.links", "NIC_links", QFT_OTHER), IQ_CONFIG,
923 lambda ctx, inst: [nicp[constants.NIC_LINK]
924 for nicp in ctx.inst_nicparams]),
925 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER), IQ_CONFIG,
926 _GetInstAllNicBridges),
930 for i in range(constants.MAX_NICS):
932 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT),
933 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
934 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT),
935 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
936 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT),
937 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
938 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT),
939 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
940 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT),
941 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
947 def _GetInstDiskUsage(ctx, inst):
948 """Get disk usage for an instance.
950 @type ctx: L{InstanceQueryData}
951 @type inst: L{objects.Instance}
952 @param inst: Instance object
955 usage = ctx.disk_usage[inst.name]
963 def _GetInstanceDiskFields():
964 """Get instance fields involving disks.
966 @return: List of field definitions used as input for L{_PrepareFieldList}
970 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT), IQ_DISKUSAGE,
972 (_MakeField("sda_size", "LegacyDisk/0", QFT_UNIT), IQ_CONFIG,
973 _GetInstDiskSize(0)),
974 (_MakeField("sdb_size", "LegacyDisk/1", QFT_UNIT), IQ_CONFIG,
975 _GetInstDiskSize(1)),
976 (_MakeField("disk.count", "Disks", QFT_NUMBER), IQ_CONFIG,
977 lambda ctx, inst: len(inst.disks)),
978 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER), IQ_CONFIG,
979 lambda ctx, inst: [disk.size for disk in inst.disks]),
984 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT),
985 IQ_CONFIG, _GetInstDiskSize(i))
986 for i in range(constants.MAX_DISKS)
992 def _GetInstanceParameterFields():
993 """Get instance fields involving parameters.
995 @return: List of field definitions used as input for L{_PrepareFieldList}
998 # TODO: Consider moving titles closer to constants
1000 constants.BE_AUTO_BALANCE: "Auto_balance",
1001 constants.BE_MEMORY: "ConfigMemory",
1002 constants.BE_VCPUS: "ConfigVCPUs",
1006 constants.HV_ACPI: "ACPI",
1007 constants.HV_BOOT_ORDER: "Boot_order",
1008 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1009 constants.HV_DISK_TYPE: "Disk_type",
1010 constants.HV_INITRD_PATH: "Initrd_path",
1011 constants.HV_KERNEL_PATH: "Kernel_path",
1012 constants.HV_NIC_TYPE: "NIC_type",
1013 constants.HV_PAE: "PAE",
1014 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1019 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER),
1020 IQ_CONFIG, lambda ctx, _: ctx.inst_hvparams),
1021 (_MakeField("beparams", "BackendParameters", QFT_OTHER),
1022 IQ_CONFIG, lambda ctx, _: ctx.inst_beparams),
1023 (_MakeField("vcpus", "LegacyVCPUs", QFT_NUMBER), IQ_CONFIG,
1024 lambda ctx, _: ctx.inst_beparams[constants.BE_VCPUS]),
1026 # Unfilled parameters
1027 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER),
1028 IQ_CONFIG, _GetItemAttr("hvparams")),
1029 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER),
1030 IQ_CONFIG, _GetItemAttr("beparams")),
1031 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER),
1032 IQ_CONFIG, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1036 def _GetInstHvParam(name):
1037 return lambda ctx, _: ctx.inst_hvparams.get(name, None)
1040 # For now all hypervisor parameters are exported as QFT_OTHER
1041 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name), QFT_OTHER),
1042 IQ_CONFIG, _GetInstHvParam(name))
1043 for name in constants.HVS_PARAMETERS
1044 if name not in constants.HVC_GLOBALS
1048 def _GetInstBeParam(name):
1049 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1052 # For now all backend parameters are exported as QFT_OTHER
1053 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name), QFT_OTHER),
1054 IQ_CONFIG, _GetInstBeParam(name))
1055 for name in constants.BES_PARAMETERS
1061 _INST_SIMPLE_FIELDS = {
1062 "disk_template": ("Disk_template", QFT_TEXT),
1063 "hypervisor": ("Hypervisor", QFT_TEXT),
1064 "name": ("Node", QFT_TEXT),
1065 # Depending on the hypervisor, the port can be None
1066 "network_port": ("Network_port", QFT_OTHER),
1067 "os": ("OS", QFT_TEXT),
1068 "serial_no": ("SerialNo", QFT_NUMBER),
1069 "uuid": ("UUID", QFT_TEXT),
1073 def _BuildInstanceFields():
1074 """Builds list of fields for instance queries.
1078 (_MakeField("pnode", "Primary_node", QFT_TEXT), IQ_CONFIG,
1079 _GetItemAttr("primary_node")),
1080 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER), IQ_CONFIG,
1081 lambda ctx, inst: list(inst.secondary_nodes)),
1082 (_MakeField("admin_state", "Autostart", QFT_BOOL), IQ_CONFIG,
1083 _GetItemAttr("admin_up")),
1084 (_MakeField("tags", "Tags", QFT_OTHER), IQ_CONFIG,
1085 lambda ctx, inst: list(inst.GetTags())),
1089 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1090 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1092 # Fields requiring talking to the node
1094 (_MakeField("oper_state", "Running", QFT_BOOL), IQ_LIVE,
1096 (_MakeField("oper_ram", "Memory", QFT_UNIT), IQ_LIVE,
1097 _GetInstLiveData("memory")),
1098 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER), IQ_LIVE,
1099 _GetInstLiveData("vcpus")),
1100 (_MakeField("status", "Status", QFT_TEXT), IQ_LIVE, _GetInstStatus),
1103 fields.extend(_GetInstanceParameterFields())
1104 fields.extend(_GetInstanceDiskFields())
1105 fields.extend(_GetInstanceNetworkFields())
1106 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1108 return _PrepareFieldList(fields)
1111 class LockQueryData:
1112 """Data container for lock data queries.
1115 def __init__(self, lockdata):
1116 """Initializes this class.
1119 self.lockdata = lockdata
1122 """Iterate over all locks.
1125 return iter(self.lockdata)
1128 def _GetLockOwners(_, data):
1129 """Returns a sorted list of a lock's current owners.
1132 (_, _, owners, _) = data
1135 owners = utils.NiceSort(owners)
1140 def _GetLockPending(_, data):
1141 """Returns a sorted list of a lock's pending acquires.
1144 (_, _, _, pending) = data
1147 pending = [(mode, utils.NiceSort(names))
1148 for (mode, names) in pending]
1153 def _BuildLockFields():
1154 """Builds list of fields for lock queries.
1157 return _PrepareFieldList([
1158 (_MakeField("name", "Name", QFT_TEXT), None,
1159 lambda ctx, (name, mode, owners, pending): name),
1160 (_MakeField("mode", "Mode", QFT_OTHER), LQ_MODE,
1161 lambda ctx, (name, mode, owners, pending): mode),
1162 (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners),
1163 (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending),
1167 class GroupQueryData:
1168 """Data container for node group data queries.
1171 def __init__(self, groups, group_to_nodes, group_to_instances):
1172 """Initializes this class.
1174 @param groups: List of node group objects
1175 @type group_to_nodes: dict; group UUID as key
1176 @param group_to_nodes: Per-group list of nodes
1177 @type group_to_instances: dict; group UUID as key
1178 @param group_to_instances: Per-group list of (primary) instances
1181 self.groups = groups
1182 self.group_to_nodes = group_to_nodes
1183 self.group_to_instances = group_to_instances
1186 """Iterate over all node groups.
1189 return iter(self.groups)
1192 _GROUP_SIMPLE_FIELDS = {
1193 "alloc_policy": ("AllocPolicy", QFT_TEXT),
1194 "name": ("Group", QFT_TEXT),
1195 "serial_no": ("SerialNo", QFT_NUMBER),
1196 "uuid": ("UUID", QFT_TEXT),
1197 "ndparams": ("NDParams", QFT_OTHER),
1201 def _BuildGroupFields():
1202 """Builds list of fields for node group queries.
1206 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1207 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1209 def _GetLength(getter):
1210 return lambda ctx, group: len(getter(ctx)[group.uuid])
1212 def _GetSortedList(getter):
1213 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
1215 group_to_nodes = operator.attrgetter("group_to_nodes")
1216 group_to_instances = operator.attrgetter("group_to_instances")
1218 # Add fields for nodes
1220 (_MakeField("node_cnt", "Nodes", QFT_NUMBER),
1221 GQ_NODE, _GetLength(group_to_nodes)),
1222 (_MakeField("node_list", "NodeList", QFT_OTHER),
1223 GQ_NODE, _GetSortedList(group_to_nodes)),
1226 # Add fields for instances
1228 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER),
1229 GQ_INST, _GetLength(group_to_instances)),
1230 (_MakeField("pinst_list", "InstanceList", QFT_OTHER),
1231 GQ_INST, _GetSortedList(group_to_instances)),
1234 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1236 return _PrepareFieldList(fields)
1239 #: Fields available for node queries
1240 NODE_FIELDS = _BuildNodeFields()
1242 #: Fields available for instance queries
1243 INSTANCE_FIELDS = _BuildInstanceFields()
1245 #: Fields available for lock queries
1246 LOCK_FIELDS = _BuildLockFields()
1248 #: Fields available for node group queries
1249 GROUP_FIELDS = _BuildGroupFields()
1251 #: All available field lists
1252 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]