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:
384 return constants.NR_MASTER
385 elif node.master_candidate:
386 return constants.NR_MCANDIDATE
388 return constants.NR_DRAINED
390 return constants.NR_OFFLINE
392 return constants.NR_REGULAR
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 # Note that none of these are available for non-vm_capable nodes
491 _NODE_LIVE_FIELDS = {
492 "bootid": ("BootID", QFT_TEXT, "bootid"),
493 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes"),
494 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets"),
495 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total"),
496 "dfree": ("DFree", QFT_UNIT, "vg_free"),
497 "dtotal": ("DTotal", QFT_UNIT, "vg_size"),
498 "mfree": ("MFree", QFT_UNIT, "memory_free"),
499 "mnode": ("MNode", QFT_UNIT, "memory_dom0"),
500 "mtotal": ("MTotal", QFT_UNIT, "memory_total"),
505 """Build function for calling another function with an node group.
507 @param cb: The callback to be called with the nodegroup
511 """Get group data for a node.
513 @type ctx: L{NodeQueryData}
514 @type inst: L{objects.Node}
515 @param inst: Node object
518 ng = ctx.groups.get(node.group, None)
520 # Nodes always have a group, or the configuration is corrupt
523 return cb(ctx, node, ng)
528 def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
529 """Returns the name of a node's group.
531 @type ctx: L{NodeQueryData}
532 @type node: L{objects.Node}
533 @param node: Node object
534 @type ng: L{objects.NodeGroup}
535 @param ng: The node group this node belongs to
541 def _GetNodePower(ctx, node):
542 """Returns the node powered state
544 @type ctx: L{NodeQueryData}
545 @type node: L{objects.Node}
546 @param node: Node object
549 if ctx.oob_support[node.name]:
555 def _GetNdParams(ctx, node, ng):
556 """Returns the ndparams for this node.
558 @type ctx: L{NodeQueryData}
559 @type node: L{objects.Node}
560 @param node: Node object
561 @type ng: L{objects.NodeGroup}
562 @param ng: The node group this node belongs to
565 return ctx.cluster.SimpleFillND(ng.FillND(node))
568 def _GetLiveNodeField(field, kind, ctx, node):
569 """Gets the value of a "live" field from L{NodeQueryData}.
571 @param field: Live field name
572 @param kind: Data kind, one of L{constants.QFT_ALL}
573 @type ctx: L{NodeQueryData}
574 @type node: L{objects.Node}
575 @param node: Node object
581 if not node.vm_capable:
584 if not ctx.curlive_data:
588 value = ctx.curlive_data[field]
595 assert kind in (QFT_NUMBER, QFT_UNIT)
597 # Try to convert into number
600 except (ValueError, TypeError):
601 logging.exception("Failed to convert node field '%s' (value %r) to int",
606 def _BuildNodeFields():
607 """Builds list of fields for node queries.
611 (_MakeField("pip", "PrimaryIP", QFT_TEXT), NQ_CONFIG,
612 _GetItemAttr("primary_ip")),
613 (_MakeField("sip", "SecondaryIP", QFT_TEXT), NQ_CONFIG,
614 _GetItemAttr("secondary_ip")),
615 (_MakeField("tags", "Tags", QFT_OTHER), NQ_CONFIG,
616 lambda ctx, node: list(node.GetTags())),
617 (_MakeField("master", "IsMaster", QFT_BOOL), NQ_CONFIG,
618 lambda ctx, node: node.name == ctx.master_name),
619 (_MakeField("role", "Role", QFT_TEXT), NQ_CONFIG,
620 lambda ctx, node: _GetNodeRole(node, ctx.master_name)),
621 (_MakeField("group", "Group", QFT_TEXT), NQ_GROUP,
622 _GetGroup(_GetNodeGroup)),
623 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT),
624 NQ_CONFIG, _GetItemAttr("group")),
625 (_MakeField("powered", "Powered", QFT_BOOL), NQ_OOB, _GetNodePower),
626 (_MakeField("ndparams", "NodeParameters", QFT_OTHER), NQ_GROUP,
627 _GetGroup(_GetNdParams)),
628 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER),
629 NQ_GROUP, _GetItemAttr("ndparams")),
632 def _GetLength(getter):
633 return lambda ctx, node: len(getter(ctx)[node.name])
635 def _GetList(getter):
636 return lambda ctx, node: list(getter(ctx)[node.name])
638 # Add fields operating on instance lists
639 for prefix, titleprefix, getter in \
640 [("p", "Pri", operator.attrgetter("node_to_primary")),
641 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
643 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER),
644 NQ_INST, _GetLength(getter)),
645 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
647 NQ_INST, _GetList(getter)),
651 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
652 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
654 # Add fields requiring live data
656 (_MakeField(name, title, kind), NQ_LIVE,
657 compat.partial(_GetLiveNodeField, nfield, kind))
658 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
662 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
664 return _PrepareFieldList(fields, [])
667 class InstanceQueryData:
668 """Data container for instance data queries.
671 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
672 live_data, wrongnode_inst, console):
673 """Initializes this class.
675 @param instances: List of instance objects
676 @param cluster: Cluster object
677 @type disk_usage: dict; instance name as key
678 @param disk_usage: Per-instance disk usage
679 @type offline_nodes: list of strings
680 @param offline_nodes: List of offline nodes
681 @type bad_nodes: list of strings
682 @param bad_nodes: List of faulty nodes
683 @type live_data: dict; instance name as key
684 @param live_data: Per-instance live data
685 @type wrongnode_inst: set
686 @param wrongnode_inst: Set of instances running on wrong node(s)
687 @type console: dict; instance name as key
688 @param console: Per-instance console information
691 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
692 "Offline nodes not included in bad nodes"
693 assert not (set(live_data.keys()) & set(bad_nodes)), \
694 "Found live data for bad or offline nodes"
696 self.instances = instances
697 self.cluster = cluster
698 self.disk_usage = disk_usage
699 self.offline_nodes = offline_nodes
700 self.bad_nodes = bad_nodes
701 self.live_data = live_data
702 self.wrongnode_inst = wrongnode_inst
703 self.console = console
705 # Used for individual rows
706 self.inst_hvparams = None
707 self.inst_beparams = None
708 self.inst_nicparams = None
711 """Iterate over all instances.
713 This function has side-effects and only one instance of the resulting
714 generator should be used at a time.
717 for inst in self.instances:
718 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
719 self.inst_beparams = self.cluster.FillBE(inst)
720 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
721 for nic in inst.nics]
726 def _GetInstOperState(ctx, inst):
727 """Get instance's operational status.
729 @type ctx: L{InstanceQueryData}
730 @type inst: L{objects.Instance}
731 @param inst: Instance object
734 # Can't use RS_OFFLINE here as it would describe the instance to
735 # be offline when we actually don't know due to missing data
736 if inst.primary_node in ctx.bad_nodes:
739 return bool(ctx.live_data.get(inst.name))
742 def _GetInstLiveData(name):
743 """Build function for retrieving live data.
746 @param name: Live data field name
750 """Get live data for an instance.
752 @type ctx: L{InstanceQueryData}
753 @type inst: L{objects.Instance}
754 @param inst: Instance object
757 if (inst.primary_node in ctx.bad_nodes or
758 inst.primary_node in ctx.offline_nodes):
759 # Can't use RS_OFFLINE here as it would describe the instance to be
760 # offline when we actually don't know due to missing data
763 if inst.name in ctx.live_data:
764 data = ctx.live_data[inst.name]
773 def _GetInstStatus(ctx, inst):
774 """Get instance status.
776 @type ctx: L{InstanceQueryData}
777 @type inst: L{objects.Instance}
778 @param inst: Instance object
781 if inst.primary_node in ctx.offline_nodes:
782 return constants.INSTST_NODEOFFLINE
784 if inst.primary_node in ctx.bad_nodes:
785 return constants.INSTST_NODEDOWN
787 if bool(ctx.live_data.get(inst.name)):
788 if inst.name in ctx.wrongnode_inst:
789 return constants.INSTST_WRONGNODE
791 return constants.INSTST_RUNNING
793 return constants.INSTST_ERRORUP
796 return constants.INSTST_ERRORDOWN
798 return constants.INSTST_ADMINDOWN
801 def _GetInstDiskSize(index):
802 """Build function for retrieving disk size.
805 @param index: Disk index
809 """Get size of a disk.
811 @type inst: L{objects.Instance}
812 @param inst: Instance object
816 return inst.disks[index].size
823 def _GetInstNic(index, cb):
824 """Build function for calling another function with an instance NIC.
827 @param index: NIC index
833 """Call helper function with instance NIC.
835 @type ctx: L{InstanceQueryData}
836 @type inst: L{objects.Instance}
837 @param inst: Instance object
841 nic = inst.nics[index]
845 return cb(ctx, index, nic)
850 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
851 """Get a NIC's IP address.
853 @type ctx: L{InstanceQueryData}
854 @type nic: L{objects.NIC}
855 @param nic: NIC object
864 def _GetInstNicBridge(ctx, index, _):
865 """Get a NIC's bridge.
867 @type ctx: L{InstanceQueryData}
869 @param index: NIC index
872 assert len(ctx.inst_nicparams) >= index
874 nicparams = ctx.inst_nicparams[index]
876 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
877 return nicparams[constants.NIC_LINK]
882 def _GetInstAllNicBridges(ctx, inst):
883 """Get all network bridges for an instance.
885 @type ctx: L{InstanceQueryData}
886 @type inst: L{objects.Instance}
887 @param inst: Instance object
890 assert len(ctx.inst_nicparams) == len(inst.nics)
894 for nicp in ctx.inst_nicparams:
895 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
896 result.append(nicp[constants.NIC_LINK])
900 assert len(result) == len(inst.nics)
905 def _GetInstNicParam(name):
906 """Build function for retrieving a NIC parameter.
909 @param name: Parameter name
912 def fn(ctx, index, _):
913 """Get a NIC's bridge.
915 @type ctx: L{InstanceQueryData}
916 @type inst: L{objects.Instance}
917 @param inst: Instance object
918 @type nic: L{objects.NIC}
919 @param nic: NIC object
922 assert len(ctx.inst_nicparams) >= index
923 return ctx.inst_nicparams[index][name]
928 def _GetInstanceNetworkFields():
929 """Get instance fields involving network interfaces.
931 @return: Tuple containing list of field definitions used as input for
932 L{_PrepareFieldList} and a list of aliases
935 nic_mac_fn = lambda ctx, _, nic: nic.mac
936 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
937 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
941 (_MakeField("nic.count", "NICs", QFT_NUMBER), IQ_CONFIG,
942 lambda ctx, inst: len(inst.nics)),
943 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER), IQ_CONFIG,
944 lambda ctx, inst: [nic.mac for nic in inst.nics]),
945 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER), IQ_CONFIG,
946 lambda ctx, inst: [nic.ip for nic in inst.nics]),
947 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER), IQ_CONFIG,
948 lambda ctx, inst: [nicp[constants.NIC_MODE]
949 for nicp in ctx.inst_nicparams]),
950 (_MakeField("nic.links", "NIC_links", QFT_OTHER), IQ_CONFIG,
951 lambda ctx, inst: [nicp[constants.NIC_LINK]
952 for nicp in ctx.inst_nicparams]),
953 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER), IQ_CONFIG,
954 _GetInstAllNicBridges),
958 for i in range(constants.MAX_NICS):
960 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT),
961 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
962 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT),
963 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
964 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT),
965 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
966 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT),
967 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
968 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT),
969 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
973 # Legacy fields for first NIC
975 ("mac", "nic.mac/0"),
976 ("bridge", "nic.bridge/0"),
977 ("nic_mode", "nic.mode/0"),
978 ("nic_link", "nic.link/0"),
981 return (fields, aliases)
984 def _GetInstDiskUsage(ctx, inst):
985 """Get disk usage for an instance.
987 @type ctx: L{InstanceQueryData}
988 @type inst: L{objects.Instance}
989 @param inst: Instance object
992 usage = ctx.disk_usage[inst.name]
1000 def _GetInstanceConsole(ctx, inst):
1001 """Get console information for instance.
1003 @type ctx: L{InstanceQueryData}
1004 @type inst: L{objects.Instance}
1005 @param inst: Instance object
1008 consinfo = ctx.console[inst.name]
1010 if consinfo is None:
1016 def _GetInstanceDiskFields():
1017 """Get instance fields involving disks.
1019 @return: List of field definitions used as input for L{_PrepareFieldList}
1023 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT), IQ_DISKUSAGE,
1025 (_MakeField("disk.count", "Disks", QFT_NUMBER), IQ_CONFIG,
1026 lambda ctx, inst: len(inst.disks)),
1027 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER), IQ_CONFIG,
1028 lambda ctx, inst: [disk.size for disk in inst.disks]),
1033 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT),
1034 IQ_CONFIG, _GetInstDiskSize(i))
1035 for i in range(constants.MAX_DISKS)
1041 def _GetInstanceParameterFields():
1042 """Get instance fields involving parameters.
1044 @return: List of field definitions used as input for L{_PrepareFieldList}
1047 # TODO: Consider moving titles closer to constants
1049 constants.BE_AUTO_BALANCE: "Auto_balance",
1050 constants.BE_MEMORY: "ConfigMemory",
1051 constants.BE_VCPUS: "ConfigVCPUs",
1055 constants.HV_ACPI: "ACPI",
1056 constants.HV_BOOT_ORDER: "Boot_order",
1057 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
1058 constants.HV_DISK_TYPE: "Disk_type",
1059 constants.HV_INITRD_PATH: "Initrd_path",
1060 constants.HV_KERNEL_PATH: "Kernel_path",
1061 constants.HV_NIC_TYPE: "NIC_type",
1062 constants.HV_PAE: "PAE",
1063 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
1068 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER),
1069 IQ_CONFIG, lambda ctx, _: ctx.inst_hvparams),
1070 (_MakeField("beparams", "BackendParameters", QFT_OTHER),
1071 IQ_CONFIG, lambda ctx, _: ctx.inst_beparams),
1073 # Unfilled parameters
1074 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER),
1075 IQ_CONFIG, _GetItemAttr("hvparams")),
1076 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER),
1077 IQ_CONFIG, _GetItemAttr("beparams")),
1078 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER),
1079 IQ_CONFIG, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1083 def _GetInstHvParam(name):
1084 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1087 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
1089 IQ_CONFIG, _GetInstHvParam(name))
1090 for name, kind in constants.HVS_PARAMETER_TYPES.items()
1091 if name not in constants.HVC_GLOBALS
1095 def _GetInstBeParam(name):
1096 return lambda ctx, _: ctx.inst_beparams.get(name, None)
1099 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
1100 _VTToQFT[kind]), IQ_CONFIG,
1101 _GetInstBeParam(name))
1102 for name, kind in constants.BES_PARAMETER_TYPES.items()
1108 _INST_SIMPLE_FIELDS = {
1109 "disk_template": ("Disk_template", QFT_TEXT),
1110 "hypervisor": ("Hypervisor", QFT_TEXT),
1111 "name": ("Instance", QFT_TEXT),
1112 # Depending on the hypervisor, the port can be None
1113 "network_port": ("Network_port", QFT_OTHER),
1114 "os": ("OS", QFT_TEXT),
1115 "serial_no": ("SerialNo", QFT_NUMBER),
1116 "uuid": ("UUID", QFT_TEXT),
1120 def _BuildInstanceFields():
1121 """Builds list of fields for instance queries.
1125 (_MakeField("pnode", "Primary_node", QFT_TEXT), IQ_CONFIG,
1126 _GetItemAttr("primary_node")),
1127 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER), IQ_CONFIG,
1128 lambda ctx, inst: list(inst.secondary_nodes)),
1129 (_MakeField("admin_state", "Autostart", QFT_BOOL), IQ_CONFIG,
1130 _GetItemAttr("admin_up")),
1131 (_MakeField("tags", "Tags", QFT_OTHER), IQ_CONFIG,
1132 lambda ctx, inst: list(inst.GetTags())),
1133 (_MakeField("console", "Console", QFT_OTHER), IQ_CONSOLE,
1134 _GetInstanceConsole),
1138 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1139 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1141 # Fields requiring talking to the node
1143 (_MakeField("oper_state", "Running", QFT_BOOL), IQ_LIVE,
1145 (_MakeField("oper_ram", "Memory", QFT_UNIT), IQ_LIVE,
1146 _GetInstLiveData("memory")),
1147 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER), IQ_LIVE,
1148 _GetInstLiveData("vcpus")),
1149 (_MakeField("status", "Status", QFT_TEXT), IQ_LIVE, _GetInstStatus),
1152 (network_fields, network_aliases) = _GetInstanceNetworkFields()
1154 fields.extend(network_fields)
1155 fields.extend(_GetInstanceParameterFields())
1156 fields.extend(_GetInstanceDiskFields())
1157 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1160 ("vcpus", "be/vcpus"),
1161 ("sda_size", "disk.size/0"),
1162 ("sdb_size", "disk.size/1"),
1165 return _PrepareFieldList(fields, aliases)
1168 class LockQueryData:
1169 """Data container for lock data queries.
1172 def __init__(self, lockdata):
1173 """Initializes this class.
1176 self.lockdata = lockdata
1179 """Iterate over all locks.
1182 return iter(self.lockdata)
1185 def _GetLockOwners(_, data):
1186 """Returns a sorted list of a lock's current owners.
1189 (_, _, owners, _) = data
1192 owners = utils.NiceSort(owners)
1197 def _GetLockPending(_, data):
1198 """Returns a sorted list of a lock's pending acquires.
1201 (_, _, _, pending) = data
1204 pending = [(mode, utils.NiceSort(names))
1205 for (mode, names) in pending]
1210 def _BuildLockFields():
1211 """Builds list of fields for lock queries.
1214 return _PrepareFieldList([
1215 (_MakeField("name", "Name", QFT_TEXT), None,
1216 lambda ctx, (name, mode, owners, pending): name),
1217 (_MakeField("mode", "Mode", QFT_OTHER), LQ_MODE,
1218 lambda ctx, (name, mode, owners, pending): mode),
1219 (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners),
1220 (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending),
1224 class GroupQueryData:
1225 """Data container for node group data queries.
1228 def __init__(self, groups, group_to_nodes, group_to_instances):
1229 """Initializes this class.
1231 @param groups: List of node group objects
1232 @type group_to_nodes: dict; group UUID as key
1233 @param group_to_nodes: Per-group list of nodes
1234 @type group_to_instances: dict; group UUID as key
1235 @param group_to_instances: Per-group list of (primary) instances
1238 self.groups = groups
1239 self.group_to_nodes = group_to_nodes
1240 self.group_to_instances = group_to_instances
1243 """Iterate over all node groups.
1246 return iter(self.groups)
1249 _GROUP_SIMPLE_FIELDS = {
1250 "alloc_policy": ("AllocPolicy", QFT_TEXT),
1251 "name": ("Group", QFT_TEXT),
1252 "serial_no": ("SerialNo", QFT_NUMBER),
1253 "uuid": ("UUID", QFT_TEXT),
1254 "ndparams": ("NDParams", QFT_OTHER),
1258 def _BuildGroupFields():
1259 """Builds list of fields for node group queries.
1263 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1264 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1266 def _GetLength(getter):
1267 return lambda ctx, group: len(getter(ctx)[group.uuid])
1269 def _GetSortedList(getter):
1270 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
1272 group_to_nodes = operator.attrgetter("group_to_nodes")
1273 group_to_instances = operator.attrgetter("group_to_instances")
1275 # Add fields for nodes
1277 (_MakeField("node_cnt", "Nodes", QFT_NUMBER),
1278 GQ_NODE, _GetLength(group_to_nodes)),
1279 (_MakeField("node_list", "NodeList", QFT_OTHER),
1280 GQ_NODE, _GetSortedList(group_to_nodes)),
1283 # Add fields for instances
1285 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER),
1286 GQ_INST, _GetLength(group_to_instances)),
1287 (_MakeField("pinst_list", "InstanceList", QFT_OTHER),
1288 GQ_INST, _GetSortedList(group_to_instances)),
1291 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1293 return _PrepareFieldList(fields, [])
1296 #: Fields available for node queries
1297 NODE_FIELDS = _BuildNodeFields()
1299 #: Fields available for instance queries
1300 INSTANCE_FIELDS = _BuildInstanceFields()
1302 #: Fields available for lock queries
1303 LOCK_FIELDS = _BuildLockFields()
1305 #: Fields available for node group queries
1306 GROUP_FIELDS = _BuildGroupFields()
1308 #: All available field lists
1309 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]