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,
108 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
109 """Gets the contents of an unknown field.
112 return (QRFS_UNKNOWN, None)
115 def _GetQueryFields(fielddefs, selected):
116 """Calculates the internal list of selected fields.
118 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
120 @type fielddefs: dict
121 @param fielddefs: Field definitions
122 @type selected: list of strings
123 @param selected: List of selected fields
128 for name in selected:
130 fdef = fielddefs[name]
132 fdef = (_MakeField(name, name, QFT_UNKNOWN), None, _GetUnknownField)
134 assert len(fdef) == 3
141 def GetAllFields(fielddefs):
142 """Extract L{objects.QueryFieldDefinition} from field definitions.
144 @rtype: list of L{objects.QueryFieldDefinition}
147 return [fdef for (fdef, _, _) in fielddefs]
151 def __init__(self, fieldlist, selected):
152 """Initializes this class.
154 The field definition is a dictionary with the field's name as a key and a
155 tuple containing, in order, the field definition object
156 (L{objects.QueryFieldDefinition}, the data kind to help calling code
157 collect data and a retrieval function. The retrieval function is called
158 with two parameters, in order, the data container and the item in container
159 (see L{Query.Query}).
161 Users of this class can call L{RequestedData} before preparing the data
162 container to determine what data is needed.
164 @type fieldlist: dictionary
165 @param fieldlist: Field definitions
166 @type selected: list of strings
167 @param selected: List of selected fields
170 self._fields = _GetQueryFields(fieldlist, selected)
172 def RequestedData(self):
173 """Gets requested kinds of data.
178 return frozenset(datakind
179 for (_, datakind, _) in self._fields
180 if datakind is not None)
183 """Returns the list of fields for this query.
185 Includes unknown fields.
187 @rtype: List of L{objects.QueryFieldDefinition}
190 return GetAllFields(self._fields)
192 def Query(self, ctx):
195 @param ctx: Data container passed to field retrieval functions, must
196 support iteration using C{__iter__}
199 result = [[fn(ctx, item) for (_, _, fn) in self._fields]
204 for (idx, row) in enumerate(result):
205 assert _VerifyResultRow(self._fields, row), \
206 ("Inconsistent result for fields %s in row %s: %r" %
207 (GetAllFields(self._fields), idx, row))
211 def OldStyleQuery(self, ctx):
212 """Query with "old" query result format.
214 See L{Query.Query} for arguments.
217 unknown = set(fdef.name
218 for (fdef, _, _) in self._fields if fdef.kind == QFT_UNKNOWN)
220 raise errors.OpPrereqError("Unknown output fields selected: %s" %
221 (utils.CommaJoin(unknown), ),
224 return [[value for (_, value) in row]
225 for row in self.Query(ctx)]
228 def _VerifyResultRow(fields, row):
229 """Verifies the contents of a query result row.
232 @param fields: Field definitions for result
233 @type row: list of tuples
237 return (len(row) == len(fields) and
238 compat.all((status == QRFS_NORMAL and _VERIFY_FN[fdef.kind](value)) or
239 # Value for an abnormal status must be None
240 (status != QRFS_NORMAL and value is None)
241 for ((status, value), (fdef, _, _)) in zip(row, fields)))
244 def _PrepareFieldList(fields):
245 """Prepares field list for use by L{Query}.
247 Converts the list to a dictionary and does some verification.
249 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
251 @param fields: List of fields, see L{Query.__init__} for a better description
253 @return: Field dictionary for L{Query}
257 duplicates = utils.FindDuplicates(fdef.title.lower()
258 for (fdef, _, _) in fields)
259 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
264 (fdef, _, fn) = field
266 assert fdef.name and fdef.title, "Name and title are required"
267 assert FIELD_NAME_RE.match(fdef.name)
268 assert TITLE_RE.match(fdef.title)
270 assert fdef.name not in result, \
271 "Duplicate field name '%s' found" % fdef.name
273 result[fdef.name] = field
275 assert len(result) == len(fields)
276 assert compat.all(name == fdef.name
277 for (name, (fdef, _, _)) in result.items())
282 def GetQueryResponse(query, ctx):
283 """Prepares the response for a query.
285 @type query: L{Query}
286 @param ctx: Data container, see L{Query.Query}
289 return objects.QueryResponse(data=query.Query(ctx),
290 fields=query.GetFields()).ToDict()
293 def QueryFields(fielddefs, selected):
294 """Returns list of available fields.
296 @type fielddefs: dict
297 @param fielddefs: Field definitions
298 @type selected: list of strings
299 @param selected: List of selected fields
300 @return: List of L{objects.QueryFieldDefinition}
304 # Client requests all fields, sort by name
305 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
306 key=operator.attrgetter("name"))
308 # Keep order as requested by client
309 fdefs = Query(fielddefs, selected).GetFields()
311 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
314 def _MakeField(name, title, kind):
315 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
317 @param name: Field name as a regular expression
318 @param title: Human-readable title
319 @param kind: Field type
322 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
325 def _GetNodeRole(node, master_name):
326 """Determine node role.
328 @type node: L{objects.Node}
329 @param node: Node object
330 @type master_name: string
331 @param master_name: Master node name
334 if node.name == master_name:
336 elif node.master_candidate:
346 def _GetItemAttr(attr):
347 """Returns a field function to return an attribute of the item.
349 @param attr: Attribute name
352 getter = operator.attrgetter(attr)
353 return lambda _, item: (QRFS_NORMAL, getter(item))
356 def _GetItemTimestamp(getter):
357 """Returns function for getting timestamp of item.
359 @type getter: callable
360 @param getter: Function to retrieve timestamp attribute
364 """Returns a timestamp of item.
367 timestamp = getter(item)
368 if timestamp is None:
369 # Old configs might not have all timestamps
370 return (QRFS_UNAVAIL, None)
372 return (QRFS_NORMAL, timestamp)
377 def _GetItemTimestampFields(datatype):
378 """Returns common timestamp fields.
380 @param datatype: Field data type for use by L{Query.RequestedData}
384 (_MakeField("ctime", "CTime", QFT_TIMESTAMP), datatype,
385 _GetItemTimestamp(operator.attrgetter("ctime"))),
386 (_MakeField("mtime", "MTime", QFT_TIMESTAMP), datatype,
387 _GetItemTimestamp(operator.attrgetter("mtime"))),
392 """Data container for node data queries.
395 def __init__(self, nodes, live_data, master_name, node_to_primary,
396 node_to_secondary, groups, oob_support, cluster):
397 """Initializes this class.
401 self.live_data = live_data
402 self.master_name = master_name
403 self.node_to_primary = node_to_primary
404 self.node_to_secondary = node_to_secondary
406 self.oob_support = oob_support
407 self.cluster = cluster
409 # Used for individual rows
410 self.curlive_data = None
413 """Iterate over all nodes.
415 This function has side-effects and only one instance of the resulting
416 generator should be used at a time.
419 for node in self.nodes:
421 self.curlive_data = self.live_data.get(node.name, None)
423 self.curlive_data = None
427 #: Fields that are direct attributes of an L{objects.Node} object
428 _NODE_SIMPLE_FIELDS = {
429 "drained": ("Drained", QFT_BOOL),
430 "master_candidate": ("MasterC", QFT_BOOL),
431 "master_capable": ("MasterCapable", QFT_BOOL),
432 "name": ("Node", QFT_TEXT),
433 "offline": ("Offline", QFT_BOOL),
434 "serial_no": ("SerialNo", QFT_NUMBER),
435 "uuid": ("UUID", QFT_TEXT),
436 "vm_capable": ("VMCapable", QFT_BOOL),
440 #: Fields requiring talking to the node
441 _NODE_LIVE_FIELDS = {
442 "bootid": ("BootID", QFT_TEXT, "bootid"),
443 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes"),
444 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets"),
445 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total"),
446 "dfree": ("DFree", QFT_UNIT, "vg_free"),
447 "dtotal": ("DTotal", QFT_UNIT, "vg_size"),
448 "mfree": ("MFree", QFT_UNIT, "memory_free"),
449 "mnode": ("MNode", QFT_UNIT, "memory_dom0"),
450 "mtotal": ("MTotal", QFT_UNIT, "memory_total"),
455 """Build function for calling another function with an node group.
457 @param cb: The callback to be called with the nodegroup
461 """Get group data for a node.
463 @type ctx: L{NodeQueryData}
464 @type inst: L{objects.Node}
465 @param inst: Node object
468 ng = ctx.groups.get(node.group, None)
470 # Nodes always have a group, or the configuration is corrupt
471 return (QRFS_UNAVAIL, None)
473 return cb(ctx, node, ng)
478 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
479 """Returns the name of a node's group.
481 @type ctx: L{NodeQueryData}
482 @type node: L{objects.Node}
483 @param node: Node object
484 @type ng: L{objects.NodeGroup}
485 @param ng: The node group this node belongs to
488 return (QRFS_NORMAL, ng.name)
491 def _GetNodePower(ctx, node):
492 """Returns the node powered state
494 @type ctx: L{NodeQueryData}
495 @type node: L{objects.Node}
496 @param node: Node object
499 if ctx.oob_support[node.name]:
500 return (QRFS_NORMAL, node.powered)
502 return (QRFS_UNAVAIL, None)
505 def _GetNdParams(ctx, node, ng):
506 """Returns the ndparams for this node.
508 @type ctx: L{NodeQueryData}
509 @type node: L{objects.Node}
510 @param node: Node object
511 @type ng: L{objects.NodeGroup}
512 @param ng: The node group this node belongs to
515 return (QRFS_NORMAL, ctx.cluster.SimpleFillND(ng.FillND(node)))
518 def _GetLiveNodeField(field, kind, ctx, node):
519 """Gets the value of a "live" field from L{NodeQueryData}.
521 @param field: Live field name
522 @param kind: Data kind, one of L{constants.QFT_ALL}
523 @type ctx: L{NodeQueryData}
524 @type node: L{objects.Node}
525 @param node: Node object
529 return (QRFS_OFFLINE, None)
531 if not ctx.curlive_data:
532 return (QRFS_NODATA, None)
535 value = ctx.curlive_data[field]
537 return (QRFS_UNAVAIL, None)
540 return (QRFS_NORMAL, value)
542 assert kind in (QFT_NUMBER, QFT_UNIT)
544 # Try to convert into number
546 return (QRFS_NORMAL, int(value))
547 except (ValueError, TypeError):
548 logging.exception("Failed to convert node field '%s' (value %r) to int",
550 return (QRFS_UNAVAIL, None)
553 def _BuildNodeFields():
554 """Builds list of fields for node queries.
558 (_MakeField("pip", "PrimaryIP", QFT_TEXT), NQ_CONFIG,
559 lambda ctx, node: (QRFS_NORMAL, node.primary_ip)),
560 (_MakeField("sip", "SecondaryIP", QFT_TEXT), NQ_CONFIG,
561 lambda ctx, node: (QRFS_NORMAL, node.secondary_ip)),
562 (_MakeField("tags", "Tags", QFT_OTHER), NQ_CONFIG,
563 lambda ctx, node: (QRFS_NORMAL, list(node.GetTags()))),
564 (_MakeField("master", "IsMaster", QFT_BOOL), NQ_CONFIG,
565 lambda ctx, node: (QRFS_NORMAL, node.name == ctx.master_name)),
566 (_MakeField("role", "Role", QFT_TEXT), NQ_CONFIG,
567 lambda ctx, node: (QRFS_NORMAL, _GetNodeRole(node, ctx.master_name))),
568 (_MakeField("group", "Group", QFT_TEXT), NQ_GROUP,
569 _GetGroup(_GetNodeGroup)),
570 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT),
571 NQ_CONFIG, lambda ctx, node: (QRFS_NORMAL, node.group)),
572 (_MakeField("powered", "Powered", QFT_BOOL), NQ_OOB, _GetNodePower),
573 (_MakeField("ndparams", "NodeParameters", QFT_OTHER), NQ_GROUP,
574 _GetGroup(_GetNdParams)),
575 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER),
576 NQ_GROUP, lambda ctx, node: (QRFS_NORMAL, node.ndparams)),
579 def _GetLength(getter):
580 return lambda ctx, node: (QRFS_NORMAL, len(getter(ctx)[node.name]))
582 def _GetList(getter):
583 return lambda ctx, node: (QRFS_NORMAL, list(getter(ctx)[node.name]))
585 # Add fields operating on instance lists
586 for prefix, titleprefix, getter in \
587 [("p", "Pri", operator.attrgetter("node_to_primary")),
588 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
590 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER),
591 NQ_INST, _GetLength(getter)),
592 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
594 NQ_INST, _GetList(getter)),
598 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
599 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
601 # Add fields requiring live data
603 (_MakeField(name, title, kind), NQ_LIVE,
604 compat.partial(_GetLiveNodeField, nfield, kind))
605 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
609 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
611 return _PrepareFieldList(fields)
614 class InstanceQueryData:
615 """Data container for instance data queries.
618 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
620 """Initializes this class.
622 @param instances: List of instance objects
623 @param cluster: Cluster object
624 @type disk_usage: dict; instance name as key
625 @param disk_usage: Per-instance disk usage
626 @type offline_nodes: list of strings
627 @param offline_nodes: List of offline nodes
628 @type bad_nodes: list of strings
629 @param bad_nodes: List of faulty nodes
630 @type live_data: dict; instance name as key
631 @param live_data: Per-instance live data
634 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
635 "Offline nodes not included in bad nodes"
636 assert not (set(live_data.keys()) & set(bad_nodes)), \
637 "Found live data for bad or offline nodes"
639 self.instances = instances
640 self.cluster = cluster
641 self.disk_usage = disk_usage
642 self.offline_nodes = offline_nodes
643 self.bad_nodes = bad_nodes
644 self.live_data = live_data
646 # Used for individual rows
647 self.inst_hvparams = None
648 self.inst_beparams = None
649 self.inst_nicparams = None
652 """Iterate over all instances.
654 This function has side-effects and only one instance of the resulting
655 generator should be used at a time.
658 for inst in self.instances:
659 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
660 self.inst_beparams = self.cluster.FillBE(inst)
661 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
662 for nic in inst.nics]
667 def _GetInstOperState(ctx, inst):
668 """Get instance's operational status.
670 @type ctx: L{InstanceQueryData}
671 @type inst: L{objects.Instance}
672 @param inst: Instance object
675 # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
676 # when we actually don't know due to missing data
677 if inst.primary_node in ctx.bad_nodes:
678 return (QRFS_NODATA, None)
680 return (QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
683 def _GetInstLiveData(name):
684 """Build function for retrieving live data.
687 @param name: Live data field name
691 """Get live data for an instance.
693 @type ctx: L{InstanceQueryData}
694 @type inst: L{objects.Instance}
695 @param inst: Instance object
698 if (inst.primary_node in ctx.bad_nodes or
699 inst.primary_node in ctx.offline_nodes):
700 # Can't use QRFS_OFFLINE here as it would describe the instance to be
701 # offline when we actually don't know due to missing data
702 return (QRFS_NODATA, None)
704 if inst.name in ctx.live_data:
705 data = ctx.live_data[inst.name]
707 return (QRFS_NORMAL, data[name])
709 return (QRFS_UNAVAIL, None)
714 def _GetInstStatus(ctx, inst):
715 """Get instance status.
717 @type ctx: L{InstanceQueryData}
718 @type inst: L{objects.Instance}
719 @param inst: Instance object
722 if inst.primary_node in ctx.offline_nodes:
723 return (QRFS_NORMAL, "ERROR_nodeoffline")
725 if inst.primary_node in ctx.bad_nodes:
726 return (QRFS_NORMAL, "ERROR_nodedown")
728 if bool(ctx.live_data.get(inst.name)):
730 return (QRFS_NORMAL, "running")
732 return (QRFS_NORMAL, "ERROR_up")
735 return (QRFS_NORMAL, "ERROR_down")
737 return (QRFS_NORMAL, "ADMIN_down")
740 def _GetInstDiskSize(index):
741 """Build function for retrieving disk size.
744 @param index: Disk index
748 """Get size of a disk.
750 @type inst: L{objects.Instance}
751 @param inst: Instance object
755 return (QRFS_NORMAL, inst.disks[index].size)
757 return (QRFS_UNAVAIL, None)
762 def _GetInstNic(index, cb):
763 """Build function for calling another function with an instance NIC.
766 @param index: NIC index
772 """Call helper function with instance NIC.
774 @type ctx: L{InstanceQueryData}
775 @type inst: L{objects.Instance}
776 @param inst: Instance object
780 nic = inst.nics[index]
782 return (QRFS_UNAVAIL, None)
784 return cb(ctx, index, nic)
789 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
790 """Get a NIC's IP address.
792 @type ctx: L{InstanceQueryData}
793 @type nic: L{objects.NIC}
794 @param nic: NIC object
798 return (QRFS_UNAVAIL, None)
800 return (QRFS_NORMAL, nic.ip)
803 def _GetInstNicBridge(ctx, index, _):
804 """Get a NIC's bridge.
806 @type ctx: L{InstanceQueryData}
808 @param index: NIC index
811 assert len(ctx.inst_nicparams) >= index
813 nicparams = ctx.inst_nicparams[index]
815 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
816 return (QRFS_NORMAL, nicparams[constants.NIC_LINK])
818 return (QRFS_UNAVAIL, None)
821 def _GetInstAllNicBridges(ctx, inst):
822 """Get all network bridges for an instance.
824 @type ctx: L{InstanceQueryData}
825 @type inst: L{objects.Instance}
826 @param inst: Instance object
829 assert len(ctx.inst_nicparams) == len(inst.nics)
833 for nicp in ctx.inst_nicparams:
834 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
835 result.append(nicp[constants.NIC_LINK])
839 assert len(result) == len(inst.nics)
841 return (QRFS_NORMAL, result)
844 def _GetInstNicParam(name):
845 """Build function for retrieving a NIC parameter.
848 @param name: Parameter name
851 def fn(ctx, index, _):
852 """Get a NIC's bridge.
854 @type ctx: L{InstanceQueryData}
855 @type inst: L{objects.Instance}
856 @param inst: Instance object
857 @type nic: L{objects.NIC}
858 @param nic: NIC object
861 assert len(ctx.inst_nicparams) >= index
862 return (QRFS_NORMAL, ctx.inst_nicparams[index][name])
867 def _GetInstanceNetworkFields():
868 """Get instance fields involving network interfaces.
870 @return: List of field definitions used as input for L{_PrepareFieldList}
873 nic_mac_fn = lambda ctx, _, nic: (QRFS_NORMAL, nic.mac)
874 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
875 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
879 (_MakeField("ip", "IP_address", QFT_TEXT), IQ_CONFIG,
880 _GetInstNic(0, _GetInstNicIp)),
881 (_MakeField("mac", "MAC_address", QFT_TEXT), IQ_CONFIG,
882 _GetInstNic(0, nic_mac_fn)),
883 (_MakeField("bridge", "Bridge", QFT_TEXT), IQ_CONFIG,
884 _GetInstNic(0, _GetInstNicBridge)),
885 (_MakeField("nic_mode", "NIC_Mode", QFT_TEXT), IQ_CONFIG,
886 _GetInstNic(0, nic_mode_fn)),
887 (_MakeField("nic_link", "NIC_Link", QFT_TEXT), IQ_CONFIG,
888 _GetInstNic(0, nic_link_fn)),
891 (_MakeField("nic.count", "NICs", QFT_NUMBER), IQ_CONFIG,
892 lambda ctx, inst: (QRFS_NORMAL, len(inst.nics))),
893 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER), IQ_CONFIG,
894 lambda ctx, inst: (QRFS_NORMAL, [nic.mac for nic in inst.nics])),
895 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER), IQ_CONFIG,
896 lambda ctx, inst: (QRFS_NORMAL, [nic.ip for nic in inst.nics])),
897 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER), IQ_CONFIG,
898 lambda ctx, inst: (QRFS_NORMAL, [nicp[constants.NIC_MODE]
899 for nicp in ctx.inst_nicparams])),
900 (_MakeField("nic.links", "NIC_links", QFT_OTHER), IQ_CONFIG,
901 lambda ctx, inst: (QRFS_NORMAL, [nicp[constants.NIC_LINK]
902 for nicp in ctx.inst_nicparams])),
903 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER), IQ_CONFIG,
904 _GetInstAllNicBridges),
908 for i in range(constants.MAX_NICS):
910 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT),
911 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
912 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT),
913 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
914 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT),
915 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
916 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT),
917 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
918 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT),
919 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
925 def _GetInstDiskUsage(ctx, inst):
926 """Get disk usage for an instance.
928 @type ctx: L{InstanceQueryData}
929 @type inst: L{objects.Instance}
930 @param inst: Instance object
933 usage = ctx.disk_usage[inst.name]
938 return (QRFS_NORMAL, usage)
941 def _GetInstanceDiskFields():
942 """Get instance fields involving disks.
944 @return: List of field definitions used as input for L{_PrepareFieldList}
948 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT), IQ_DISKUSAGE,
950 (_MakeField("sda_size", "LegacyDisk/0", QFT_UNIT), IQ_CONFIG,
951 _GetInstDiskSize(0)),
952 (_MakeField("sdb_size", "LegacyDisk/1", QFT_UNIT), IQ_CONFIG,
953 _GetInstDiskSize(1)),
954 (_MakeField("disk.count", "Disks", QFT_NUMBER), IQ_CONFIG,
955 lambda ctx, inst: (QRFS_NORMAL, len(inst.disks))),
956 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER), IQ_CONFIG,
957 lambda ctx, inst: (QRFS_NORMAL, [disk.size for disk in inst.disks])),
962 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT),
963 IQ_CONFIG, _GetInstDiskSize(i))
964 for i in range(constants.MAX_DISKS)
970 def _GetInstanceParameterFields():
971 """Get instance fields involving parameters.
973 @return: List of field definitions used as input for L{_PrepareFieldList}
976 # TODO: Consider moving titles closer to constants
978 constants.BE_AUTO_BALANCE: "Auto_balance",
979 constants.BE_MEMORY: "Configured_memory",
980 constants.BE_VCPUS: "VCPUs",
984 constants.HV_ACPI: "ACPI",
985 constants.HV_BOOT_ORDER: "Boot_order",
986 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
987 constants.HV_DISK_TYPE: "Disk_type",
988 constants.HV_INITRD_PATH: "Initrd_path",
989 constants.HV_KERNEL_PATH: "Kernel_path",
990 constants.HV_NIC_TYPE: "NIC_type",
991 constants.HV_PAE: "PAE",
992 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
997 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER),
998 IQ_CONFIG, lambda ctx, _: (QRFS_NORMAL, ctx.inst_hvparams)),
999 (_MakeField("beparams", "BackendParameters", QFT_OTHER),
1000 IQ_CONFIG, lambda ctx, _: (QRFS_NORMAL, ctx.inst_beparams)),
1001 (_MakeField("vcpus", "LegacyVCPUs", QFT_NUMBER), IQ_CONFIG,
1002 lambda ctx, _: (QRFS_NORMAL, ctx.inst_beparams[constants.BE_VCPUS])),
1004 # Unfilled parameters
1005 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER),
1006 IQ_CONFIG, lambda ctx, inst: (QRFS_NORMAL, inst.hvparams)),
1007 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER),
1008 IQ_CONFIG, lambda ctx, inst: (QRFS_NORMAL, inst.beparams)),
1009 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER),
1010 IQ_CONFIG, lambda ctx, inst: (QRFS_NORMAL,
1011 [nic.nicparams for nic in inst.nics])),
1015 def _GetInstHvParam(name):
1016 return lambda ctx, _: (QRFS_NORMAL, ctx.inst_hvparams.get(name, None))
1019 # For now all hypervisor parameters are exported as QFT_OTHER
1020 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name), QFT_OTHER),
1021 IQ_CONFIG, _GetInstHvParam(name))
1022 for name in constants.HVS_PARAMETERS
1023 if name not in constants.HVC_GLOBALS
1027 def _GetInstBeParam(name):
1028 return lambda ctx, _: (QRFS_NORMAL, ctx.inst_beparams.get(name, None))
1031 # For now all backend parameters are exported as QFT_OTHER
1032 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name), QFT_OTHER),
1033 IQ_CONFIG, _GetInstBeParam(name))
1034 for name in constants.BES_PARAMETERS
1040 _INST_SIMPLE_FIELDS = {
1041 "disk_template": ("Disk_template", QFT_TEXT),
1042 "hypervisor": ("Hypervisor", QFT_TEXT),
1043 "name": ("Node", QFT_TEXT),
1044 # Depending on the hypervisor, the port can be None
1045 "network_port": ("Network_port", QFT_OTHER),
1046 "os": ("OS", QFT_TEXT),
1047 "serial_no": ("SerialNo", QFT_NUMBER),
1048 "uuid": ("UUID", QFT_TEXT),
1052 def _BuildInstanceFields():
1053 """Builds list of fields for instance queries.
1057 (_MakeField("pnode", "Primary_node", QFT_TEXT), IQ_CONFIG,
1058 lambda ctx, inst: (QRFS_NORMAL, inst.primary_node)),
1059 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER), IQ_CONFIG,
1060 lambda ctx, inst: (QRFS_NORMAL, list(inst.secondary_nodes))),
1061 (_MakeField("admin_state", "Autostart", QFT_BOOL), IQ_CONFIG,
1062 lambda ctx, inst: (QRFS_NORMAL, inst.admin_up)),
1063 (_MakeField("tags", "Tags", QFT_OTHER), IQ_CONFIG,
1064 lambda ctx, inst: (QRFS_NORMAL, list(inst.GetTags()))),
1068 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1069 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1071 # Fields requiring talking to the node
1073 (_MakeField("oper_state", "Running", QFT_BOOL), IQ_LIVE,
1075 (_MakeField("oper_ram", "RuntimeMemory", QFT_UNIT), IQ_LIVE,
1076 _GetInstLiveData("memory")),
1077 (_MakeField("oper_vcpus", "RuntimeVCPUs", QFT_NUMBER), IQ_LIVE,
1078 _GetInstLiveData("vcpus")),
1079 (_MakeField("status", "Status", QFT_TEXT), IQ_LIVE, _GetInstStatus),
1082 fields.extend(_GetInstanceParameterFields())
1083 fields.extend(_GetInstanceDiskFields())
1084 fields.extend(_GetInstanceNetworkFields())
1085 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1087 return _PrepareFieldList(fields)
1090 class LockQueryData:
1091 """Data container for lock data queries.
1094 def __init__(self, lockdata):
1095 """Initializes this class.
1098 self.lockdata = lockdata
1101 """Iterate over all locks.
1104 return iter(self.lockdata)
1107 def _GetLockOwners(_, data):
1108 """Returns a sorted list of a lock's current owners.
1111 (_, _, owners, _) = data
1114 owners = utils.NiceSort(owners)
1116 return (QRFS_NORMAL, owners)
1119 def _GetLockPending(_, data):
1120 """Returns a sorted list of a lock's pending acquires.
1123 (_, _, _, pending) = data
1126 pending = [(mode, utils.NiceSort(names))
1127 for (mode, names) in pending]
1129 return (QRFS_NORMAL, pending)
1132 def _BuildLockFields():
1133 """Builds list of fields for lock queries.
1136 return _PrepareFieldList([
1137 (_MakeField("name", "Name", QFT_TEXT), None,
1138 lambda ctx, (name, mode, owners, pending): (QRFS_NORMAL, name)),
1139 (_MakeField("mode", "Mode", QFT_OTHER), LQ_MODE,
1140 lambda ctx, (name, mode, owners, pending): (QRFS_NORMAL, mode)),
1141 (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners),
1142 (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending),
1146 class GroupQueryData:
1147 """Data container for node group data queries.
1150 def __init__(self, groups, group_to_nodes, group_to_instances):
1151 """Initializes this class.
1153 @param groups: List of node group objects
1154 @type group_to_nodes: dict; group UUID as key
1155 @param group_to_nodes: Per-group list of nodes
1156 @type group_to_instances: dict; group UUID as key
1157 @param group_to_instances: Per-group list of (primary) instances
1160 self.groups = groups
1161 self.group_to_nodes = group_to_nodes
1162 self.group_to_instances = group_to_instances
1165 """Iterate over all node groups.
1168 return iter(self.groups)
1171 _GROUP_SIMPLE_FIELDS = {
1172 "alloc_policy": ("AllocPolicy", QFT_TEXT),
1173 "name": ("Group", QFT_TEXT),
1174 "serial_no": ("SerialNo", QFT_NUMBER),
1175 "uuid": ("UUID", QFT_TEXT),
1176 "ndparams": ("NDParams", QFT_OTHER),
1180 def _BuildGroupFields():
1181 """Builds list of fields for node group queries.
1185 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1186 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1188 def _GetLength(getter):
1189 return lambda ctx, group: (QRFS_NORMAL, len(getter(ctx)[group.uuid]))
1191 def _GetSortedList(getter):
1192 return lambda ctx, group: (QRFS_NORMAL,
1193 utils.NiceSort(getter(ctx)[group.uuid]))
1195 group_to_nodes = operator.attrgetter("group_to_nodes")
1196 group_to_instances = operator.attrgetter("group_to_instances")
1198 # Add fields for nodes
1200 (_MakeField("node_cnt", "Nodes", QFT_NUMBER),
1201 GQ_NODE, _GetLength(group_to_nodes)),
1202 (_MakeField("node_list", "NodeList", QFT_OTHER),
1203 GQ_NODE, _GetSortedList(group_to_nodes)),
1206 # Add fields for instances
1208 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER),
1209 GQ_INST, _GetLength(group_to_instances)),
1210 (_MakeField("pinst_list", "InstanceList", QFT_OTHER),
1211 GQ_INST, _GetSortedList(group_to_instances)),
1214 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1216 return _PrepareFieldList(fields)
1219 #: Fields available for node queries
1220 NODE_FIELDS = _BuildNodeFields()
1222 #: Fields available for instance queries
1223 INSTANCE_FIELDS = _BuildInstanceFields()
1225 #: Fields available for lock queries
1226 LOCK_FIELDS = _BuildLockFields()
1228 #: Fields available for node group queries
1229 GROUP_FIELDS = _BuildGroupFields()
1231 #: All available field lists
1232 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]