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
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
65 # Constants for requesting data from the caller/data provider. Each property
66 # collected/computed separately by the data provider should have its own to
67 # only collect the requested data and not more.
77 IQ_DISKUSAGE) = range(100, 103)
81 LQ_PENDING) = range(10, 13)
85 GQ_INST) = range(200, 203)
88 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
89 TITLE_RE = re.compile(r"^[^\s]+$")
91 #: Verification function for each field type
93 constants.QFT_UNKNOWN: ht.TNone,
94 constants.QFT_TEXT: ht.TString,
95 constants.QFT_BOOL: ht.TBool,
96 constants.QFT_NUMBER: ht.TInt,
97 constants.QFT_UNIT: ht.TInt,
98 constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
99 constants.QFT_OTHER: lambda _: True,
103 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
104 """Gets the contents of an unknown field.
107 return (constants.QRFS_UNKNOWN, None)
110 def _GetQueryFields(fielddefs, selected):
111 """Calculates the internal list of selected fields.
113 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
115 @type fielddefs: dict
116 @param fielddefs: Field definitions
117 @type selected: list of strings
118 @param selected: List of selected fields
123 for name in selected:
125 fdef = fielddefs[name]
127 fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
128 None, _GetUnknownField)
130 assert len(fdef) == 3
137 def GetAllFields(fielddefs):
138 """Extract L{objects.QueryFieldDefinition} from field definitions.
140 @rtype: list of L{objects.QueryFieldDefinition}
143 return [fdef for (fdef, _, _) in fielddefs]
147 def __init__(self, fieldlist, selected):
148 """Initializes this class.
150 The field definition is a dictionary with the field's name as a key and a
151 tuple containing, in order, the field definition object
152 (L{objects.QueryFieldDefinition}, the data kind to help calling code
153 collect data and a retrieval function. The retrieval function is called
154 with two parameters, in order, the data container and the item in container
155 (see L{Query.Query}).
157 Users of this class can call L{RequestedData} before preparing the data
158 container to determine what data is needed.
160 @type fieldlist: dictionary
161 @param fieldlist: Field definitions
162 @type selected: list of strings
163 @param selected: List of selected fields
166 self._fields = _GetQueryFields(fieldlist, selected)
168 def RequestedData(self):
169 """Gets requested kinds of data.
174 return frozenset(datakind
175 for (_, datakind, _) in self._fields
176 if datakind is not None)
179 """Returns the list of fields for this query.
181 Includes unknown fields.
183 @rtype: List of L{objects.QueryFieldDefinition}
186 return GetAllFields(self._fields)
188 def Query(self, ctx):
191 @param ctx: Data container passed to field retrieval functions, must
192 support iteration using C{__iter__}
195 result = [[fn(ctx, item) for (_, _, fn) in self._fields]
200 for (idx, row) in enumerate(result):
201 assert _VerifyResultRow(self._fields, row), \
202 ("Inconsistent result for fields %s in row %s: %r" %
203 (GetAllFields(self._fields), idx, row))
207 def OldStyleQuery(self, ctx):
208 """Query with "old" query result format.
210 See L{Query.Query} for arguments.
213 unknown = set(fdef.name
214 for (fdef, _, _) in self._fields
215 if fdef.kind == constants.QFT_UNKNOWN)
217 raise errors.OpPrereqError("Unknown output fields selected: %s" %
218 (utils.CommaJoin(unknown), ),
221 return [[value for (_, value) in row]
222 for row in self.Query(ctx)]
225 def _VerifyResultRow(fields, row):
226 """Verifies the contents of a query result row.
229 @param fields: Field definitions for result
230 @type row: list of tuples
234 return (len(row) == len(fields) and
235 compat.all((status == constants.QRFS_NORMAL and
236 _VERIFY_FN[fdef.kind](value)) or
237 # Value for an abnormal status must be None
238 (status != constants.QRFS_NORMAL and value is None)
239 for ((status, value), (fdef, _, _)) in zip(row, fields)))
242 def _PrepareFieldList(fields):
243 """Prepares field list for use by L{Query}.
245 Converts the list to a dictionary and does some verification.
247 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
249 @param fields: List of fields, see L{Query.__init__} for a better description
251 @return: Field dictionary for L{Query}
255 duplicates = utils.FindDuplicates(fdef.title.lower()
256 for (fdef, _, _) in fields)
257 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
262 (fdef, _, fn) = field
264 assert fdef.name and fdef.title, "Name and title are required"
265 assert FIELD_NAME_RE.match(fdef.name)
266 assert TITLE_RE.match(fdef.title)
268 assert fdef.name not in result, \
269 "Duplicate field name '%s' found" % fdef.name
271 result[fdef.name] = field
273 assert len(result) == len(fields)
274 assert compat.all(name == fdef.name
275 for (name, (fdef, _, _)) in result.items())
280 def GetQueryResponse(query, ctx):
281 """Prepares the response for a query.
283 @type query: L{Query}
284 @param ctx: Data container, see L{Query.Query}
287 return objects.QueryResponse(data=query.Query(ctx),
288 fields=query.GetFields()).ToDict()
291 def QueryFields(fielddefs, selected):
292 """Returns list of available fields.
294 @type fielddefs: dict
295 @param fielddefs: Field definitions
296 @type selected: list of strings
297 @param selected: List of selected fields
298 @return: List of L{objects.QueryFieldDefinition}
302 # Client requests all fields, sort by name
303 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
304 key=operator.attrgetter("name"))
306 # Keep order as requested by client
307 fdefs = Query(fielddefs, selected).GetFields()
309 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
312 def _MakeField(name, title, kind):
313 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
315 @param name: Field name as a regular expression
316 @param title: Human-readable title
317 @param kind: Field type
320 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
323 def _GetNodeRole(node, master_name):
324 """Determine node role.
326 @type node: L{objects.Node}
327 @param node: Node object
328 @type master_name: string
329 @param master_name: Master node name
332 if node.name == master_name:
334 elif node.master_candidate:
344 def _GetItemAttr(attr):
345 """Returns a field function to return an attribute of the item.
347 @param attr: Attribute name
350 getter = operator.attrgetter(attr)
351 return lambda _, item: (constants.QRFS_NORMAL, getter(item))
354 def _GetItemTimestamp(getter):
355 """Returns function for getting timestamp of item.
357 @type getter: callable
358 @param getter: Function to retrieve timestamp attribute
362 """Returns a timestamp of item.
365 timestamp = getter(item)
366 if timestamp is None:
367 # Old configs might not have all timestamps
368 return (constants.QRFS_UNAVAIL, None)
370 return (constants.QRFS_NORMAL, timestamp)
375 def _GetItemTimestampFields(datatype):
376 """Returns common timestamp fields.
378 @param datatype: Field data type for use by L{Query.RequestedData}
382 (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
383 _GetItemTimestamp(operator.attrgetter("ctime"))),
384 (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
385 _GetItemTimestamp(operator.attrgetter("mtime"))),
390 """Data container for node data queries.
393 def __init__(self, nodes, live_data, master_name, node_to_primary,
394 node_to_secondary, groups, oob_support, cluster):
395 """Initializes this class.
399 self.live_data = live_data
400 self.master_name = master_name
401 self.node_to_primary = node_to_primary
402 self.node_to_secondary = node_to_secondary
404 self.oob_support = oob_support
405 self.cluster = cluster
407 # Used for individual rows
408 self.curlive_data = None
411 """Iterate over all nodes.
413 This function has side-effects and only one instance of the resulting
414 generator should be used at a time.
417 for node in self.nodes:
419 self.curlive_data = self.live_data.get(node.name, None)
421 self.curlive_data = None
425 #: Fields that are direct attributes of an L{objects.Node} object
426 _NODE_SIMPLE_FIELDS = {
427 "drained": ("Drained", constants.QFT_BOOL),
428 "master_candidate": ("MasterC", constants.QFT_BOOL),
429 "master_capable": ("MasterCapable", constants.QFT_BOOL),
430 "name": ("Node", constants.QFT_TEXT),
431 "offline": ("Offline", constants.QFT_BOOL),
432 "serial_no": ("SerialNo", constants.QFT_NUMBER),
433 "uuid": ("UUID", constants.QFT_TEXT),
434 "vm_capable": ("VMCapable", constants.QFT_BOOL),
438 #: Fields requiring talking to the node
439 _NODE_LIVE_FIELDS = {
440 "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
441 "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
442 "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
443 "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
444 "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
445 "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
446 "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
447 "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
448 "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
453 """Build function for calling another function with an node group.
455 @param cb: The callback to be called with the nodegroup
459 """Get group data for a node.
461 @type ctx: L{NodeQueryData}
462 @type inst: L{objects.Node}
463 @param inst: Node object
466 ng = ctx.groups.get(node.group, None)
468 # Nodes always have a group, or the configuration is corrupt
469 return (constants.QRFS_UNAVAIL, None)
471 return cb(ctx, node, ng)
476 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
477 """Returns the name of a node's group.
479 @type ctx: L{NodeQueryData}
480 @type node: L{objects.Node}
481 @param node: Node object
482 @type ng: L{objects.NodeGroup}
483 @param ng: The node group this node belongs to
486 return (constants.QRFS_NORMAL, ng.name)
489 def _GetNodePower(ctx, node):
490 """Returns the node powered state
492 @type ctx: L{NodeQueryData}
493 @type node: L{objects.Node}
494 @param node: Node object
497 if ctx.oob_support[node.name]:
498 return (constants.QRFS_NORMAL, node.powered)
500 return (constants.QRFS_UNAVAIL, None)
503 def _GetNdParams(ctx, node, ng):
504 """Returns the ndparams for this node.
506 @type ctx: L{NodeQueryData}
507 @type node: L{objects.Node}
508 @param node: Node object
509 @type ng: L{objects.NodeGroup}
510 @param ng: The node group this node belongs to
513 return (constants.QRFS_NORMAL, ctx.cluster.SimpleFillND(ng.FillND(node)))
516 def _GetLiveNodeField(field, kind, ctx, node):
517 """Gets the value of a "live" field from L{NodeQueryData}.
519 @param field: Live field name
520 @param kind: Data kind, one of L{constants.QFT_ALL}
521 @type ctx: L{NodeQueryData}
522 @type node: L{objects.Node}
523 @param node: Node object
527 return (constants.QRFS_OFFLINE, None)
529 if not ctx.curlive_data:
530 return (constants.QRFS_NODATA, None)
533 value = ctx.curlive_data[field]
535 return (constants.QRFS_UNAVAIL, None)
537 if kind == constants.QFT_TEXT:
538 return (constants.QRFS_NORMAL, value)
540 assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
542 # Try to convert into number
544 return (constants.QRFS_NORMAL, int(value))
545 except (ValueError, TypeError):
546 logging.exception("Failed to convert node field '%s' (value %r) to int",
548 return (constants.QRFS_UNAVAIL, None)
551 def _BuildNodeFields():
552 """Builds list of fields for node queries.
556 (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
557 lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
558 (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
559 lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
560 (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
561 lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
562 (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
563 lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
564 (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
565 lambda ctx, node: (constants.QRFS_NORMAL,
566 _GetNodeRole(node, ctx.master_name))),
567 (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP,
568 _GetGroup(_GetNodeGroup)),
569 (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
570 NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
571 (_MakeField("powered", "Powered", constants.QFT_BOOL), NQ_OOB,
573 (_MakeField("ndparams", "NodeParameters", constants.QFT_OTHER), NQ_GROUP,
574 _GetGroup(_GetNdParams)),
575 (_MakeField("custom_ndparams", "CustomNodeParameters", constants.QFT_OTHER),
576 NQ_GROUP, lambda ctx, node: (constants.QRFS_NORMAL, node.ndparams)),
579 def _GetLength(getter):
580 return lambda ctx, node: (constants.QRFS_NORMAL,
581 len(getter(ctx)[node.name]))
583 def _GetList(getter):
584 return lambda ctx, node: (constants.QRFS_NORMAL,
585 list(getter(ctx)[node.name]))
587 # Add fields operating on instance lists
588 for prefix, titleprefix, getter in \
589 [("p", "Pri", operator.attrgetter("node_to_primary")),
590 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
592 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
593 constants.QFT_NUMBER),
594 NQ_INST, _GetLength(getter)),
595 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
596 constants.QFT_OTHER),
597 NQ_INST, _GetList(getter)),
601 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
602 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
604 # Add fields requiring live data
606 (_MakeField(name, title, kind), NQ_LIVE,
607 compat.partial(_GetLiveNodeField, nfield, kind))
608 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
612 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
614 return _PrepareFieldList(fields)
617 class InstanceQueryData:
618 """Data container for instance data queries.
621 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
623 """Initializes this class.
625 @param instances: List of instance objects
626 @param cluster: Cluster object
627 @type disk_usage: dict; instance name as key
628 @param disk_usage: Per-instance disk usage
629 @type offline_nodes: list of strings
630 @param offline_nodes: List of offline nodes
631 @type bad_nodes: list of strings
632 @param bad_nodes: List of faulty nodes
633 @type live_data: dict; instance name as key
634 @param live_data: Per-instance live data
637 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
638 "Offline nodes not included in bad nodes"
639 assert not (set(live_data.keys()) & set(bad_nodes)), \
640 "Found live data for bad or offline nodes"
642 self.instances = instances
643 self.cluster = cluster
644 self.disk_usage = disk_usage
645 self.offline_nodes = offline_nodes
646 self.bad_nodes = bad_nodes
647 self.live_data = live_data
649 # Used for individual rows
650 self.inst_hvparams = None
651 self.inst_beparams = None
652 self.inst_nicparams = None
655 """Iterate over all instances.
657 This function has side-effects and only one instance of the resulting
658 generator should be used at a time.
661 for inst in self.instances:
662 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
663 self.inst_beparams = self.cluster.FillBE(inst)
664 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
665 for nic in inst.nics]
670 def _GetInstOperState(ctx, inst):
671 """Get instance's operational status.
673 @type ctx: L{InstanceQueryData}
674 @type inst: L{objects.Instance}
675 @param inst: Instance object
678 # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
679 # when we actually don't know due to missing data
680 if inst.primary_node in ctx.bad_nodes:
681 return (constants.QRFS_NODATA, None)
683 return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
686 def _GetInstLiveData(name):
687 """Build function for retrieving live data.
690 @param name: Live data field name
694 """Get live data for an instance.
696 @type ctx: L{InstanceQueryData}
697 @type inst: L{objects.Instance}
698 @param inst: Instance object
701 if (inst.primary_node in ctx.bad_nodes or
702 inst.primary_node in ctx.offline_nodes):
703 # Can't use QRFS_OFFLINE here as it would describe the instance to be
704 # offline when we actually don't know due to missing data
705 return (constants.QRFS_NODATA, None)
707 if inst.name in ctx.live_data:
708 data = ctx.live_data[inst.name]
710 return (constants.QRFS_NORMAL, data[name])
712 return (constants.QRFS_UNAVAIL, None)
717 def _GetInstStatus(ctx, inst):
718 """Get instance status.
720 @type ctx: L{InstanceQueryData}
721 @type inst: L{objects.Instance}
722 @param inst: Instance object
725 if inst.primary_node in ctx.offline_nodes:
726 return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
728 if inst.primary_node in ctx.bad_nodes:
729 return (constants.QRFS_NORMAL, "ERROR_nodedown")
731 if bool(ctx.live_data.get(inst.name)):
733 return (constants.QRFS_NORMAL, "running")
735 return (constants.QRFS_NORMAL, "ERROR_up")
738 return (constants.QRFS_NORMAL, "ERROR_down")
740 return (constants.QRFS_NORMAL, "ADMIN_down")
743 def _GetInstDiskSize(index):
744 """Build function for retrieving disk size.
747 @param index: Disk index
751 """Get size of a disk.
753 @type inst: L{objects.Instance}
754 @param inst: Instance object
758 return (constants.QRFS_NORMAL, inst.disks[index].size)
760 return (constants.QRFS_UNAVAIL, None)
765 def _GetInstNic(index, cb):
766 """Build function for calling another function with an instance NIC.
769 @param index: NIC index
775 """Call helper function with instance NIC.
777 @type ctx: L{InstanceQueryData}
778 @type inst: L{objects.Instance}
779 @param inst: Instance object
783 nic = inst.nics[index]
785 return (constants.QRFS_UNAVAIL, None)
787 return cb(ctx, index, nic)
792 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
793 """Get a NIC's IP address.
795 @type ctx: L{InstanceQueryData}
796 @type nic: L{objects.NIC}
797 @param nic: NIC object
801 return (constants.QRFS_UNAVAIL, None)
803 return (constants.QRFS_NORMAL, nic.ip)
806 def _GetInstNicBridge(ctx, index, _):
807 """Get a NIC's bridge.
809 @type ctx: L{InstanceQueryData}
811 @param index: NIC index
814 assert len(ctx.inst_nicparams) >= index
816 nicparams = ctx.inst_nicparams[index]
818 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
819 return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
821 return (constants.QRFS_UNAVAIL, None)
824 def _GetInstAllNicBridges(ctx, inst):
825 """Get all network bridges for an instance.
827 @type ctx: L{InstanceQueryData}
828 @type inst: L{objects.Instance}
829 @param inst: Instance object
832 assert len(ctx.inst_nicparams) == len(inst.nics)
836 for nicp in ctx.inst_nicparams:
837 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
838 result.append(nicp[constants.NIC_LINK])
842 assert len(result) == len(inst.nics)
844 return (constants.QRFS_NORMAL, result)
847 def _GetInstNicParam(name):
848 """Build function for retrieving a NIC parameter.
851 @param name: Parameter name
854 def fn(ctx, index, _):
855 """Get a NIC's bridge.
857 @type ctx: L{InstanceQueryData}
858 @type inst: L{objects.Instance}
859 @param inst: Instance object
860 @type nic: L{objects.NIC}
861 @param nic: NIC object
864 assert len(ctx.inst_nicparams) >= index
865 return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
870 def _GetInstanceNetworkFields():
871 """Get instance fields involving network interfaces.
873 @return: List of field definitions used as input for L{_PrepareFieldList}
876 nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
877 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
878 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
882 (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
883 _GetInstNic(0, _GetInstNicIp)),
884 (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
885 _GetInstNic(0, nic_mac_fn)),
886 (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
887 _GetInstNic(0, _GetInstNicBridge)),
888 (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
889 _GetInstNic(0, nic_mode_fn)),
890 (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
891 _GetInstNic(0, nic_link_fn)),
894 (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
895 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
896 (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
897 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
898 (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
899 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
900 (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
901 lambda ctx, inst: (constants.QRFS_NORMAL,
902 [nicp[constants.NIC_MODE]
903 for nicp in ctx.inst_nicparams])),
904 (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
905 lambda ctx, inst: (constants.QRFS_NORMAL,
906 [nicp[constants.NIC_LINK]
907 for nicp in ctx.inst_nicparams])),
908 (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
909 _GetInstAllNicBridges),
913 for i in range(constants.MAX_NICS):
915 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
916 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
917 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
918 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
919 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
920 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
921 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
922 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
923 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
924 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
930 def _GetInstDiskUsage(ctx, inst):
931 """Get disk usage for an instance.
933 @type ctx: L{InstanceQueryData}
934 @type inst: L{objects.Instance}
935 @param inst: Instance object
938 usage = ctx.disk_usage[inst.name]
943 return (constants.QRFS_NORMAL, usage)
946 def _GetInstanceDiskFields():
947 """Get instance fields involving disks.
949 @return: List of field definitions used as input for L{_PrepareFieldList}
953 (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
955 (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
956 _GetInstDiskSize(0)),
957 (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
958 _GetInstDiskSize(1)),
959 (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
960 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
961 (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
962 lambda ctx, inst: (constants.QRFS_NORMAL,
963 [disk.size for disk in inst.disks])),
968 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
969 IQ_CONFIG, _GetInstDiskSize(i))
970 for i in range(constants.MAX_DISKS)
976 def _GetInstanceParameterFields():
977 """Get instance fields involving parameters.
979 @return: List of field definitions used as input for L{_PrepareFieldList}
982 # TODO: Consider moving titles closer to constants
984 constants.BE_AUTO_BALANCE: "Auto_balance",
985 constants.BE_MEMORY: "Configured_memory",
986 constants.BE_VCPUS: "VCPUs",
990 constants.HV_ACPI: "ACPI",
991 constants.HV_BOOT_ORDER: "Boot_order",
992 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
993 constants.HV_DISK_TYPE: "Disk_type",
994 constants.HV_INITRD_PATH: "Initrd_path",
995 constants.HV_KERNEL_PATH: "Kernel_path",
996 constants.HV_NIC_TYPE: "NIC_type",
997 constants.HV_PAE: "PAE",
998 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1003 (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
1004 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
1005 (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
1006 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
1007 (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
1008 lambda ctx, _: (constants.QRFS_NORMAL,
1009 ctx.inst_beparams[constants.BE_VCPUS])),
1011 # Unfilled parameters
1012 (_MakeField("custom_hvparams", "CustomHypervisorParameters",
1013 constants.QFT_OTHER),
1014 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
1015 (_MakeField("custom_beparams", "CustomBackendParameters",
1016 constants.QFT_OTHER),
1017 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
1018 (_MakeField("custom_nicparams", "CustomNicParameters",
1019 constants.QFT_OTHER),
1020 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
1021 [nic.nicparams for nic in inst.nics])),
1025 def _GetInstHvParam(name):
1026 return lambda ctx, _: (constants.QRFS_NORMAL,
1027 ctx.inst_hvparams.get(name, None))
1030 # For now all hypervisor parameters are exported as QFT_OTHER
1031 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1032 constants.QFT_OTHER),
1033 IQ_CONFIG, _GetInstHvParam(name))
1034 for name in constants.HVS_PARAMETERS
1035 if name not in constants.HVC_GLOBALS
1039 def _GetInstBeParam(name):
1040 return lambda ctx, _: (constants.QRFS_NORMAL,
1041 ctx.inst_beparams.get(name, None))
1044 # For now all backend parameters are exported as QFT_OTHER
1045 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1046 constants.QFT_OTHER),
1047 IQ_CONFIG, _GetInstBeParam(name))
1048 for name in constants.BES_PARAMETERS
1054 _INST_SIMPLE_FIELDS = {
1055 "disk_template": ("Disk_template", constants.QFT_TEXT),
1056 "hypervisor": ("Hypervisor", constants.QFT_TEXT),
1057 "name": ("Node", constants.QFT_TEXT),
1058 # Depending on the hypervisor, the port can be None
1059 "network_port": ("Network_port", constants.QFT_OTHER),
1060 "os": ("OS", constants.QFT_TEXT),
1061 "serial_no": ("SerialNo", constants.QFT_NUMBER),
1062 "uuid": ("UUID", constants.QFT_TEXT),
1066 def _BuildInstanceFields():
1067 """Builds list of fields for instance queries.
1071 (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
1072 lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
1073 (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
1074 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
1075 (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
1076 lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
1077 (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
1078 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
1082 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1083 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1085 # Fields requiring talking to the node
1087 (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
1089 (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
1090 _GetInstLiveData("memory")),
1091 (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
1092 _GetInstLiveData("vcpus")),
1093 (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
1097 fields.extend(_GetInstanceParameterFields())
1098 fields.extend(_GetInstanceDiskFields())
1099 fields.extend(_GetInstanceNetworkFields())
1100 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1102 return _PrepareFieldList(fields)
1105 class LockQueryData:
1106 """Data container for lock data queries.
1109 def __init__(self, lockdata):
1110 """Initializes this class.
1113 self.lockdata = lockdata
1116 """Iterate over all locks.
1119 return iter(self.lockdata)
1122 def _GetLockOwners(_, data):
1123 """Returns a sorted list of a lock's current owners.
1126 (_, _, owners, _) = data
1129 owners = utils.NiceSort(owners)
1131 return (constants.QRFS_NORMAL, owners)
1134 def _GetLockPending(_, data):
1135 """Returns a sorted list of a lock's pending acquires.
1138 (_, _, _, pending) = data
1141 pending = [(mode, utils.NiceSort(names))
1142 for (mode, names) in pending]
1144 return (constants.QRFS_NORMAL, pending)
1147 def _BuildLockFields():
1148 """Builds list of fields for lock queries.
1151 return _PrepareFieldList([
1152 (_MakeField("name", "Name", constants.QFT_TEXT), None,
1153 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1154 (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1155 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1156 (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1158 (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1163 class GroupQueryData:
1164 """Data container for node group data queries.
1167 def __init__(self, groups, group_to_nodes, group_to_instances):
1168 """Initializes this class.
1170 @param groups: List of node group objects
1171 @type group_to_nodes: dict; group UUID as key
1172 @param group_to_nodes: Per-group list of nodes
1173 @type group_to_instances: dict; group UUID as key
1174 @param group_to_instances: Per-group list of (primary) instances
1177 self.groups = groups
1178 self.group_to_nodes = group_to_nodes
1179 self.group_to_instances = group_to_instances
1182 """Iterate over all node groups.
1185 return iter(self.groups)
1188 _GROUP_SIMPLE_FIELDS = {
1189 "alloc_policy": ("AllocPolicy", constants.QFT_TEXT),
1190 "name": ("Group", constants.QFT_TEXT),
1191 "serial_no": ("SerialNo", constants.QFT_NUMBER),
1192 "uuid": ("UUID", constants.QFT_TEXT),
1193 "ndparams": ("NDParams", constants.QFT_OTHER),
1197 def _BuildGroupFields():
1198 """Builds list of fields for node group queries.
1202 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1203 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1205 def _GetLength(getter):
1206 return lambda ctx, group: (constants.QRFS_NORMAL,
1207 len(getter(ctx)[group.uuid]))
1209 def _GetSortedList(getter):
1210 return lambda ctx, group: (constants.QRFS_NORMAL,
1211 utils.NiceSort(getter(ctx)[group.uuid]))
1213 group_to_nodes = operator.attrgetter("group_to_nodes")
1214 group_to_instances = operator.attrgetter("group_to_instances")
1216 # Add fields for nodes
1218 (_MakeField("node_cnt", "Nodes", constants.QFT_NUMBER),
1219 GQ_NODE, _GetLength(group_to_nodes)),
1220 (_MakeField("node_list", "NodeList", constants.QFT_OTHER),
1221 GQ_NODE, _GetSortedList(group_to_nodes)),
1224 # Add fields for instances
1226 (_MakeField("pinst_cnt", "Instances", constants.QFT_NUMBER),
1227 GQ_INST, _GetLength(group_to_instances)),
1228 (_MakeField("pinst_list", "InstanceList", constants.QFT_OTHER),
1229 GQ_INST, _GetSortedList(group_to_instances)),
1232 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1234 return _PrepareFieldList(fields)
1237 #: Fields available for node queries
1238 NODE_FIELDS = _BuildNodeFields()
1240 #: Fields available for instance queries
1241 INSTANCE_FIELDS = _BuildInstanceFields()
1243 #: Fields available for lock queries
1244 LOCK_FIELDS = _BuildLockFields()
1246 #: Fields available for node group queries
1247 GROUP_FIELDS = _BuildGroupFields()
1249 #: All available field lists
1250 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]