Merge pull request #871 from maunope/maunope/network-dashboards-updates
Firewall Policy Metrics, parallel writes, aligned timestamps
This commit is contained in:
commit
61dd7f0ac8
|
@ -26,9 +26,12 @@ Clone this repository, then go through the following steps to create resources:
|
||||||
Once the resources are deployed, go to the following page to see the dashboard: https://console.cloud.google.com/monitoring/dashboards?project=<YOUR-MONITORING-PROJECT>.
|
Once the resources are deployed, go to the following page to see the dashboard: https://console.cloud.google.com/monitoring/dashboards?project=<YOUR-MONITORING-PROJECT>.
|
||||||
A dashboard called "quotas-utilization" should be created.
|
A dashboard called "quotas-utilization" should be created.
|
||||||
|
|
||||||
The Cloud Function runs every 5 minutes by default so you should start getting some data points after a few minutes.
|
The Cloud Function runs every 10 minutes by default so you should start getting some data points after a few minutes.
|
||||||
|
You can use the metric explorer to view the data points for the different custom metrics created: https://console.cloud.google.com/monitoring/metrics-explorer?project=<YOUR-MONITORING-PROJECT>.
|
||||||
You can change this frequency by modifying the "schedule_cron" variable in variables.tf.
|
You can change this frequency by modifying the "schedule_cron" variable in variables.tf.
|
||||||
|
|
||||||
|
Note that some charts in the dashboard align values over 1h so you might need to wait 1h to see charts on the dashboard views.
|
||||||
|
|
||||||
Once done testing, you can clean up resources by running `terraform destroy`.
|
Once done testing, you can clean up resources by running `terraform destroy`.
|
||||||
|
|
||||||
## Supported limits and quotas
|
## Supported limits and quotas
|
||||||
|
@ -46,6 +49,7 @@ The Cloud Function currently tracks usage, limit and utilization of:
|
||||||
- Dynamic routes per VPC peering group
|
- Dynamic routes per VPC peering group
|
||||||
- IP utilization per subnet (% of IP addresses used in a subnet)
|
- IP utilization per subnet (% of IP addresses used in a subnet)
|
||||||
- VPC firewall rules per project (VPC drill down is available for usage)
|
- VPC firewall rules per project (VPC drill down is available for usage)
|
||||||
|
- Tuples per Firewall Policy
|
||||||
|
|
||||||
It writes this values to custom metrics in Cloud Monitoring and creates a dashboard to visualize the current utilization of these metrics in Cloud Monitoring.
|
It writes this values to custom metrics in Cloud Monitoring and creates a dashboard to visualize the current utilization of these metrics in Cloud Monitoring.
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,14 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# CFv2 define whether to use Cloud function 2nd generation or 1st generation
|
# CFv2 define whether to use Cloud function 2nd generation or 1st generation
|
||||||
|
|
||||||
|
import re
|
||||||
from distutils.command.config import config
|
from distutils.command.config import config
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from google.cloud import monitoring_v3, asset_v1
|
from google.cloud import monitoring_v3, asset_v1
|
||||||
from google.protobuf import field_mask_pb2
|
from google.protobuf import field_mask_pb2
|
||||||
from googleapiclient import discovery
|
from googleapiclient import discovery
|
||||||
from metrics import ilb_fwrules, instances, networks, metrics, limits, peerings, routes, subnets, vpc_firewalls
|
from metrics import ilb_fwrules, firewall_policies, instances, networks, metrics, limits, peerings, routes, subnets, vpc_firewalls
|
||||||
|
|
||||||
CFv2 = False
|
CFv2 = False
|
||||||
if CFv2:
|
if CFv2:
|
||||||
|
@ -123,6 +124,7 @@ config = {
|
||||||
"asset_client": asset_v1.AssetServiceClient(),
|
"asset_client": asset_v1.AssetServiceClient(),
|
||||||
"monitoring_client": monitoring_v3.MetricServiceClient()
|
"monitoring_client": monitoring_v3.MetricServiceClient()
|
||||||
},
|
},
|
||||||
|
"series_buffer": []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,6 +152,7 @@ def main(event, context):
|
||||||
project_quotas_dict = limits.get_quota_project_limit(config)
|
project_quotas_dict = limits.get_quota_project_limit(config)
|
||||||
|
|
||||||
firewalls_dict = vpc_firewalls.get_firewalls_dict(config)
|
firewalls_dict = vpc_firewalls.get_firewalls_dict(config)
|
||||||
|
firewall_policies_dict = firewall_policies.get_firewall_policies_dict(config)
|
||||||
|
|
||||||
# IP utilization subnet level metrics
|
# IP utilization subnet level metrics
|
||||||
subnets.get_subnets(config, metrics_dict)
|
subnets.get_subnets(config, metrics_dict)
|
||||||
|
@ -160,10 +163,14 @@ def main(event, context):
|
||||||
l7_forwarding_rules_dict = ilb_fwrules.get_forwarding_rules_dict(config, "L7")
|
l7_forwarding_rules_dict = ilb_fwrules.get_forwarding_rules_dict(config, "L7")
|
||||||
subnet_range_dict = networks.get_subnet_ranges_dict(config)
|
subnet_range_dict = networks.get_subnet_ranges_dict(config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
# Per Project metrics
|
# Per Project metrics
|
||||||
vpc_firewalls.get_firewalls_data(config, metrics_dict, project_quotas_dict,
|
vpc_firewalls.get_firewalls_data(config, metrics_dict, project_quotas_dict,
|
||||||
firewalls_dict)
|
firewalls_dict)
|
||||||
|
# Per Firewall Policy metrics
|
||||||
|
firewall_policies.get_firewal_policies_data(config, metrics_dict,
|
||||||
|
firewall_policies_dict)
|
||||||
# Per Network metrics
|
# Per Network metrics
|
||||||
instances.get_gce_instances_data(config, metrics_dict, gce_instance_dict,
|
instances.get_gce_instances_data(config, metrics_dict, gce_instance_dict,
|
||||||
limits_dict['number_of_instances_limit'])
|
limits_dict['number_of_instances_limit'])
|
||||||
|
@ -203,8 +210,13 @@ def main(event, context):
|
||||||
config, metrics_dict["metrics_per_peering_group"]
|
config, metrics_dict["metrics_per_peering_group"]
|
||||||
["dynamic_routes_per_peering_group"], dynamic_routes_dict,
|
["dynamic_routes_per_peering_group"], dynamic_routes_dict,
|
||||||
limits_dict['dynamic_routes_per_peering_group_limit'])
|
limits_dict['dynamic_routes_per_peering_group_limit'])
|
||||||
|
except Exception as e:
|
||||||
|
print("Error writing metrics")
|
||||||
|
print(e)
|
||||||
|
finally:
|
||||||
|
metrics.flush_series_buffer(config)
|
||||||
|
|
||||||
return 'Function executed successfully'
|
return 'Function execution completed'
|
||||||
|
|
||||||
|
|
||||||
if CFv2:
|
if CFv2:
|
||||||
|
|
|
@ -172,3 +172,17 @@ metrics_per_project:
|
||||||
utilization:
|
utilization:
|
||||||
name: firewalls_per_project_utilization
|
name: firewalls_per_project_utilization
|
||||||
description: Number of VPC firewall rules in a project - utilization.
|
description: Number of VPC firewall rules in a project - utilization.
|
||||||
|
metrics_per_firewall_policy:
|
||||||
|
firewall_policy_tuples:
|
||||||
|
usage:
|
||||||
|
name: firewall_policy_tuples_per_policy_usage
|
||||||
|
description: Number of tuples in a firewall policy - usage.
|
||||||
|
limit:
|
||||||
|
# This limit is not visibile through Google APIs, set default_value
|
||||||
|
name: firewall_policy_tuples_per_policy_limit
|
||||||
|
description: Number of tuples in a firewall policy - limit.
|
||||||
|
values:
|
||||||
|
default_value: 2000
|
||||||
|
utilization:
|
||||||
|
name: firewall_policy_tuples_per_policy_utilization
|
||||||
|
description: Number of tuples in a firewall policy - utilization.
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
#
|
||||||
|
# Copyright 2022 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from pydoc import doc
|
||||||
|
from collections import defaultdict
|
||||||
|
from google.protobuf import field_mask_pb2
|
||||||
|
from . import metrics, networks, limits
|
||||||
|
|
||||||
|
|
||||||
|
def get_firewall_policies_dict(config: dict):
|
||||||
|
'''
|
||||||
|
Calls the Asset Inventory API to get all Firewall Policies under the GCP organization
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
config (dict): The dict containing config like clients and limits
|
||||||
|
Returns:
|
||||||
|
firewal_policies_dict (dictionary of dictionary): Keys are policy ids, subkeys are policy field values
|
||||||
|
'''
|
||||||
|
|
||||||
|
firewall_policies_dict = defaultdict(int)
|
||||||
|
read_mask = field_mask_pb2.FieldMask()
|
||||||
|
read_mask.FromJsonString('name,versionedResources')
|
||||||
|
|
||||||
|
response = config["clients"]["asset_client"].search_all_resources(
|
||||||
|
request={
|
||||||
|
"scope": f"organizations/{config['organization']}",
|
||||||
|
"asset_types": ["compute.googleapis.com/FirewallPolicy"],
|
||||||
|
"read_mask": read_mask,
|
||||||
|
})
|
||||||
|
for resource in response:
|
||||||
|
for versioned in resource.versioned_resources:
|
||||||
|
firewall_policy = dict()
|
||||||
|
for field_name, field_value in versioned.resource.items():
|
||||||
|
firewall_policy[field_name] = field_value
|
||||||
|
firewall_policies_dict[firewall_policy['id']] = firewall_policy
|
||||||
|
return firewall_policies_dict
|
||||||
|
|
||||||
|
|
||||||
|
def get_firewal_policies_data(config, metrics_dict, firewall_policies_dict):
|
||||||
|
'''
|
||||||
|
Gets the data for VPC Firewall lorem ipsum
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
config (dict): The dict containing config like clients and limits
|
||||||
|
metrics_dict (dictionary of dictionary of string: string): metrics names and descriptions.
|
||||||
|
firewall_policies_dict (dictionary of of dictionary of string: string): Keys are policies ids, subkeys are policies values
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
'''
|
||||||
|
|
||||||
|
current_tuples_limit = None
|
||||||
|
try:
|
||||||
|
current_tuples_limit = metrics_dict["metrics_per_firewall_policy"][
|
||||||
|
"firewall_policy_tuples"]["limit"]["values"]["default_value"]
|
||||||
|
except Exception:
|
||||||
|
print(
|
||||||
|
f"Could not determine number of tuples metric limit due to missing default value"
|
||||||
|
)
|
||||||
|
if current_tuples_limit < 0:
|
||||||
|
print(
|
||||||
|
f"Could not determine number of tuples metric limit as default value is <= 0"
|
||||||
|
)
|
||||||
|
|
||||||
|
timestamp = time.time()
|
||||||
|
for firewall_policy_key in firewall_policies_dict:
|
||||||
|
firewall_policy = firewall_policies_dict[firewall_policy_key]
|
||||||
|
|
||||||
|
# may either be a org, a folder, or a project
|
||||||
|
# folder and org require to split {folder,organization}\/\w+
|
||||||
|
parent = re.search("(\w+$)", firewall_policy["parent"]).group(
|
||||||
|
1) if "parent" in firewall_policy else re.search(
|
||||||
|
"([\d,a-z,-]+)(\/[\d,a-z,-]+\/firewallPolicies/[\d,a-z,-]*$)",
|
||||||
|
firewall_policy["selfLink"]).group(1)
|
||||||
|
parent_type = re.search("(^\w+)", firewall_policy["parent"]).group(
|
||||||
|
1) if "parent" in firewall_policy else "projects"
|
||||||
|
|
||||||
|
metric_labels = {'parent': parent, 'parent_type': parent_type}
|
||||||
|
|
||||||
|
metric_labels["name"] = firewall_policy[
|
||||||
|
"displayName"] if "displayName" in firewall_policy else firewall_policy[
|
||||||
|
"name"]
|
||||||
|
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_firewall_policy"]
|
||||||
|
[f"firewall_policy_tuples"]["usage"]["name"],
|
||||||
|
firewall_policy['ruleTupleCount'], metric_labels, timestamp=timestamp)
|
||||||
|
if not current_tuples_limit == None and current_tuples_limit > 0:
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_firewall_policy"]
|
||||||
|
[f"firewall_policy_tuples"]["limit"]["name"], current_tuples_limit,
|
||||||
|
metric_labels, timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_firewall_policy"]
|
||||||
|
[f"firewall_policy_tuples"]["utilization"]["name"],
|
||||||
|
firewall_policy['ruleTupleCount'] / current_tuples_limit,
|
||||||
|
metric_labels, timestamp=timestamp)
|
||||||
|
|
||||||
|
print(f"Buffered number tuples per Firewall Policy")
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from google.protobuf import field_mask_pb2
|
from google.protobuf import field_mask_pb2
|
||||||
from . import metrics, networks, limits
|
from . import metrics, networks, limits
|
||||||
|
@ -75,15 +77,17 @@ def get_forwarding_rules_data(config, metrics_dict, forwarding_rules_dict,
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
'''
|
'''
|
||||||
for project in config["monitored_projects"]:
|
|
||||||
network_dict = networks.get_networks(config, project)
|
timestamp = time.time()
|
||||||
|
for project_id in config["monitored_projects"]:
|
||||||
|
network_dict = networks.get_networks(config, project_id)
|
||||||
|
|
||||||
current_quota_limit = limits.get_quota_current_limit(
|
current_quota_limit = limits.get_quota_current_limit(
|
||||||
config, f"projects/{project}", config["limit_names"][layer])
|
config, f"projects/{project_id}", config["limit_names"][layer])
|
||||||
|
|
||||||
if current_quota_limit is None:
|
if current_quota_limit is None:
|
||||||
print(
|
print(
|
||||||
f"Could not write {layer} forwarding rules to metric for projects/{project} due to missing quotas"
|
f"Could not determine {layer} forwarding rules to metric for projects/{project_id} due to missing quotas"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -95,20 +99,24 @@ def get_forwarding_rules_data(config, metrics_dict, forwarding_rules_dict,
|
||||||
usage = 0
|
usage = 0
|
||||||
if net['self_link'] in forwarding_rules_dict:
|
if net['self_link'] in forwarding_rules_dict:
|
||||||
usage = forwarding_rules_dict[net['self_link']]
|
usage = forwarding_rules_dict[net['self_link']]
|
||||||
metrics.write_data_to_metric(
|
|
||||||
config, project, usage, metrics_dict["metrics_per_network"]
|
metric_labels = {
|
||||||
|
'project': project_id,
|
||||||
|
'network_name': net['network_name']
|
||||||
|
}
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]
|
||||||
[f"{layer.lower()}_forwarding_rules_per_network"]["usage"]["name"],
|
[f"{layer.lower()}_forwarding_rules_per_network"]["usage"]["name"],
|
||||||
net['network_name'])
|
usage, metric_labels, timestamp=timestamp)
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, net['limit'], metrics_dict["metrics_per_network"]
|
config, metrics_dict["metrics_per_network"]
|
||||||
[f"{layer.lower()}_forwarding_rules_per_network"]["limit"]["name"],
|
[f"{layer.lower()}_forwarding_rules_per_network"]["limit"]["name"],
|
||||||
net['network_name'])
|
net['limit'], metric_labels, timestamp=timestamp)
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, usage / net['limit'],
|
config, metrics_dict["metrics_per_network"]
|
||||||
metrics_dict["metrics_per_network"]
|
|
||||||
[f"{layer.lower()}_forwarding_rules_per_network"]["utilization"]
|
[f"{layer.lower()}_forwarding_rules_per_network"]["utilization"]
|
||||||
["name"], net['network_name'])
|
["name"], usage / net['limit'], metric_labels, timestamp=timestamp)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"Wrote number of {layer} forwarding rules to metric for projects/{project}"
|
f"Buffered number of {layer} forwarding rules to metric for projects/{project_id}"
|
||||||
)
|
)
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from code import interact
|
from code import interact
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from . import metrics, networks, limits
|
from . import metrics, networks, limits
|
||||||
|
@ -61,15 +63,16 @@ def get_gce_instances_data(config, metrics_dict, gce_instance_dict, limit_dict):
|
||||||
Returns:
|
Returns:
|
||||||
gce_instance_dict
|
gce_instance_dict
|
||||||
'''
|
'''
|
||||||
|
timestamp = time.time()
|
||||||
for project in config["monitored_projects"]:
|
for project_id in config["monitored_projects"]:
|
||||||
network_dict = networks.get_networks(config, project)
|
network_dict = networks.get_networks(config, project_id)
|
||||||
|
|
||||||
current_quota_limit = limits.get_quota_current_limit(
|
current_quota_limit = limits.get_quota_current_limit(
|
||||||
config, f"projects/{project}", config["limit_names"]["GCE_INSTANCES"])
|
config, f"projects/{project_id}",
|
||||||
|
config["limit_names"]["GCE_INSTANCES"])
|
||||||
if current_quota_limit is None:
|
if current_quota_limit is None:
|
||||||
print(
|
print(
|
||||||
f"Could not write number of instances for projects/{project} due to missing quotas"
|
f"Could not determine number of instances for projects/{project_id} due to missing quotas"
|
||||||
)
|
)
|
||||||
|
|
||||||
current_quota_limit_view = metrics.customize_quota_view(current_quota_limit)
|
current_quota_limit_view = metrics.customize_quota_view(current_quota_limit)
|
||||||
|
@ -81,15 +84,19 @@ def get_gce_instances_data(config, metrics_dict, gce_instance_dict, limit_dict):
|
||||||
if net['self_link'] in gce_instance_dict:
|
if net['self_link'] in gce_instance_dict:
|
||||||
usage = gce_instance_dict[net['self_link']]
|
usage = gce_instance_dict[net['self_link']]
|
||||||
|
|
||||||
metrics.write_data_to_metric(
|
metric_labels = {
|
||||||
config, project, usage, metrics_dict["metrics_per_network"]
|
'project': project_id,
|
||||||
["instance_per_network"]["usage"]["name"], net['network_name'])
|
'network_name': net['network_name']
|
||||||
metrics.write_data_to_metric(
|
}
|
||||||
config, project, net['limit'], metrics_dict["metrics_per_network"]
|
metrics.append_data_to_series_buffer(
|
||||||
["instance_per_network"]["limit"]["name"], net['network_name'])
|
config, metrics_dict["metrics_per_network"]["instance_per_network"]
|
||||||
metrics.write_data_to_metric(
|
["usage"]["name"], usage, metric_labels, timestamp=timestamp)
|
||||||
config, project, usage / net['limit'],
|
metrics.append_data_to_series_buffer(
|
||||||
metrics_dict["metrics_per_network"]["instance_per_network"]
|
config, metrics_dict["metrics_per_network"]["instance_per_network"]
|
||||||
["utilization"]["name"], net['network_name'])
|
["limit"]["name"], net['limit'], metric_labels, timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]["instance_per_network"]
|
||||||
|
["utilization"]["name"], usage / net['limit'], metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
|
||||||
print(f"Wrote number of instances to metric for projects/{project}")
|
print(f"Buffered number of instances to metric for projects/{project_id}")
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from google.api_core import exceptions
|
from google.api_core import exceptions
|
||||||
from google.cloud import monitoring_v3
|
from google.cloud import monitoring_v3
|
||||||
from . import metrics
|
from . import metrics
|
||||||
|
@ -173,6 +175,8 @@ def count_effective_limit(config, project_id, network_dict, usage_metric_name,
|
||||||
None
|
None
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
timestamp = time.time()
|
||||||
|
|
||||||
if network_dict['peerings'] == []:
|
if network_dict['peerings'] == []:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -215,11 +219,16 @@ def count_effective_limit(config, project_id, network_dict, usage_metric_name,
|
||||||
# Calculates effective limit: Step 4: Find maximum from step 1 and step 3
|
# Calculates effective limit: Step 4: Find maximum from step 1 and step 3
|
||||||
effective_limit = max(limit_step1, limit_step3)
|
effective_limit = max(limit_step1, limit_step3)
|
||||||
utilization = peering_group_usage / effective_limit
|
utilization = peering_group_usage / effective_limit
|
||||||
|
metric_labels = {
|
||||||
metrics.write_data_to_metric(config, project_id, peering_group_usage,
|
'project': project_id,
|
||||||
usage_metric_name, network_dict['network_name'])
|
'network_name': network_dict['network_name']
|
||||||
metrics.write_data_to_metric(config, project_id, effective_limit,
|
}
|
||||||
limit_metric_name, network_dict['network_name'])
|
metrics.append_data_to_series_buffer(config, usage_metric_name,
|
||||||
metrics.write_data_to_metric(config, project_id, utilization,
|
peering_group_usage, metric_labels,
|
||||||
utilization_metric_name,
|
timestamp=timestamp)
|
||||||
network_dict['network_name'])
|
metrics.append_data_to_series_buffer(config, limit_metric_name,
|
||||||
|
effective_limit, metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(config, utilization_metric_name,
|
||||||
|
utilization, metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
|
|
@ -14,12 +14,16 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from curses import KEY_MARK
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
from google.api import metric_pb2 as ga_metric
|
from google.api import metric_pb2 as ga_metric
|
||||||
from google.cloud import monitoring_v3
|
from google.cloud import monitoring_v3
|
||||||
from . import peerings, limits, networks
|
from . import peerings, limits, networks
|
||||||
|
|
||||||
|
BUFFER_LEN = 10
|
||||||
|
|
||||||
|
|
||||||
def create_metrics(monitoring_project):
|
def create_metrics(monitoring_project):
|
||||||
'''
|
'''
|
||||||
|
@ -84,35 +88,32 @@ def create_metric(metric_name, description, monitoring_project):
|
||||||
print("Created {}.".format(descriptor.name))
|
print("Created {}.".format(descriptor.name))
|
||||||
|
|
||||||
|
|
||||||
def write_data_to_metric(config, monitored_project_id, value, metric_name,
|
def append_data_to_series_buffer(config, metric_name, metric_value,
|
||||||
network_name=None, subnet_id=None):
|
metric_labels, timestamp=None):
|
||||||
'''
|
'''
|
||||||
Writes data to Cloud Monitoring custom metrics.
|
Writes data to Cloud Monitoring custom metrics.
|
||||||
Parameters:
|
Parameters:
|
||||||
config (dict): The dict containing config like clients and limits
|
config (dict): The dict containing config like clients and limits
|
||||||
monitored_project_id: ID of the project where the resource lives (will be added as a label)
|
|
||||||
value (int): Value for the data point of the metric.
|
|
||||||
metric_name (string): Name of the metric
|
metric_name (string): Name of the metric
|
||||||
network_name (string): Name of the network (will be added as a label)
|
metric_value (int): Value for the data point of the metric.
|
||||||
subnet_id (string): Identifier of the Subnet (region/name of the subnet)
|
matric_labels (dictionary of dictionary of string: string): metric labels names and values
|
||||||
|
timestamp (float): seconds since the epoch, in UTC
|
||||||
Returns:
|
Returns:
|
||||||
usage (int): Current usage for that network.
|
usage (int): Current usage for that network.
|
||||||
limit (int): Current usage for that network.
|
limit (int): Current usage for that network.
|
||||||
'''
|
'''
|
||||||
client = monitoring_v3.MetricServiceClient()
|
|
||||||
|
|
||||||
series = monitoring_v3.TimeSeries()
|
series = monitoring_v3.TimeSeries()
|
||||||
series.metric.type = f"custom.googleapis.com/{metric_name}"
|
series.metric.type = f"custom.googleapis.com/{metric_name}"
|
||||||
series.resource.type = "global"
|
series.resource.type = "global"
|
||||||
series.metric.labels["project"] = monitored_project_id
|
|
||||||
if network_name != None:
|
|
||||||
series.metric.labels["network_name"] = network_name
|
|
||||||
if subnet_id != None:
|
|
||||||
series.metric.labels["subnet_id"] = subnet_id
|
|
||||||
|
|
||||||
now = time.time()
|
for label_name in metric_labels:
|
||||||
seconds = int(now)
|
if (metric_labels[label_name] != None):
|
||||||
nanos = int((now - seconds) * 10**9)
|
series.metric.labels[label_name] = metric_labels[label_name]
|
||||||
|
|
||||||
|
timestamp = timestamp if timestamp != None else time.time()
|
||||||
|
seconds = int(timestamp)
|
||||||
|
nanos = int((timestamp - seconds) * 10**9)
|
||||||
interval = monitoring_v3.TimeInterval(
|
interval = monitoring_v3.TimeInterval(
|
||||||
{"end_time": {
|
{"end_time": {
|
||||||
"seconds": seconds,
|
"seconds": seconds,
|
||||||
|
@ -121,20 +122,39 @@ def write_data_to_metric(config, monitored_project_id, value, metric_name,
|
||||||
point = monitoring_v3.Point({
|
point = monitoring_v3.Point({
|
||||||
"interval": interval,
|
"interval": interval,
|
||||||
"value": {
|
"value": {
|
||||||
"double_value": value
|
"double_value": metric_value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
series.points = [point]
|
series.points = [point]
|
||||||
|
|
||||||
# TODO: sometimes this cashes with 'DeadlineExceeded: 504 Deadline expired before operation could complete' error
|
# TODO: sometimes this cashes with 'DeadlineExceeded: 504 Deadline expired before operation could complete' error
|
||||||
# Implement exponential backoff retries?
|
# Implement exponential backoff retries?
|
||||||
|
config["series_buffer"].append(series)
|
||||||
|
if len(config["series_buffer"]) >= BUFFER_LEN:
|
||||||
|
flush_series_buffer(config)
|
||||||
|
|
||||||
|
|
||||||
|
def flush_series_buffer(config):
|
||||||
|
'''
|
||||||
|
writes buffered metrics to Google Cloud Monitoring, empties buffer upon failure
|
||||||
|
config (dict): The dict containing config like clients and limits
|
||||||
|
'''
|
||||||
try:
|
try:
|
||||||
|
if config["series_buffer"] and len(config["series_buffer"]) > 0:
|
||||||
|
client = monitoring_v3.MetricServiceClient()
|
||||||
client.create_time_series(name=config["monitoring_project_link"],
|
client.create_time_series(name=config["monitoring_project_link"],
|
||||||
time_series=[series])
|
time_series=config["series_buffer"])
|
||||||
|
series_names = [
|
||||||
|
re.search("\/(.+$)", series.metric.type).group(1)
|
||||||
|
for series in config["series_buffer"]
|
||||||
|
]
|
||||||
|
print("Wrote time series: ", series_names)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error while writing data point for metric", metric_name)
|
print("Error while flushing series buffer")
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
config["series_buffer"] = []
|
||||||
|
|
||||||
|
|
||||||
def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
||||||
'''
|
'''
|
||||||
|
@ -148,18 +168,18 @@ def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
'''
|
'''
|
||||||
for project in config["monitored_projects"]:
|
for project_id in config["monitored_projects"]:
|
||||||
network_dict_list = peerings.gather_peering_data(config, project)
|
network_dict_list = peerings.gather_peering_data(config, project_id)
|
||||||
# Network dict list is a list of dictionary (one for each network)
|
# Network dict list is a list of dictionary (one for each network)
|
||||||
# For each network, this dictionary contains:
|
# For each network, this dictionary contains:
|
||||||
# project_id, network_name, network_id, usage, limit, peerings (list of peered networks)
|
# project_id, network_name, network_id, usage, limit, peerings (list of peered networks)
|
||||||
# peerings is a list of dictionary (one for each peered network) and contains:
|
# peerings is a list of dictionary (one for each peered network) and contains:
|
||||||
# project_id, network_name, network_id
|
# project_id, network_name, network_id
|
||||||
current_quota_limit = limits.get_quota_current_limit(
|
current_quota_limit = limits.get_quota_current_limit(
|
||||||
config, f"projects/{project}", limit_metric)
|
config, f"projects/{project_id}", limit_metric)
|
||||||
if current_quota_limit is None:
|
if current_quota_limit is None:
|
||||||
print(
|
print(
|
||||||
f"Could not write number of L7 forwarding rules to metric for projects/{project} due to missing quotas"
|
f"Could not determine number of L7 forwarding rules to metric for projects/{project_id} due to missing quotas"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -169,10 +189,10 @@ def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
||||||
for network_dict in network_dict_list:
|
for network_dict in network_dict_list:
|
||||||
if network_dict['network_id'] == 0:
|
if network_dict['network_id'] == 0:
|
||||||
print(
|
print(
|
||||||
f"Could not write {metric_dict['usage']['name']} for peering group {network_dict['network_name']} in {project} due to missing permissions."
|
f"Could not determine {metric_dict['usage']['name']} for peering group {network_dict['network_name']} in {project_id} due to missing permissions."
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
network_link = f"https://www.googleapis.com/compute/v1/projects/{project}/global/networks/{network_dict['network_name']}"
|
network_link = f"https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network_dict['network_name']}"
|
||||||
|
|
||||||
limit = networks.get_limit_network(network_dict, network_link,
|
limit = networks.get_limit_network(network_dict, network_link,
|
||||||
current_quota_limit_view, limit_dict)
|
current_quota_limit_view, limit_dict)
|
||||||
|
@ -197,7 +217,7 @@ def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
||||||
limit_metric)
|
limit_metric)
|
||||||
if current_peered_quota_limit is None:
|
if current_peered_quota_limit is None:
|
||||||
print(
|
print(
|
||||||
f"Could not write metrics for peering to projects/{peered_network_dict['project_id']} due to missing quotas"
|
f"Could not determine metrics for peering to projects/{peered_network_dict['project_id']} due to missing quotas"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -211,13 +231,13 @@ def get_pgg_data(config, metric_dict, usage_dict, limit_metric, limit_dict):
|
||||||
peered_network_dict["usage"] = peered_usage
|
peered_network_dict["usage"] = peered_usage
|
||||||
peered_network_dict["limit"] = peered_limit
|
peered_network_dict["limit"] = peered_limit
|
||||||
|
|
||||||
limits.count_effective_limit(config, project, network_dict,
|
limits.count_effective_limit(config, project_id, network_dict,
|
||||||
metric_dict["usage"]["name"],
|
metric_dict["usage"]["name"],
|
||||||
metric_dict["limit"]["name"],
|
metric_dict["limit"]["name"],
|
||||||
metric_dict["utilization"]["name"],
|
metric_dict["utilization"]["name"],
|
||||||
limit_dict)
|
limit_dict)
|
||||||
print(
|
print(
|
||||||
f"Wrote {metric_dict['usage']['name']} for peering group {network_dict['network_name']} in {project}"
|
f"Buffered {metric_dict['usage']['name']} for peering group {network_dict['network_name']} in {project_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from . import metrics, networks, limits
|
from . import metrics, networks, limits
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,40 +30,53 @@ def get_vpc_peering_data(config, metrics_dict, limit_dict):
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
'''
|
'''
|
||||||
|
timestamp = time.time()
|
||||||
for project in config["monitored_projects"]:
|
for project in config["monitored_projects"]:
|
||||||
active_vpc_peerings, vpc_peerings = gather_vpc_peerings_data(
|
active_vpc_peerings, vpc_peerings = gather_vpc_peerings_data(
|
||||||
config, project, limit_dict)
|
config, project, limit_dict)
|
||||||
|
|
||||||
for peering in active_vpc_peerings:
|
for peering in active_vpc_peerings:
|
||||||
metrics.write_data_to_metric(
|
metric_labels = {
|
||||||
config, project, peering['active_peerings'],
|
'project': project,
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_active_per_network"]
|
'network_name': peering['network_name']
|
||||||
["usage"]["name"], peering['network_name'])
|
}
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, peering['network_limit'],
|
config, metrics_dict["metrics_per_network"]
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_active_per_network"]
|
["vpc_peering_active_per_network"]["usage"]["name"],
|
||||||
["limit"]["name"], peering['network_name'])
|
peering['active_peerings'], metric_labels, timestamp=timestamp)
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project,
|
config, metrics_dict["metrics_per_network"]
|
||||||
peering['active_peerings'] / peering['network_limit'],
|
["vpc_peering_active_per_network"]["limit"]["name"],
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_active_per_network"]
|
peering['network_limit'], metric_labels, timestamp=timestamp)
|
||||||
["utilization"]["name"], peering['network_name'])
|
metrics.append_data_to_series_buffer(
|
||||||
print("Wrote number of active VPC peerings to custom metric for project:",
|
config, metrics_dict["metrics_per_network"]
|
||||||
|
["vpc_peering_active_per_network"]["utilization"]["name"],
|
||||||
|
peering['active_peerings'] / peering['network_limit'], metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
print(
|
||||||
|
"Buffered number of active VPC peerings to custom metric for project:",
|
||||||
project)
|
project)
|
||||||
|
|
||||||
for peering in vpc_peerings:
|
for peering in vpc_peerings:
|
||||||
metrics.write_data_to_metric(
|
metric_labels = {
|
||||||
config, project, peering['peerings'],
|
'project': project,
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
'network_name': peering['network_name']
|
||||||
["usage"]["name"], peering['network_name'])
|
}
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, peering['network_limit'],
|
config, metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
["usage"]["name"], peering['peerings'], metric_labels,
|
||||||
["limit"]["name"], peering['network_name'])
|
timestamp=timestamp)
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, peering['peerings'] / peering['network_limit'],
|
config, metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
||||||
metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
["limit"]["name"], peering['network_limit'], metric_labels,
|
||||||
["utilization"]["name"], peering['network_name'])
|
timestamp=timestamp)
|
||||||
print("Wrote number of VPC peerings to custom metric for project:", project)
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]["vpc_peering_per_network"]
|
||||||
|
["utilization"]["name"],
|
||||||
|
peering['peerings'] / peering['network_limit'], metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
print("Buffered number of VPC peerings to custom metric for project:",
|
||||||
|
project)
|
||||||
|
|
||||||
|
|
||||||
def gather_peering_data(config, project_id):
|
def gather_peering_data(config, project_id):
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from . import metrics, networks, limits, peerings, routers
|
from . import metrics, networks, limits, peerings, routers
|
||||||
|
|
||||||
|
@ -88,16 +90,17 @@ def get_dynamic_routes(config, metrics_dict, limits_dict):
|
||||||
routers_dict = routers.get_routers(config)
|
routers_dict = routers.get_routers(config)
|
||||||
dynamic_routes_dict = defaultdict(int)
|
dynamic_routes_dict = defaultdict(int)
|
||||||
|
|
||||||
for project_id in config["monitored_projects"]:
|
timestamp = time.time()
|
||||||
network_dict = networks.get_networks(config, project_id)
|
for project in config["monitored_projects"]:
|
||||||
|
network_dict = networks.get_networks(config, project)
|
||||||
|
|
||||||
for network in network_dict:
|
for net in network_dict:
|
||||||
sum_routes = get_routes_for_network(config, network['self_link'],
|
sum_routes = get_routes_for_network(config, net['self_link'], project,
|
||||||
project_id, routers_dict)
|
routers_dict)
|
||||||
dynamic_routes_dict[network['self_link']] = sum_routes
|
dynamic_routes_dict[net['self_link']] = sum_routes
|
||||||
|
|
||||||
if network['self_link'] in limits_dict:
|
if net['self_link'] in limits_dict:
|
||||||
limit = limits_dict[network['self_link']]
|
limit = limits_dict[net['self_link']]
|
||||||
else:
|
else:
|
||||||
if 'default_value' in limits_dict:
|
if 'default_value' in limits_dict:
|
||||||
limit = limits_dict['default_value']
|
limit = limits_dict['default_value']
|
||||||
|
@ -106,21 +109,21 @@ def get_dynamic_routes(config, metrics_dict, limits_dict):
|
||||||
break
|
break
|
||||||
|
|
||||||
utilization = sum_routes / limit
|
utilization = sum_routes / limit
|
||||||
|
metric_labels = {'project': project, 'network_name': net['network_name']}
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]
|
||||||
|
["dynamic_routes_per_network"]["usage"]["name"], sum_routes,
|
||||||
|
metric_labels, timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]
|
||||||
|
["dynamic_routes_per_network"]["limit"]["name"], limit, metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_network"]
|
||||||
|
["dynamic_routes_per_network"]["utilization"]["name"], utilization,
|
||||||
|
metric_labels, timestamp=timestamp)
|
||||||
|
|
||||||
metrics.write_data_to_metric(
|
print("Buffered metrics for dynamic routes for VPCs in project", project)
|
||||||
config, project_id, sum_routes, metrics_dict["metrics_per_network"]
|
|
||||||
["dynamic_routes_per_network"]["usage"]["name"],
|
|
||||||
network['network_name'])
|
|
||||||
metrics.write_data_to_metric(
|
|
||||||
config, project_id, limit, metrics_dict["metrics_per_network"]
|
|
||||||
["dynamic_routes_per_network"]["limit"]["name"],
|
|
||||||
network['network_name'])
|
|
||||||
metrics.write_data_to_metric(
|
|
||||||
config, project_id, utilization, metrics_dict["metrics_per_network"]
|
|
||||||
["dynamic_routes_per_network"]["utilization"]["name"],
|
|
||||||
network['network_name'])
|
|
||||||
|
|
||||||
print("Wrote metrics for dynamic routes for VPCs in project", project_id)
|
|
||||||
|
|
||||||
return dynamic_routes_dict
|
return dynamic_routes_dict
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from . import metrics
|
from . import metrics
|
||||||
from google.protobuf import field_mask_pb2
|
from google.protobuf import field_mask_pb2
|
||||||
from google.protobuf.json_format import MessageToDict
|
from google.protobuf.json_format import MessageToDict
|
||||||
|
@ -225,6 +227,7 @@ def get_subnets(config, metrics_dict):
|
||||||
# Updates all_subnets_dict with the IP utilization info
|
# Updates all_subnets_dict with the IP utilization info
|
||||||
compute_subnet_utilization(config, all_subnets_dict)
|
compute_subnet_utilization(config, all_subnets_dict)
|
||||||
|
|
||||||
|
timestamp = time.time()
|
||||||
for project_id in config["monitored_projects"]:
|
for project_id in config["monitored_projects"]:
|
||||||
if project_id not in all_subnets_dict:
|
if project_id not in all_subnets_dict:
|
||||||
continue
|
continue
|
||||||
|
@ -236,18 +239,23 @@ def get_subnets(config, metrics_dict):
|
||||||
|
|
||||||
# Building unique identifier with subnet region/name
|
# Building unique identifier with subnet region/name
|
||||||
subnet_id = f"{subnet_dict['region']}/{subnet_dict['name']}"
|
subnet_id = f"{subnet_dict['region']}/{subnet_dict['name']}"
|
||||||
metrics.write_data_to_metric(
|
metric_labels = {
|
||||||
config, project_id, subnet_dict['used_ip_addresses'],
|
'project': project_id,
|
||||||
metrics_dict["metrics_per_subnet"]["ip_usage_per_subnet"]["usage"]
|
'network_name': subnet_dict['network_name'],
|
||||||
["name"], subnet_dict['network_name'], subnet_id)
|
'subnet_id': subnet_id
|
||||||
metrics.write_data_to_metric(
|
}
|
||||||
config, project_id, subnet_dict['total_ip_addresses'],
|
metrics.append_data_to_series_buffer(
|
||||||
metrics_dict["metrics_per_subnet"]["ip_usage_per_subnet"]["limit"]
|
config, metrics_dict["metrics_per_subnet"]["ip_usage_per_subnet"]
|
||||||
["name"], subnet_dict['network_name'], subnet_id)
|
["usage"]["name"], subnet_dict['used_ip_addresses'], metric_labels,
|
||||||
metrics.write_data_to_metric(
|
timestamp=timestamp)
|
||||||
config, project_id, ip_utilization, metrics_dict["metrics_per_subnet"]
|
metrics.append_data_to_series_buffer(
|
||||||
["ip_usage_per_subnet"]["utilization"]["name"],
|
config, metrics_dict["metrics_per_subnet"]["ip_usage_per_subnet"]
|
||||||
subnet_dict['network_name'], subnet_id)
|
["limit"]["name"], subnet_dict['total_ip_addresses'], metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config, metrics_dict["metrics_per_subnet"]["ip_usage_per_subnet"]
|
||||||
|
["utilization"]["name"], ip_utilization, metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
|
|
||||||
print("Wrote metrics for subnet ip utilization for VPCs in project",
|
print("Buffered metrics for subnet ip utilization for VPCs in project",
|
||||||
project_id)
|
project_id)
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pydoc import doc
|
from pydoc import doc
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from google.protobuf import field_mask_pb2
|
from google.protobuf import field_mask_pb2
|
||||||
from . import metrics, networks, limits, peerings, routers
|
from . import metrics, networks, limits
|
||||||
|
|
||||||
|
|
||||||
def get_firewalls_dict(config: dict):
|
def get_firewalls_dict(config: dict):
|
||||||
|
@ -69,43 +71,51 @@ def get_firewalls_data(config, metrics_dict, project_quotas_dict,
|
||||||
Parameters:
|
Parameters:
|
||||||
config (dict): The dict containing config like clients and limits
|
config (dict): The dict containing config like clients and limits
|
||||||
metrics_dict (dictionary of dictionary of string: string): metrics names and descriptions.
|
metrics_dict (dictionary of dictionary of string: string): metrics names and descriptions.
|
||||||
limit_dict (dictionary of string:int): Dictionary with the network link as key and the limit as value.
|
project_quotas_dict (dictionary of string:int): Dictionary with the network link as key and the limit as value.
|
||||||
firewalls_dict (dictionary of dictionary): Keys are projects, subkeys are networks, values count #of VPC Firewall Rules
|
firewalls_dict (dictionary of of dictionary of string: string): Keys are projects, subkeys are networks, values count #of VPC Firewall Rules
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
'''
|
'''
|
||||||
for project in config["monitored_projects"]:
|
|
||||||
|
|
||||||
current_quota_limit = project_quotas_dict[project]['global']["firewalls"]
|
timestamp = time.time()
|
||||||
|
for project_id in config["monitored_projects"]:
|
||||||
|
|
||||||
|
current_quota_limit = project_quotas_dict[project_id]['global']["firewalls"]
|
||||||
if current_quota_limit is None:
|
if current_quota_limit is None:
|
||||||
print(
|
print(
|
||||||
f"Could not write VPC firewal rules to metric for projects/{project} due to missing quotas"
|
f"Could not determine VPC firewal rules to metric for projects/{project_id} due to missing quotas"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
network_dict = networks.get_networks(config, project)
|
network_dict = networks.get_networks(config, project_id)
|
||||||
|
|
||||||
project_usage = 0
|
project_usage = 0
|
||||||
for net in network_dict:
|
for net in network_dict:
|
||||||
usage = 0
|
usage = 0
|
||||||
if project in firewalls_dict and net['network_name'] in firewalls_dict[
|
if project_id in firewalls_dict and net['network_name'] in firewalls_dict[
|
||||||
project]:
|
project_id]:
|
||||||
usage = firewalls_dict[project][net['network_name']]
|
usage = firewalls_dict[project_id][net['network_name']]
|
||||||
project_usage += usage
|
project_usage += usage
|
||||||
metrics.write_data_to_metric(
|
metric_labels = {
|
||||||
config, project, usage,
|
'project': project_id,
|
||||||
|
'network_name': net['network_name']
|
||||||
|
}
|
||||||
|
metrics.append_data_to_series_buffer(
|
||||||
|
config,
|
||||||
metrics_dict["metrics_per_project"][f"firewalls"]["usage"]["name"],
|
metrics_dict["metrics_per_project"][f"firewalls"]["usage"]["name"],
|
||||||
net['network_name'])
|
usage, metric_labels, timestamp=timestamp)
|
||||||
|
|
||||||
|
metric_labels = {'project': project_id}
|
||||||
# firewall quotas are per project, not per single VPC
|
# firewall quotas are per project, not per single VPC
|
||||||
metrics.write_data_to_metric(
|
metrics.append_data_to_series_buffer(
|
||||||
config, project, current_quota_limit['limit'],
|
config,
|
||||||
metrics_dict["metrics_per_project"][f"firewalls"]["limit"]["name"])
|
metrics_dict["metrics_per_project"][f"firewalls"]["limit"]["name"],
|
||||||
metrics.write_data_to_metric(
|
current_quota_limit['limit'], metric_labels, timestamp=timestamp)
|
||||||
config, project, project_usage / current_quota_limit['limit']
|
metrics.append_data_to_series_buffer(
|
||||||
if current_quota_limit['limit'] != 0 else 0,
|
config, metrics_dict["metrics_per_project"][f"firewalls"]["utilization"]
|
||||||
metrics_dict["metrics_per_project"][f"firewalls"]["utilization"]
|
["name"], project_usage / current_quota_limit['limit']
|
||||||
["name"])
|
if current_quota_limit['limit'] != 0 else 0, metric_labels,
|
||||||
|
timestamp=timestamp)
|
||||||
print(
|
print(
|
||||||
f"Wrote number of VPC Firewall Rules to metric for projects/{project}")
|
f"Buffered number of VPC Firewall Rules to metric for projects/{project_id}"
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"category": "CUSTOM",
|
"category": "CUSTOM",
|
||||||
"displayName": "quotas_utilization",
|
"displayName": "quotas_utilization_updated",
|
||||||
"mosaicLayout": {
|
"mosaicLayout": {
|
||||||
"columns": 12,
|
"columns": 12,
|
||||||
"tiles": [
|
"tiles": [
|
||||||
|
@ -22,11 +22,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l4_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l4_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "1800s",
|
"alignmentPeriod": "1800s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,11 +64,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l7_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l7_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,11 +106,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/number_of_instances_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/number_of_instances_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,11 +148,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/number_of_vpc_peerings_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/number_of_vpc_peerings_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,11 +190,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/number_of_active_vpc_peerings_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/number_of_active_vpc_peerings_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_INTERPOLATE"
|
"perSeriesAligner": "ALIGN_INTERPOLATE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,11 +232,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/number_of_subnet_IP_ranges_ppg_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/number_of_subnet_IP_ranges_ppg_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,11 +274,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l4_ppg_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l4_ppg_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,11 +316,13 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l7_ppg_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/internal_forwarding_rules_l7_ppg_utilization\" resource.type=\"global\"",
|
||||||
"secondaryAggregation": {
|
"secondaryAggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,6 +358,7 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "3600s",
|
"alignmentPeriod": "3600s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
"perSeriesAligner": "ALIGN_NEXT_OLDER"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/number_of_instances_ppg_utilization\" resource.type=\"global\""
|
"filter": "metric.type=\"custom.googleapis.com/number_of_instances_ppg_utilization\" resource.type=\"global\""
|
||||||
|
@ -378,6 +395,7 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/dynamic_routes_per_network_utilization\" resource.type=\"global\""
|
"filter": "metric.type=\"custom.googleapis.com/dynamic_routes_per_network_utilization\" resource.type=\"global\""
|
||||||
|
@ -393,8 +411,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"width": 6,
|
"width": 6,
|
||||||
"xPos": 6,
|
"xPos": 0,
|
||||||
"yPos": 16
|
"yPos": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"height": 4,
|
"height": 4,
|
||||||
|
@ -420,16 +438,11 @@
|
||||||
],
|
],
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_vpc_usage\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_vpc_usage\" resource.type=\"global\""
|
||||||
"secondaryAggregation": {
|
|
||||||
"alignmentPeriod": "60s",
|
|
||||||
"perSeriesAligner": "ALIGN_NONE"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"thresholds": [],
|
|
||||||
"timeshiftDuration": "0s",
|
"timeshiftDuration": "0s",
|
||||||
"yAxis": {
|
"yAxis": {
|
||||||
"label": "y1Axis",
|
"label": "y1Axis",
|
||||||
|
@ -439,7 +452,7 @@
|
||||||
},
|
},
|
||||||
"width": 6,
|
"width": 6,
|
||||||
"xPos": 0,
|
"xPos": 0,
|
||||||
"yPos": 20
|
"yPos": 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"height": 4,
|
"height": 4,
|
||||||
|
@ -465,16 +478,11 @@
|
||||||
],
|
],
|
||||||
"perSeriesAligner": "ALIGN_MAX"
|
"perSeriesAligner": "ALIGN_MAX"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_utilization\" resource.type=\"global\"",
|
"filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_utilization\" resource.type=\"global\""
|
||||||
"secondaryAggregation": {
|
|
||||||
"alignmentPeriod": "60s",
|
|
||||||
"perSeriesAligner": "ALIGN_NONE"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"thresholds": [],
|
|
||||||
"timeshiftDuration": "0s",
|
"timeshiftDuration": "0s",
|
||||||
"yAxis": {
|
"yAxis": {
|
||||||
"label": "y1Axis",
|
"label": "y1Axis",
|
||||||
|
@ -484,7 +492,44 @@
|
||||||
},
|
},
|
||||||
"width": 6,
|
"width": 6,
|
||||||
"xPos": 6,
|
"xPos": 6,
|
||||||
"yPos": 20
|
"yPos": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height": 4,
|
||||||
|
"widget": {
|
||||||
|
"title": "tuples_per_firewall_policy_utilization",
|
||||||
|
"xyChart": {
|
||||||
|
"chartOptions": {
|
||||||
|
"mode": "COLOR"
|
||||||
|
},
|
||||||
|
"dataSets": [
|
||||||
|
{
|
||||||
|
"minAlignmentPeriod": "60s",
|
||||||
|
"plotType": "LINE",
|
||||||
|
"targetAxis": "Y1",
|
||||||
|
"timeSeriesQuery": {
|
||||||
|
"apiSource": "DEFAULT_CLOUD",
|
||||||
|
"timeSeriesFilter": {
|
||||||
|
"aggregation": {
|
||||||
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
|
},
|
||||||
|
"filter": "metric.type=\"custom.googleapis.com/firewall_policy_tuples_per_policy_utilization\" resource.type=\"global\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeshiftDuration": "0s",
|
||||||
|
"yAxis": {
|
||||||
|
"label": "y1Axis",
|
||||||
|
"scale": "LINEAR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"width": 6,
|
||||||
|
"xPos": 0,
|
||||||
|
"yPos": 28
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"height": 4,
|
"height": 4,
|
||||||
|
@ -504,6 +549,7 @@
|
||||||
"timeSeriesFilter": {
|
"timeSeriesFilter": {
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"alignmentPeriod": "60s",
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
"perSeriesAligner": "ALIGN_MEAN"
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
},
|
},
|
||||||
"filter": "metric.type=\"custom.googleapis.com/ip_addresses_per_subnet_utilization\" resource.type=\"global\""
|
"filter": "metric.type=\"custom.googleapis.com/ip_addresses_per_subnet_utilization\" resource.type=\"global\""
|
||||||
|
@ -519,8 +565,45 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"width": 6,
|
"width": 6,
|
||||||
"xPos": 0,
|
"xPos": 6,
|
||||||
"yPos": 24
|
"yPos": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height": 4,
|
||||||
|
"widget": {
|
||||||
|
"title": "dynamic_routes_ppg_utilization",
|
||||||
|
"xyChart": {
|
||||||
|
"chartOptions": {
|
||||||
|
"mode": "COLOR"
|
||||||
|
},
|
||||||
|
"dataSets": [
|
||||||
|
{
|
||||||
|
"minAlignmentPeriod": "60s",
|
||||||
|
"plotType": "LINE",
|
||||||
|
"targetAxis": "Y1",
|
||||||
|
"timeSeriesQuery": {
|
||||||
|
"apiSource": "DEFAULT_CLOUD",
|
||||||
|
"timeSeriesFilter": {
|
||||||
|
"aggregation": {
|
||||||
|
"alignmentPeriod": "60s",
|
||||||
|
"crossSeriesReducer": "REDUCE_NONE",
|
||||||
|
"perSeriesAligner": "ALIGN_MEAN"
|
||||||
|
},
|
||||||
|
"filter": "metric.type=\"custom.googleapis.com/dynamic_routes_per_peering_group_utilization\" resource.type=\"global\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeshiftDuration": "0s",
|
||||||
|
"yAxis": {
|
||||||
|
"label": "y1Axis",
|
||||||
|
"scale": "LINEAR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"width": 6,
|
||||||
|
"xPos": 6,
|
||||||
|
"yPos": 20
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue