6ca3d40e6a040a38a539c669f8e826effbfc4be8
[osm/MON.git] / osm_mon / plugins / vRealiseOps / mon_plugin_vrops.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: osslegalrouting@vmware.com
22 ##
23
24 """
25 Monitoring metrics & creating Alarm definitions in vROPs
26 """
27 import pytz
28 import requests
29 import logging
30
31 import six
32 from pyvcloud.vcd.client import BasicLoginCredentials
33 from pyvcloud.vcd.client import Client
34 API_VERSION = '5.9'
35
36 from xml.etree import ElementTree as XmlElementTree
37 import traceback
38 import time
39 import json
40 from OpenSSL.crypto import load_certificate, FILETYPE_PEM
41 import os
42 import sys
43 import datetime
44 from socket import getfqdn
45
46 import urllib3
47 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
48
49 sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..'))
50 from osm_mon.core.database import DatabaseManager
51
52 OPERATION_MAPPING = {'GE':'GT_EQ', 'LE':'LT_EQ', 'GT':'GT', 'LT':'LT', 'EQ':'EQ'}
53 severity_mano2vrops = {'WARNING':'WARNING', 'MINOR':'WARNING', 'MAJOR':"IMMEDIATE",\
54 'CRITICAL':'CRITICAL', 'INDETERMINATE':'UNKNOWN'}
55 PERIOD_MSEC = {'HR':3600000,'DAY':86400000,'WEEK':604800000,'MONTH':2678400000,'YEAR':31536000000}
56
57 #To Do - Add actual webhook url & certificate
58 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/www.vrops_webservice.com.cert'
59 #webhook_url = "https://mano-dev-1:8080/notify/" #for testing
60 webhook_url = "https://" + getfqdn() + ":8080/notify/"
61 SSL_CERTIFICATE_FILE_NAME = ('vROPs_Webservice/SSL_certificate/' + getfqdn() + ".cert")
62 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/10.172.137.214.cert' #for testing
63
64 MODULE_DIR = os.path.dirname(__file__)
65 CONFIG_FILE_NAME = 'vrops_config.xml'
66 CONFIG_FILE_PATH = os.path.join(MODULE_DIR, CONFIG_FILE_NAME)
67 SSL_CERTIFICATE_FILE_PATH = os.path.join(MODULE_DIR, SSL_CERTIFICATE_FILE_NAME)
68
69 class MonPlugin():
70 """MON Plugin class for vROPs telemetry plugin
71 """
72 def __init__(self, access_config=None):
73 """Constructor of MON plugin
74 Params:
75 'access_config': dictionary with VIM access information based on VIM type.
76 This contains a consolidate version of VIM & monitoring tool config at creation and
77 particular VIM config at their attachment.
78 For VIM type: 'vmware',
79 access_config - {'vrops_site':<>, 'vrops_user':<>, 'vrops_password':<>,
80 'vcloud-site':<>,'admin_username':<>,'admin_password':<>,
81 'nsx_manager':<>,'nsx_user':<>,'nsx_password':<>,
82 'vcenter_ip':<>,'vcenter_port':<>,'vcenter_user':<>,'vcenter_password':<>,
83 'vim_tenant_name':<>,'orgname':<>}
84
85 #To Do
86 Returns: Raise an exception if some needed parameter is missing, but it must not do any connectivity
87 check against the VIM
88 """
89
90 self.logger = logging.getLogger('PluginReceiver.MonPlugin')
91 self.logger.setLevel(logging.DEBUG)
92
93 if access_config is None:
94 self.logger.error("VIM Access Configuration not provided")
95 raise KeyError("VIM Access Configuration not provided")
96
97 self.database_manager = DatabaseManager()
98
99 self.access_config = access_config
100 if not bool(access_config):
101 self.logger.error("VIM Account details are not added. Please add a VIM account")
102 raise KeyError("VIM Account details are not added. Please add a VIM account")
103
104 try:
105 self.vrops_site = access_config['vrops_site']
106 self.vrops_user = access_config['vrops_user']
107 self.vrops_password = access_config['vrops_password']
108 self.vcloud_site = access_config['vim_url']
109 self.admin_username = access_config['admin_username']
110 self.admin_password = access_config['admin_password']
111 #self.tenant_id = access_config['tenant_id']
112 self.vim_uuid = access_config['vim_uuid']
113
114 except KeyError as exp:
115 self.logger.error("Required VIM account details not provided: {}".format(exp))
116 raise KeyError("Required VIM account details not provided: {}".format(exp))
117
118
119
120 def configure_alarm(self, config_dict = {}):
121 """Configures or creates a new alarm using the input parameters in config_dict
122 Params:
123 "alarm_name": Alarm name in string format
124 "description": Description of alarm in string format
125 "resource_uuid": Resource UUID for which alarm needs to be configured. in string format
126 "Resource type": String resource type: 'VDU' or 'host'
127 "Severity": 'WARNING', 'MINOR', 'MAJOR', 'CRITICAL'
128 "metric_name": Metric key in string format
129 "operation": One of ('GE', 'LE', 'GT', 'LT', 'EQ')
130 "threshold_value": Defines the threshold (up to 2 fraction digits) that,
131 if crossed, will trigger the alarm.
132 "statistic": AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM
133
134 Default parameters for each alarm are read from the plugin specific config file.
135 Dict of default parameters is as follows:
136 default_params keys = {'cancel_cycles','wait_cycles','resource_kind','adapter_kind',
137 'alarm_type','alarm_subType',impact}
138
139 Returns the UUID of created alarm or None
140 """
141 alarm_def = None
142 #1) get alarm & metrics parameters from plugin specific file
143 def_a_params = self.get_default_Params(config_dict['alarm_name'])
144 if not def_a_params:
145 self.logger.warning("Alarm not supported: {}".format(config_dict['alarm_name']))
146 return None
147 metric_key_params = self.get_default_Params(config_dict['metric_name'])
148 if not metric_key_params:
149 self.logger.warning("Metric not supported: {}".format(config_dict['metric_name']))
150 return None
151
152 #1.2) Check if alarm definition already exists
153 vrops_alarm_name = def_a_params['vrops_alarm']+ '-' + config_dict['resource_uuid']
154 alert_def_list = self.get_alarm_defination_by_name(vrops_alarm_name)
155 if alert_def_list:
156 self.logger.warning("Alarm already exists: {}. Try updating by update_alarm_request"\
157 .format(vrops_alarm_name))
158 return None
159
160 #2) create symptom definition
161 symptom_params ={'cancel_cycles': (def_a_params['cancel_period']/300)*def_a_params['cancel_cycles'],
162 'wait_cycles': (def_a_params['period']/300)*def_a_params['evaluation'],
163 'resource_kind_key': def_a_params['resource_kind'],
164 'adapter_kind_key': def_a_params['adapter_kind'],
165 'symptom_name':vrops_alarm_name,
166 'severity': severity_mano2vrops[config_dict['severity'].upper()],
167 'metric_key':metric_key_params['metric_key'],
168 'operation':OPERATION_MAPPING[config_dict['operation']],
169 'threshold_value':config_dict['threshold_value']}
170
171 symptom_uuid = self.create_symptom(symptom_params)
172 if symptom_uuid is not None:
173 self.logger.info("Symptom defined: {} with ID: {}".format(symptom_params['symptom_name'],symptom_uuid))
174 else:
175 self.logger.warning("Failed to create Symptom: {}".format(symptom_params['symptom_name']))
176 return None
177 #3) create alert definition
178 #To Do - Get type & subtypes for all 5 alarms
179 alarm_params = {'name':vrops_alarm_name,
180 'description':config_dict['description']\
181 if 'description' in config_dict and config_dict['description'] is not None else config_dict['alarm_name'],
182 'adapterKindKey':def_a_params['adapter_kind'],
183 'resourceKindKey':def_a_params['resource_kind'],
184 'waitCycles':1, 'cancelCycles':1,
185 'type':def_a_params['alarm_type'], 'subType':def_a_params['alarm_subType'],
186 'severity':severity_mano2vrops[config_dict['severity'].upper()],
187 'symptomDefinitionId':symptom_uuid,
188 'impact':def_a_params['impact']}
189
190 alarm_def = self.create_alarm_definition(alarm_params)
191 if alarm_def is None:
192 self.logger.warning("Failed to create Alert: {}".format(alarm_params['name']))
193 return None
194
195 self.logger.info("Alarm defined: {} with ID: {}".format(alarm_params['name'],alarm_def))
196
197 #4) Find vm_moref_id from vApp uuid in vCD
198 vm_moref_id = self.get_vm_moref_id(config_dict['resource_uuid'])
199 if vm_moref_id is None:
200 self.logger.warning("Failed to find vm morefid for vApp in vCD: {}".format(config_dict['resource_uuid']))
201 return None
202
203 #5) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
204 resource_id = self.get_vm_resource_id(vm_moref_id)
205 if resource_id is None:
206 self.logger.warning("Failed to find resource in vROPs: {}".format(config_dict['resource_uuid']))
207 return None
208
209 #6) Configure alarm notification for a particular VM using it's resource_id
210 notification_id = self.create_alarm_notification_rule(vrops_alarm_name, alarm_def, resource_id)
211 if notification_id is None:
212 return None
213 else:
214 alarm_def_uuid = alarm_def.split('-', 1)[1]
215 self.logger.info("Alarm definition created with notification: {} with ID: {}"\
216 .format(alarm_params['name'],alarm_def_uuid))
217 ##self.database_manager.save_alarm(alarm_def_uuid, alarm_params['name'], self.vim_uuid)
218 self.database_manager.save_alarm(alarm_def_uuid,
219 self.vim_uuid,
220 ##alarm_params['name'],
221 config_dict['threshold_value'],
222 config_dict['operation'],
223 config_dict['metric_name'].lower(),
224 config_dict['vdu_name'].lower(),
225 config_dict['vnf_member_index'].lower(),
226 config_dict['ns_id'].lower()
227 )
228
229 #Return alarm definition UUID by removing 'AlertDefinition' from UUID
230 return (alarm_def_uuid)
231
232 def get_default_Params(self, metric_alarm_name):
233 """
234 Read the default config parameters from plugin specific file stored with plugin file.
235 Params:
236 metric_alarm_name: Name of the alarm, whose config parameters to be read from the config file.
237 """
238 a_params = {}
239 try:
240 source = open(CONFIG_FILE_PATH, 'r')
241 except IOError as exp:
242 msg = ("Could not read Config file: {}, \nException: {}"\
243 .format(CONFIG_FILE_PATH, exp))
244 self.logger.error(msg)
245 raise IOError(msg)
246
247 tree = XmlElementTree.parse(source)
248 alarms = tree.getroot()
249 for alarm in alarms:
250 if alarm.tag.lower() == metric_alarm_name.lower():
251 for param in alarm:
252 if param.tag in ("period", "evaluation", "cancel_period", "alarm_type",\
253 "cancel_cycles", "alarm_subType"):
254 a_params[param.tag] = int(param.text)
255 elif param.tag in ("enabled", "repeat"):
256 if(param.text.lower() == "true"):
257 a_params[param.tag] = True
258 else:
259 a_params[param.tag] = False
260 else:
261 a_params[param.tag] = param.text
262 source.close()
263 return a_params
264
265
266 def create_symptom(self, symptom_params):
267 """Create Symptom definition for an alarm
268 Params:
269 symptom_params: Dict of parameters required for defining a symptom as follows
270 cancel_cycles
271 wait_cycles
272 resource_kind_key = "VirtualMachine"
273 adapter_kind_key = "VMWARE"
274 symptom_name = Test_Memory_Usage_TooHigh
275 severity
276 metric_key
277 operation = GT_EQ
278 threshold_value = 85
279 Returns the uuid of Symptom definition
280 """
281 symptom_id = None
282
283 try:
284 api_url = '/suite-api/api/symptomdefinitions'
285 headers = {'Content-Type': 'application/json','Accept': 'application/json'}
286 data = {
287 "id": None,
288 "name": symptom_params['symptom_name'],
289 "adapterKindKey": symptom_params['adapter_kind_key'],
290 "resourceKindKey": symptom_params['resource_kind_key'],
291 "waitCycles": symptom_params['wait_cycles'],
292 "cancelCycles": symptom_params['cancel_cycles'],
293 "state": {
294 "severity": symptom_params['severity'],
295 "condition": {
296 "type": "CONDITION_HT",
297 "key": symptom_params['metric_key'],
298 "operator": symptom_params['operation'],
299 "value": symptom_params['threshold_value'],
300 "valueType": "NUMERIC",
301 "instanced": False,
302 "thresholdType": "STATIC"
303 }
304 }
305 }
306
307 resp = requests.post(self.vrops_site + api_url,
308 auth=(self.vrops_user, self.vrops_password),
309 headers=headers,
310 verify = False,
311 data=json.dumps(data))
312
313 if resp.status_code != 201:
314 self.logger.warning("Failed to create Symptom definition: {}, response {}"\
315 .format(symptom_params['symptom_name'], resp.content))
316 return None
317
318 resp_data = json.loads(resp.content)
319 if resp_data.get('id') is not None:
320 symptom_id = resp_data['id']
321
322 return symptom_id
323
324 except Exception as exp:
325 self.logger.warning("Error creating symptom definition : {}\n{}"\
326 .format(exp, traceback.format_exc()))
327
328
329 def create_alarm_definition(self, alarm_params):
330 """
331 Create an alarm definition in vROPs
332 Params:
333 'name': Alarm Name,
334 'description':Alarm description,
335 'adapterKindKey': Adapter type in vROPs "VMWARE",
336 'resourceKindKey':Resource type in vROPs "VirtualMachine",
337 'waitCycles': No of wait cycles,
338 'cancelCycles': No of cancel cycles,
339 'type': Alarm type,
340 'subType': Alarm subtype,
341 'severity': Severity in vROPs "CRITICAL",
342 'symptomDefinitionId':symptom Definition uuid,
343 'impact': impact 'risk'
344 Returns:
345 'alarm_uuid': returns alarm uuid
346 """
347
348 alarm_uuid = None
349
350 try:
351 api_url = '/suite-api/api/alertdefinitions'
352 headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
353 data = {
354 "name": alarm_params['name'],
355 "description": alarm_params['description'],
356 "adapterKindKey": alarm_params['adapterKindKey'],
357 "resourceKindKey": alarm_params['resourceKindKey'],
358 "waitCycles": 1,
359 "cancelCycles": 1,
360 "type": alarm_params['type'],
361 "subType": alarm_params['subType'],
362 "states": [
363 {
364 "severity": alarm_params['severity'],
365 "base-symptom-set":
366 {
367 "type": "SYMPTOM_SET",
368 "relation": "SELF",
369 "aggregation": "ALL",
370 "symptomSetOperator": "AND",
371 "symptomDefinitionIds": [alarm_params['symptomDefinitionId']]
372 },
373 "impact": {
374 "impactType": "BADGE",
375 "detail": alarm_params['impact']
376 }
377 }
378 ]
379 }
380
381 resp = requests.post(self.vrops_site + api_url,
382 auth=(self.vrops_user, self.vrops_password),
383 headers=headers,
384 verify = False,
385 data=json.dumps(data))
386
387 if resp.status_code != 201:
388 self.logger.warning("Failed to create Alarm definition: {}, response {}"\
389 .format(alarm_params['name'], resp.content))
390 return None
391
392 resp_data = json.loads(resp.content)
393 if resp_data.get('id') is not None:
394 alarm_uuid = resp_data['id']
395
396 return alarm_uuid
397
398 except Exception as exp:
399 self.logger.warning("Error creating alarm definition : {}\n{}".format(exp, traceback.format_exc()))
400
401
402 def configure_rest_plugin(self):
403 """
404 Creates REST Plug-in for vROPs outbound alerts
405
406 Returns Plugin ID
407 """
408 plugin_id = None
409 plugin_name = 'MON_module_REST_Plugin'
410 plugin_id = self.check_if_plugin_configured(plugin_name)
411
412 #If REST plugin not configured, configure it
413 if plugin_id is not None:
414 return plugin_id
415 else:
416 try:
417 cert_file_string = open(SSL_CERTIFICATE_FILE_PATH, "rb").read()
418 except IOError as exp:
419 msg = ("Could not read SSL certificate file: {}".format(SSL_CERTIFICATE_FILE_PATH))
420 self.logger.error(msg)
421 raise IOError(msg)
422 cert = load_certificate(FILETYPE_PEM, cert_file_string)
423 certificate = cert.digest("sha1")
424 api_url = '/suite-api/api/alertplugins'
425 headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
426 data = {
427 "pluginTypeId": "RestPlugin",
428 "name": plugin_name,
429 "configValues": [
430 {
431 "name": "Url",
432 "value": webhook_url
433 },
434 {
435 "name": "Content-type",
436 "value": "application/json"
437 },
438 {
439 "name": "Certificate",
440 "value": certificate
441 },
442 {
443 "name": "ConnectionCount",
444 "value": "20"
445 }
446 ]
447 }
448
449 resp = requests.post(self.vrops_site + api_url,
450 auth=(self.vrops_user, self.vrops_password),
451 headers=headers,
452 verify = False,
453 data=json.dumps(data))
454
455 if resp.status_code is not 201:
456 self.logger.warning("Failed to create REST Plugin: {} for url: {}, \nresponse code: {},"\
457 "\nresponse content: {}".format(plugin_name, webhook_url,\
458 resp.status_code, resp.content))
459 return None
460
461 resp_data = json.loads(resp.content)
462 if resp_data.get('pluginId') is not None:
463 plugin_id = resp_data['pluginId']
464
465 if plugin_id is None:
466 self.logger.warning("Failed to get REST Plugin ID for {}, url: {}".format(plugin_name, webhook_url))
467 return None
468 else:
469 self.logger.info("Created REST Plugin: {} with ID : {} for url: {}".format(plugin_name, plugin_id, webhook_url))
470 status = self.enable_rest_plugin(plugin_id, plugin_name)
471 if status is False:
472 self.logger.warning("Failed to enable created REST Plugin: {} for url: {}".format(plugin_name, webhook_url))
473 return None
474 else:
475 self.logger.info("Enabled REST Plugin: {} for url: {}".format(plugin_name, webhook_url))
476 return plugin_id
477
478 def check_if_plugin_configured(self, plugin_name):
479 """Check if the REST plugin is already created
480 Returns: plugin_id: if already created, None: if needs to be created
481 """
482 plugin_id = None
483 #Find the REST Plugin id details for - MON_module_REST_Plugin
484 api_url = '/suite-api/api/alertplugins'
485 headers = {'Accept': 'application/json'}
486
487 resp = requests.get(self.vrops_site + api_url,
488 auth=(self.vrops_user, self.vrops_password),
489 verify = False, headers = headers)
490
491 if resp.status_code is not 200:
492 self.logger.warning("Failed to REST GET Alarm plugin details \nResponse code: {}\nResponse content: {}"\
493 .format(resp.status_code, resp.content))
494 return None
495
496 # Look for specific plugin & parse pluginId for 'MON_module_REST_Plugin'
497 plugins_list = json.loads(resp.content)
498 if plugins_list.get('notificationPluginInstances') is not None:
499 for notify_plugin in plugins_list['notificationPluginInstances']:
500 if notify_plugin.get('name') is not None and notify_plugin['name'] == plugin_name:
501 plugin_id = notify_plugin.get('pluginId')
502
503 if plugin_id is None:
504 self.logger.warning("REST plugin {} not found".format(plugin_name))
505 return None
506 else:
507 self.logger.info("Found REST Plugin: {}".format(plugin_name))
508 return plugin_id
509
510
511 def enable_rest_plugin(self, plugin_id, plugin_name):
512 """
513 Enable the REST plugin using plugin_id
514 Params: plugin_id: plugin ID string that is to be enabled
515 Returns: status (Boolean) - True for success, False for failure
516 """
517
518 if plugin_id is None or plugin_name is None:
519 self.logger.debug("enable_rest_plugin() : Plugin ID or plugin_name not provided for {} plugin"\
520 .format(plugin_name))
521 return False
522
523 try:
524 api_url = "/suite-api/api/alertplugins/{}/enable/True".format(plugin_id)
525
526 resp = requests.put(self.vrops_site + api_url,
527 auth=(self.vrops_user, self.vrops_password),
528 verify = False)
529
530 if resp.status_code is not 204:
531 self.logger.warning("Failed to enable REST plugin {}. \nResponse code {}\nResponse Content: {}"\
532 .format(plugin_name, resp.status_code, resp.content))
533 return False
534
535 self.logger.info("Enabled REST plugin {}.".format(plugin_name))
536 return True
537
538 except Exception as exp:
539 self.logger.warning("Error enabling REST plugin for {} plugin: Exception: {}\n{}"\
540 .format(plugin_name, exp, traceback.format_exc()))
541
542 def create_alarm_notification_rule(self, alarm_name, alarm_id, resource_id):
543 """
544 Create notification rule for each alarm
545 Params:
546 alarm_name
547 alarm_id
548 resource_id
549
550 Returns:
551 notification_id: notification_id or None
552 """
553 notification_name = 'notify_' + alarm_name
554 notification_id = None
555 plugin_name = 'MON_module_REST_Plugin'
556
557 #1) Find the REST Plugin id details for - MON_module_REST_Plugin
558 plugin_id = self.check_if_plugin_configured(plugin_name)
559 if plugin_id is None:
560 self.logger.warning("Failed to get REST plugin_id for : {}".format('MON_module_REST_Plugin'))
561 return None
562
563 #2) Create Alarm notification rule
564 api_url = '/suite-api/api/notifications/rules'
565 headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
566 data = {
567 "name" : notification_name,
568 "pluginId" : plugin_id,
569 "resourceFilter": {
570 "matchResourceIdOnly": True,
571 "resourceId": resource_id
572 },
573 "alertDefinitionIdFilters" : {
574 "values" : [ alarm_id ]
575 }
576 }
577
578 resp = requests.post(self.vrops_site + api_url,
579 auth=(self.vrops_user, self.vrops_password),
580 headers=headers,
581 verify = False,
582 data=json.dumps(data))
583
584 if resp.status_code is not 201:
585 self.logger.warning("Failed to create Alarm notification rule {} for {} alarm."\
586 "\nResponse code: {}\nResponse content: {}"\
587 .format(notification_name, alarm_name, resp.status_code, resp.content))
588 return None
589
590 #parse notification id from response
591 resp_data = json.loads(resp.content)
592 if resp_data.get('id') is not None:
593 notification_id = resp_data['id']
594
595 self.logger.info("Created Alarm notification rule {} for {} alarm.".format(notification_name, alarm_name))
596 return notification_id
597
598 def get_vm_moref_id(self, vapp_uuid):
599 """
600 Get the moref_id of given VM
601 """
602 try:
603 if vapp_uuid:
604 vm_details = self.get_vapp_details_rest(vapp_uuid)
605 if vm_details and "vm_vcenter_info" in vm_details:
606 vm_moref_id = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
607
608 self.logger.info("Found vm_moref_id: {} for vApp UUID: {}".format(vm_moref_id, vapp_uuid))
609 return vm_moref_id
610
611 except Exception as exp:
612 self.logger.warning("Error occurred while getting VM moref ID for VM : {}\n{}"\
613 .format(exp, traceback.format_exc()))
614
615
616 def get_vapp_details_rest(self, vapp_uuid=None):
617 """
618 Method retrieve vapp detail from vCloud director
619
620 Args:
621 vapp_uuid - is vapp identifier.
622
623 Returns:
624 Returns VM MOref ID or return None
625 """
626
627 parsed_respond = {}
628 vca = None
629
630 if vapp_uuid is None:
631 return parsed_respond
632
633 vca = self.connect_as_admin()
634 if not vca:
635 self.logger.warning("Failed to connect to vCD")
636 return parsed_respond
637
638 url_list = [self.vcloud_site, '/api/vApp/vapp-', vapp_uuid]
639 get_vapp_restcall = ''.join(url_list)
640
641 if vca._session:
642 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
643 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
644 response = requests.get(get_vapp_restcall,
645 headers=headers,
646 verify=False)
647
648 if response.status_code != 200:
649 self.logger.warning("REST API call {} failed. Return status code {}"\
650 .format(get_vapp_restcall, response.content))
651 return parsed_respond
652
653 try:
654 xmlroot_respond = XmlElementTree.fromstring(response.content)
655
656 namespaces = {'vm': 'http://www.vmware.com/vcloud/v1.5',
657 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
658 "xmlns":"http://www.vmware.com/vcloud/v1.5"
659 }
660
661 # parse children section for other attrib
662 children_section = xmlroot_respond.find('vm:Children/', namespaces)
663 if children_section is not None:
664 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
665 if vCloud_extension_section is not None:
666 vm_vcenter_info = {}
667 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
668 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
669 if vmext is not None:
670 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
671 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
672
673 except Exception as exp :
674 self.logger.warning("Error occurred calling rest api for getting vApp details: {}\n{}"\
675 .format(exp, traceback.format_exc()))
676
677 return parsed_respond
678
679
680 def connect_as_admin(self):
681 """ Method connect as pvdc admin user to vCloud director.
682 There are certain action that can be done only by provider vdc admin user.
683 Organization creation / provider network creation etc.
684
685 Returns:
686 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
687 """
688
689 self.logger.debug("Logging into vCD org as admin.")
690
691 try:
692 host = self.vcloud_site
693 org = 'System'
694 client_as_admin = Client(host, verify_ssl_certs=False)
695 client_as_admin.set_credentials(BasicLoginCredentials(self.admin_username, org,\
696 self.admin_password))
697 except Exception as e:
698 self.logger.warning("Can't connect to a vCloud director as: {} with exception {}"\
699 .format(self.admin_username, e))
700
701 return client_as_admin
702
703
704 def get_vm_resource_id(self, vm_moref_id):
705 """ Find resource ID in vROPs using vm_moref_id
706 """
707 if vm_moref_id is None:
708 return None
709
710 api_url = '/suite-api/api/resources?resourceKind=VirtualMachine'
711 headers = {'Accept': 'application/json'}
712
713 resp = requests.get(self.vrops_site + api_url,
714 auth=(self.vrops_user, self.vrops_password),
715 verify = False, headers = headers)
716
717 if resp.status_code is not 200:
718 self.logger.warning("Failed to get resource details from vROPs for {}"\
719 "\nResponse code:{}\nResponse Content: {}"\
720 .format(vm_moref_id, resp.status_code, resp.content))
721 return None
722
723 vm_resource_id = None
724 try:
725 resp_data = json.loads(resp.content)
726 if resp_data.get('resourceList') is not None:
727 resource_list = resp_data.get('resourceList')
728 for resource in resource_list:
729 if resource.get('resourceKey') is not None:
730 resource_details = resource['resourceKey']
731 if resource_details.get('resourceIdentifiers') is not None:
732 resource_identifiers = resource_details['resourceIdentifiers']
733 for resource_identifier in resource_identifiers:
734 if resource_identifier['identifierType']['name']=='VMEntityObjectID':
735 if resource_identifier.get('value') is not None and \
736 resource_identifier['value']==vm_moref_id:
737 vm_resource_id = resource['identifier']
738 self.logger.info("Found VM resource ID: {} for vm_moref_id: {}"\
739 .format(vm_resource_id, vm_moref_id))
740
741 except Exception as exp:
742 self.logger.warning("get_vm_resource_id: Error in parsing {}\n{}"\
743 .format(exp, traceback.format_exc()))
744
745 return vm_resource_id
746
747
748 def get_metrics_data(self, metric={}):
749 """Get an individual metric's data of a resource.
750 Params:
751 'metric_name': Normalized name of metric (string)
752 'resource_uuid': Resource UUID (string)
753 'period': Time period in Period Unit for which metrics data to be collected from
754 Monitoring tool from now.
755 'period_unit': Period measurement unit can be one of 'HR', 'DAY', 'MONTH', 'YEAR'
756
757 Return a dict that contains:
758 'metric_name': Normalized name of metric (string)
759 'resource_uuid': Resource UUID (string)
760 'tenant_id': tenent id name in which the resource is present in string format
761 'metrics_data': Dictionary containing time_series & metrics_series data.
762 'time_series': List of individual time stamp values in msec
763 'metrics_series': List of individual metrics data values
764 Raises an exception upon error or when network is not found
765 """
766 return_data = {}
767 return_data['schema_version'] = "1.0"
768 return_data['schema_type'] = 'read_metric_data_response'
769 return_data['vim_uuid'] = metric['vim_uuid']
770 return_data['metric_name'] = metric['metric_name']
771 #To do - No metric_uuid in vROPs, thus returning '0'
772 return_data['metric_uuid'] = '0'
773 return_data['correlation_id'] = metric['correlation_id']
774 return_data['resource_uuid'] = metric['resource_uuid']
775 return_data['metrics_data'] = {'time_series':[], 'metrics_series':[]}
776 #To do - Need confirmation about uuid & id
777 ##if 'tenant_uuid' in metric and metric['tenant_uuid'] is not None:
778 ## return_data['tenant_uuid'] = metric['tenant_uuid']
779 ##else:
780 ## return_data['tenant_uuid'] = None
781 return_data['unit'] = None
782 #return_data['tenant_id'] = self.tenant_id
783 #self.logger.warning("return_data: {}".format(return_data))
784
785 #1) Get metric details from plugin specific file & format it into vROPs metrics
786 metric_key_params = self.get_default_Params(metric['metric_name'])
787
788 if not metric_key_params:
789 self.logger.warning("Metric not supported: {}".format(metric['metric_name']))
790 #To Do: Return message
791 return return_data
792
793 return_data['unit'] = metric_key_params['unit']
794
795 #2) Find the resource id in vROPs based on OSM resource_uuid
796 #2.a) Find vm_moref_id from vApp uuid in vCD
797 vm_moref_id = self.get_vm_moref_id(metric['resource_uuid'])
798 if vm_moref_id is None:
799 self.logger.warning("Failed to find vm morefid for vApp in vCD: {}".format(metric['resource_uuid']))
800 return return_data
801 #2.b) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
802 resource_id = self.get_vm_resource_id(vm_moref_id)
803 if resource_id is None:
804 self.logger.warning("Failed to find resource in vROPs: {}".format(metric['resource_uuid']))
805 return return_data
806
807 #3) Calculate begin & end time for period & period unit
808 end_time = int(round(time.time() * 1000))
809 if metric['collection_unit'] == 'YR':
810 time_diff = PERIOD_MSEC[metric['collection_unit']]
811 else:
812 time_diff = metric['collection_period']* PERIOD_MSEC[metric['collection_unit']]
813 begin_time = end_time - time_diff
814
815 #4) Get the metrics data
816 self.logger.info("metric_key_params['metric_key'] = {}".format(metric_key_params['metric_key']))
817 self.logger.info("end_time: {}, begin_time: {}".format(end_time, begin_time))
818
819 url_list = ['/suite-api/api/resources/', resource_id, '/stats?statKey=',\
820 metric_key_params['metric_key'], '&begin=', str(begin_time),'&end=',str(end_time)]
821 api_url = ''.join(url_list)
822 headers = {'Accept': 'application/json'}
823
824 resp = requests.get(self.vrops_site + api_url,
825 auth=(self.vrops_user, self.vrops_password),
826 verify = False, headers = headers)
827
828 if resp.status_code is not 200:
829 self.logger.warning("Failed to retrieve Metric data from vROPs for {}\nResponse code:{}\nResponse Content: {}"\
830 .format(metric['metric_name'], resp.status_code, resp.content))
831 return return_data
832
833 #5) Convert to required format
834 metrics_data = {}
835 json_data = json.loads(resp.content)
836 for resp_key,resp_val in six.iteritems(json_data):
837 if resp_key == 'values':
838 data = json_data['values'][0]
839 for data_k,data_v in six.iteritems(data):
840 if data_k == 'stat-list':
841 stat_list = data_v
842 for stat_list_k,stat_list_v in six.iteritems(stat_list):
843 for stat_keys,stat_vals in six.iteritems(stat_list_v[0]):
844 if stat_keys == 'timestamps':
845 metrics_data['time_series'] = stat_list_v[0]['timestamps']
846 if stat_keys == 'data':
847 metrics_data['metrics_series'] = stat_list_v[0]['data']
848
849 return_data['metrics_data'] = metrics_data
850
851 return return_data
852
853 def update_alarm_configuration(self, new_alarm_config):
854 """Update alarm configuration (i.e. Symptom & alarm) as per request
855 """
856 if new_alarm_config.get('alarm_uuid') is None:
857 self.logger.warning("alarm_uuid is required to update an Alarm")
858 return None
859 #1) Get Alarm details from it's uuid & find the symptom definition
860 alarm_details_json, alarm_details = self.get_alarm_defination_details(new_alarm_config['alarm_uuid'])
861 if alarm_details_json is None:
862 return None
863
864 try:
865 #2) Update the symptom definition
866 if alarm_details['alarm_id'] is not None and alarm_details['symptom_definition_id'] is not None:
867 symptom_defination_id = alarm_details['symptom_definition_id']
868 else:
869 self.logger.info("Symptom Definition ID not found for {}".format(new_alarm_config['alarm_uuid']))
870 return None
871
872 symptom_uuid = self.update_symptom_defination(symptom_defination_id, new_alarm_config)
873
874 #3) Update the alarm definition & Return UUID if successful update
875 if symptom_uuid is None:
876 self.logger.info("Symptom Definition details not found for {}"\
877 .format(new_alarm_config['alarm_uuid']))
878 return None
879 else:
880 alarm_uuid = self.reconfigure_alarm(alarm_details_json, new_alarm_config)
881 if alarm_uuid is None:
882 return None
883 else:
884 return alarm_uuid
885 except:
886 self.logger.error("Exception while updating alarm: {}".format(traceback.format_exc()))
887
888 def get_alarm_defination_details(self, alarm_uuid):
889 """Get alarm details based on alarm UUID
890 """
891 if alarm_uuid is None:
892 self.logger.warning("get_alarm_defination_details: Alarm UUID not provided")
893 return None, None
894
895 alarm_details = {}
896 json_data = {}
897 api_url = '/suite-api/api/alertdefinitions/AlertDefinition-'
898 headers = {'Accept': 'application/json'}
899
900 resp = requests.get(self.vrops_site + api_url + alarm_uuid,
901 auth=(self.vrops_user, self.vrops_password),
902 verify = False, headers = headers)
903
904 if resp.status_code is not 200:
905 self.logger.warning("Alarm to be updated not found: {}\nResponse code:{}\nResponse Content: {}"\
906 .format(alarm_uuid, resp.status_code, resp.content))
907 return None, None
908
909 try:
910 json_data = json.loads(resp.content)
911 if json_data['id'] is not None:
912 alarm_details['alarm_id'] = json_data['id']
913 alarm_details['alarm_name'] = json_data['name']
914 alarm_details['adapter_kind'] = json_data['adapterKindKey']
915 alarm_details['resource_kind'] = json_data['resourceKindKey']
916 alarm_details['type'] = json_data['type']
917 alarm_details['sub_type'] = json_data['subType']
918 alarm_details['symptom_definition_id'] = json_data['states'][0]['base-symptom-set']['symptomDefinitionIds'][0]
919 except Exception as exp:
920 self.logger.warning("Exception while retrieving alarm definition details: {}".format(exp))
921 return None, None
922
923 return json_data, alarm_details
924
925
926 def get_alarm_defination_by_name(self, alarm_name):
927 """Get alarm details based on alarm name
928 """
929 status = False
930 alert_match_list = []
931
932 if alarm_name is None:
933 self.logger.warning("get_alarm_defination_by_name: Alarm name not provided")
934 return alert_match_list
935
936 json_data = {}
937 api_url = '/suite-api/api/alertdefinitions'
938 headers = {'Accept': 'application/json'}
939
940 resp = requests.get(self.vrops_site + api_url,
941 auth=(self.vrops_user, self.vrops_password),
942 verify = False, headers = headers)
943
944 if resp.status_code is not 200:
945 self.logger.warning("get_alarm_defination_by_name: Error in response: {}\nResponse code:{}"\
946 "\nResponse Content: {}".format(alarm_name, resp.status_code, resp.content))
947 return alert_match_list
948
949 try:
950 json_data = json.loads(resp.content)
951 if json_data['alertDefinitions'] is not None:
952 alerts_list = json_data['alertDefinitions']
953 alert_match_list = list(filter(lambda alert: alert['name'] == alarm_name, alerts_list))
954 status = False if not alert_match_list else True
955 #self.logger.debug("Found alert_match_list: {}for larm_name: {},\nstatus: {}".format(alert_match_list, alarm_name,status))
956
957 return alert_match_list
958
959 except Exception as exp:
960 self.logger.warning("Exception while searching alarm definition: {}".format(exp))
961 return alert_match_list
962
963
964 def update_symptom_defination(self, symptom_uuid, new_alarm_config):
965 """Update symptom definition based on new alarm input configuration
966 """
967 #1) Get symptom definition details
968 symptom_details = self.get_symptom_defination_details(symptom_uuid)
969 #print "\n\nsymptom_details: {}".format(symptom_details)
970 if symptom_details is None:
971 return None
972
973 if 'severity' in new_alarm_config and new_alarm_config['severity'] is not None:
974 symptom_details['state']['severity'] = severity_mano2vrops[new_alarm_config['severity']]
975 if 'operation' in new_alarm_config and new_alarm_config['operation'] is not None:
976 symptom_details['state']['condition']['operator'] = OPERATION_MAPPING[new_alarm_config['operation']]
977 if 'threshold_value' in new_alarm_config and new_alarm_config['threshold_value'] is not None:
978 symptom_details['state']['condition']['value'] = new_alarm_config['threshold_value']
979 #Find vrops metric key from metric_name, if required
980 """
981 if 'metric_name' in new_alarm_config and new_alarm_config['metric_name'] is not None:
982 metric_key_params = self.get_default_Params(new_alarm_config['metric_name'])
983 if not metric_key_params:
984 self.logger.warning("Metric not supported: {}".format(config_dict['metric_name']))
985 return None
986 symptom_details['state']['condition']['key'] = metric_key_params['metric_key']
987 """
988 self.logger.info("Fetched Symptom details : {}".format(symptom_details))
989
990 api_url = '/suite-api/api/symptomdefinitions'
991 headers = {'Content-Type': 'application/json', 'Accept':'application/json'}
992 data = json.dumps(symptom_details)
993 resp = requests.put(self.vrops_site + api_url,
994 auth=(self.vrops_user, self.vrops_password),
995 headers=headers,
996 verify = False,
997 data=data)
998
999 if resp.status_code != 200:
1000 self.logger.warning("Failed to update Symptom definition: {}, response {}"\
1001 .format(symptom_uuid, resp.content))
1002 return None
1003
1004
1005 if symptom_uuid is not None:
1006 self.logger.info("Symptom definition updated {} for alarm: {}"\
1007 .format(symptom_uuid, new_alarm_config['alarm_uuid']))
1008 return symptom_uuid
1009 else:
1010 self.logger.warning("Failed to update Symptom Definition {} for : {}"\
1011 .format(symptom_uuid, new_alarm_config['alarm_uuid']))
1012 return None
1013
1014
1015 def get_symptom_defination_details(self, symptom_uuid):
1016 """Get symptom definition details
1017 """
1018 symptom_details = {}
1019 if symptom_uuid is None:
1020 self.logger.warning("get_symptom_defination_details: Symptom UUID not provided")
1021 return None
1022
1023 api_url = '/suite-api/api/symptomdefinitions/'
1024 headers = {'Accept': 'application/json'}
1025
1026 resp = requests.get(self.vrops_site + api_url + symptom_uuid,
1027 auth=(self.vrops_user, self.vrops_password),
1028 verify = False, headers = headers)
1029
1030 if resp.status_code is not 200:
1031 self.logger.warning("Symptom definition not found {} \nResponse code:{}\nResponse Content: {}"\
1032 .format(symptom_uuid, resp.status_code, resp.content))
1033 return None
1034
1035 symptom_details = json.loads(resp.content)
1036 #print "New symptom Details: {}".format(symptom_details)
1037 return symptom_details
1038
1039
1040 def reconfigure_alarm(self, alarm_details_json, new_alarm_config):
1041 """Reconfigure alarm definition as per input
1042 """
1043 if 'severity' in new_alarm_config and new_alarm_config['severity'] is not None:
1044 alarm_details_json['states'][0]['severity'] = new_alarm_config['severity']
1045 if 'description' in new_alarm_config and new_alarm_config['description'] is not None:
1046 alarm_details_json['description'] = new_alarm_config['description']
1047
1048 api_url = '/suite-api/api/alertdefinitions'
1049 headers = {'Content-Type': 'application/json', 'Accept':'application/json'}
1050 data = json.dumps(alarm_details_json)
1051 resp = requests.put(self.vrops_site + api_url,
1052 auth=(self.vrops_user, self.vrops_password),
1053 headers=headers,
1054 verify = False,
1055 data=data)
1056
1057 if resp.status_code != 200:
1058 self.logger.warning("Failed to update Alarm definition: {}, response code {}, response content: {}"\
1059 .format(alarm_details_json['id'], resp.status_code, resp.content))
1060 return None
1061 else:
1062 parsed_alarm_details = json.loads(resp.content)
1063 alarm_def_uuid = parsed_alarm_details['id'].split('-', 1)[1]
1064 self.logger.info("Successfully updated Alarm definition: {}".format(alarm_def_uuid))
1065 return alarm_def_uuid
1066
1067 def delete_alarm_configuration(self, delete_alarm_req_dict):
1068 """Delete complete alarm configuration
1069 """
1070 if delete_alarm_req_dict['alarm_uuid'] is None:
1071 self.logger.info("delete_alarm_configuration: Alarm UUID not provided")
1072 return None
1073 #1)Get alarm & symptom definition details
1074 alarm_details_json, alarm_details = self.get_alarm_defination_details(delete_alarm_req_dict['alarm_uuid'])
1075 if alarm_details is None or alarm_details_json is None:
1076 return None
1077
1078 #2) Delete alarm notification
1079 rule_id = self.delete_notification_rule(alarm_details['alarm_name'])
1080 if rule_id is None:
1081 return None
1082
1083 #3) Delete alarm configuration
1084 alarm_id = self.delete_alarm_defination(alarm_details['alarm_id'])
1085 if alarm_id is None:
1086 return None
1087
1088 #4) Delete alarm symptom
1089 symptom_id = self.delete_symptom_definition(alarm_details['symptom_definition_id'])
1090 if symptom_id is None:
1091 return None
1092 else:
1093 self.logger.info("Completed deleting alarm configuration: {}"\
1094 .format(delete_alarm_req_dict['alarm_uuid']))
1095 return delete_alarm_req_dict['alarm_uuid']
1096
1097 def delete_notification_rule(self, alarm_name):
1098 """Deleted notification rule defined for a particular alarm
1099 """
1100 rule_id = self.get_notification_rule_id_by_alarm_name(alarm_name)
1101 if rule_id is None:
1102 return None
1103 else:
1104 api_url = '/suite-api/api/notifications/rules/'
1105 headers = {'Accept':'application/json'}
1106 resp = requests.delete(self.vrops_site + api_url + rule_id,
1107 auth=(self.vrops_user, self.vrops_password),
1108 verify = False, headers = headers)
1109 if resp.status_code is not 204:
1110 self.logger.warning("Failed to delete notification rules for {}".format(alarm_name))
1111 return None
1112 else:
1113 self.logger.info("Deleted notification rules for {}".format(alarm_name))
1114 return rule_id
1115
1116 def get_notification_rule_id_by_alarm_name(self, alarm_name):
1117 """Find created Alarm notification rule id by alarm name
1118 """
1119 alarm_notify_id = 'notify_' + alarm_name
1120 api_url = '/suite-api/api/notifications/rules'
1121 headers = {'Content-Type': 'application/json', 'Accept':'application/json'}
1122 resp = requests.get(self.vrops_site + api_url,
1123 auth=(self.vrops_user, self.vrops_password),
1124 verify = False, headers = headers)
1125
1126 if resp.status_code is not 200:
1127 self.logger.warning("Failed to get notification rules details for {}"\
1128 .format(alarm_name))
1129 return None
1130
1131 notifications = json.loads(resp.content)
1132 if notifications is not None and 'notification-rule' in notifications:
1133 notifications_list = notifications['notification-rule']
1134 for dict in notifications_list:
1135 if dict['name'] is not None and dict['name'] == alarm_notify_id:
1136 notification_id = dict['id']
1137 self.logger.info("Found Notification id to be deleted: {} for {}"\
1138 .format(notification_id, alarm_name))
1139 return notification_id
1140
1141 self.logger.warning("Notification id to be deleted not found for {}"\
1142 .format(alarm_name))
1143 return None
1144
1145 def delete_alarm_defination(self, alarm_id):
1146 """Delete created Alarm definition
1147 """
1148 api_url = '/suite-api/api/alertdefinitions/'
1149 headers = {'Accept':'application/json'}
1150 resp = requests.delete(self.vrops_site + api_url + alarm_id,
1151 auth=(self.vrops_user, self.vrops_password),
1152 verify = False, headers = headers)
1153 if resp.status_code is not 204:
1154 self.logger.warning("Failed to delete alarm definition {}".format(alarm_id))
1155 return None
1156 else:
1157 self.logger.info("Deleted alarm definition {}".format(alarm_id))
1158 return alarm_id
1159
1160 def delete_symptom_definition(self, symptom_id):
1161 """Delete symptom definition
1162 """
1163 api_url = '/suite-api/api/symptomdefinitions/'
1164 headers = {'Accept':'application/json'}
1165 resp = requests.delete(self.vrops_site + api_url + symptom_id,
1166 auth=(self.vrops_user, self.vrops_password),
1167 verify = False, headers = headers)
1168 if resp.status_code is not 204:
1169 self.logger.warning("Failed to delete symptom definition {}".format(symptom_id))
1170 return None
1171 else:
1172 self.logger.info("Deleted symptom definition {}".format(symptom_id))
1173 return symptom_id
1174
1175
1176 def verify_metric_support(self, metric_info):
1177 """Verify, if Metric is supported by vROPs plugin, verify metric unit & return status
1178 Returns:
1179 status: True if supported, False if not supported
1180 """
1181 status = False
1182 if 'metric_name' not in metric_info:
1183 self.logger.debug("Metric name not provided: {}".format(metric_info))
1184 return status
1185 metric_key_params = self.get_default_Params(metric_info['metric_name'].lower())
1186 if not metric_key_params:
1187 self.logger.warning("Metric not supported: {}".format(metric_info['metric_name']))
1188 return status
1189 else:
1190 #If Metric is supported, verify optional metric unit & return status
1191 if 'metric_unit' in metric_info:
1192 if metric_key_params.get('unit') == metric_info['metric_unit']:
1193 self.logger.info("Metric is supported with unit: {}".format(metric_info['metric_name']))
1194 status = True
1195 else:
1196 self.logger.debug("Metric supported but there is unit mismatch for: {}."\
1197 "Supported unit: {}"\
1198 .format(metric_info['metric_name'],metric_key_params['unit']))
1199 status = True
1200 return status
1201
1202 def get_triggered_alarms_list(self, list_alarm_input):
1203 """Get list of triggered alarms on a resource based on alarm input request.
1204 """
1205 #TO Do - Need to add filtering of alarms based on Severity & alarm name
1206
1207 triggered_alarms_list = []
1208 if list_alarm_input.get('resource_uuid') is None:
1209 self.logger.warning("Resource UUID is required to get triggered alarms list")
1210 return triggered_alarms_list
1211
1212 #1)Find vROPs resource ID using RO resource UUID
1213 vrops_resource_id = self.get_vrops_resourceid_from_ro_uuid(list_alarm_input['resource_uuid'])
1214 if vrops_resource_id is None:
1215 return triggered_alarms_list
1216
1217 #2)Get triggered alarms on particular resource
1218 triggered_alarms_list = self.get_triggered_alarms_on_resource(list_alarm_input['resource_uuid'], vrops_resource_id)
1219 return triggered_alarms_list
1220
1221 def get_vrops_resourceid_from_ro_uuid(self, ro_resource_uuid):
1222 """Fetch vROPs resource ID using resource UUID from RO/SO
1223 """
1224 #1) Find vm_moref_id from vApp uuid in vCD
1225 vm_moref_id = self.get_vm_moref_id(ro_resource_uuid)
1226 if vm_moref_id is None:
1227 self.logger.warning("Failed to find vm morefid for vApp in vCD: {}".format(ro_resource_uuid))
1228 return None
1229
1230 #2) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
1231 vrops_resource_id = self.get_vm_resource_id(vm_moref_id)
1232 if vrops_resource_id is None:
1233 self.logger.warning("Failed to find resource in vROPs: {}".format(ro_resource_uuid))
1234 return None
1235 return vrops_resource_id
1236
1237
1238 def get_triggered_alarms_on_resource(self, ro_resource_uuid, vrops_resource_id):
1239 """Get triggered alarms on particular resource & return list of dictionary of alarms
1240 """
1241 resource_alarms = []
1242 api_url = '/suite-api/api/alerts?resourceId='
1243 headers = {'Accept':'application/json'}
1244 resp = requests.get(self.vrops_site + api_url + vrops_resource_id,
1245 auth=(self.vrops_user, self.vrops_password),
1246 verify = False, headers = headers)
1247
1248 if resp.status_code is not 200:
1249 self.logger.warning("Failed to get triggered alarms for {}"\
1250 .format(ro_resource_uuid))
1251 return None
1252
1253 all_alerts = json.loads(resp.content)
1254 if 'alerts' in all_alerts:
1255 if not all_alerts['alerts']:
1256 self.logger.info("No alarms present on resource {}".format(ro_resource_uuid))
1257 return resource_alarms
1258 all_alerts_list = all_alerts['alerts']
1259 for alarm in all_alerts_list:
1260 #self.logger.info("Triggered Alarm {}".format(alarm))
1261 if alarm['alertDefinitionName'] is not None and\
1262 len(alarm['alertDefinitionName'].split('-', 1)) == 2:
1263 if alarm['alertDefinitionName'].split('-', 1)[1] == ro_resource_uuid:
1264 alarm_instance = {}
1265 alarm_instance['alarm_uuid'] = alarm['alertDefinitionId'].split('-', 1)[1]
1266 alarm_instance['resource_uuid'] = ro_resource_uuid
1267 alarm_instance['alarm_instance_uuid'] = alarm['alertId']
1268 alarm_instance['vim_type'] = 'VMware'
1269 #find severity of alarm
1270 severity = None
1271 for key,value in six.iteritems(severity_mano2vrops):
1272 if value == alarm['alertLevel']:
1273 severity = key
1274 if severity is None:
1275 severity = 'INDETERMINATE'
1276 alarm_instance['severity'] = severity
1277 alarm_instance['status'] = alarm['status']
1278 alarm_instance['start_date'] = self.convert_date_time(alarm['startTimeUTC'])
1279 alarm_instance['update_date'] = self.convert_date_time(alarm['updateTimeUTC'])
1280 alarm_instance['cancel_date'] = self.convert_date_time(alarm['cancelTimeUTC'])
1281 self.logger.info("Triggered Alarm on resource {}".format(alarm_instance))
1282 resource_alarms.append(alarm_instance)
1283 if not resource_alarms:
1284 self.logger.info("No alarms present on resource {}".format(ro_resource_uuid))
1285 return resource_alarms
1286
1287 def convert_date_time(self, date_time):
1288 """Convert the input UTC time in msec to OSM date time format
1289 """
1290 date_time_formatted = '0000-00-00T00:00:00'
1291 if date_time != 0:
1292 complete_datetime = datetime.datetime.fromtimestamp(date_time/1000.0, tz=pytz.utc).isoformat('T')
1293 date_time_formatted = complete_datetime.split('.',1)[0]
1294 return date_time_formatted
1295