Commit 306bed0e authored by Apollon Oikonomopoulos's avatar Apollon Oikonomopoulos Committed by Iustin Pop
Browse files

Implement LUNetworkQuery



Summarily list all existing networks
Supply detailed info for every existing network
 - List used/free IPs
 - List instances with NICs assigned to the corresponding network
 - List NIC index and IP for the above instances

Implement complementary config methods for retrieving networks.
Signed-off-by: default avatarApollon Oikonomopoulos <apollon@noc.grnet.gr>
Signed-off-by: default avatarDimitris Aragiorgis <dimara@grnet.gr>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 6c0a75db
......@@ -15479,18 +15479,120 @@ class LUNetworkSetParams(LogicalUnit):
 
 
class _NetworkQuery(_QueryBase):
FIELDS = query.NETWORK_FIELDS
def ExpandNames(self, lu):
pass
lu.needed_locks = {}
self._all_networks = lu.cfg.GetAllNetworksInfo()
name_to_uuid = dict((n.name, n.uuid) for n in self._all_networks.values())
if not self.names:
self.wanted = [name_to_uuid[name]
for name in utils.NiceSort(name_to_uuid.keys())]
else:
# Accept names to be either names or UUIDs.
missing = []
self.wanted = []
all_uuid = frozenset(self._all_networks.keys())
for name in self.names:
if name in all_uuid:
self.wanted.append(name)
elif name in name_to_uuid:
self.wanted.append(name_to_uuid[name])
else:
missing.append(name)
if missing:
raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
errors.ECODE_NOENT)
 
def DeclareLocks(self, lu, level):
pass
 
def _GetQueryData(self, lu):
pass
"""Computes the list of networks and their attributes.
"""
do_instances = query.NETQ_INST in self.requested_data
do_groups = do_instances or (query.NETQ_GROUP in self.requested_data)
do_stats = query.NETQ_STATS in self.requested_data
cluster = lu.cfg.GetClusterInfo()
network_to_groups = None
network_to_instances = None
stats = None
# For NETQ_GROUP, we need to map network->[groups]
if do_groups:
all_groups = lu.cfg.GetAllNodeGroupsInfo()
network_to_groups = dict((uuid, []) for uuid in self.wanted)
default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
if do_instances:
all_instances = lu.cfg.GetAllInstancesInfo()
all_nodes = lu.cfg.GetAllNodesInfo()
network_to_instances = dict((uuid, []) for uuid in self.wanted)
for group in all_groups.values():
if do_instances:
group_nodes = [node.name for node in all_nodes.values() if
node.group == group.uuid]
group_instances = [instance for instance in all_instances.values()
if instance.primary_node in group_nodes]
for net_uuid in group.networks.keys():
if net_uuid in network_to_groups:
netparams = group.networks[net_uuid]
mode = netparams[constants.NIC_MODE]
link = netparams[constants.NIC_LINK]
info = group.name + '(' + mode + ', ' + link + ')'
network_to_groups[net_uuid].append(info)
if do_instances:
for instance in group_instances:
for nic in instance.nics:
if nic.network == self._all_networks[net_uuid].name:
network_to_instances[net_uuid].append(instance.name)
break
if do_stats:
stats = {}
for uuid, net in self._all_networks.items():
if uuid in self.wanted:
pool = network.AddressPool(net)
stats[uuid] = {
"free_count": pool.GetFreeCount(),
"reserved_count": pool.GetReservedCount(),
"map": pool.GetMap(),
"external_reservations": ", ".join(pool.GetExternalReservations()),
}
return query.NetworkQueryData([self._all_networks[uuid]
for uuid in self.wanted],
network_to_groups,
network_to_instances,
stats)
 
 
class LUNetworkQuery(NoHooksLU):
pass
"""Logical unit for querying networks.
"""
REQ_BGL = False
def CheckArguments(self):
self.nq = _NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names),
self.op.output_fields, False)
def ExpandNames(self):
self.nq.ExpandNames(self)
def Exec(self, feedback_fn):
return self.nq.OldStyleQuery(self)
 
 
class LUNetworkConnect(LogicalUnit):
......
......@@ -62,6 +62,7 @@ REQ_QUERY_JOBS = "QueryJobs"
REQ_QUERY_INSTANCES = "QueryInstances"
REQ_QUERY_NODES = "QueryNodes"
REQ_QUERY_GROUPS = "QueryGroups"
REQ_QUERY_NETWORKS = "QueryNetworks"
REQ_QUERY_EXPORTS = "QueryExports"
REQ_QUERY_CONFIG_VALUES = "QueryConfigValues"
REQ_QUERY_CLUSTER_INFO = "QueryClusterInfo"
......@@ -566,6 +567,9 @@ class Client(object):
def QueryGroups(self, names, fields, use_locking):
return self.CallMethod(REQ_QUERY_GROUPS, (names, fields, use_locking))
def QueryNetworks(self, names, fields, use_locking):
return self.CallMethod(REQ_QUERY_NETWORKS, (names, fields, use_locking))
def QueryExports(self, nodes, use_locking):
return self.CallMethod(REQ_QUERY_EXPORTS, (nodes, use_locking))
......
......@@ -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
......@@ -2422,6 +2426,131 @@ def _BuildClusterFields():
])
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, "The network"),
"network": ("Subnet", QFT_TEXT, 0, "The subnet"),
"gateway": ("Gateway", QFT_OTHER, 0, "The gateway"),
"network6": ("IPv6Subnet", QFT_OTHER, 0, "The ipv6 subnet"),
"gateway6": ("IPv6Gateway", QFT_OTHER, 0, "The ipv6 gateway"),
"mac_prefix": ("MacPrefix", QFT_OTHER, 0, "The mac prefix"),
"network_type": ("NetworkType", QFT_OTHER, 0, "The network type"),
}
_NETWORK_STATS_FIELDS = {
"free_count": ("FreeCount", QFT_NUMBER, 0, "How many addresses are free"),
"reserved_count": ("ReservedCount", QFT_NUMBER, 0, "How many addresses are reserved"),
"map": ("Map", QFT_TEXT, 0, "The actual mapping"),
"external_reservations": ("ExternalReservations", QFT_TEXT, 0, "The external reservations"),
}
def _GetNetworkStatsField(field, kind, ctx, net):
"""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}
"""
try:
value = ctx.curstats[field]
except KeyError:
return _FS_UNAVAIL
if kind == QFT_TEXT:
return value
assert kind in (QFT_NUMBER, QFT_UNIT)
# Try to convert into number
try:
return int(value)
except (ValueError, TypeError):
logging.exception("Failed to convert network field '%s' (value %r) to int",
value, field)
return _FS_UNAVAIL
def _BuildNetworkFields():
"""Builds list of fields for network queries.
"""
# Add simple fields
fields = [
(_MakeField(name, title, kind, doc),
NETQ_CONFIG, 0, _GetItemAttr(name))
for (name, (title, kind, flags, 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"),
NETQ_GROUP, 0, _GetSortedList(network_to_groups)),
])
# 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, flags, doc)) in _NETWORK_STATS_FIELDS.items()
])
return _PrepareFieldList(fields, [])
#: Fields for cluster information
CLUSTER_FIELDS = _BuildClusterFields()
......@@ -2446,6 +2575,9 @@ 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,
......@@ -2456,6 +2588,7 @@ ALL_FIELDS = {
constants.QR_OS: OS_FIELDS,
constants.QR_JOB: JOB_FIELDS,
constants.QR_EXPORT: EXPORT_FIELDS,
constants.QR_NETWORK: NETWORK_FIELDS,
}
#: All available field lists
......
......@@ -397,6 +397,15 @@ class ClientOps:
op = opcodes.OpGroupQuery(names=names, output_fields=fields)
return self._Query(op)
elif method == luxi.REQ_QUERY_NETWORKS:
(names, fields, use_locking) = args
logging.info("Received network query request for %s", names)
if use_locking:
raise errors.OpPrereqError("Sync queries are not allowed",
errors.ECODE_INVAL)
op = opcodes.OpNetworkQuery(names=names, output_fields=fields)
return self._Query(op)
elif method == luxi.REQ_QUERY_EXPORTS:
(nodes, use_locking) = args
if use_locking:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment