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 RS_NORMAL, RS_UNKNOWN, RS_NODATA,
67 RS_UNAVAIL, RS_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.
83 IQ_CONSOLE) = range(100, 104)
87 LQ_PENDING) = range(10, 13)
91 GQ_INST) = range(200, 203)
94 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
95 TITLE_RE = re.compile(r"^[^\s]+$")
97 #: Verification function for each field type
99 QFT_UNKNOWN: ht.TNone,
100 QFT_TEXT: ht.TString,
104 QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
105 QFT_OTHER: lambda _: True,
108 # Unique objects for special field statuses
109 _FS_UNKNOWN = object()
110 _FS_NODATA = object()
111 _FS_UNAVAIL = object()
112 _FS_OFFLINE = object()
114 #: VType to QFT mapping
116 # TODO: fix validation of empty strings
117 constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
118 constants.VTYPE_MAYBE_STRING: QFT_OTHER,
119 constants.VTYPE_BOOL: QFT_BOOL,
120 constants.VTYPE_SIZE: QFT_UNIT,
121 constants.VTYPE_INT: QFT_NUMBER,
125 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
126 """Gets the contents of an unknown field.
132 def _GetQueryFields(fielddefs, selected):
133 """Calculates the internal list of selected fields.
135 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
137 @type fielddefs: dict
138 @param fielddefs: Field definitions
139 @type selected: list of strings
140 @param selected: List of selected fields
145 for name in selected:
147 fdef = fielddefs[name]
149 fdef = (_MakeField(name, name, QFT_UNKNOWN), None, _GetUnknownField)
151 assert len(fdef) == 3
158 def GetAllFields(fielddefs):
159 """Extract L{objects.QueryFieldDefinition} from field definitions.
161 @rtype: list of L{objects.QueryFieldDefinition}
164 return [fdef for (fdef, _, _) in fielddefs]
168 def __init__(self, fieldlist, selected):
169 """Initializes this class.
171 The field definition is a dictionary with the field's name as a key and a
172 tuple containing, in order, the field definition object
173 (L{objects.QueryFieldDefinition}, the data kind to help calling code
174 collect data and a retrieval function. The retrieval function is called
175 with two parameters, in order, the data container and the item in container
176 (see L{Query.Query}).
178 Users of this class can call L{RequestedData} before preparing the data
179 container to determine what data is needed.
181 @type fieldlist: dictionary
182 @param fieldlist: Field definitions
183 @type selected: list of strings
184 @param selected: List of selected fields
187 self._fields = _GetQueryFields(fieldlist, selected)
189 def RequestedData(self):
190 """Gets requested kinds of data.
195 return frozenset(datakind
196 for (_, datakind, _) in self._fields
197 if datakind is not None)
200 """Returns the list of fields for this query.
202 Includes unknown fields.
204 @rtype: List of L{objects.QueryFieldDefinition}
207 return GetAllFields(self._fields)
209 def Query(self, ctx):
212 @param ctx: Data container passed to field retrieval functions, must
213 support iteration using C{__iter__}
216 result = [[_ProcessResult(fn(ctx, item)) for (_, _, fn) in self._fields]
222 _VerifyResultRow(self._fields, row)
226 def OldStyleQuery(self, ctx):
227 """Query with "old" query result format.
229 See L{Query.Query} for arguments.
232 unknown = set(fdef.name
233 for (fdef, _, _) in self._fields if fdef.kind == QFT_UNKNOWN)
235 raise errors.OpPrereqError("Unknown output fields selected: %s" %
236 (utils.CommaJoin(unknown), ),
239 return [[value for (_, value) in row]
240 for row in self.Query(ctx)]
243 def _ProcessResult(value):
244 """Converts result values into externally-visible ones.
247 if value is _FS_UNKNOWN:
248 return (RS_UNKNOWN, None)
249 elif value is _FS_NODATA:
250 return (RS_NODATA, None)
251 elif value is _FS_UNAVAIL:
252 return (RS_UNAVAIL, None)
253 elif value is _FS_OFFLINE:
254 return (RS_OFFLINE, None)
256 return (RS_NORMAL, value)
259 def _VerifyResultRow(fields, row):
260 """Verifies the contents of a query result row.
263 @param fields: Field definitions for result
264 @type row: list of tuples
268 assert len(row) == len(fields)
270 for ((status, value), (fdef, _, _)) in zip(row, fields):
271 if status == RS_NORMAL:
272 if not _VERIFY_FN[fdef.kind](value):
273 errs.append("normal field %s fails validation (value is %s)" %
275 elif value is not None:
276 errs.append("abnormal field %s has a non-None value" % fdef.name)
277 assert not errs, ("Failed validation: %s in row %s" %
278 (utils.CommaJoin(errors), row))
281 def _PrepareFieldList(fields, aliases):
282 """Prepares field list for use by L{Query}.
284 Converts the list to a dictionary and does some verification.
286 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
287 kind, retrieval function)
288 @param fields: List of fields, see L{Query.__init__} for a better
290 @type aliases: list of tuples; (alias, target)
291 @param aliases: list of tuples containing aliases; for each
292 alias/target pair, a duplicate will be created in the field list
294 @return: Field dictionary for L{Query}
298 duplicates = utils.FindDuplicates(fdef.title.lower()
299 for (fdef, _, _) in fields)
300 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
305 (fdef, _, fn) = field
307 assert fdef.name and fdef.title, "Name and title are required"
308 assert FIELD_NAME_RE.match(fdef.name)
309 assert TITLE_RE.match(fdef.title)
311 assert fdef.name not in result, \
312 "Duplicate field name '%s' found" % fdef.name
314 result[fdef.name] = field
316 for alias, target in aliases:
317 assert alias not in result, "Alias %s overrides an existing field" % alias
318 assert target in result, "Missing target %s for alias %s" % (target, alias)
319 (fdef, k, fn) = result[target]
322 result[alias] = (fdef, k, fn)
324 assert len(result) == len(fields) + len(aliases)
325 assert compat.all(name == fdef.name
326 for (name, (fdef, _, _)) in result.items())
331 def GetQueryResponse(query, ctx):
332 """Prepares the response for a query.
334 @type query: L{Query}
335 @param ctx: Data container, see L{Query.Query}
338 return objects.QueryResponse(data=query.Query(ctx),
339 fields=query.GetFields()).ToDict()
342 def QueryFields(fielddefs, selected):
343 """Returns list of available fields.
345 @type fielddefs: dict
346 @param fielddefs: Field definitions
347 @type selected: list of strings
348 @param selected: List of selected fields
349 @return: List of L{objects.QueryFieldDefinition}
353 # Client requests all fields, sort by name
354 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
355 key=operator.attrgetter("name"))
357 # Keep order as requested by client
358 fdefs = Query(fielddefs, selected).GetFields()
360 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
363 def _MakeField(name, title, kind):
364 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
366 @param name: Field name as a regular expression
367 @param title: Human-readable title
368 @param kind: Field type
371 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
374 def _GetNodeRole(node, master_name):
375 """Determine node role.
377 @type node: L{objects.Node}
378 @param node: Node object
379 @type master_name: string
380 @param master_name: Master node name
383 if node.name == master_name:
385 elif node.master_candidate:
395 def _GetItemAttr(attr):
396 """Returns a field function to return an attribute of the item.
398 @param attr: Attribute name
401 getter = operator.attrgetter(attr)
402 return lambda _, item: getter(item)
405 def _GetItemTimestamp(getter):
406 """Returns function for getting timestamp of item.
408 @type getter: callable
409 @param getter: Function to retrieve timestamp attribute
413 """Returns a timestamp of item.
416 timestamp = getter(item)
417 if timestamp is None:
418 # Old configs might not have all timestamps
426 def _GetItemTimestampFields(datatype):
427 """Returns common timestamp fields.
429 @param datatype: Field data type for use by L{Query.RequestedData}
433 (_MakeField("ctime", "CTime", QFT_TIMESTAMP), datatype,
434 _GetItemTimestamp(operator.attrgetter("ctime"))),
435 (_MakeField("mtime", "MTime", QFT_TIMESTAMP), datatype,
436 _GetItemTimestamp(operator.attrgetter("mtime"))),
441 """Data container for node data queries.
444 def __init__(self, nodes, live_data, master_name, node_to_primary,
445 node_to_secondary, groups, oob_support, cluster):
446 """Initializes this class.
450 self.live_data = live_data
451 self.master_name = master_name
452 self.node_to_primary = node_to_primary
453 self.node_to_secondary = node_to_secondary
455 self.oob_support = oob_support
456 self.cluster = cluster
458 # Used for individual rows
459 self.curlive_data = None
462 """Iterate over all nodes.
464 This function has side-effects and only one instance of the resulting
465 generator should be used at a time.
468 for node in self.nodes:
470 self.curlive_data = self.live_data.get(node.name, None)
472 self.curlive_data = None
476 #: Fields that are direct attributes of an L{objects.Node} object
477 _NODE_SIMPLE_FIELDS = {
478 "drained": ("Drained", QFT_BOOL),
479 "master_candidate": ("MasterC", QFT_BOOL),
480 "master_capable": ("MasterCapable", QFT_BOOL),
481 "name": ("Node", QFT_TEXT),
482 "offline": ("Offline", QFT_BOOL),
483 "serial_no": ("SerialNo", QFT_NUMBER),
484 "uuid": ("UUID", QFT_TEXT),
485 "vm_capable": ("VMCapable", QFT_BOOL),
489 #: Fields requiring talking to the node
490 _NODE_LIVE_FIELDS = {
491 "bootid": ("BootID", QFT_TEXT, "bootid"),
492 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes"),
493 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets"),
494 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total"),
495 "dfree": ("DFree", QFT_UNIT, "vg_free"),
496 "dtotal": ("DTotal", QFT_UNIT, "vg_size"),
497 "mfree": ("MFree", QFT_UNIT, "memory_free"),
498 "mnode": ("MNode", QFT_UNIT, "memory_dom0"),
499 "mtotal": ("MTotal", QFT_UNIT, "memory_total"),
504 """Build function for calling another function with an node group.
506 @param cb: The callback to be called with the nodegroup
510 """Get group data for a node.
512 @type ctx: L{NodeQueryData}
513 @type inst: L{objects.Node}
514 @param inst: Node object
517 ng = ctx.groups.get(node.group, None)
519 # Nodes always have a group, or the configuration is corrupt
522 return cb(ctx, node, ng)
527 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
528 """Returns the name of a node's group.
530 @type ctx: L{NodeQueryData}
531 @type node: L{objects.Node}
532 @param node: Node object
533 @type ng: L{objects.NodeGroup}
534 @param ng: The node group this node belongs to
540 def _GetNodePower(ctx, node):
541 """Returns the node powered state
543 @type ctx: L{NodeQueryData}
544 @type node: L{objects.Node}
545 @param node: Node object
548 if ctx.oob_support[node.name]:
554 def _GetNdParams(ctx, node, ng):
555 """Returns the ndparams for this node.
557 @type ctx: L{NodeQueryData}
558 @type node: L{objects.Node}
559 @param node: Node object
560 @type ng: L{objects.NodeGroup}
561 @param ng: The node group this node belongs to
564 return ctx.cluster.SimpleFillND(ng.FillND(node))
567 def _GetLiveNodeField(field, kind, ctx, node):
568 """Gets the value of a "live" field from L{NodeQueryData}.
570 @param field: Live field name
571 @param kind: Data kind, one of L{constants.QFT_ALL}
572 @type ctx: L{NodeQueryData}
573 @type node: L{objects.Node}
574 @param node: Node object
580 if not ctx.curlive_data:
584 value = ctx.curlive_data[field]
591 assert kind in (QFT_NUMBER, QFT_UNIT)
593 # Try to convert into number
596 except (ValueError, TypeError):
597 logging.exception("Failed to convert node field '%s' (value %r) to int",
602 def _BuildNodeFields():
603 """Builds list of fields for node queries.
607 (_MakeField("pip", "PrimaryIP", QFT_TEXT), NQ_CONFIG,
608 _GetItemAttr("primary_ip")),
609 (_MakeField("sip", "SecondaryIP", QFT_TEXT), NQ_CONFIG,
610 _GetItemAttr("secondary_ip")),
611 (_MakeField("tags", "Tags", QFT_OTHER), NQ_CONFIG,
612 lambda ctx, node: list(node.GetTags())),
613 (_MakeField("master", "IsMaster", QFT_BOOL), NQ_CONFIG,
614 lambda ctx, node: node.name == ctx.master_name),
615 (_MakeField("role", "Role", QFT_TEXT), NQ_CONFIG,
616 lambda ctx, node: _GetNodeRole(node, ctx.master_name)),
617 (_MakeField("group", "Group", QFT_TEXT), NQ_GROUP,
618 _GetGroup(_GetNodeGroup)),
619 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT),
620 NQ_CONFIG, _GetItemAttr("group")),
621 (_MakeField("powered", "Powered", QFT_BOOL), NQ_OOB, _GetNodePower),
622 (_MakeField("ndparams", "NodeParameters", QFT_OTHER), NQ_GROUP,
623 _GetGroup(_GetNdParams)),
624 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER),
625 NQ_GROUP, _GetItemAttr("ndparams")),
628 def _GetLength(getter):
629 return lambda ctx, node: len(getter(ctx)[node.name])
631 def _GetList(getter):
632 return lambda ctx, node: list(getter(ctx)[node.name])
634 # Add fields operating on instance lists
635 for prefix, titleprefix, getter in \
636 [("p", "Pri", operator.attrgetter("node_to_primary")),
637 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
639 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER),
640 NQ_INST, _GetLength(getter)),
641 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
643 NQ_INST, _GetList(getter)),
647 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
648 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
650 # Add fields requiring live data
652 (_MakeField(name, title, kind), NQ_LIVE,
653 compat.partial(_GetLiveNodeField, nfield, kind))
654 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
658 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
660 return _PrepareFieldList(fields, [])
663 class InstanceQueryData:
664 """Data container for instance data queries.
667 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
668 live_data, wrongnode_inst, console):
669 """Initializes this class.
671 @param instances: List of instance objects
672 @param cluster: Cluster object
673 @type disk_usage: dict; instance name as key
674 @param disk_usage: Per-instance disk usage
675 @type offline_nodes: list of strings
676 @param offline_nodes: List of offline nodes
677 @type bad_nodes: list of strings
678 @param bad_nodes: List of faulty nodes
679 @type live_data: dict; instance name as key
680 @param live_data: Per-instance live data
681 @type wrongnode_inst: set
682 @param wrongnode_inst: Set of instances running on wrong node(s)
683 @type console: dict; instance name as key
684 @param console: Per-instance console information
687 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
688 "Offline nodes not included in bad nodes"
689 assert not (set(live_data.keys()) & set(bad_nodes)), \
690 "Found live data for bad or offline nodes"
692 self.instances = instances
693 self.cluster = cluster
694 self.disk_usage = disk_usage
695 self.offline_nodes = offline_nodes
696 self.bad_nodes = bad_nodes
697 self.live_data = live_data
698 self.wrongnode_inst = wrongnode_inst
699 self.console = console
701 # Used for individual rows
702 self.inst_hvparams = None
703 self.inst_beparams = None
704 self.inst_nicparams = None
707 """Iterate over all instances.
709 This function has side-effects and only one instance of the resulting
710 generator should be used at a time.
713 for inst in self.instances:
714 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
715 self.inst_beparams = self.cluster.FillBE(inst)
716 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
717 for nic in inst.nics]
722 def _GetInstOperState(ctx, inst):
723 """Get instance's operational status.
725 @type ctx: L{InstanceQueryData}
726 @type inst: L{objects.Instance}
727 @param inst: Instance object
730 # Can't use RS_OFFLINE here as it would describe the instance to
731 # be offline when we actually don't know due to missing data
732 if inst.primary_node in ctx.bad_nodes:
735 return bool(ctx.live_data.get(inst.name))
738 def _GetInstLiveData(name):
739 """Build function for retrieving live data.
742 @param name: Live data field name
746 """Get live data for an instance.
748 @type ctx: L{InstanceQueryData}
749 @type inst: L{objects.Instance}
750 @param inst: Instance object
753 if (inst.primary_node in ctx.bad_nodes or
754 inst.primary_node in ctx.offline_nodes):
755 # Can't use RS_OFFLINE here as it would describe the instance to be
756 # offline when we actually don't know due to missing data
759 if inst.name in ctx.live_data:
760 data = ctx.live_data[inst.name]
769 def _GetInstStatus(ctx, inst):
770 """Get instance status.
772 @type ctx: L{InstanceQueryData}
773 @type inst: L{objects.Instance}
774 @param inst: Instance object
777 if inst.primary_node in ctx.offline_nodes:
778 return "ERROR_nodeoffline"
780 if inst.primary_node in ctx.bad_nodes:
781 return "ERROR_nodedown"
783 if bool(ctx.live_data.get(inst.name)):
784 if inst.name in ctx.wrongnode_inst:
785 return "ERROR_wrongnode"
797 def _GetInstDiskSize(index):
798 """Build function for retrieving disk size.
801 @param index: Disk index
805 """Get size of a disk.
807 @type inst: L{objects.Instance}
808 @param inst: Instance object
812 return inst.disks[index].size
819 def _GetInstNic(index, cb):
820 """Build function for calling another function with an instance NIC.
823 @param index: NIC index
829 """Call helper function with instance NIC.
831 @type ctx: L{InstanceQueryData}
832 @type inst: L{objects.Instance}
833 @param inst: Instance object
837 nic = inst.nics[index]
841 return cb(ctx, index, nic)
846 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
847 """Get a NIC's IP address.
849 @type ctx: L{InstanceQueryData}
850 @type nic: L{objects.NIC}
851 @param nic: NIC object
860 def _GetInstNicBridge(ctx, index, _):
861 """Get a NIC's bridge.
863 @type ctx: L{InstanceQueryData}
865 @param index: NIC index
868 assert len(ctx.inst_nicparams) >= index
870 nicparams = ctx.inst_nicparams[index]
872 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
873 return nicparams[constants.NIC_LINK]
878 def _GetInstAllNicBridges(ctx, inst):
879 """Get all network bridges for an instance.
881 @type ctx: L{InstanceQueryData}
882 @type inst: L{objects.Instance}
883 @param inst: Instance object
886 assert len(ctx.inst_nicparams) == len(inst.nics)
890 for nicp in ctx.inst_nicparams:
891 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
892 result.append(nicp[constants.NIC_LINK])
896 assert len(result) == len(inst.nics)
901 def _GetInstNicParam(name):
902 """Build function for retrieving a NIC parameter.
905 @param name: Parameter name
908 def fn(ctx, index, _):
909 """Get a NIC's bridge.
911 @type ctx: L{InstanceQueryData}
912 @type inst: L{objects.Instance}
913 @param inst: Instance object
914 @type nic: L{objects.NIC}
915 @param nic: NIC object
918 assert len(ctx.inst_nicparams) >= index
919 return ctx.inst_nicparams[index][name]
924 def _GetInstanceNetworkFields():
925 """Get instance fields involving network interfaces.
927 @return: List of field definitions used as input for L{_PrepareFieldList}
930 nic_mac_fn = lambda ctx, _, nic: nic.mac
931 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
932 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
936 (_MakeField("ip", "IP_address", QFT_TEXT), IQ_CONFIG,
937 _GetInstNic(0, _GetInstNicIp)),
938 (_MakeField("mac", "MAC_address", QFT_TEXT), IQ_CONFIG,
939 _GetInstNic(0, nic_mac_fn)),
940 (_MakeField("bridge", "Bridge", QFT_TEXT), IQ_CONFIG,
941 _GetInstNic(0, _GetInstNicBridge)),
942 (_MakeField("nic_mode", "NIC_Mode", QFT_TEXT), IQ_CONFIG,
943 _GetInstNic(0, nic_mode_fn)),
944 (_MakeField("nic_link", "NIC_Link", QFT_TEXT), IQ_CONFIG,
945 _GetInstNic(0, nic_link_fn)),
948 (_MakeField("nic.count", "NICs", QFT_NUMBER), IQ_CONFIG,
949 lambda ctx, inst: len(inst.nics)),
950 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER), IQ_CONFIG,
951 lambda ctx, inst: [nic.mac for nic in inst.nics]),
952 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER), IQ_CONFIG,
953 lambda ctx, inst: [nic.ip for nic in inst.nics]),
954 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER), IQ_CONFIG,
955 lambda ctx, inst: [nicp[constants.NIC_MODE]
956 for nicp in ctx.inst_nicparams]),
957 (_MakeField("nic.links", "NIC_links", QFT_OTHER), IQ_CONFIG,
958 lambda ctx, inst: [nicp[constants.NIC_LINK]
959 for nicp in ctx.inst_nicparams]),
960 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER), IQ_CONFIG,
961 _GetInstAllNicBridges),
965 for i in range(constants.MAX_NICS):
967 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT),
968 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
969 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT),
970 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
971 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT),
972 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
973 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT),
974 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
975 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT),
976 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
982 def _GetInstDiskUsage(ctx, inst):
983 """Get disk usage for an instance.
985 @type ctx: L{InstanceQueryData}
986 @type inst: L{objects.Instance}
987 @param inst: Instance object
990 usage = ctx.disk_usage[inst.name]
998 def _GetInstanceConsole(ctx, inst):
999 """Get console information for instance.
1001 @type ctx: L{InstanceQueryData}
1002 @type inst: L{objects.Instance}
1003 @param inst: Instance object
1006 consinfo = ctx.console[inst.name]
1008 if consinfo is None:
1014 def _GetInstanceDiskFields():
1015 """Get instance fields involving disks.
1017 @return: List of field definitions used as input for L{_PrepareFieldList}
1021 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT), IQ_DISKUSAGE,
1023 (_MakeField("disk.count", "Disks", QFT_NUMBER), IQ_CONFIG,
1024 lambda ctx, inst: len(inst.disks)),
1025 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER), IQ_CONFIG,
1026 lambda ctx, inst: [disk.size for disk in inst.disks]),
1031 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT),
1032 IQ_CONFIG, _GetInstDiskSize(i))
1033 for i in range(constants.MAX_DISKS)
1039 def _GetInstanceParameterFields():
1040 """Get instance fields involving parameters.
1042 @return: List of field definitions used as input for L{_PrepareFieldList}
1045 # TODO: Consider moving titles closer to constants
1047 constants.BE_AUTO_BALANCE: "Auto_balance",
1048 constants.BE_MEMORY: "ConfigMemory",
1049 constants.BE_VCPUS: "ConfigVCPUs",
1053 constants.HV_ACPI: "ACPI",
1054 constants.HV_BOOT_ORDER: "Boot_order",
1055 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1056 constants.HV_DISK_TYPE: "Disk_type",
1057 constants.HV_INITRD_PATH: "Initrd_path",
1058 constants.HV_KERNEL_PATH: "Kernel_path",
1059 constants.HV_NIC_TYPE: "NIC_type",
1060 constants.HV_PAE: "PAE",
1061 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1066 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER),
1067 IQ_CONFIG, lambda ctx, _: ctx.inst_hvparams),
1068 (_MakeField("beparams", "BackendParameters", QFT_OTHER),
1069 IQ_CONFIG, lambda ctx, _: ctx.inst_beparams),
1071 # Unfilled parameters
1072 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER),
1073 IQ_CONFIG, _GetItemAttr("hvparams")),
1074 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER),
1075 IQ_CONFIG, _GetItemAttr("beparams")),
1076 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER),
1077 IQ_CONFIG, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1081 def _GetInstHvParam(name):
1082 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1085 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1087 IQ_CONFIG, _GetInstHvParam(name))
1088 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1089 if name not in constants.HVC_GLOBALS
1093 def _GetInstBeParam(name):
1094 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1097 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1098 _VTToQFT[kind]), IQ_CONFIG,
1099 _GetInstBeParam(name))
1100 for name, kind in constants.BES_PARAMETER_TYPES.items()
1106 _INST_SIMPLE_FIELDS = {
1107 "disk_template": ("Disk_template", QFT_TEXT),
1108 "hypervisor": ("Hypervisor", QFT_TEXT),
1109 "name": ("Node", QFT_TEXT),
1110 # Depending on the hypervisor, the port can be None
1111 "network_port": ("Network_port", QFT_OTHER),
1112 "os": ("OS", QFT_TEXT),
1113 "serial_no": ("SerialNo", QFT_NUMBER),
1114 "uuid": ("UUID", QFT_TEXT),
1118 def _BuildInstanceFields():
1119 """Builds list of fields for instance queries.
1123 (_MakeField("pnode", "Primary_node", QFT_TEXT), IQ_CONFIG,
1124 _GetItemAttr("primary_node")),
1125 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER), IQ_CONFIG,
1126 lambda ctx, inst: list(inst.secondary_nodes)),
1127 (_MakeField("admin_state", "Autostart", QFT_BOOL), IQ_CONFIG,
1128 _GetItemAttr("admin_up")),
1129 (_MakeField("tags", "Tags", QFT_OTHER), IQ_CONFIG,
1130 lambda ctx, inst: list(inst.GetTags())),
1131 (_MakeField("console", "Console", QFT_OTHER), IQ_CONSOLE,
1132 _GetInstanceConsole),
1136 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1137 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1139 # Fields requiring talking to the node
1141 (_MakeField("oper_state", "Running", QFT_BOOL), IQ_LIVE,
1143 (_MakeField("oper_ram", "Memory", QFT_UNIT), IQ_LIVE,
1144 _GetInstLiveData("memory")),
1145 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER), IQ_LIVE,
1146 _GetInstLiveData("vcpus")),
1147 (_MakeField("status", "Status", QFT_TEXT), IQ_LIVE, _GetInstStatus),
1150 fields.extend(_GetInstanceParameterFields())
1151 fields.extend(_GetInstanceDiskFields())
1152 fields.extend(_GetInstanceNetworkFields())
1153 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1156 ("vcpus", "be/vcpus"),
1157 ("sda_size", "disk.size/0"),
1158 ("sdb_size", "disk.size/1"),
1161 return _PrepareFieldList(fields, aliases)
1164 class LockQueryData:
1165 """Data container for lock data queries.
1168 def __init__(self, lockdata):
1169 """Initializes this class.
1172 self.lockdata = lockdata
1175 """Iterate over all locks.
1178 return iter(self.lockdata)
1181 def _GetLockOwners(_, data):
1182 """Returns a sorted list of a lock's current owners.
1185 (_, _, owners, _) = data
1188 owners = utils.NiceSort(owners)
1193 def _GetLockPending(_, data):
1194 """Returns a sorted list of a lock's pending acquires.
1197 (_, _, _, pending) = data
1200 pending = [(mode, utils.NiceSort(names))
1201 for (mode, names) in pending]
1206 def _BuildLockFields():
1207 """Builds list of fields for lock queries.
1210 return _PrepareFieldList([
1211 (_MakeField("name", "Name", QFT_TEXT), None,
1212 lambda ctx, (name, mode, owners, pending): name),
1213 (_MakeField("mode", "Mode", QFT_OTHER), LQ_MODE,
1214 lambda ctx, (name, mode, owners, pending): mode),
1215 (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners),
1216 (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending),
1220 class GroupQueryData:
1221 """Data container for node group data queries.
1224 def __init__(self, groups, group_to_nodes, group_to_instances):
1225 """Initializes this class.
1227 @param groups: List of node group objects
1228 @type group_to_nodes: dict; group UUID as key
1229 @param group_to_nodes: Per-group list of nodes
1230 @type group_to_instances: dict; group UUID as key
1231 @param group_to_instances: Per-group list of (primary) instances
1234 self.groups = groups
1235 self.group_to_nodes = group_to_nodes
1236 self.group_to_instances = group_to_instances
1239 """Iterate over all node groups.
1242 return iter(self.groups)
1245 _GROUP_SIMPLE_FIELDS = {
1246 "alloc_policy": ("AllocPolicy", QFT_TEXT),
1247 "name": ("Group", QFT_TEXT),
1248 "serial_no": ("SerialNo", QFT_NUMBER),
1249 "uuid": ("UUID", QFT_TEXT),
1250 "ndparams": ("NDParams", QFT_OTHER),
1254 def _BuildGroupFields():
1255 """Builds list of fields for node group queries.
1259 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1260 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1262 def _GetLength(getter):
1263 return lambda ctx, group: len(getter(ctx)[group.uuid])
1265 def _GetSortedList(getter):
1266 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
1268 group_to_nodes = operator.attrgetter("group_to_nodes")
1269 group_to_instances = operator.attrgetter("group_to_instances")
1271 # Add fields for nodes
1273 (_MakeField("node_cnt", "Nodes", QFT_NUMBER),
1274 GQ_NODE, _GetLength(group_to_nodes)),
1275 (_MakeField("node_list", "NodeList", QFT_OTHER),
1276 GQ_NODE, _GetSortedList(group_to_nodes)),
1279 # Add fields for instances
1281 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER),
1282 GQ_INST, _GetLength(group_to_instances)),
1283 (_MakeField("pinst_list", "InstanceList", QFT_OTHER),
1284 GQ_INST, _GetSortedList(group_to_instances)),
1287 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1289 return _PrepareFieldList(fields, [])
1292 #: Fields available for node queries
1293 NODE_FIELDS = _BuildNodeFields()
1295 #: Fields available for instance queries
1296 INSTANCE_FIELDS = _BuildInstanceFields()
1298 #: Fields available for lock queries
1299 LOCK_FIELDS = _BuildLockFields()
1301 #: Fields available for node group queries
1302 GROUP_FIELDS = _BuildGroupFields()
1304 #: All available field lists
1305 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]