4 # Copyright (C) 2010 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Module for query operations"""
28 from ganeti import constants
29 from ganeti import errors
30 from ganeti import utils
31 from ganeti import compat
32 from ganeti import objects
44 IQ_DISKUSAGE) = range(100, 103)
48 LQ_PENDING) = range(10, 13)
52 GQ_INST) = range(200, 203)
55 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
56 TITLE_RE = re.compile(r"^[^\s]+$")
58 #: Verification function for each field type
60 constants.QFT_UNKNOWN: ht.TNone,
61 constants.QFT_TEXT: ht.TString,
62 constants.QFT_BOOL: ht.TBool,
63 constants.QFT_NUMBER: ht.TInt,
64 constants.QFT_UNIT: ht.TInt,
65 constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
66 constants.QFT_OTHER: lambda _: True,
70 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
71 """Gets the contents of an unknown field.
74 return (constants.QRFS_UNKNOWN, None)
77 def _GetQueryFields(fielddefs, selected):
78 """Calculates the internal list of selected fields.
80 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
83 @param fielddefs: Field definitions
84 @type selected: list of strings
85 @param selected: List of selected fields
92 fdef = fielddefs[name]
94 fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
95 None, _GetUnknownField)
104 def GetAllFields(fielddefs):
105 """Extract L{objects.QueryFieldDefinition} from field definitions.
107 @rtype: list of L{objects.QueryFieldDefinition}
110 return [fdef for (fdef, _, _) in fielddefs]
114 def __init__(self, fieldlist, selected):
115 """Initializes this class.
117 The field definition is a dictionary with the field's name as a key and a
118 tuple containing, in order, the field definition object
119 (L{objects.QueryFieldDefinition}, the data kind to help calling code
120 collect data and a retrieval function. The retrieval function is called
121 with two parameters, in order, the data container and the item in container
122 (see L{Query.Query}).
124 Users of this class can call L{RequestedData} before preparing the data
125 container to determine what data is needed.
127 @type fieldlist: dictionary
128 @param fieldlist: Field definitions
129 @type selected: list of strings
130 @param selected: List of selected fields
133 self._fields = _GetQueryFields(fieldlist, selected)
135 def RequestedData(self):
136 """Gets requested kinds of data.
141 return frozenset(datakind
142 for (_, datakind, _) in self._fields
143 if datakind is not None)
146 """Returns the list of fields for this query.
148 Includes unknown fields.
150 @rtype: List of L{objects.QueryFieldDefinition}
153 return GetAllFields(self._fields)
155 def Query(self, ctx):
158 @param ctx: Data container passed to field retrieval functions, must
159 support iteration using C{__iter__}
162 result = [[fn(ctx, item) for (_, _, fn) in self._fields]
167 for (idx, row) in enumerate(result):
168 assert _VerifyResultRow(self._fields, row), \
169 ("Inconsistent result for fields %s in row %s: %r" %
170 (GetAllFields(self._fields), idx, row))
174 def OldStyleQuery(self, ctx):
175 """Query with "old" query result format.
177 See L{Query.Query} for arguments.
180 unknown = set(fdef.name
181 for (fdef, _, _) in self._fields
182 if fdef.kind == constants.QFT_UNKNOWN)
184 raise errors.OpPrereqError("Unknown output fields selected: %s" %
185 (utils.CommaJoin(unknown), ),
188 return [[value for (_, value) in row]
189 for row in self.Query(ctx)]
192 def _VerifyResultRow(fields, row):
193 """Verifies the contents of a query result row.
196 @param fields: Field definitions for result
197 @type row: list of tuples
201 return (len(row) == len(fields) and
202 compat.all((status == constants.QRFS_NORMAL and
203 _VERIFY_FN[fdef.kind](value)) or
204 # Value for an abnormal status must be None
205 (status != constants.QRFS_NORMAL and value is None)
206 for ((status, value), (fdef, _, _)) in zip(row, fields)))
209 def _PrepareFieldList(fields):
210 """Prepares field list for use by L{Query}.
212 Converts the list to a dictionary and does some verification.
214 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
216 @param fields: List of fields
218 @return: Field dictionary for L{Query}
222 duplicates = utils.FindDuplicates(fdef.title.lower()
223 for (fdef, _, _) in fields)
224 assert not duplicates, "Duplicate title(s) found: %r" % duplicates
229 (fdef, _, fn) = field
231 assert fdef.name and fdef.title, "Name and title are required"
232 assert FIELD_NAME_RE.match(fdef.name)
233 assert TITLE_RE.match(fdef.title)
235 assert fdef.name not in result, \
236 "Duplicate field name '%s' found" % fdef.name
238 result[fdef.name] = field
240 assert len(result) == len(fields)
241 assert compat.all(name == fdef.name
242 for (name, (fdef, _, _)) in result.items())
247 def GetQueryResponse(query, ctx):
248 """Prepares the response for a query.
250 @type query: L{Query}
251 @param ctx: Data container, see L{Query.Query}
254 return objects.QueryResponse(data=query.Query(ctx),
255 fields=query.GetFields()).ToDict()
258 def QueryFields(fielddefs, selected):
259 """Returns list of available fields.
261 @type fielddefs: dict
262 @param fielddefs: Field definitions
263 @type selected: list of strings
264 @param selected: List of selected fields
265 @return: List of L{objects.QueryFieldDefinition}
269 # Client requests all fields, sort by name
270 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
271 key=operator.attrgetter("name"))
273 # Keep order as requested by client
274 fdefs = Query(fielddefs, selected).GetFields()
276 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
279 def _MakeField(name, title, kind):
280 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
282 @param name: Field name as a regular expression
283 @param title: Human-readable title
284 @param kind: Field type
287 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
290 def _GetNodeRole(node, master_name):
291 """Determine node role.
293 @type node: L{objects.Node}
294 @param node: Node object
295 @type master_name: string
296 @param master_name: Master node name
299 if node.name == master_name:
301 elif node.master_candidate:
311 def _GetItemAttr(attr):
312 """Returns a field function to return an attribute of the item.
314 @param attr: Attribute name
317 getter = operator.attrgetter(attr)
318 return lambda _, item: (constants.QRFS_NORMAL, getter(item))
321 def _GetItemTimestamp(getter):
322 """Returns function for getting timestamp of item.
324 @type getter: callable
325 @param getter: Function to retrieve timestamp attribute
329 """Returns a timestamp of item.
332 timestamp = getter(item)
333 if timestamp is None:
334 # Old configs might not have all timestamps
335 return (constants.QRFS_UNAVAIL, None)
337 return (constants.QRFS_NORMAL, timestamp)
342 def _GetItemTimestampFields(datatype):
343 """Returns common timestamp fields.
345 @param datatype: Field data type for use by L{Query.RequestedData}
349 (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
350 _GetItemTimestamp(operator.attrgetter("ctime"))),
351 (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
352 _GetItemTimestamp(operator.attrgetter("mtime"))),
357 """Data container for node data queries.
360 def __init__(self, nodes, live_data, master_name, node_to_primary,
361 node_to_secondary, groups, oob_support):
362 """Initializes this class.
366 self.live_data = live_data
367 self.master_name = master_name
368 self.node_to_primary = node_to_primary
369 self.node_to_secondary = node_to_secondary
371 self.oob_support = oob_support
373 # Used for individual rows
374 self.curlive_data = None
377 """Iterate over all nodes.
379 This function has side-effects and only one instance of the resulting
380 generator should be used at a time.
383 for node in self.nodes:
385 self.curlive_data = self.live_data.get(node.name, None)
387 self.curlive_data = None
391 #: Fields that are direct attributes of an L{objects.Node} object
392 _NODE_SIMPLE_FIELDS = {
393 "drained": ("Drained", constants.QFT_BOOL),
394 "master_candidate": ("MasterC", constants.QFT_BOOL),
395 "master_capable": ("MasterCapable", constants.QFT_BOOL),
396 "name": ("Node", constants.QFT_TEXT),
397 "offline": ("Offline", constants.QFT_BOOL),
398 "serial_no": ("SerialNo", constants.QFT_NUMBER),
399 "uuid": ("UUID", constants.QFT_TEXT),
400 "vm_capable": ("VMCapable", constants.QFT_BOOL),
404 #: Fields requiring talking to the node
405 _NODE_LIVE_FIELDS = {
406 "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
407 "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
408 "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
409 "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
410 "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
411 "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
412 "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
413 "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
414 "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
418 def _GetNodeGroup(ctx, node):
419 """Returns the name of a node's group.
421 @type ctx: L{NodeQueryData}
422 @type node: L{objects.Node}
423 @param node: Node object
426 ng = ctx.groups.get(node.group, None)
428 # Nodes always have a group, or the configuration is corrupt
429 return (constants.QRFS_UNAVAIL, None)
431 return (constants.QRFS_NORMAL, ng.name)
434 def _GetNodePower(ctx, node):
435 """Returns the node powered state
437 @type ctx: L{NodeQueryData}
438 @type node: L{objects.Node}
439 @param node: Node object
442 if ctx.oob_support[node.name]:
443 return (constants.QRFS_NORMAL, node.powered)
445 return (constants.QRFS_UNAVAIL, None)
448 def _GetLiveNodeField(field, kind, ctx, node):
449 """Gets the value of a "live" field from L{NodeQueryData}.
451 @param field: Live field name
452 @param kind: Data kind, one of L{constants.QFT_ALL}
453 @type ctx: L{NodeQueryData}
454 @type node: L{objects.Node}
455 @param node: Node object
459 return (constants.QRFS_OFFLINE, None)
461 if not ctx.curlive_data:
462 return (constants.QRFS_NODATA, None)
465 value = ctx.curlive_data[field]
467 return (constants.QRFS_UNAVAIL, None)
469 if kind == constants.QFT_TEXT:
470 return (constants.QRFS_NORMAL, value)
472 assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
474 # Try to convert into number
476 return (constants.QRFS_NORMAL, int(value))
477 except (ValueError, TypeError):
478 logging.exception("Failed to convert node field '%s' (value %r) to int",
480 return (constants.QRFS_UNAVAIL, None)
483 def _BuildNodeFields():
484 """Builds list of fields for node queries.
488 (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
489 lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
490 (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
491 lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
492 (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
493 lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
494 (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
495 lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
496 (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
497 lambda ctx, node: (constants.QRFS_NORMAL,
498 _GetNodeRole(node, ctx.master_name))),
499 (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP, _GetNodeGroup),
500 (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
501 NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
502 (_MakeField("powered", "Powered", constants.QFT_BOOL), NQ_OOB,
506 def _GetLength(getter):
507 return lambda ctx, node: (constants.QRFS_NORMAL,
508 len(getter(ctx)[node.name]))
510 def _GetList(getter):
511 return lambda ctx, node: (constants.QRFS_NORMAL,
512 list(getter(ctx)[node.name]))
514 # Add fields operating on instance lists
515 for prefix, titleprefix, getter in \
516 [("p", "Pri", operator.attrgetter("node_to_primary")),
517 ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
519 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
520 constants.QFT_NUMBER),
521 NQ_INST, _GetLength(getter)),
522 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
523 constants.QFT_OTHER),
524 NQ_INST, _GetList(getter)),
528 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
529 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
531 # Add fields requiring live data
533 (_MakeField(name, title, kind), NQ_LIVE,
534 compat.partial(_GetLiveNodeField, nfield, kind))
535 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
539 fields.extend(_GetItemTimestampFields(NQ_CONFIG))
541 return _PrepareFieldList(fields)
544 class InstanceQueryData:
545 """Data container for instance data queries.
548 def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
550 """Initializes this class.
552 @param instances: List of instance objects
553 @param cluster: Cluster object
554 @type disk_usage: dict; instance name as key
555 @param disk_usage: Per-instance disk usage
556 @type offline_nodes: list of strings
557 @param offline_nodes: List of offline nodes
558 @type bad_nodes: list of strings
559 @param bad_nodes: List of faulty nodes
560 @type live_data: dict; instance name as key
561 @param live_data: Per-instance live data
564 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
565 "Offline nodes not included in bad nodes"
566 assert not (set(live_data.keys()) & set(bad_nodes)), \
567 "Found live data for bad or offline nodes"
569 self.instances = instances
570 self.cluster = cluster
571 self.disk_usage = disk_usage
572 self.offline_nodes = offline_nodes
573 self.bad_nodes = bad_nodes
574 self.live_data = live_data
576 # Used for individual rows
577 self.inst_hvparams = None
578 self.inst_beparams = None
579 self.inst_nicparams = None
582 """Iterate over all instances.
584 This function has side-effects and only one instance of the resulting
585 generator should be used at a time.
588 for inst in self.instances:
589 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
590 self.inst_beparams = self.cluster.FillBE(inst)
591 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
592 for nic in inst.nics]
597 def _GetInstOperState(ctx, inst):
598 """Get instance's operational status.
600 @type ctx: L{InstanceQueryData}
601 @type inst: L{objects.Instance}
602 @param inst: Instance object
605 # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
606 # when we actually don't know due to missing data
607 if inst.primary_node in ctx.bad_nodes:
608 return (constants.QRFS_NODATA, None)
610 return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
613 def _GetInstLiveData(name):
614 """Build function for retrieving live data.
617 @param name: Live data field name
621 """Get live data for an instance.
623 @type ctx: L{InstanceQueryData}
624 @type inst: L{objects.Instance}
625 @param inst: Instance object
628 if (inst.primary_node in ctx.bad_nodes or
629 inst.primary_node in ctx.offline_nodes):
630 # Can't use QRFS_OFFLINE here as it would describe the instance to be
631 # offline when we actually don't know due to missing data
632 return (constants.QRFS_NODATA, None)
634 if inst.name in ctx.live_data:
635 data = ctx.live_data[inst.name]
637 return (constants.QRFS_NORMAL, data[name])
639 return (constants.QRFS_UNAVAIL, None)
644 def _GetInstStatus(ctx, inst):
645 """Get instance status.
647 @type ctx: L{InstanceQueryData}
648 @type inst: L{objects.Instance}
649 @param inst: Instance object
652 if inst.primary_node in ctx.offline_nodes:
653 return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
655 if inst.primary_node in ctx.bad_nodes:
656 return (constants.QRFS_NORMAL, "ERROR_nodedown")
658 if bool(ctx.live_data.get(inst.name)):
660 return (constants.QRFS_NORMAL, "running")
662 return (constants.QRFS_NORMAL, "ERROR_up")
665 return (constants.QRFS_NORMAL, "ERROR_down")
667 return (constants.QRFS_NORMAL, "ADMIN_down")
670 def _GetInstDiskSize(index):
671 """Build function for retrieving disk size.
674 @param index: Disk index
678 """Get size of a disk.
680 @type inst: L{objects.Instance}
681 @param inst: Instance object
685 return (constants.QRFS_NORMAL, inst.disks[index].size)
687 return (constants.QRFS_UNAVAIL, None)
692 def _GetInstNic(index, cb):
693 """Build function for calling another function with an instance NIC.
696 @param index: NIC index
702 """Call helper function with instance NIC.
704 @type ctx: L{InstanceQueryData}
705 @type inst: L{objects.Instance}
706 @param inst: Instance object
710 nic = inst.nics[index]
712 return (constants.QRFS_UNAVAIL, None)
714 return cb(ctx, index, nic)
719 def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
720 """Get a NIC's IP address.
722 @type ctx: L{InstanceQueryData}
723 @type nic: L{objects.NIC}
724 @param nic: NIC object
728 return (constants.QRFS_UNAVAIL, None)
730 return (constants.QRFS_NORMAL, nic.ip)
733 def _GetInstNicBridge(ctx, index, _):
734 """Get a NIC's bridge.
736 @type ctx: L{InstanceQueryData}
738 @param index: NIC index
741 assert len(ctx.inst_nicparams) >= index
743 nicparams = ctx.inst_nicparams[index]
745 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
746 return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
748 return (constants.QRFS_UNAVAIL, None)
751 def _GetInstAllNicBridges(ctx, inst):
752 """Get all network bridges for an instance.
754 @type ctx: L{InstanceQueryData}
755 @type inst: L{objects.Instance}
756 @param inst: Instance object
759 assert len(ctx.inst_nicparams) == len(inst.nics)
763 for nicp in ctx.inst_nicparams:
764 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
765 result.append(nicp[constants.NIC_LINK])
769 assert len(result) == len(inst.nics)
771 return (constants.QRFS_NORMAL, result)
774 def _GetInstNicParam(name):
775 """Build function for retrieving a NIC parameter.
778 @param name: Parameter name
781 def fn(ctx, index, _):
782 """Get a NIC's bridge.
784 @type ctx: L{InstanceQueryData}
785 @type inst: L{objects.Instance}
786 @param inst: Instance object
787 @type nic: L{objects.NIC}
788 @param nic: NIC object
791 assert len(ctx.inst_nicparams) >= index
792 return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
797 def _GetInstanceNetworkFields():
798 """Get instance fields involving network interfaces.
800 @return: List of field definitions used as input for L{_PrepareFieldList}
803 nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
804 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
805 nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
809 (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
810 _GetInstNic(0, _GetInstNicIp)),
811 (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
812 _GetInstNic(0, nic_mac_fn)),
813 (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
814 _GetInstNic(0, _GetInstNicBridge)),
815 (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
816 _GetInstNic(0, nic_mode_fn)),
817 (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
818 _GetInstNic(0, nic_link_fn)),
821 (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
822 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
823 (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
824 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
825 (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
826 lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
827 (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
828 lambda ctx, inst: (constants.QRFS_NORMAL,
829 [nicp[constants.NIC_MODE]
830 for nicp in ctx.inst_nicparams])),
831 (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
832 lambda ctx, inst: (constants.QRFS_NORMAL,
833 [nicp[constants.NIC_LINK]
834 for nicp in ctx.inst_nicparams])),
835 (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
836 _GetInstAllNicBridges),
840 for i in range(constants.MAX_NICS):
842 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
843 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
844 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
845 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
846 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
847 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
848 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
849 IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
850 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
851 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
857 def _GetInstDiskUsage(ctx, inst):
858 """Get disk usage for an instance.
860 @type ctx: L{InstanceQueryData}
861 @type inst: L{objects.Instance}
862 @param inst: Instance object
865 usage = ctx.disk_usage[inst.name]
870 return (constants.QRFS_NORMAL, usage)
873 def _GetInstanceDiskFields():
874 """Get instance fields involving disks.
876 @return: List of field definitions used as input for L{_PrepareFieldList}
880 (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
882 (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
883 _GetInstDiskSize(0)),
884 (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
885 _GetInstDiskSize(1)),
886 (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
887 lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
888 (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
889 lambda ctx, inst: (constants.QRFS_NORMAL,
890 [disk.size for disk in inst.disks])),
895 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
896 IQ_CONFIG, _GetInstDiskSize(i))
897 for i in range(constants.MAX_DISKS)
903 def _GetInstanceParameterFields():
904 """Get instance fields involving parameters.
906 @return: List of field definitions used as input for L{_PrepareFieldList}
909 # TODO: Consider moving titles closer to constants
911 constants.BE_AUTO_BALANCE: "Auto_balance",
912 constants.BE_MEMORY: "Configured_memory",
913 constants.BE_VCPUS: "VCPUs",
917 constants.HV_ACPI: "ACPI",
918 constants.HV_BOOT_ORDER: "Boot_order",
919 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
920 constants.HV_DISK_TYPE: "Disk_type",
921 constants.HV_INITRD_PATH: "Initrd_path",
922 constants.HV_KERNEL_PATH: "Kernel_path",
923 constants.HV_NIC_TYPE: "NIC_type",
924 constants.HV_PAE: "PAE",
925 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
930 (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
931 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
932 (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
933 IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
934 (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
935 lambda ctx, _: (constants.QRFS_NORMAL,
936 ctx.inst_beparams[constants.BE_VCPUS])),
938 # Unfilled parameters
939 (_MakeField("custom_hvparams", "CustomHypervisorParameters",
940 constants.QFT_OTHER),
941 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
942 (_MakeField("custom_beparams", "CustomBackendParameters",
943 constants.QFT_OTHER),
944 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
945 (_MakeField("custom_nicparams", "CustomNicParameters",
946 constants.QFT_OTHER),
947 IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
948 [nic.nicparams for nic in inst.nics])),
952 def _GetInstHvParam(name):
953 return lambda ctx, _: (constants.QRFS_NORMAL,
954 ctx.inst_hvparams.get(name, None))
957 # For now all hypervisor parameters are exported as QFT_OTHER
958 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
959 constants.QFT_OTHER),
960 IQ_CONFIG, _GetInstHvParam(name))
961 for name in constants.HVS_PARAMETERS
962 if name not in constants.HVC_GLOBALS
966 def _GetInstBeParam(name):
967 return lambda ctx, _: (constants.QRFS_NORMAL,
968 ctx.inst_beparams.get(name, None))
971 # For now all backend parameters are exported as QFT_OTHER
972 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
973 constants.QFT_OTHER),
974 IQ_CONFIG, _GetInstBeParam(name))
975 for name in constants.BES_PARAMETERS
981 _INST_SIMPLE_FIELDS = {
982 "disk_template": ("Disk_template", constants.QFT_TEXT),
983 "hypervisor": ("Hypervisor", constants.QFT_TEXT),
984 "name": ("Node", constants.QFT_TEXT),
985 # Depending on the hypervisor, the port can be None
986 "network_port": ("Network_port", constants.QFT_OTHER),
987 "os": ("OS", constants.QFT_TEXT),
988 "serial_no": ("SerialNo", constants.QFT_NUMBER),
989 "uuid": ("UUID", constants.QFT_TEXT),
993 def _BuildInstanceFields():
994 """Builds list of fields for instance queries.
998 (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
999 lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
1000 (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
1001 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
1002 (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
1003 lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
1004 (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
1005 lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
1009 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
1010 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
1012 # Fields requiring talking to the node
1014 (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
1016 (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
1017 _GetInstLiveData("memory")),
1018 (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
1019 _GetInstLiveData("vcpus")),
1020 (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
1024 fields.extend(_GetInstanceParameterFields())
1025 fields.extend(_GetInstanceDiskFields())
1026 fields.extend(_GetInstanceNetworkFields())
1027 fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1029 return _PrepareFieldList(fields)
1032 class LockQueryData:
1033 """Data container for lock data queries.
1036 def __init__(self, lockdata):
1037 """Initializes this class.
1040 self.lockdata = lockdata
1043 """Iterate over all locks.
1046 return iter(self.lockdata)
1049 def _GetLockOwners(_, data):
1050 """Returns a sorted list of a lock's current owners.
1053 (_, _, owners, _) = data
1056 owners = utils.NiceSort(owners)
1058 return (constants.QRFS_NORMAL, owners)
1061 def _GetLockPending(_, data):
1062 """Returns a sorted list of a lock's pending acquires.
1065 (_, _, _, pending) = data
1068 pending = [(mode, utils.NiceSort(names))
1069 for (mode, names) in pending]
1071 return (constants.QRFS_NORMAL, pending)
1074 def _BuildLockFields():
1075 """Builds list of fields for lock queries.
1078 return _PrepareFieldList([
1079 (_MakeField("name", "Name", constants.QFT_TEXT), None,
1080 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1081 (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1082 lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1083 (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1085 (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1090 class GroupQueryData:
1091 """Data container for node group data queries.
1094 def __init__(self, groups, group_to_nodes, group_to_instances):
1095 """Initializes this class.
1097 @param groups: List of node group objects
1098 @type group_to_nodes: dict; group UUID as key
1099 @param group_to_nodes: Per-group list of nodes
1100 @type group_to_instances: dict; group UUID as key
1101 @param group_to_instances: Per-group list of (primary) instances
1104 self.groups = groups
1105 self.group_to_nodes = group_to_nodes
1106 self.group_to_instances = group_to_instances
1109 """Iterate over all node groups.
1112 return iter(self.groups)
1115 _GROUP_SIMPLE_FIELDS = {
1116 "alloc_policy": ("AllocPolicy", constants.QFT_TEXT),
1117 "name": ("Group", constants.QFT_TEXT),
1118 "serial_no": ("SerialNo", constants.QFT_NUMBER),
1119 "uuid": ("UUID", constants.QFT_TEXT),
1123 def _BuildGroupFields():
1124 """Builds list of fields for node group queries.
1128 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name))
1129 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()]
1131 def _GetLength(getter):
1132 return lambda ctx, group: (constants.QRFS_NORMAL,
1133 len(getter(ctx)[group.uuid]))
1135 def _GetSortedList(getter):
1136 return lambda ctx, group: (constants.QRFS_NORMAL,
1137 utils.NiceSort(getter(ctx)[group.uuid]))
1139 group_to_nodes = operator.attrgetter("group_to_nodes")
1140 group_to_instances = operator.attrgetter("group_to_instances")
1142 # Add fields for nodes
1144 (_MakeField("node_cnt", "Nodes", constants.QFT_NUMBER),
1145 GQ_NODE, _GetLength(group_to_nodes)),
1146 (_MakeField("node_list", "NodeList", constants.QFT_OTHER),
1147 GQ_NODE, _GetSortedList(group_to_nodes)),
1150 # Add fields for instances
1152 (_MakeField("pinst_cnt", "Instances", constants.QFT_NUMBER),
1153 GQ_INST, _GetLength(group_to_instances)),
1154 (_MakeField("pinst_list", "InstanceList", constants.QFT_OTHER),
1155 GQ_INST, _GetSortedList(group_to_instances)),
1158 fields.extend(_GetItemTimestampFields(GQ_CONFIG))
1160 return _PrepareFieldList(fields)
1163 #: Fields available for node queries
1164 NODE_FIELDS = _BuildNodeFields()
1166 #: Fields available for instance queries
1167 INSTANCE_FIELDS = _BuildInstanceFields()
1169 #: Fields available for lock queries
1170 LOCK_FIELDS = _BuildLockFields()
1172 #: Fields available for node group queries
1173 GROUP_FIELDS = _BuildGroupFields()
1175 #: All available field lists
1176 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS]