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 #######################################################################################
19 from typing
import Dict
, List
21 from azure
.identity
import ClientSecretCredential
22 from azure
.mgmt
.compute
import ComputeManagementClient
23 from azure
.mgmt
.monitor
import MonitorManagementClient
24 from azure
.profiles
import ProfileDefinition
25 from osm_mon
.vim_connectors
.base_vim
import VIMConnector
28 log
= logging
.getLogger(__name__
)
33 "metricname": "Percentage CPU",
34 "aggregation": "Average",
36 "average_memory_utilization": {
37 "metricname": "Available Memory Bytes",
38 "aggregation": "Average",
41 "metricname": "Disk Read Operations/Sec",
42 "aggregation": "Average",
45 "metricname": "Disk Write Operations/Sec",
46 "aggregation": "Average",
49 "metricname": "Disk Read Bytes",
50 "aggregation": "Total",
53 "metricname": "Disk Write Bytes",
54 "aggregation": "Total",
56 # "packets_in_dropped": {},
57 # "packets_out_dropped": {},
58 # "packets_received": {},
63 class AzureCollector(VIMConnector
):
64 # Translate azure provisioning state to OSM provision state.
65 # The first three ones are the transitional status once a user initiated
66 # action has been requested. Once the operation is complete, it will
67 # transition into the states Succeeded or Failed
68 # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
69 provision_state2osm
= {
72 "Deleting": "INACTIVE",
73 "Succeeded": "ACTIVE",
77 # Translate azure power state to OSM provision state
79 "starting": "INACTIVE",
81 "stopping": "INACTIVE",
82 "stopped": "INACTIVE",
84 "deallocated": "BUILD",
85 "deallocating": "BUILD",
88 AZURE_COMPUTE_MGMT_CLIENT_API_VERSION
= "2021-03-01"
89 AZURE_COMPUTE_MGMT_PROFILE_TAG
= "azure.mgmt.compute.ComputeManagementClient"
90 AZURE_COMPUTE_MGMT_PROFILE
= ProfileDefinition(
92 AZURE_COMPUTE_MGMT_PROFILE_TAG
: {
93 None: AZURE_COMPUTE_MGMT_CLIENT_API_VERSION
,
94 "availability_sets": "2020-12-01",
95 "dedicated_host_groups": "2020-12-01",
96 "dedicated_hosts": "2020-12-01",
97 "disk_accesses": "2020-12-01",
98 "disk_encryption_sets": "2020-12-01",
99 "disk_restore_point": "2020-12-01",
100 "disks": "2020-12-01",
101 "galleries": "2020-09-30",
102 "gallery_application_versions": "2020-09-30",
103 "gallery_applications": "2020-09-30",
104 "gallery_image_versions": "2020-09-30",
105 "gallery_images": "2020-09-30",
106 "gallery_sharing_profile": "2020-09-30",
107 "images": "2020-12-01",
108 "log_analytics": "2020-12-01",
109 "operations": "2020-12-01",
110 "proximity_placement_groups": "2020-12-01",
111 "resource_skus": "2019-04-01",
112 "shared_galleries": "2020-09-30",
113 "shared_gallery_image_versions": "2020-09-30",
114 "shared_gallery_images": "2020-09-30",
115 "snapshots": "2020-12-01",
116 "ssh_public_keys": "2020-12-01",
117 "usage": "2020-12-01",
118 "virtual_machine_extension_images": "2020-12-01",
119 "virtual_machine_extensions": "2020-12-01",
120 "virtual_machine_images": "2020-12-01",
121 "virtual_machine_images_edge_zone": "2020-12-01",
122 "virtual_machine_run_commands": "2020-12-01",
123 "virtual_machine_scale_set_extensions": "2020-12-01",
124 "virtual_machine_scale_set_rolling_upgrades": "2020-12-01",
125 "virtual_machine_scale_set_vm_extensions": "2020-12-01",
126 "virtual_machine_scale_set_vm_run_commands": "2020-12-01",
127 "virtual_machine_scale_set_vms": "2020-12-01",
128 "virtual_machine_scale_sets": "2020-12-01",
129 "virtual_machine_sizes": "2020-12-01",
130 "virtual_machines": "2020-12-01",
133 AZURE_COMPUTE_MGMT_PROFILE_TAG
+ " osm",
136 def __init__(self
, vim_account
: Dict
):
137 self
.vim_account
= vim_account
138 self
.reload_client
= True
141 # Store config to create azure subscription later
143 "user": vim_account
["vim_user"],
144 "passwd": vim_account
["vim_password"],
145 "tenant": vim_account
["vim_tenant_name"],
149 config
= vim_account
["config"]
150 if "subscription_id" in config
:
151 self
._config
["subscription_id"] = config
.get("subscription_id")
152 log
.info("Subscription: %s", self
._config
["subscription_id"])
154 log
.error("Subscription not specified")
158 if "resource_group" in config
:
159 self
.resource_group
= config
.get("resource_group")
161 log
.error("Azure resource_group is not specified at config")
165 if "region_name" in config
:
166 self
.region
= config
.get("region_name")
168 log
.error("Azure region_name is not specified at config")
171 def _reload_connection(self
):
172 if self
.reload_client
:
173 log
.debug("reloading azure client")
175 self
.credentials
= ClientSecretCredential(
176 client_id
=self
._config
["user"],
177 client_secret
=self
._config
["passwd"],
178 tenant_id
=self
._config
["tenant"],
180 self
.conn_compute
= ComputeManagementClient(
182 self
._config
["subscription_id"],
183 profile
=self
.AZURE_COMPUTE_MGMT_PROFILE
,
186 self
.conn_monitor
= MonitorManagementClient(
188 self
._config
["subscription_id"],
190 # Set to client created
191 self
.reload_client
= False
192 except Exception as e
:
195 def _get_region_vm_sizes(self
):
196 if len(self
.vm_sizes
) == 0:
197 log
.debug("getting VM sizes available in region")
199 for size
in self
.conn_compute
.virtual_machine_sizes
.list(self
.region
):
200 self
.vm_sizes
[size
.name
] = size
201 except Exception as e
:
204 def collect_servers_status(self
) -> List
[Dict
]:
206 log
.debug("collect_servers_status")
207 self
._reload
_connection
()
209 for vm
in self
.conn_compute
.virtual_machines
.list(self
.resource_group
):
211 array
= id.split("/")
213 status
= self
.provision_state2osm
.get(vm
.provisioning_state
, "OTHER")
214 if vm
.provisioning_state
== "Succeeded":
215 # check if machine is running or stopped
216 instance_view
= self
.conn_compute
.virtual_machines
.instance_view(
217 self
.resource_group
, name
219 for status
in instance_view
.statuses
:
220 splitted_status
= status
.code
.split("/")
222 len(splitted_status
) == 2
223 and splitted_status
[0] == "PowerState"
225 status
= self
.power_state2osm
.get(
226 splitted_status
[1], "OTHER"
231 "status": (1 if (status
== "ACTIVE") else 0),
234 except Exception as e
:
239 def is_vim_ok(self
) -> bool:
241 self
.reload_client
= True
243 self
._reload
_connection
()
245 except Exception as e
:
249 def collect_metrics(self
, metric_list
: List
[Dict
]) -> List
[Dict
]:
250 log
.debug("collect_metrics")
251 self
._reload
_connection
()
254 # VMs RAM cache for calculating "average_memory_utilization" metric
256 for metric
in metric_list
:
257 server
= metric
["vm_id"]
258 metric_name
= metric
["metric"]
259 metric_mapping
= METRIC_MAPPINGS
.get(metric_name
)
260 if not metric_mapping
:
262 if metric_name
== "average_memory_utilization" and len(cache
) == 0:
263 # storing VMs RAM sizes in cache
264 self
._get
_region
_vm
_sizes
()
266 for vm
in self
.conn_compute
.virtual_machines
.list(
270 size_name
= vm
.hardware_profile
.vm_size
271 vm_size
= self
.vm_sizes
.get(size_name
)
273 ram
= vm_size
.memory_in_mb
275 except Exception as e
:
277 azure_metric_name
= metric_mapping
["metricname"]
278 azure_aggregation
= metric_mapping
["aggregation"]
279 end
= datetime
.datetime
.now()
280 init
= end
- datetime
.timedelta(minutes
=5)
282 metrics_data
= self
.conn_monitor
.metrics
.list(
284 timespan
="{}/{}".format(init
, end
),
286 metricnames
=azure_metric_name
,
287 aggregation
=azure_aggregation
,
289 except Exception as e
:
294 for item
in metrics_data
.value
:
295 log
.info("{} ({})".format(item
.name
.localized_value
, item
.unit
))
296 for timeserie
in item
.timeseries
:
297 for data
in timeserie
.data
:
298 if azure_aggregation
== "Average":
300 elif azure_aggregation
== "Total":
304 log
.info("{}: {}".format(data
.time_stamp
, val
))
309 value
= total
/ n_metrics
310 if metric_name
== "average_memory_utilization":
311 ram
= cache
.get(server
)
313 log
.info(f
"VM RAM = {ram}")
314 value
= ram
- (value
/ 1048576)
316 log
.error(f
"Not found RAM value for server {server}")
318 if value
is not None:
319 log
.info(f
"value = {value}")
320 metric
["value"] = value
321 metric_results
.append(metric
)
323 log
.info("No metric available")
325 return metric_results