4 # Copyright (C) 2010 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"""
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
44 IQ_DISKUSAGE) = range(100, 103)
48 LQ_PENDING) = range(10, 13)
52 GQ_INST) = range(200, 203)
55 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
56 TITLE_RE = re.compile(r"^[^\s]+$")
58 #: Verification function for each field type
60 constants.QFT_UNKNOWN: ht.TNone,
61 constants.QFT_TEXT: ht.TString,
62 constants.QFT_BOOL: ht.TBool,
63 constants.QFT_NUMBER: ht.TInt,
64 constants.QFT_UNIT: ht.TInt,
65 constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
66 constants.QFT_OTHER: lambda _: True,
70 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
71 """Gets the contents of an unknown field.
74 return (constants.QRFS_UNKNOWN, None)
77 def _GetQueryFields(fielddefs, selected):
78 """Calculates the internal list of selected fields.
80 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
83 @param fielddefs: Field definitions
84 @type selected: list of strings
85 @param selected: List of selected fields
92 fdef = fielddefs[name]
94 fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
95 None, _GetUnknownField)
104 def GetAllFields(fielddefs):
105 """Extract L{objects.QueryFieldDefinition} from field definitions.
107 @rtype: list of L{objects.QueryFieldDefinition}
110 return [fdef for (fdef, _, _) in fielddefs]
114 def __init__(self, fieldlist, selected):
115 """Initializes this class.
117 The field definition is a dictionary with the field's name as a key and a
118 tuple containing, in order, the field definition object
119 (L{objects.QueryFieldDefinition}, the data kind to help calling code
120 collect data and a retrieval function. The retrieval function is called
121 with two parameters, in order, the data container and the item in container
122 (see L{Query.Query}).
124 Users of this class can call L{RequestedData} before preparing the data
125 container to determine what data is needed.
127 @type fieldlist: dictionary
128 @param fieldlist: Field definitions
129 @type selected: list of strings
130 @param selected: List of selected fields
133 self._fields = _GetQueryFields(fieldlist, selected)
135 def RequestedData(self):
136 """Gets requested kinds of data.
141 return frozenset(datakind
142 for (_, datakind, _) in self._fields
143 if datakind is not None)
146 """Returns the list of fields for this query.
148 Includes unknown fields.
150 @rtype: List of L{objects.QueryFieldDefinition}
153 return GetAllFields(self._fields)
155 def Query(self, ctx):
158 @param ctx: Data container passed to field retrieval functions, must
159 support iteration using C{__iter__}
162 result = [[fn(ctx, item) for (_, _, fn) in self._fields]
167 for (idx, row) in enumerate(result):
168 assert _VerifyResultRow(self._fields, row), \
169 ("Inconsistent result for fields %s in row %s: %r" %
170 (GetAllFields(self._fields), idx, row))
174 def OldStyleQuery(self, ctx):
175 """Query with "old" query result format.
177 See L{Query.Query} for arguments.
180 unknown = set(fdef.name
181 for (fdef, _, _) in self._fields
182 if fdef.kind == constants.QFT_UNKNOWN)
184 raise errors.OpPrereqError("Unknown output fields selected: %s" %
185 (utils.CommaJoin(unknown), ),
188 return [[value for (_, value) in row]
189 for row in self.Query(ctx)]
192 def _VerifyResultRow(fields, row):
193 """Verifies the contents of a query result row.
196 @param fields: Field definitions for result
197 @type row: list of tuples
201 return (len(row) == len(fields) and
202 compat.all((status == constants.QRFS_NORMAL and
203 _VERIFY_FN[fdef.kind](value)) or
204 # Value for an abnormal status must be None
205 (status != constants.QRFS_NORMAL and value is None)
206 for ((status, value), (fdef, _, _)) in zip(row, fields)))
209 def _PrepareFieldList(fields):
210 """Prepares field list for use by L{Query}.
212 Converts the list to a dictionary and does some verification.
214 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
216 @param fields: List of fields
218 @return: Field dictionary for L{Query}
222 duplicates = utils.FindDuplicates(fdef.title.lower()
223 for (fdef, _, _) in fields)
224 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
229 (fdef, _, fn) = field
231 assert fdef.name and fdef.title, "Name and title are required"
232 assert FIELD_NAME_RE.match(fdef.name)
233 assert TITLE_RE.match(fdef.title)
235 assert fdef.name not in result, \
236 "Duplicate field name '%s' found" % fdef.name
238 result[fdef.name] = field
240 assert len(result) == len(fields)
241 assert compat.all(name == fdef.name
242 for (name, (fdef, _, _)) in result.items())
247 def GetQueryResponse(query, ctx):
248 """Prepares the response for a query.
250 @type query: L{Query}
251 @param ctx: Data container, see L{Query.Query}
254 return objects.QueryResponse(data=query.Query(ctx),
255 fields=query.GetFields()).ToDict()
258 def QueryFields(fielddefs, selected):
259 """Returns list of available fields.
261 @type fielddefs: dict
262 @param fielddefs: Field definitions
263 @type selected: list of strings
264 @param selected: List of selected fields
265 @return: List of L{objects.QueryFieldDefinition}
269 # Client requests all fields, sort by name
270 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
271 key=operator.attrgetter("name"))
273 # Keep order as requested by client
274 fdefs = Query(fielddefs, selected).GetFields()
276 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
279 def _MakeField(name, title, kind):
280 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
282 @param name: Field name as a regular expression
283 @param title: Human-readable title
284 @param kind: Field type
287 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
290 def _GetNodeRole(node, master_name):
291 """Determine node role.
293 @type node: L{objects.Node}
294 @param node: Node object
295 @type master_name: string
296 @param master_name: Master node name
299 if node.name == master_name:
301 elif node.master_candidate:
311 def _GetItemAttr(attr):
312 """Returns a field function to return an attribute of the item.
314 @param attr: Attribute name
317 getter = operator.attrgetter(attr)
318 return lambda _, item: (constants.QRFS_NORMAL, getter(item))
321 def _GetItemTimestamp(getter):
322 """Returns function for getting timestamp of item.
324 @type getter: callable
325 @param getter: Function to retrieve timestamp attribute
329 """Returns a timestamp of item.
332 timestamp = getter(item)
333 if timestamp is None:
334 # Old configs might not have all timestamps
335 return (constants.QRFS_UNAVAIL, None)
337 return (constants.QRFS_NORMAL, timestamp)
342 def _GetItemTimestampFields(datatype):
343 """Returns common timestamp fields.
345 @param datatype: Field data type for use by L{Query.RequestedData}
349 (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
350 _GetItemTimestamp(operator.attrgetter("ctime"))),
351 (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
352 _GetItemTimestamp(operator.attrgetter("mtime"))),
357 """Data container for node data queries.
360 def __init__(self, nodes, live_data, master_name, node_to_primary,
361 node_to_secondary, groups, oob_support, cluster):
362 """Initializes this class.
366 self.live_data = live_data
367 self.master_name = master_name
368 self.node_to_primary = node_to_primary
369 self.node_to_secondary = node_to_secondary
371 self.oob_support = oob_support
372 self.cluster = cluster
374 # Used for individual rows
375 self.curlive_data = None
378 """Iterate over all nodes.
380 This function has side-effects and only one instance of the resulting
381 generator should be used at a time.
384 for node in self.nodes:
386 self.curlive_data = self.live_data.get(node.name, None)
388 self.curlive_data = None
392 #: Fields that are direct attributes of an L{objects.Node} object
393 _NODE_SIMPLE_FIELDS = {
394 "drained": ("Drained", constants.QFT_BOOL),
395 "master_candidate": ("MasterC", constants.QFT_BOOL),
396 "master_capable": ("MasterCapable", constants.QFT_BOOL),
397 "name": ("Node", constants.QFT_TEXT),
398 "offline": ("Offline", constants.QFT_BOOL),
399 "serial_no": ("SerialNo", constants.QFT_NUMBER),
400 "uuid": ("UUID", constants.QFT_TEXT),
401 "vm_capable": ("VMCapable", constants.QFT_BOOL),
405 #: Fields requiring talking to the node
406 _NODE_LIVE_FIELDS = {
407 "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
408 "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
409 "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
410 "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
411 "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
412 "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
413 "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
414 "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
415 "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
420 """Build function for calling another function with an node group.
422 @param cb: The callback to be called with the nodegroup
426 """Get group data for a node.
428 @type ctx: L{NodeQueryData}
429 @type inst: L{objects.Node}
430 @param inst: Node object
433 ng = ctx.groups.get(node.group, None)
435 # Nodes always have a group, or the configuration is corrupt
436 return (constants.QRFS_UNAVAIL, None)
438 return cb(ctx, node, ng)
443 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
444 """Returns the name of a node's group.
446 @type ctx: L{NodeQueryData}
447 @type node: L{objects.Node}
448 @param node: Node object
449 @type ng: L{objects.NodeGroup}
450 @param ng: The node group this node belongs to
453 return (constants.QRFS_NORMAL, ng.name)
456 def _GetNodePower(ctx, node):
457 """Returns the node powered state
459 @type ctx: L{NodeQueryData}
460 @type node: L{objects.Node}
461 @param node: Node object
464 if ctx.oob_support[node.name]:
465 return (constants.QRFS_NORMAL, node.powered)
467 return (constants.QRFS_UNAVAIL, None)
470 def _GetNdParams(ctx, node, ng):
471 """Returns the ndparams for this node.
473 @type ctx: L{NodeQueryData}
474 @type node: L{objects.Node}
475 @param node: Node object
476 @type ng: L{objects.NodeGroup}
477 @param ng: The node group this node belongs to
480 return (constants.QRFS_NORMAL, ctx.cluster.SimpleFillND(ng.FillND(node)))
483 def _GetLiveNodeField(field, kind, ctx, node):
484 """Gets the value of a "live" field from L{NodeQueryData}.
486 @param field: Live field name
487 @param kind: Data kind, one of L{constants.QFT_ALL}
488 @type ctx: L{NodeQueryData}
489 @type node: L{objects.Node}
490 @param node: Node object
494 return (constants.QRFS_OFFLINE, None)
496 if not ctx.curlive_data:
497 return (constants.QRFS_NODATA, None)
500 value = ctx.curlive_data[field]
502 return (constants.QRFS_UNAVAIL, None)
504 if kind == constants.QFT_TEXT:
505 return (constants.QRFS_NORMAL, value)
507 assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
509 # Try to convert into number
511 return (constants.QRFS_NORMAL, int(value))
512 except (ValueError, TypeError):
513 logging.exception("Failed to convert node field '%s' (value %r) to int",
515 return (constants.QRFS_UNAVAIL, None)
518 def _BuildNodeFields():
519 """Builds list of fields for node queries.
523 (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
524 lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
525 (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
526 lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
527 (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
528 lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
529 (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
530 lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
531 (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
532 lambda ctx, node: (constants.QRFS_NORMAL,
533 _GetNodeRole(node, ctx.master_name))),
534 (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP,
535 _GetGroup(_GetNodeGroup)),
536 (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
537 NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
538 (_MakeField("powered", "Powered", constants.QFT_BOOL), NQ_OOB,
540 (_MakeField("ndparams", "NodeParameters", constants.QFT_OTHER), NQ_GROUP,
541 _GetGroup(_GetNdParams)),
542 (_MakeField("custom_ndparams", "CustomNodeParameters", constants.QFT_OTHER),
543 NQ_GROUP, lambda ctx, node: (constants.QRFS_NORMAL, node.ndparams)),
546 def _GetLength(getter):
547 return lambda ctx, node: (constants.QRFS_NORMAL,
548 len(getter(ctx)[node.name]))
550 def _GetList(getter):
551 return lambda ctx, node: (constants.QRFS_NORMAL,
552 list(getter(ctx)[node.name]))
554 # Add fields operating on instance lists
555 for prefix, titleprefix, getter in \
556 [("p", "Pri", operator.attrgetter("node_to_primary")),
557 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
559 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
560 constants.QFT_NUMBER),
561 NQ_INST, _GetLength(getter)),
562 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
563 constants.QFT_OTHER),
564 NQ_INST, _GetList(getter)),
568 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
569 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
571 # Add fields requiring live data
573 (_MakeField(name, title, kind), NQ_LIVE,
574 compat.partial(_GetLiveNodeField, nfield, kind))
575 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
579 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
581 return _PrepareFieldList(fields)
584 class InstanceQueryData:
585 """Data container for instance data queries.
588 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
590 """Initializes this class.
592 @param instances: List of instance objects
593 @param cluster: Cluster object
594 @type disk_usage: dict; instance name as key
595 @param disk_usage: Per-instance disk usage
596 @type offline_nodes: list of strings
597 @param offline_nodes: List of offline nodes
598 @type bad_nodes: list of strings
599 @param bad_nodes: List of faulty nodes
600 @type live_data: dict; instance name as key
601 @param live_data: Per-instance live data
604 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
605 "Offline nodes not included in bad nodes"
606 assert not (set(live_data.keys()) & set(bad_nodes)), \
607 "Found live data for bad or offline nodes"
609 self.instances = instances
610 self.cluster = cluster
611 self.disk_usage = disk_usage
612 self.offline_nodes = offline_nodes
613 self.bad_nodes = bad_nodes
614 self.live_data = live_data
616 # Used for individual rows
617 self.inst_hvparams = None
618 self.inst_beparams = None
619 self.inst_nicparams = None
622 """Iterate over all instances.
624 This function has side-effects and only one instance of the resulting
625 generator should be used at a time.
628 for inst in self.instances:
629 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
630 self.inst_beparams = self.cluster.FillBE(inst)
631 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
632 for nic in inst.nics]
637 def _GetInstOperState(ctx, inst):
638 """Get instance's operational status.
640 @type ctx: L{InstanceQueryData}
641 @type inst: L{objects.Instance}
642 @param inst: Instance object
645 # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
646 # when we actually don't know due to missing data
647 if inst.primary_node in ctx.bad_nodes:
648 return (constants.QRFS_NODATA, None)
650 return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
653 def _GetInstLiveData(name):
654 """Build function for retrieving live data.
657 @param name: Live data field name
661 """Get live data for an instance.
663 @type ctx: L{InstanceQueryData}
664 @type inst: L{objects.Instance}
665 @param inst: Instance object
668 if (inst.primary_node in ctx.bad_nodes or
669 inst.primary_node in ctx.offline_nodes):
670 # Can't use QRFS_OFFLINE here as it would describe the instance to be
671 # offline when we actually don't know due to missing data
672 return (constants.QRFS_NODATA, None)
674 if inst.name in ctx.live_data:
675 data = ctx.live_data[inst.name]
677 return (constants.QRFS_NORMAL, data[name])
679 return (constants.QRFS_UNAVAIL, None)
684 def _GetInstStatus(ctx, inst):
685 """Get instance status.
687 @type ctx: L{InstanceQueryData}
688 @type inst: L{objects.Instance}
689 @param inst: Instance object
692 if inst.primary_node in ctx.offline_nodes:
693 return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
695 if inst.primary_node in ctx.bad_nodes:
696 return (constants.QRFS_NORMAL, "ERROR_nodedown")
698 if bool(ctx.live_data.get(inst.name)):
700 return (constants.QRFS_NORMAL, "running")
702 return (constants.QRFS_NORMAL, "ERROR_up")
705 return (constants.QRFS_NORMAL, "ERROR_down")
707 return (constants.QRFS_NORMAL, "ADMIN_down")
710 def _GetInstDiskSize(index):
711 """Build function for retrieving disk size.
714 @param index: Disk index
718 """Get size of a disk.
720 @type inst: L{objects.Instance}
721 @param inst: Instance object
725 return (constants.QRFS_NORMAL, inst.disks[index].size)
727 return (constants.QRFS_UNAVAIL, None)
732 def _GetInstNic(index, cb):
733 """Build function for calling another function with an instance NIC.
736 @param index: NIC index
742 """Call helper function with instance NIC.
744 @type ctx: L{InstanceQueryData}
745 @type inst: L{objects.Instance}
746 @param inst: Instance object
750 nic = inst.nics[index]
752 return (constants.QRFS_UNAVAIL, None)
754 return cb(ctx, index, nic)
759 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
760 """Get a NIC's IP address.
762 @type ctx: L{InstanceQueryData}
763 @type nic: L{objects.NIC}
764 @param nic: NIC object
768 return (constants.QRFS_UNAVAIL, None)
770 return (constants.QRFS_NORMAL, nic.ip)
773 def _GetInstNicBridge(ctx, index, _):
774 """Get a NIC's bridge.
776 @type ctx: L{InstanceQueryData}
778 @param index: NIC index
781 assert len(ctx.inst_nicparams) >= index
783 nicparams = ctx.inst_nicparams[index]
785 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
786 return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
788 return (constants.QRFS_UNAVAIL, None)
791 def _GetInstAllNicBridges(ctx, inst):
792 """Get all network bridges for an instance.
794 @type ctx: L{InstanceQueryData}
795 @type inst: L{objects.Instance}
796 @param inst: Instance object
799 assert len(ctx.inst_nicparams) == len(inst.nics)
803 for nicp in ctx.inst_nicparams:
804 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
805 result.append(nicp[constants.NIC_LINK])
809 assert len(result) == len(inst.nics)
811 return (constants.QRFS_NORMAL, result)
814 def _GetInstNicParam(name):
815 """Build function for retrieving a NIC parameter.
818 @param name: Parameter name
821 def fn(ctx, index, _):
822 """Get a NIC's bridge.
824 @type ctx: L{InstanceQueryData}
825 @type inst: L{objects.Instance}
826 @param inst: Instance object
827 @type nic: L{objects.NIC}
828 @param nic: NIC object
831 assert len(ctx.inst_nicparams) >= index
832 return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
837 def _GetInstanceNetworkFields():
838 """Get instance fields involving network interfaces.
840 @return: List of field definitions used as input for L{_PrepareFieldList}
843 nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
844 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
845 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
849 (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
850 _GetInstNic(0, _GetInstNicIp)),
851 (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
852 _GetInstNic(0, nic_mac_fn)),
853 (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
854 _GetInstNic(0, _GetInstNicBridge)),
855 (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
856 _GetInstNic(0, nic_mode_fn)),
857 (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
858 _GetInstNic(0, nic_link_fn)),
861 (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
862 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
863 (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
864 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
865 (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
866 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
867 (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
868 lambda ctx, inst: (constants.QRFS_NORMAL,
869 [nicp[constants.NIC_MODE]
870 for nicp in ctx.inst_nicparams])),
871 (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
872 lambda ctx, inst: (constants.QRFS_NORMAL,
873 [nicp[constants.NIC_LINK]
874 for nicp in ctx.inst_nicparams])),
875 (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
876 _GetInstAllNicBridges),
880 for i in range(constants.MAX_NICS):
882 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
883 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
884 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
885 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
886 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
887 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
888 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
889 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
890 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
891 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
897 def _GetInstDiskUsage(ctx, inst):
898 """Get disk usage for an instance.
900 @type ctx: L{InstanceQueryData}
901 @type inst: L{objects.Instance}
902 @param inst: Instance object
905 usage = ctx.disk_usage[inst.name]
910 return (constants.QRFS_NORMAL, usage)
913 def _GetInstanceDiskFields():
914 """Get instance fields involving disks.
916 @return: List of field definitions used as input for L{_PrepareFieldList}
920 (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
922 (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
923 _GetInstDiskSize(0)),
924 (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
925 _GetInstDiskSize(1)),
926 (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
927 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
928 (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
929 lambda ctx, inst: (constants.QRFS_NORMAL,
930 [disk.size for disk in inst.disks])),
935 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
936 IQ_CONFIG, _GetInstDiskSize(i))
937 for i in range(constants.MAX_DISKS)
943 def _GetInstanceParameterFields():
944 """Get instance fields involving parameters.
946 @return: List of field definitions used as input for L{_PrepareFieldList}
949 # TODO: Consider moving titles closer to constants
951 constants.BE_AUTO_BALANCE: "Auto_balance",
952 constants.BE_MEMORY: "Configured_memory",
953 constants.BE_VCPUS: "VCPUs",
957 constants.HV_ACPI: "ACPI",
958 constants.HV_BOOT_ORDER: "Boot_order",
959 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
960 constants.HV_DISK_TYPE: "Disk_type",
961 constants.HV_INITRD_PATH: "Initrd_path",
962 constants.HV_KERNEL_PATH: "Kernel_path",
963 constants.HV_NIC_TYPE: "NIC_type",
964 constants.HV_PAE: "PAE",
965 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
970 (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
971 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
972 (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
973 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
974 (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
975 lambda ctx, _: (constants.QRFS_NORMAL,
976 ctx.inst_beparams[constants.BE_VCPUS])),
978 # Unfilled parameters
979 (_MakeField("custom_hvparams", "CustomHypervisorParameters",
980 constants.QFT_OTHER),
981 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
982 (_MakeField("custom_beparams", "CustomBackendParameters",
983 constants.QFT_OTHER),
984 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
985 (_MakeField("custom_nicparams", "CustomNicParameters",
986 constants.QFT_OTHER),
987 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
988 [nic.nicparams for nic in inst.nics])),
992 def _GetInstHvParam(name):
993 return lambda ctx, _: (constants.QRFS_NORMAL,
994 ctx.inst_hvparams.get(name, None))
997 # For now all hypervisor parameters are exported as QFT_OTHER
998 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
999 constants.QFT_OTHER),
1000 IQ_CONFIG, _GetInstHvParam(name))
1001 for name in constants.HVS_PARAMETERS
1002 if name not in constants.HVC_GLOBALS
1006 def _GetInstBeParam(name):
1007 return lambda ctx, _: (constants.QRFS_NORMAL,
1008 ctx.inst_beparams.get(name, None))
1011 # For now all backend parameters are exported as QFT_OTHER
1012 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1013 constants.QFT_OTHER),
1014 IQ_CONFIG, _GetInstBeParam(name))
1015 for name in constants.BES_PARAMETERS
1021 _INST_SIMPLE_FIELDS = {
1022 "disk_template": ("Disk_template", constants.QFT_TEXT),
1023 "hypervisor": ("Hypervisor", constants.QFT_TEXT),
1024 "name": ("Node", constants.QFT_TEXT),
1025 # Depending on the hypervisor, the port can be None
1026 "network_port": ("Network_port", constants.QFT_OTHER),
1027 "os": ("OS", constants.QFT_TEXT),
1028 "serial_no": ("SerialNo", constants.QFT_NUMBER),
1029 "uuid": ("UUID", constants.QFT_TEXT),
1033 def _BuildInstanceFields():
1034 """Builds list of fields for instance queries.
1038 (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
1039 lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
1040 (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
1041 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
1042 (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
1043 lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
1044 (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
1045 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
1049 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1050 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1052 # Fields requiring talking to the node
1054 (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
1056 (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
1057 _GetInstLiveData("memory")),
1058 (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
1059 _GetInstLiveData("vcpus")),
1060 (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
1064 fields.extend(_GetInstanceParameterFields())
1065 fields.extend(_GetInstanceDiskFields())
1066 fields.extend(_GetInstanceNetworkFields())
1067 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1069 return _PrepareFieldList(fields)
1072 class LockQueryData:
1073 """Data container for lock data queries.
1076 def __init__(self, lockdata):
1077 """Initializes this class.
1080 self.lockdata = lockdata
1083 """Iterate over all locks.
1086 return iter(self.lockdata)
1089 def _GetLockOwners(_, data):
1090 """Returns a sorted list of a lock's current owners.
1093 (_, _, owners, _) = data
1096 owners = utils.NiceSort(owners)
1098 return (constants.QRFS_NORMAL, owners)
1101 def _GetLockPending(_, data):
1102 """Returns a sorted list of a lock's pending acquires.
1105 (_, _, _, pending) = data
1108 pending = [(mode, utils.NiceSort(names))
1109 for (mode, names) in pending]
1111 return (constants.QRFS_NORMAL, pending)
1114 def _BuildLockFields():
1115 """Builds list of fields for lock queries.
1118 return _PrepareFieldList([
1119 (_MakeField("name", "Name", constants.QFT_TEXT), None,
1120 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1121 (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1122 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1123 (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1125 (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1130 class GroupQueryData:
1131 """Data container for node group data queries.
1134 def __init__(self, groups, group_to_nodes, group_to_instances):
1135 """Initializes this class.
1137 @param groups: List of node group objects
1138 @type group_to_nodes: dict; group UUID as key
1139 @param group_to_nodes: Per-group list of nodes
1140 @type group_to_instances: dict; group UUID as key
1141 @param group_to_instances: Per-group list of (primary) instances
1144 self.groups = groups
1145 self.group_to_nodes = group_to_nodes
1146 self.group_to_instances = group_to_instances
1149 """Iterate over all node groups.
1152 return iter(self.groups)
1155 _GROUP_SIMPLE_FIELDS = {
1156 "alloc_policy": ("AllocPolicy", constants.QFT_TEXT),
1157 "name": ("Group", constants.QFT_TEXT),
1158 "serial_no": ("SerialNo", constants.QFT_NUMBER),
1159 "uuid": ("UUID", constants.QFT_TEXT),
1163 def _BuildGroupFields():
1164 """Builds list of fields for node group queries.
1168 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1169 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1171 def _GetLength(getter):
1172 return lambda ctx, group: (constants.QRFS_NORMAL,
1173 len(getter(ctx)[group.uuid]))
1175 def _GetSortedList(getter):
1176 return lambda ctx, group: (constants.QRFS_NORMAL,
1177 utils.NiceSort(getter(ctx)[group.uuid]))
1179 group_to_nodes = operator.attrgetter("group_to_nodes")
1180 group_to_instances = operator.attrgetter("group_to_instances")
1182 # Add fields for nodes
1184 (_MakeField("node_cnt", "Nodes", constants.QFT_NUMBER),
1185 GQ_NODE, _GetLength(group_to_nodes)),
1186 (_MakeField("node_list", "NodeList", constants.QFT_OTHER),
1187 GQ_NODE, _GetSortedList(group_to_nodes)),
1190 # Add fields for instances
1192 (_MakeField("pinst_cnt", "Instances", constants.QFT_NUMBER),
1193 GQ_INST, _GetLength(group_to_instances)),
1194 (_MakeField("pinst_list", "InstanceList", constants.QFT_OTHER),
1195 GQ_INST, _GetSortedList(group_to_instances)),
1198 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1200 return _PrepareFieldList(fields)
1203 #: Fields available for node queries
1204 NODE_FIELDS = _BuildNodeFields()
1206 #: Fields available for instance queries
1207 INSTANCE_FIELDS = _BuildInstanceFields()
1209 #: Fields available for lock queries
1210 LOCK_FIELDS = _BuildLockFields()
1212 #: Fields available for node group queries
1213 GROUP_FIELDS = _BuildGroupFields()
1215 #: All available field lists
1216 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]