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