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