cloud-foundation-fabric/blueprints/cloud-operations/network-quota-monitoring/src/plugins/series-peering-groups.py

181 lines
6.6 KiB
Python

# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'Prepares descriptors and timeseries for peering group metrics.'
import itertools
import logging
from . import MetricDescriptor, TimeSeries, register_timeseries
DESCRIPTOR_ATTRS = {
'forwarding_rules_l4_available':
'L4 fwd rules limit per peering group',
'forwarding_rules_l4_used':
'L4 fwd rules used per peering group',
'forwarding_rules_l4_used_ratio':
'L4 fwd rules used ratio per peering group',
'forwarding_rules_l7_available':
'L7 fwd rules limit per peering group',
'forwarding_rules_l7_used':
'L7 fwd rules used per peering group',
'forwarding_rules_l7_used_ratio':
'L7 fwd rules used ratio per peering group',
'instances_available':
'Instance limit per peering group',
'instances_used':
'Instance used per peering group',
'instances_used_ratio':
'Instance used ratio per peering group',
'routes_dynamic_available':
'Dynamic route limit per peering group',
'routes_dynamic_used':
'Dynamic route used per peering group',
'routes_dynamic_used_ratio':
'Dynamic route used ratio per peering group',
'routes_static_available':
'Static route limit per peering group',
'routes_static_used':
'Static route used per peering group',
'routes_static_used_ratio':
'Static route used ratio per peering group',
}
LIMITS = {
'forwarding_rules_l4': {
'pg': ('INTERNAL_FORWARDING_RULES_PER_PEERING_GROUP', 500),
'prj': ('INTERNAL_FORWARDING_RULES_PER_NETWORK', 500)
},
'forwarding_rules_l7': {
'pg': ('INTERNAL_MANAGED_FORWARDING_RULES_PER_PEERING_GROUP', 175),
'prj': ('INTERNAL_MANAGED_FORWARDING_RULES_PER_NETWORK', 75)
},
'instances': {
'pg': ('INSTANCES_PER_PEERING_GROUP', 15500),
'prj': ('INSTANCES_PER_NETWORK_GLOBAL', 15000)
},
'routes_static': {
'pg': ('STATIC_ROUTES_PER_PEERING_GROUP', 300),
'prj': ('ROUTES', 250)
},
'routes_dynamic': {
'pg': ('DYNAMIC_ROUTES_PER_PEERING_GROUP', 300),
'prj': ('', 100)
}
}
LOGGER = logging.getLogger('net-dash.timeseries.peerings')
def _count_forwarding_rules_l4(resources, network_ids):
'Returns count of L4 forwarding rules for specified network ids.'
return len([
r for r in resources['forwarding_rules'].values() if
r['network'] in network_ids and r['load_balancing_scheme'] == 'INTERNAL'
])
def _count_forwarding_rules_l7(resources, network_ids):
'Returns count of L7 forwarding rules for specified network ids.'
return len([
r for r in resources['forwarding_rules'].values()
if r['network'] in network_ids and
r['load_balancing_scheme'] == 'INTERNAL_MANAGED'
])
def _count_instances(resources, network_ids):
'Returns count of instances for specified network ids.'
count = 0
for i in resources['instances'].values():
if any(n['network'] in network_ids for n in i['networks']):
count += 1
return count
def _count_routes_static(resources, network_ids):
'Returns count of static routes for specified network ids.'
return len(
[r for r in resources['routes'].values() if r['network'] in network_ids])
def _count_routes_dynamic(resources, network_ids):
'Returns count of dynamic routes for specified network ids.'
return sum([
sum(v.values())
for k, v in resources['routes_dynamic'].items()
if k in network_ids
])
def _get_limit_max(quota, network_id, project_id, resource_name):
'Returns maximum limit value in project / peering group / network limits.'
pg_name, pg_default = LIMITS[resource_name]['pg']
prj_name, prj_default = LIMITS[resource_name]['prj']
network_quota = quota.get(network_id, {})
project_quota = quota.get(project_id, {}).get('global', {})
return max([
network_quota.get(pg_name, 0),
project_quota.get(prj_name, prj_default),
project_quota.get(pg_name, pg_default)
])
def _get_limit(quota, network, resource_name):
'Computes and returns peering group limit.'
# reference https://cloud.google.com/vpc/docs/quota#vpc-peering-ilb-example
# step 1 - vpc_max = max(vpc limit, pg limit)
vpc_max = _get_limit_max(quota, network['self_link'], network['project_id'],
resource_name)
# step 2 - peers_max = [max(vpc limit, pg limit) for v in peered vpcs]
# step 3 - peers_min = min(peers_max)
peers_min = min([
_get_limit_max(quota, p['network'], p['project_id'], resource_name)
for p in network['peerings']
])
# step 4 - max(vpc_max, peers_min)
return max([vpc_max, peers_min])
def _peering_group_timeseries(resources, network):
'Computes and returns peering group timeseries for network.'
if len(network['peerings']) == 0:
return
network_ids = [network['self_link']
] + [p['network'] for p in network['peerings']]
for resource_name in LIMITS:
limit = _get_limit(resources['quota'], network, resource_name)
func = globals().get(f'_count_{resource_name}')
if not func or not callable(func):
LOGGER.critical(f'no handler for {resource_name} or handler not callable')
continue
count = func(resources, network_ids)
labels = {'project': network['project_id'], 'network': network['name']}
yield TimeSeries(f'peering_group/{resource_name}_used', count, labels)
yield TimeSeries(f'peering_group/{resource_name}_available', limit, labels)
yield TimeSeries(f'peering_group/{resource_name}_used_ratio', count / limit,
labels)
@register_timeseries
def timeseries(resources):
'Returns peering group timeseries for all networks.'
LOGGER.info('timeseries')
# returns metric descriptors
for dtype, name in DESCRIPTOR_ATTRS.items():
yield MetricDescriptor(f'peering_group/{dtype}', name,
('project', 'network'), dtype.endswith('ratio'))
# chain timeseries for each network and return each one individually
results = itertools.chain(*(_peering_group_timeseries(resources, n)
for n in resources['networks'].values()))
for result in results:
yield result