X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/0422250ef29e1ec5cd0c0b581cfa15623b21d47c..afa9bb2ed8704b064c101b059e0f4a18a7730778:/lib/query.py diff --git a/lib/query.py b/lib/query.py index d041ad5..dc07159 100644 --- a/lib/query.py +++ b/lib/query.py @@ -71,6 +71,10 @@ from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER, RS_NORMAL, RS_UNKNOWN, RS_NODATA, RS_UNAVAIL, RS_OFFLINE) +(NETQ_CONFIG, + NETQ_GROUP, + NETQ_STATS, + NETQ_INST) = range(300, 304) # Constants for requesting data from the caller/data provider. Each property # collected/computed separately by the data provider should have its own to @@ -133,7 +137,12 @@ _FS_UNAVAIL = object() _FS_OFFLINE = object() #: List of all special status -_FS_ALL = frozenset([_FS_UNKNOWN, _FS_NODATA, _FS_UNAVAIL, _FS_OFFLINE]) +_FS_ALL = compat.UniqueFrozenset([ + _FS_UNKNOWN, + _FS_NODATA, + _FS_UNAVAIL, + _FS_OFFLINE, + ]) #: VType to QFT mapping _VTToQFT = { @@ -1225,8 +1234,24 @@ def _GetLiveNodeField(field, kind, ctx, node): if not ctx.curlive_data: return _FS_NODATA + return _GetStatsField(field, kind, ctx.curlive_data) + + +def _GetStatsField(field, kind, data): + """Gets a value from live statistics. + + If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is + numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL} + is returned. + + @param field: Live field name + @param kind: Data kind, one of L{constants.QFT_ALL} + @type data: dict + @param data: Statistics + + """ try: - value = ctx.curlive_data[field] + value = data[field] except KeyError: return _FS_UNAVAIL @@ -1240,7 +1265,7 @@ def _GetLiveNodeField(field, kind, ctx, node): return int(value) except (ValueError, TypeError): logging.exception("Failed to convert node field '%s' (value %r) to int", - value, field) + field, value) return _FS_UNAVAIL @@ -1339,15 +1364,13 @@ def _BuildNodeFields(): # Add simple fields fields.extend([ (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name)) - for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items() - ]) + for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()]) # Add fields requiring live data fields.extend([ (_MakeField(name, title, kind, doc), NQ_LIVE, 0, compat.partial(_GetLiveNodeField, nfield, kind)) - for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items() - ]) + for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()]) # Add timestamps fields.extend(_GetItemTimestampFields(NQ_CONFIG)) @@ -1546,6 +1569,20 @@ def _GetInstNic(index, cb): return fn +def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613 + """Get a NIC's Network. + + @type ctx: L{InstanceQueryData} + @type nic: L{objects.NIC} + @param nic: NIC object + + """ + if nic.network is None: + return _FS_UNAVAIL + else: + return nic.network + + def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613 """Get a NIC's IP address. @@ -1657,6 +1694,9 @@ def _GetInstanceNetworkFields(): (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER, "List containing each network interface's bridge"), IQ_CONFIG, 0, _GetInstAllNicBridges), + (_MakeField("nic.networks", "NIC_networks", QFT_OTHER, + "List containing each interface's network"), IQ_CONFIG, 0, + lambda ctx, inst: [nic.network for nic in inst.nics]), ] # NICs by number @@ -1678,6 +1718,9 @@ def _GetInstanceNetworkFields(): (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT, "Bridge of %s network interface" % numtext), IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)), + (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT, + "Network of %s network interface" % numtext), + IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)), ]) aliases = [ @@ -1687,6 +1730,7 @@ def _GetInstanceNetworkFields(): ("bridge", "nic.bridge/0"), ("nic_mode", "nic.mode/0"), ("nic_link", "nic.link/0"), + ("nic_network", "nic.network/0"), ] return (fields, aliases) @@ -1747,8 +1791,7 @@ def _GetInstanceDiskFields(): (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT, "Disk size of %s disk" % utils.FormatOrdinal(i + 1)), IQ_CONFIG, 0, _GetInstDiskSize(i)) - for i in range(constants.MAX_DISKS) - ]) + for i in range(constants.MAX_DISKS)]) return fields @@ -1796,8 +1839,7 @@ def _GetInstanceParameterFields(): _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name), IQ_CONFIG, 0, _GetInstHvParam(name)) for name, kind in constants.HVS_PARAMETER_TYPES.items() - if name not in constants.HVC_GLOBALS - ]) + if name not in constants.HVC_GLOBALS]) # BE params def _GetInstBeParam(name): @@ -1808,8 +1850,7 @@ def _GetInstanceParameterFields(): constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name), _VTToQFT[kind], "The \"%s\" backend parameter" % name), IQ_CONFIG, 0, _GetInstBeParam(name)) - for name, kind in constants.BES_PARAMETER_TYPES.items() - ]) + for name, kind in constants.BES_PARAMETER_TYPES.items()]) return fields @@ -1912,8 +1953,7 @@ def _BuildInstanceFields(): # Add simple fields fields.extend([ (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name)) - for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items() - ]) + for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()]) # Fields requiring talking to the node fields.extend([ @@ -2194,6 +2234,36 @@ def _BuildOsFields(): return _PrepareFieldList(fields, []) +class ExtStorageInfo(objects.ConfigObject): + __slots__ = [ + "name", + "node_status", + "nodegroup_status", + "parameters", + ] + + +def _BuildExtStorageFields(): + """Builds list of fields for extstorage provider queries. + + """ + fields = [ + (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"), + None, 0, _GetItemAttr("name")), + (_MakeField("node_status", "NodeStatus", QFT_OTHER, + "Status from node"), + None, 0, _GetItemAttr("node_status")), + (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER, + "Overall Nodegroup status"), + None, 0, _GetItemAttr("nodegroup_status")), + (_MakeField("parameters", "Parameters", QFT_OTHER, + "ExtStorage provider parameters"), + None, 0, _GetItemAttr("parameters")), + ] + + return _PrepareFieldList(fields, []) + + def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613 """Return L{_FS_UNAVAIL} if job is None. @@ -2406,21 +2476,136 @@ def _BuildClusterFields(): fields.extend([ (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name)) for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items() - ]) + ],) # Version fields fields.extend([ (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value)) - for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items() - ]) + for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()]) # Add timestamps fields.extend(_GetItemTimestampFields(CQ_CONFIG)) return _PrepareFieldList(fields, [ - ("name", "cluster_name"), + ("name", "cluster_name")]) + + +class NetworkQueryData: + """Data container for network data queries. + + """ + def __init__(self, networks, network_to_groups, + network_to_instances, stats): + """Initializes this class. + + @param networks: List of network objects + @type network_to_groups: dict; network UUID as key + @param network_to_groups: Per-network list of groups + @type network_to_instances: dict; network UUID as key + @param network_to_instances: Per-network list of instances + @type stats: dict; network UUID as key + @param stats: Per-network usage statistics + + """ + self.networks = networks + self.network_to_groups = network_to_groups + self.network_to_instances = network_to_instances + self.stats = stats + + def __iter__(self): + """Iterate over all networks. + + """ + for net in self.networks: + if self.stats: + self.curstats = self.stats.get(net.uuid, None) + else: + self.curstats = None + yield net + + +_NETWORK_SIMPLE_FIELDS = { + "name": ("Network", QFT_TEXT, 0, "Name"), + "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"), + "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"), + "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"), + "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"), + "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"), + "network_type": ("NetworkType", QFT_OTHER, 0, "Network type"), + "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"), + "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"), + } + + +_NETWORK_STATS_FIELDS = { + "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"), + "reserved_count": + ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"), + "map": ("Map", QFT_TEXT, 0, "Actual mapping"), + "external_reservations": + ("ExternalReservations", QFT_TEXT, 0, "External reservations"), + } + + +def _GetNetworkStatsField(field, kind, ctx, _): + """Gets the value of a "stats" field from L{NetworkQueryData}. + + @param field: Field name + @param kind: Data kind, one of L{constants.QFT_ALL} + @type ctx: L{NetworkQueryData} + + """ + return _GetStatsField(field, kind, ctx.curstats) + + +def _BuildNetworkFields(): + """Builds list of fields for network queries. + + """ + fields = [ + (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0, + lambda ctx, inst: list(inst.GetTags())), + ] + + # Add simple fields + fields.extend([ + (_MakeField(name, title, kind, doc), + NETQ_CONFIG, 0, _GetItemAttr(name)) + for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()]) + + def _GetLength(getter): + return lambda ctx, network: len(getter(ctx)[network.uuid]) + + def _GetSortedList(getter): + return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid]) + + network_to_groups = operator.attrgetter("network_to_groups") + network_to_instances = operator.attrgetter("network_to_instances") + + # Add fields for node groups + fields.extend([ + (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"), + NETQ_GROUP, 0, _GetLength(network_to_groups)), + (_MakeField("group_list", "GroupList", QFT_OTHER, + "List of nodegroups (group name, NIC mode, NIC link)"), + NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]), + ]) + + # Add fields for instances + fields.extend([ + (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"), + NETQ_INST, 0, _GetLength(network_to_instances)), + (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"), + NETQ_INST, 0, _GetSortedList(network_to_instances)), ]) + # Add fields for usage statistics + fields.extend([ + (_MakeField(name, title, kind, doc), NETQ_STATS, 0, + compat.partial(_GetNetworkStatsField, name, kind)) + for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()]) + + return _PrepareFieldList(fields, []) #: Fields for cluster information CLUSTER_FIELDS = _BuildClusterFields() @@ -2440,12 +2625,18 @@ GROUP_FIELDS = _BuildGroupFields() #: Fields available for operating system queries OS_FIELDS = _BuildOsFields() +#: Fields available for extstorage provider queries +EXTSTORAGE_FIELDS = _BuildExtStorageFields() + #: Fields available for job queries JOB_FIELDS = _BuildJobFields() #: Fields available for exports EXPORT_FIELDS = _BuildExportFields() +#: Fields available for network queries +NETWORK_FIELDS = _BuildNetworkFields() + #: All available resources ALL_FIELDS = { constants.QR_CLUSTER: CLUSTER_FIELDS, @@ -2454,8 +2645,10 @@ ALL_FIELDS = { constants.QR_LOCK: LOCK_FIELDS, constants.QR_GROUP: GROUP_FIELDS, constants.QR_OS: OS_FIELDS, + constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS, constants.QR_JOB: JOB_FIELDS, constants.QR_EXPORT: EXPORT_FIELDS, + constants.QR_NETWORK: NETWORK_FIELDS, } #: All available field lists