Add VIO support in DAGs
[osm/NG-SA.git] / src / osm_ngsa / osm_mon / vim_connectors / vrops_helper.py
1 #######################################################################################
2 # Copyright ETSI Contributors and Others.
3 #
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
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
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
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #######################################################################################
17
18 import json
19 import logging
20 import traceback
21
22 import requests
23
24 log = logging.getLogger(__name__)
25
26 # Ref: https://docs.vmware.com/en/vRealize-Operations-Manager/7.0/vrealize-operations-manager-70-reference-guide.pdf
27 # Potential metrics of interest
28 # "cpu|capacity_contentionPct"
29 # "cpu|corecount_provisioned"
30 # "cpu|costopPct"
31 # "cpu|demandmhz"
32 # "cpu|demandPct"
33 # "cpu|effective_limit"
34 # "cpu|iowaitPct"
35 # "cpu|readyPct"
36 # "cpu|swapwaitPct"
37 # "cpu|usage_average"
38 # "cpu|usagemhz_average"
39 # "cpu|usagemhz_average_mtd"
40 # "cpu|vm_capacity_provisioned"
41 # "cpu|workload"
42 # "guestfilesystem|percentage_total"
43 # "guestfilesystem|usage_total"
44 # "mem|consumedPct"
45 # "mem|guest_usage"
46 # "mem|host_contentionPct"
47 # "mem|reservation_used"
48 # "mem|swapinRate_average"
49 # "mem|swapoutRate_average"
50 # "mem|swapped_average"
51 # "mem|usage_average"
52 # "net:Aggregate of all instances|droppedPct"
53 # "net|broadcastTx_summation"
54 # "net|droppedTx_summation"
55 # "net|multicastTx_summation"
56 # "net|pnicBytesRx_average"
57 # "net|pnicBytesTx_average"
58 # "net|received_average"
59 # "net|transmitted_average"
60 # "net|usage_average"
61 # "virtualDisk:Aggregate of all instances|commandsAveraged_average"
62 # "virtualDisk:Aggregate of all instances|numberReadAveraged_average"
63 # "virtualDisk:Aggregate of all instances|numberWriteAveraged_average"
64 # "virtualDisk:Aggregate of all instances|totalLatency"
65 # "virtualDisk:Aggregate of all instances|totalReadLatency_average"
66 # "virtualDisk:Aggregate of all instances|totalWriteLatency_average"
67 # "virtualDisk:Aggregate of all instances|usage"
68 # "virtualDisk:Aggregate of all instances|vDiskOIO"
69 # "virtualDisk|read_average"
70 # "virtualDisk|write_average"
71 METRIC_MAPPINGS = {
72 # Percent guest operating system active memory.
73 "average_memory_utilization": "mem|usage_average",
74 # Percentage of CPU that was used out of all the CPU that was allocated.
75 "cpu_utilization": "cpu|usage_average",
76 # KB/s of data read in the performance interval
77 "disk_read_bytes": "virtualDisk|read_average",
78 # Average of read commands per second during the collection interval.
79 "disk_read_ops": "virtualDisk:aggregate of all instances|numberReadAveraged_average",
80 # KB/s of data written in the performance interval.
81 "disk_write_bytes": "virtualDisk|write_average",
82 # Average of write commands per second during the collection interval.
83 "disk_write_ops": "virtualDisk:aggregate of all instances|numberWriteAveraged_average",
84 # Not supported by vROPS, will always return 0.
85 "packets_in_dropped": "net|droppedRx_summation",
86 # Transmitted packets dropped in the collection interval.
87 "packets_out_dropped": "net|droppedTx_summation",
88 # Bytes received in the performance interval.
89 "packets_received": "net|received_average",
90 # Packets transmitted in the performance interval.
91 "packets_sent": "net|transmitted_average",
92 }
93
94 # If the unit from vROPS does not align with the expected value. multiply by the specified amount to ensure
95 # the correct unit is returned.
96 METRIC_MULTIPLIERS = {
97 "disk_read_bytes": 1024,
98 "disk_write_bytes": 1024,
99 "packets_received": 1024,
100 "packets_sent": 1024,
101 }
102
103
104 class vROPS_Helper:
105 def __init__(self, vrops_site="https://vrops", vrops_user="", vrops_password=""):
106 self.vrops_site = vrops_site
107 self.vrops_user = vrops_user
108 self.vrops_password = vrops_password
109
110 def get_vrops_token(self):
111 """Fetches token from vrops"""
112 auth_url = "/suite-api/api/auth/token/acquire"
113 headers = {"Content-Type": "application/json", "Accept": "application/json"}
114 req_body = {"username": self.vrops_user, "password": self.vrops_password}
115 resp = requests.post(
116 self.vrops_site + auth_url, json=req_body, verify=False, headers=headers
117 )
118 if resp.status_code != 200:
119 log.error(
120 "Failed to get token from vROPS: {} {}".format(
121 resp.status_code, resp.content
122 )
123 )
124 return None
125
126 resp_data = json.loads(resp.content.decode("utf-8"))
127 return resp_data["token"]
128
129 def get_vm_resource_list_from_vrops(self):
130 """Find all known resource IDs in vROPs"""
131 auth_token = self.get_vrops_token()
132 api_url = "/suite-api/api/resources?resourceKind=VirtualMachine"
133 headers = {
134 "Accept": "application/json",
135 "Authorization": "vRealizeOpsToken {}".format(auth_token),
136 }
137 resource_list = []
138
139 resp = requests.get(self.vrops_site + api_url, verify=False, headers=headers)
140
141 if resp.status_code != 200:
142 log.error(
143 "Failed to get resource list from vROPS: {} {}".format(
144 resp.status_code, resp.content
145 )
146 )
147 return resource_list
148
149 try:
150 resp_data = json.loads(resp.content.decode("utf-8"))
151 if resp_data.get("resourceList") is not None:
152 resource_list = resp_data.get("resourceList")
153
154 except Exception as exp:
155 log.error(
156 "get_vm_resource_id: Error in parsing {}\n{}".format(
157 exp, traceback.format_exc()
158 )
159 )
160
161 return resource_list
162
163 def get_metrics(self, metrics_list=[]):
164 monitoring_keys = {}
165 vdus = {}
166 # Collect the names of all the metrics we need to query
167 for metric in metrics_list:
168 metric_name = metric["metric"]
169 if metric_name not in METRIC_MAPPINGS:
170 log.debug(f"Metric {metric_name} not supported, ignoring")
171 continue
172 monitoring_keys[metric_name] = METRIC_MAPPINGS[metric_name]
173 vrops_id = metric["vrops_id"]
174 vdus[vrops_id] = 1
175
176 metrics = []
177 # Make a query for only the stats we have been asked for
178 stats_key = ""
179 for stat in monitoring_keys.values():
180 stats_key += "&statKey={}".format(stat)
181
182 # And only ask for the resource ids that we are interested in
183 resource_ids = ""
184 for key in vdus.keys():
185 resource_ids += "&resourceId={}".format(key)
186
187 try:
188 # Now we can make a single call to vROPS to collect all relevant metrics for resources we need to monitor
189 api_url = (
190 "/suite-api/api/resources/stats?IntervalType=MINUTES&IntervalCount=1"
191 "&rollUpType=MAX&currentOnly=true{}{}".format(stats_key, resource_ids)
192 )
193
194 auth_token = self.get_vrops_token()
195 headers = {
196 "Accept": "application/json",
197 "Authorization": "vRealizeOpsToken {}".format(auth_token),
198 }
199
200 resp = requests.get(
201 self.vrops_site + api_url, verify=False, headers=headers
202 )
203
204 if resp.status_code != 200:
205 log.error(
206 f"Failed to get Metrics data from vROPS for {resp.status_code} {resp.content}"
207 )
208 return []
209 m_data = json.loads(resp.content.decode("utf-8"))
210 if "values" not in m_data:
211 return metrics
212
213 statistics = m_data["values"]
214 for vdu_stat in statistics:
215 vrops_id = vdu_stat["resourceId"]
216 log.info(f"vrops_id: {vrops_id}")
217 for item in vdu_stat["stat-list"]["stat"]:
218 reported_metric = item["statKey"]["key"]
219 if reported_metric not in METRIC_MAPPINGS.values():
220 continue
221
222 # Convert the vROPS metric name back to OSM key
223 metric_name = list(METRIC_MAPPINGS.keys())[
224 list(METRIC_MAPPINGS.values()).index(reported_metric)
225 ]
226 if metric_name in monitoring_keys.keys():
227 metric_value = item["data"][-1]
228 if metric_name in METRIC_MULTIPLIERS:
229 metric_value *= METRIC_MULTIPLIERS[metric_name]
230 log.info(f" {metric_name} ({reported_metric}): {metric_value}")
231
232 # Find the associated metric in requested list
233 for item in metrics_list:
234 if (
235 item["vrops_id"] == vrops_id
236 and item["metric"] == metric_name
237 ):
238 metric = item
239 metric["value"] = metric_value
240 metrics.append(metric)
241 break
242
243 except Exception as exp:
244 log.error(
245 "Exception while parsing metrics data from vROPS {}\n{}".format(
246 exp, traceback.format_exc()
247 )
248 )
249
250 return metrics