1 #######################################################################################
2 # Copyright ETSI Contributors and Others.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #######################################################################################
17 # pylint: disable=E1101
21 from typing
import Dict
, List
23 from google
.cloud
import monitoring_v3
24 from google
.oauth2
import service_account
25 import googleapiclient
.discovery
26 from osm_mon
.vim_connectors
.base_vim
import VIMConnector
28 log
= logging
.getLogger(__name__
)
33 "metrictype": "compute.googleapis.com/instance/cpu/utilization",
36 "average_memory_utilization": {
37 # metric only available in e2 family
38 "metrictype": "compute.googleapis.com/instance/memory/balloon/ram_used",
39 "multiplier": 0.000001,
42 "metrictype": "compute.googleapis.com/instance/disk/read_ops_count",
45 "metrictype": "compute.googleapis.com/instance/disk/write_ops_count",
48 "metrictype": "compute.googleapis.com/instance/disk/read_bytes_count",
51 "metrictype": "compute.googleapis.com/instance/disk/write_bytes_count",
54 "metrictype": "compute.googleapis.com/instance/network/received_packets_count",
57 "metrictype": "compute.googleapis.com/instance/network/sent_packets_count",
59 # "packets_in_dropped": {},
60 # "packets_out_dropped": {},
64 class GcpCollector(VIMConnector
):
65 def __init__(self
, vim_account
: Dict
):
66 self
.vim_account
= vim_account
67 self
.project
= vim_account
["vim_tenant_name"] or vim_account
["vim_tenant_id"]
69 # REGION - Google Cloud considers regions and zones. A specific region
70 # can have more than one zone (for instance: region us-west1 with the
71 # zones us-west1-a, us-west1-b and us-west1-c). So the region name
72 # specified in the config will be considered as a specific zone for GC
73 # and the region will be calculated from that without the preffix.
74 if "config" in vim_account
:
75 config
= vim_account
["config"]
76 if "region_name" in config
:
77 self
.zone
= config
.get("region_name")
78 self
.region
= self
.zone
.rsplit("-", 1)[0]
80 log
.error("Google Cloud region_name not specified in config")
82 log
.error("config is not specified in VIM")
85 scopes
= ["https://www.googleapis.com/auth/cloud-platform"]
86 self
.credentials
= None
87 if "credentials" in config
:
88 log
.debug("Setting credentials")
89 # Settings Google Cloud credentials dict
90 creds_body
= config
["credentials"]
91 creds
= service_account
.Credentials
.from_service_account_info(creds_body
)
92 if "sa_file" in config
:
93 creds
= service_account
.Credentials
.from_service_account_file(
94 config
.get("sa_file"), scopes
=scopes
96 log
.debug("Credentials: %s", creds
)
97 # Construct a Resource for interacting with an API.
98 self
.credentials
= creds
100 self
.conn_compute
= googleapiclient
.discovery
.build(
101 "compute", "v1", credentials
=creds
103 except Exception as e
:
105 # Construct a client for interacting with metrics API.
107 self
.metric_client
= monitoring_v3
.MetricServiceClient(
110 except Exception as e
:
113 log
.error("It is not possible to init GCP with no credentials")
115 def collect_servers_status(self
) -> List
[Dict
]:
119 self
.conn_compute
.instances()
120 .list(project
=self
.project
, zone
=self
.zone
)
123 if "items" in response
:
124 log
.info(response
["items"])
125 for server
in response
["items"]:
127 "id": server
["name"],
128 "name": server
["name"],
129 "status": (1 if (server
["status"] == "RUNNING") else 0),
132 except Exception as e
:
136 def is_vim_ok(self
) -> bool:
139 self
.conn_compute
.zones().get(
140 project
=self
.project
, zone
=self
.zone
143 except Exception as e
:
147 def collect_metrics(self
, metric_list
: List
[Dict
]) -> List
[Dict
]:
148 log
.debug("collect_metrics")
151 log
.info(metric_list
)
152 for metric
in metric_list
:
153 server
= metric
["vm_id"]
154 metric_name
= metric
["metric"]
155 metric_mapping
= METRIC_MAPPINGS
.get(metric_name
)
156 if not metric_mapping
:
157 # log.info(f"Metric {metric_name} not available in GCP")
159 gcp_metric_type
= metric_mapping
["metrictype"]
160 metric_multiplier
= metric_mapping
.get("multiplier", 1)
161 log
.info(f
"server: {server}, gcp_metric_type: {gcp_metric_type}")
163 end
= int(time
.time())
165 interval
= monitoring_v3
.TimeInterval(
167 "end_time": {"seconds": end
},
168 "start_time": {"seconds": start
},
171 aggregation
= monitoring_v3
.Aggregation(
173 "alignment_period": {"seconds": 600},
174 "per_series_aligner": monitoring_v3
.Aggregation
.Aligner
.ALIGN_MEAN
,
177 filter = f
'metric.type = "{gcp_metric_type}" AND metric.labels.instance_name = "{server}"'
178 project
= f
"projects/{self.project}"
179 log
.info(f
"filter: {filter}")
180 results
= self
.metric_client
.list_time_series(
184 "interval": interval
,
185 "view": monitoring_v3
.ListTimeSeriesRequest
.TimeSeriesView
.FULL
,
186 "aggregation": aggregation
,
190 for result
in results
:
191 for point
in result
.points
:
192 value
= point
.value
.double_value
193 if value
is not None:
194 metric
["value"] = value
* metric_multiplier
195 log
.info(f
'value: {metric["value"]}')
196 metric_results
.append(metric
)
198 return metric_results