2023-10-20 09:17:47 -07:00
|
|
|
# Copyright 2023 Google LLC
|
2022-12-18 01:07:24 -08:00
|
|
|
#
|
|
|
|
# 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
|