1 # -*- coding: utf-8 -*-
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: osslegalrouting@vmware.com
25 Monitoring metrics & creating Alarm definitions in vROPs
31 from pyvcloud
.vcd
.client
import BasicLoginCredentials
32 from pyvcloud
.vcd
.client
import Client
35 from xml
.etree
import ElementTree
as XmlElementTree
39 from OpenSSL
.crypto
import load_certificate
, FILETYPE_PEM
42 from socket
import getfqdn
44 from requests
.packages
.urllib3
.exceptions
import InsecureRequestWarning
45 requests
.packages
.urllib3
.disable_warnings(InsecureRequestWarning
)
47 OPERATION_MAPPING
= {'GE':'GT_EQ', 'LE':'LT_EQ', 'GT':'GT', 'LT':'LT', 'EQ':'EQ'}
48 severity_mano2vrops
= {'WARNING':'WARNING', 'MINOR':'WARNING', 'MAJOR':"IMMEDIATE",\
49 'CRITICAL':'CRITICAL', 'INDETERMINATE':'UNKNOWN'}
50 PERIOD_MSEC
= {'HR':3600000,'DAY':86400000,'WEEK':604800000,'MONTH':2678400000,'YEAR':31536000000}
52 #To Do - Add actual webhook url & certificate
53 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/www.vrops_webservice.com.cert'
54 #webhook_url = "https://mano-dev-1:8080/notify/" #for testing
55 webhook_url
= "https://" + getfqdn() + ":8080/notify/"
56 SSL_CERTIFICATE_FILE_NAME
= ('vROPs_Webservice/SSL_certificate/' + getfqdn() + ".cert")
57 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/10.172.137.214.cert' #for testing
59 MODULE_DIR
= os
.path
.dirname(__file__
)
60 CONFIG_FILE_NAME
= 'vrops_config.xml'
61 CONFIG_FILE_PATH
= os
.path
.join(MODULE_DIR
, CONFIG_FILE_NAME
)
62 SSL_CERTIFICATE_FILE_PATH
= os
.path
.join(MODULE_DIR
, SSL_CERTIFICATE_FILE_NAME
)
65 """MON Plugin class for vROPs telemetry plugin
68 """Constructor of MON plugin
70 'access_config': dictionary with VIM access information based on VIM type.
71 This contains a consolidate version of VIM & monitoring tool config at creation and
72 particular VIM config at their attachment.
73 For VIM type: 'vmware',
74 access_config - {'vrops_site':<>, 'vrops_user':<>, 'vrops_password':<>,
75 'vcloud-site':<>,'admin_username':<>,'admin_password':<>,
76 'nsx_manager':<>,'nsx_user':<>,'nsx_password':<>,
77 'vcenter_ip':<>,'vcenter_port':<>,'vcenter_user':<>,'vcenter_password':<>,
78 'vim_tenant_name':<>,'orgname':<>}
81 Returns: Raise an exception if some needed parameter is missing, but it must not do any connectivity
84 self
.logger
= logging
.getLogger('PluginReceiver.MonPlugin')
85 self
.logger
.setLevel(logging
.DEBUG
)
87 access_config
= self
.get_default_Params('Access_Config')
88 self
.access_config
= access_config
89 if not bool(access_config
):
90 self
.logger
.error("Access configuration not provided in vROPs Config file")
91 raise KeyError("Access configuration not provided in vROPs Config file")
94 self
.vrops_site
= access_config
['vrops_site']
95 self
.vrops_user
= access_config
['vrops_user']
96 self
.vrops_password
= access_config
['vrops_password']
97 self
.vcloud_site
= access_config
['vcloud-site']
98 self
.admin_username
= access_config
['admin_username']
99 self
.admin_password
= access_config
['admin_password']
100 self
.tenant_id
= access_config
['tenant_id']
101 except KeyError as exp
:
102 self
.logger
.error("Check Access configuration in vROPs Config file: {}".format(exp
))
103 raise KeyError("Check Access configuration in vROPs Config file: {}".format(exp
))
106 def configure_alarm(self
, config_dict
= {}):
107 """Configures or creates a new alarm using the input parameters in config_dict
109 "alarm_name": Alarm name in string format
110 "description": Description of alarm in string format
111 "resource_uuid": Resource UUID for which alarm needs to be configured. in string format
112 "Resource type": String resource type: 'VDU' or 'host'
113 "Severity": 'WARNING', 'MINOR', 'MAJOR', 'CRITICAL'
114 "metric_name": Metric key in string format
115 "operation": One of ('GE', 'LE', 'GT', 'LT', 'EQ')
116 "threshold_value": Defines the threshold (up to 2 fraction digits) that,
117 if crossed, will trigger the alarm.
118 "unit": Unit of measurement in string format
119 "statistic": AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM
121 Default parameters for each alarm are read from the plugin specific config file.
122 Dict of default parameters is as follows:
123 default_params keys = {'cancel_cycles','wait_cycles','resource_kind','adapter_kind',
124 'alarm_type','alarm_subType',impact}
126 Returns the UUID of created alarm or None
129 #1) get alarm & metrics parameters from plugin specific file
130 def_a_params
= self
.get_default_Params(config_dict
['alarm_name'])
132 self
.logger
.warn("Alarm not supported: {}".format(config_dict
['alarm_name']))
134 metric_key_params
= self
.get_default_Params(config_dict
['metric_name'])
135 if not metric_key_params
:
136 self
.logger
.warn("Metric not supported: {}".format(config_dict
['metric_name']))
139 #1.2) Check if alarm definition already exists
140 vrops_alarm_name
= def_a_params
['vrops_alarm']+ '-' + config_dict
['resource_uuid']
141 alert_def_list
= self
.get_alarm_defination_by_name(vrops_alarm_name
)
143 self
.logger
.warn("Alarm already exists: {}. Try updating by update_alarm_request"\
144 .format(vrops_alarm_name
))
147 #2) create symptom definition
148 symptom_params
={'cancel_cycles': (def_a_params
['cancel_period']/300)*def_a_params
['cancel_cycles'],
149 'wait_cycles': (def_a_params
['period']/300)*def_a_params
['evaluation'],
150 'resource_kind_key': def_a_params
['resource_kind'],
151 'adapter_kind_key': def_a_params
['adapter_kind'],
152 'symptom_name':vrops_alarm_name
,
153 'severity': severity_mano2vrops
[config_dict
['severity']],
154 'metric_key':metric_key_params
['metric_key'],
155 'operation':OPERATION_MAPPING
[config_dict
['operation']],
156 'threshold_value':config_dict
['threshold_value']}
157 symptom_uuid
= self
.create_symptom(symptom_params
)
158 if symptom_uuid
is not None:
159 self
.logger
.info("Symptom defined: {} with ID: {}".format(symptom_params
['symptom_name'],symptom_uuid
))
161 self
.logger
.warn("Failed to create Symptom: {}".format(symptom_params
['symptom_name']))
163 #3) create alert definition
164 #To Do - Get type & subtypes for all 5 alarms
165 alarm_params
= {'name':vrops_alarm_name
,
166 'description':config_dict
['description']\
167 if config_dict
.has_key('description') and config_dict
['description'] is not None else config_dict
['alarm_name'],
168 'adapterKindKey':def_a_params
['adapter_kind'],
169 'resourceKindKey':def_a_params
['resource_kind'],
170 'waitCycles':1, 'cancelCycles':1,
171 'type':def_a_params
['alarm_type'], 'subType':def_a_params
['alarm_subType'],
172 'severity':severity_mano2vrops
[config_dict
['severity']],
173 'symptomDefinitionId':symptom_uuid
,
174 'impact':def_a_params
['impact']}
176 alarm_def
= self
.create_alarm_definition(alarm_params
)
177 if alarm_def
is None:
178 self
.logger
.warn("Failed to create Alert: {}".format(alarm_params
['name']))
181 self
.logger
.info("Alarm defined: {} with ID: {}".format(alarm_params
['name'],alarm_def
))
183 #4) Find vm_moref_id from vApp uuid in vCD
184 vm_moref_id
= self
.get_vm_moref_id(config_dict
['resource_uuid'])
185 if vm_moref_id
is None:
186 self
.logger
.warn("Failed to find vm morefid for vApp in vCD: {}".format(config_dict
['resource_uuid']))
189 #5) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
190 resource_id
= self
.get_vm_resource_id(vm_moref_id
)
191 if resource_id
is None:
192 self
.logger
.warn("Failed to find resource in vROPs: {}".format(config_dict
['resource_uuid']))
195 #6) Configure alarm notification for a particular VM using it's resource_id
196 notification_id
= self
.create_alarm_notification_rule(vrops_alarm_name
, alarm_def
, resource_id
)
197 if notification_id
is None:
200 alarm_def_uuid
= alarm_def
.split('-', 1)[1]
201 self
.logger
.info("Alarm defination created with notification: {} with ID: {}"\
202 .format(alarm_params
['name'],alarm_def_uuid
))
203 #Return alarm defination UUID by removing 'AlertDefinition' from UUID
204 return (alarm_def_uuid
)
206 def get_default_Params(self
, metric_alarm_name
):
208 Read the default config parameters from plugin specific file stored with plugin file.
210 metric_alarm_name: Name of the alarm, whose config parameters to be read from the config file.
214 source
= open(CONFIG_FILE_PATH
, 'r')
215 except IOError as exp
:
216 msg
= ("Could not read Config file: {}, \nException: {}"\
217 .format(CONFIG_FILE_PATH
, exp
))
218 self
.logger
.error(msg
)
221 tree
= XmlElementTree
.parse(source
)
222 alarms
= tree
.getroot()
224 if alarm
.tag
== metric_alarm_name
:
226 if param
.tag
in ("period", "evaluation", "cancel_period", "alarm_type",\
227 "cancel_cycles", "alarm_subType"):
228 a_params
[param
.tag
] = int(param
.text
)
229 elif param
.tag
in ("enabled", "repeat"):
230 if(param
.text
.lower() == "true"):
231 a_params
[param
.tag
] = True
233 a_params
[param
.tag
] = False
235 a_params
[param
.tag
] = param
.text
240 def create_symptom(self
, symptom_params
):
241 """Create Symptom definition for an alarm
243 symptom_params: Dict of parameters required for defining a symptom as follows
246 resource_kind_key = "VirtualMachine"
247 adapter_kind_key = "VMWARE"
248 symptom_name = Test_Memory_Usage_TooHigh
253 Returns the uuid of Symptom definition
258 api_url
= '/suite-api/api/symptomdefinitions'
259 headers
= {'Content-Type': 'application/json','Accept': 'application/json'}
262 "name": symptom_params
['symptom_name'],
263 "adapterKindKey": symptom_params
['adapter_kind_key'],
264 "resourceKindKey": symptom_params
['resource_kind_key'],
265 "waitCycles": symptom_params
['wait_cycles'],
266 "cancelCycles": symptom_params
['cancel_cycles'],
268 "severity": symptom_params
['severity'],
270 "type": "CONDITION_HT",
271 "key": symptom_params
['metric_key'],
272 "operator": symptom_params
['operation'],
273 "value": symptom_params
['threshold_value'],
274 "valueType": "NUMERIC",
276 "thresholdType": "STATIC"
281 resp
= requests
.post(self
.vrops_site
+ api_url
,
282 auth
=(self
.vrops_user
, self
.vrops_password
),
285 data
=json
.dumps(data
))
287 if resp
.status_code
!= 201:
288 self
.logger
.warn("Failed to create Symptom definition: {}, response {}"\
289 .format(symptom_params
['symptom_name'], resp
.content
))
292 resp_data
= json
.loads(resp
.content
)
293 if resp_data
.get('id') is not None:
294 symptom_id
= resp_data
['id']
298 except Exception as exp
:
299 self
.logger
.warn("Error creating symptom definition : {}\n{}"\
300 .format(exp
, traceback
.format_exc()))
303 def create_alarm_definition(self
, alarm_params
):
305 Create an alarm definition in vROPs
308 'description':Alarm description,
309 'adapterKindKey': Adapter type in vROPs "VMWARE",
310 'resourceKindKey':Resource type in vROPs "VirtualMachine",
311 'waitCycles': No of wait cycles,
312 'cancelCycles': No of cancel cycles,
314 'subType': Alarm subtype,
315 'severity': Severity in vROPs "CRITICAL",
316 'symptomDefinitionId':symptom Definition uuid,
317 'impact': impact 'risk'
319 'alarm_uuid': returns alarm uuid
325 api_url
= '/suite-api/api/alertdefinitions'
326 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
328 "name": alarm_params
['name'],
329 "description": alarm_params
['description'],
330 "adapterKindKey": alarm_params
['adapterKindKey'],
331 "resourceKindKey": alarm_params
['resourceKindKey'],
334 "type": alarm_params
['type'],
335 "subType": alarm_params
['subType'],
338 "severity": alarm_params
['severity'],
341 "type": "SYMPTOM_SET",
343 "aggregation": "ALL",
344 "symptomSetOperator": "AND",
345 "symptomDefinitionIds": [alarm_params
['symptomDefinitionId']]
348 "impactType": "BADGE",
349 "detail": alarm_params
['impact']
355 resp
= requests
.post(self
.vrops_site
+ api_url
,
356 auth
=(self
.vrops_user
, self
.vrops_password
),
359 data
=json
.dumps(data
))
361 if resp
.status_code
!= 201:
362 self
.logger
.warn("Failed to create Alarm definition: {}, response {}"\
363 .format(alarm_params
['name'], resp
.content
))
366 resp_data
= json
.loads(resp
.content
)
367 if resp_data
.get('id') is not None:
368 alarm_uuid
= resp_data
['id']
372 except Exception as exp
:
373 self
.logger
.warn("Error creating alarm definition : {}\n{}".format(exp
, traceback
.format_exc()))
376 def configure_rest_plugin(self
):
378 Creates REST Plug-in for vROPs outbound alerts
383 plugin_name
= 'MON_module_REST_Plugin'
384 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
386 #If REST plugin not configured, configure it
387 if plugin_id
is not None:
391 cert_file_string
= open(SSL_CERTIFICATE_FILE_PATH
, "rb").read()
392 except IOError as exp
:
393 msg
= ("Could not read SSL certificate file: {}".format(SSL_CERTIFICATE_FILE_PATH
))
394 self
.logger
.error(msg
)
396 cert
= load_certificate(FILETYPE_PEM
, cert_file_string
)
397 certificate
= cert
.digest("sha1")
398 api_url
= '/suite-api/api/alertplugins'
399 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
401 "pluginTypeId": "RestPlugin",
409 "name": "Content-type",
410 "value": "application/json"
413 "name": "Certificate",
417 "name": "ConnectionCount",
423 resp
= requests
.post(self
.vrops_site
+ api_url
,
424 auth
=(self
.vrops_user
, self
.vrops_password
),
427 data
=json
.dumps(data
))
429 if resp
.status_code
is not 201:
430 self
.logger
.warn("Failed to create REST Plugin: {} for url: {}, \nresponse code: {},"\
431 "\nresponse content: {}".format(plugin_name
, webhook_url
,\
432 resp
.status_code
, resp
.content
))
435 resp_data
= json
.loads(resp
.content
)
436 if resp_data
.get('pluginId') is not None:
437 plugin_id
= resp_data
['pluginId']
439 if plugin_id
is None:
440 self
.logger
.warn("Failed to get REST Plugin ID for {}, url: {}".format(plugin_name
, webhook_url
))
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
)
446 self
.logger
.warn("Failed to enable created REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
449 self
.logger
.info("Enabled REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
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
457 #Find the REST Plugin id details for - MON_module_REST_Plugin
458 api_url
= '/suite-api/api/alertplugins'
459 headers
= {'Accept': 'application/json'}
461 resp
= requests
.get(self
.vrops_site
+ api_url
,
462 auth
=(self
.vrops_user
, self
.vrops_password
),
463 verify
= False, headers
= headers
)
465 if resp
.status_code
is not 200:
466 self
.logger
.warn("Failed to REST GET Alarm plugin details \nResponse code: {}\nResponse content: {}"\
467 .format(resp
.status_code
, resp
.content
))
470 # Look for specific plugin & parse pluginId for 'MON_module_REST_Plugin'
471 plugins_list
= json
.loads(resp
.content
)
472 if plugins_list
.get('notificationPluginInstances') is not None:
473 for notify_plugin
in plugins_list
['notificationPluginInstances']:
474 if notify_plugin
.get('name') is not None and notify_plugin
['name'] == plugin_name
:
475 plugin_id
= notify_plugin
.get('pluginId')
477 if plugin_id
is None:
478 self
.logger
.warn("REST plugin {} not found".format(plugin_name
))
481 self
.logger
.info("Found REST Plugin: {}".format(plugin_name
))
485 def enable_rest_plugin(self
, plugin_id
, plugin_name
):
487 Enable the REST plugin using plugin_id
488 Params: plugin_id: plugin ID string that is to be enabled
489 Returns: status (Boolean) - True for success, False for failure
492 if plugin_id
is None or plugin_name
is None:
493 self
.logger
.debug("enable_rest_plugin() : Plugin ID or plugin_name not provided for {} plugin"\
494 .format(plugin_name
))
498 api_url
= "/suite-api/api/alertplugins/{}/enable/True".format(plugin_id
)
500 resp
= requests
.put(self
.vrops_site
+ api_url
,
501 auth
=(self
.vrops_user
, self
.vrops_password
),
504 if resp
.status_code
is not 204:
505 self
.logger
.warn("Failed to enable REST plugin {}. \nResponse code {}\nResponse Content: {}"\
506 .format(plugin_name
, resp
.status_code
, resp
.content
))
509 self
.logger
.info("Enabled REST plugin {}.".format(plugin_name
))
512 except Exception as exp
:
513 self
.logger
.warn("Error enabling REST plugin for {} plugin: Exception: {}\n{}"\
514 .format(plugin_name
, exp
, traceback
.format_exc()))
516 def create_alarm_notification_rule(self
, alarm_name
, alarm_id
, resource_id
):
518 Create notification rule for each alarm
525 notification_id: notification_id or None
527 notification_name
= 'notify_' + alarm_name
528 notification_id
= None
529 plugin_name
= 'MON_module_REST_Plugin'
531 #1) Find the REST Plugin id details for - MON_module_REST_Plugin
532 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
533 if plugin_id
is None:
534 self
.logger
.warn("Failed to get REST plugin_id for : {}".format('MON_module_REST_Plugin'))
537 #2) Create Alarm notification rule
538 api_url
= '/suite-api/api/notifications/rules'
539 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
541 "name" : notification_name
,
542 "pluginId" : plugin_id
,
544 "matchResourceIdOnly": True,
545 "resourceId": resource_id
547 "alertDefinitionIdFilters" : {
548 "values" : [ alarm_id
]
552 resp
= requests
.post(self
.vrops_site
+ api_url
,
553 auth
=(self
.vrops_user
, self
.vrops_password
),
556 data
=json
.dumps(data
))
558 if resp
.status_code
is not 201:
559 self
.logger
.warn("Failed to create Alarm notification rule {} for {} alarm."\
560 "\nResponse code: {}\nResponse content: {}"\
561 .format(notification_name
, alarm_name
, resp
.status_code
, resp
.content
))
564 #parse notification id from response
565 resp_data
= json
.loads(resp
.content
)
566 if resp_data
.get('id') is not None:
567 notification_id
= resp_data
['id']
569 self
.logger
.info("Created Alarm notification rule {} for {} alarm.".format(notification_name
, alarm_name
))
570 return notification_id
572 def get_vm_moref_id(self
, vapp_uuid
):
574 Get the moref_id of given VM
578 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
579 if vm_details
and "vm_vcenter_info" in vm_details
:
580 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
582 self
.logger
.info("Found vm_moref_id: {} for vApp UUID: {}".format(vm_moref_id
, vapp_uuid
))
585 except Exception as exp
:
586 self
.logger
.warn("Error occurred while getting VM moref ID for VM : {}\n{}"\
587 .format(exp
, traceback
.format_exc()))
590 def get_vapp_details_rest(self
, vapp_uuid
=None):
592 Method retrieve vapp detail from vCloud director
595 vapp_uuid - is vapp identifier.
598 Returns VM MOref ID or return None
604 if vapp_uuid
is None:
607 vca
= self
.connect_as_admin()
609 self
.logger
.warn("Failed to connect to vCD")
610 return parsed_respond
612 url_list
= [self
.vcloud_site
, '/api/vApp/vapp-', vapp_uuid
]
613 get_vapp_restcall
= ''.join(url_list
)
616 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
617 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
618 response
= requests
.get(get_vapp_restcall
,
622 if response
.status_code
!= 200:
623 self
.logger
.warn("REST API call {} failed. Return status code {}"\
624 .format(get_vapp_restcall
, response
.content
))
625 return parsed_respond
628 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
630 namespaces
= {'vm': 'http://www.vmware.com/vcloud/v1.5',
631 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
632 "xmlns":"http://www.vmware.com/vcloud/v1.5"
635 # parse children section for other attrib
636 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
637 if children_section
is not None:
638 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
639 if vCloud_extension_section
is not None:
641 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
642 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
643 if vmext
is not None:
644 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
645 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
647 except Exception as exp
:
648 self
.logger
.warn("Error occurred calling rest api for getting vApp details: {}\n{}"\
649 .format(exp
, traceback
.format_exc()))
651 return parsed_respond
654 def connect_as_admin(self
):
655 """ Method connect as pvdc admin user to vCloud director.
656 There are certain action that can be done only by provider vdc admin user.
657 Organization creation / provider network creation etc.
660 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
663 self
.logger
.debug("Logging into vCD org as admin.")
666 host
= self
.vcloud_site
668 client_as_admin
= Client(host
, verify_ssl_certs
=False)
669 client_as_admin
.set_credentials(BasicLoginCredentials(self
.admin_username
, org
,\
670 self
.admin_password
))
671 except Exception as e
:
672 self
.logger
.warn("Can't connect to a vCloud director as: {} with exception {}"\
673 .format(self
.admin_username
, e
))
675 return client_as_admin
678 def get_vm_resource_id(self
, vm_moref_id
):
679 """ Find resource ID in vROPs using vm_moref_id
681 if vm_moref_id
is None:
684 api_url
= '/suite-api/api/resources?resourceKind=VirtualMachine'
685 headers
= {'Accept': 'application/json'}
687 resp
= requests
.get(self
.vrops_site
+ api_url
,
688 auth
=(self
.vrops_user
, self
.vrops_password
),
689 verify
= False, headers
= headers
)
691 if resp
.status_code
is not 200:
692 self
.logger
.warn("Failed to get resource details from vROPs for {}"\
693 "\nResponse code:{}\nResponse Content: {}"\
694 .format(vm_moref_id
, resp
.status_code
, resp
.content
))
697 vm_resource_id
= None
699 resp_data
= json
.loads(resp
.content
)
700 if resp_data
.get('resourceList') is not None:
701 resource_list
= resp_data
.get('resourceList')
702 for resource
in resource_list
:
703 if resource
.get('resourceKey') is not None:
704 resource_details
= resource
['resourceKey']
705 if resource_details
.get('resourceIdentifiers') is not None:
706 resource_identifiers
= resource_details
['resourceIdentifiers']
707 for resource_identifier
in resource_identifiers
:
708 if resource_identifier
['identifierType']['name']=='VMEntityObjectID':
709 if resource_identifier
.get('value') is not None and \
710 resource_identifier
['value']==vm_moref_id
:
711 vm_resource_id
= resource
['identifier']
712 self
.logger
.info("Found VM resource ID: {} for vm_moref_id: {}"\
713 .format(vm_resource_id
, vm_moref_id
))
715 except Exception as exp
:
716 self
.logger
.warn("get_vm_resource_id: Error in parsing {}\n{}"\
717 .format(exp
, traceback
.format_exc()))
719 return vm_resource_id
722 def get_metrics_data(self
, metric
={}):
723 """Get an individual metric's data of a resource.
725 'metric_name': Normalized name of metric (string)
726 'resource_uuid': Resource UUID (string)
727 'period': Time period in Period Unit for which metrics data to be collected from
728 Monitoring tool from now.
729 'period_unit': Period measurement unit can be one of 'HR', 'DAY', 'MONTH', 'YEAR'
731 Return a dict that contains:
732 'metric_name': Normalized name of metric (string)
733 'resource_uuid': Resource UUID (string)
734 'tenant_id': tenent id name in which the resource is present in string format
735 'metrics_data': Dictionary containing time_series & metrics_series data.
736 'time_series': List of individual time stamp values in msec
737 'metrics_series': List of individual metrics data values
738 Raises an exception upon error or when network is not found
741 return_data
['schema_version'] = "1.0"
742 return_data
['schema_type'] = 'read_metric_data_response'
743 return_data
['metric_name'] = metric
['metric_name']
744 #To do - No metric_uuid in vROPs, thus returning '0'
745 return_data
['metric_uuid'] = '0'
746 return_data
['correlation_id'] = metric
['correlation_id']
747 return_data
['resource_uuid'] = metric
['resource_uuid']
748 return_data
['metrics_data'] = {'time_series':[], 'metrics_series':[]}
749 #To do - Need confirmation about uuid & id
750 if 'tenant_uuid' in metric
and metric
['tenant_uuid'] is not None:
751 return_data
['tenant_uuid'] = metric
['tenant_uuid']
753 return_data
['tenant_uuid'] = None
754 return_data
['unit'] = None
755 #return_data['tenant_id'] = self.tenant_id
756 #self.logger.warn("return_data: {}".format(return_data))
758 #1) Get metric details from plugin specific file & format it into vROPs metrics
759 metric_key_params
= self
.get_default_Params(metric
['metric_name'])
761 if not metric_key_params
:
762 self
.logger
.warn("Metric not supported: {}".format(metric
['metric_name']))
763 #To Do: Return message
766 return_data
['unit'] = metric_key_params
['unit']
768 #2) Find the resource id in vROPs based on OSM resource_uuid
769 #2.a) Find vm_moref_id from vApp uuid in vCD
770 vm_moref_id
= self
.get_vm_moref_id(metric
['resource_uuid'])
771 if vm_moref_id
is None:
772 self
.logger
.warn("Failed to find vm morefid for vApp in vCD: {}".format(metric
['resource_uuid']))
774 #2.b) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
775 resource_id
= self
.get_vm_resource_id(vm_moref_id
)
776 if resource_id
is None:
777 self
.logger
.warn("Failed to find resource in vROPs: {}".format(metric
['resource_uuid']))
780 #3) Calculate begin & end time for period & period unit
781 end_time
= int(round(time
.time() * 1000))
782 if metric
['collection_unit'] == 'YR':
783 time_diff
= PERIOD_MSEC
[metric
['collection_unit']]
785 time_diff
= metric
['collection_period']* PERIOD_MSEC
[metric
['collection_unit']]
786 begin_time
= end_time
- time_diff
788 #4) Get the metrics data
789 self
.logger
.info("metric_key_params['metric_key'] = {}".format(metric_key_params
['metric_key']))
790 self
.logger
.info("end_time: {}, begin_time: {}".format(end_time
, begin_time
))
792 url_list
= ['/suite-api/api/resources/', resource_id
, '/stats?statKey=',\
793 metric_key_params
['metric_key'], '&begin=', str(begin_time
),'&end=',str(end_time
)]
794 api_url
= ''.join(url_list
)
795 headers
= {'Accept': 'application/json'}
797 resp
= requests
.get(self
.vrops_site
+ api_url
,
798 auth
=(self
.vrops_user
, self
.vrops_password
),
799 verify
= False, headers
= headers
)
801 if resp
.status_code
is not 200:
802 self
.logger
.warn("Failed to retrive Metric data from vROPs for {}\nResponse code:{}\nResponse Content: {}"\
803 .format(metric
['metric_name'], resp
.status_code
, resp
.content
))
806 #5) Convert to required format
808 json_data
= json
.loads(resp
.content
)
809 for resp_key
,resp_val
in json_data
.iteritems():
810 if resp_key
== 'values':
811 data
= json_data
['values'][0]
812 for data_k
,data_v
in data
.iteritems():
813 if data_k
== 'stat-list':
815 for stat_list_k
,stat_list_v
in stat_list
.iteritems():
816 for stat_keys
,stat_vals
in stat_list_v
[0].iteritems():
817 if stat_keys
== 'timestamps':
818 metrics_data
['time_series'] = stat_list_v
[0]['timestamps']
819 if stat_keys
== 'data':
820 metrics_data
['metrics_series'] = stat_list_v
[0]['data']
822 return_data
['metrics_data'] = metrics_data
826 def update_alarm_configuration(self
, new_alarm_config
):
827 """Update alarm configuration (i.e. Symptom & alarm) as per request
829 if new_alarm_config
.get('alarm_uuid') is None:
830 self
.logger
.warn("alarm_uuid is required to update an Alarm")
832 #1) Get Alarm details from it's uuid & find the symptom defination
833 alarm_details_json
, alarm_details
= self
.get_alarm_defination_details(new_alarm_config
['alarm_uuid'])
834 if alarm_details_json
is None:
838 #2) Update the symptom defination
839 if alarm_details
['alarm_id'] is not None and alarm_details
['symptom_definition_id'] is not None:
840 symptom_defination_id
= alarm_details
['symptom_definition_id']
842 self
.logger
.info("Symptom Defination ID not found for {}".format(new_alarm_config
['alarm_uuid']))
845 symptom_uuid
= self
.update_symptom_defination(symptom_defination_id
, new_alarm_config
)
847 #3) Update the alarm defination & Return UUID if successful update
848 if symptom_uuid
is None:
849 self
.logger
.info("Symptom Defination details not found for {}"\
850 .format(new_alarm_config
['alarm_uuid']))
853 alarm_uuid
= self
.reconfigure_alarm(alarm_details_json
, new_alarm_config
)
854 if alarm_uuid
is None:
859 self
.logger
.error("Exception while updating alarm: {}".format(traceback
.format_exc()))
861 def get_alarm_defination_details(self
, alarm_uuid
):
862 """Get alarm details based on alarm UUID
864 if alarm_uuid
is None:
865 self
.logger
.warn("get_alarm_defination_details: Alarm UUID not provided")
870 api_url
= '/suite-api/api/alertdefinitions/AlertDefinition-'
871 headers
= {'Accept': 'application/json'}
873 resp
= requests
.get(self
.vrops_site
+ api_url
+ alarm_uuid
,
874 auth
=(self
.vrops_user
, self
.vrops_password
),
875 verify
= False, headers
= headers
)
877 if resp
.status_code
is not 200:
878 self
.logger
.warn("Alarm to be updated not found: {}\nResponse code:{}\nResponse Content: {}"\
879 .format(alarm_uuid
, resp
.status_code
, resp
.content
))
883 json_data
= json
.loads(resp
.content
)
884 if json_data
['id'] is not None:
885 alarm_details
['alarm_id'] = json_data
['id']
886 alarm_details
['alarm_name'] = json_data
['name']
887 alarm_details
['adapter_kind'] = json_data
['adapterKindKey']
888 alarm_details
['resource_kind'] = json_data
['resourceKindKey']
889 alarm_details
['type'] = json_data
['type']
890 alarm_details
['sub_type'] = json_data
['subType']
891 alarm_details
['symptom_definition_id'] = json_data
['states'][0]['base-symptom-set']['symptomDefinitionIds'][0]
892 except Exception as exp
:
893 self
.logger
.warn("Exception while retriving alarm defination details: {}".format(exp
))
896 return json_data
, alarm_details
899 def get_alarm_defination_by_name(self
, alarm_name
):
900 """Get alarm details based on alarm name
903 alert_match_list
= []
905 if alarm_name
is None:
906 self
.logger
.warn("get_alarm_defination_by_name: Alarm name not provided")
907 return alert_match_list
910 api_url
= '/suite-api/api/alertdefinitions'
911 headers
= {'Accept': 'application/json'}
913 resp
= requests
.get(self
.vrops_site
+ api_url
,
914 auth
=(self
.vrops_user
, self
.vrops_password
),
915 verify
= False, headers
= headers
)
917 if resp
.status_code
is not 200:
918 self
.logger
.warn("get_alarm_defination_by_name: Error in response: {}\nResponse code:{}"\
919 "\nResponse Content: {}".format(alarm_name
, resp
.status_code
, resp
.content
))
920 return alert_match_list
923 json_data
= json
.loads(resp
.content
)
924 if json_data
['alertDefinitions'] is not None:
925 alerts_list
= json_data
['alertDefinitions']
926 alert_match_list
= filter(lambda alert
: alert
['name'] == alarm_name
, alerts_list
)
927 status
= False if not alert_match_list
else True
928 #self.logger.debug("Found alert_match_list: {}for larm_name: {},\nstatus: {}".format(alert_match_list, alarm_name,status))
930 return alert_match_list
932 except Exception as exp
:
933 self
.logger
.warn("Exception while searching alarm defination: {}".format(exp
))
934 return alert_match_list
937 def update_symptom_defination(self
, symptom_uuid
, new_alarm_config
):
938 """Update symptom defination based on new alarm input configuration
940 #1) Get symptom defination details
941 symptom_details
= self
.get_symptom_defination_details(symptom_uuid
)
942 #print "\n\nsymptom_details: {}".format(symptom_details)
943 if symptom_details
is None:
946 if new_alarm_config
.has_key('severity') and new_alarm_config
['severity'] is not None:
947 symptom_details
['state']['severity'] = severity_mano2vrops
[new_alarm_config
['severity']]
948 if new_alarm_config
.has_key('operation') and new_alarm_config
['operation'] is not None:
949 symptom_details
['state']['condition']['operator'] = OPERATION_MAPPING
[new_alarm_config
['operation']]
950 if new_alarm_config
.has_key('threshold_value') and new_alarm_config
['threshold_value'] is not None:
951 symptom_details
['state']['condition']['value'] = new_alarm_config
['threshold_value']
952 #Find vrops metric key from metric_name, if required
954 if new_alarm_config.has_key('metric_name') and new_alarm_config['metric_name'] is not None:
955 metric_key_params = self.get_default_Params(new_alarm_config['metric_name'])
956 if not metric_key_params:
957 self.logger.warn("Metric not supported: {}".format(config_dict['metric_name']))
959 symptom_details['state']['condition']['key'] = metric_key_params['metric_key']
961 self
.logger
.info("Fetched Symptom details : {}".format(symptom_details
))
963 api_url
= '/suite-api/api/symptomdefinitions'
964 headers
= {'Content-Type': 'application/json', 'Accept':'application/json'}
965 data
= json
.dumps(symptom_details
)
966 resp
= requests
.put(self
.vrops_site
+ api_url
,
967 auth
=(self
.vrops_user
, self
.vrops_password
),
972 if resp
.status_code
!= 200:
973 self
.logger
.warn("Failed to update Symptom definition: {}, response {}"\
974 .format(symptom_uuid
, resp
.content
))
978 if symptom_uuid
is not None:
979 self
.logger
.info("Symptom defination updated {} for alarm: {}"\
980 .format(symptom_uuid
, new_alarm_config
['alarm_uuid']))
983 self
.logger
.warn("Failed to update Symptom Defination {} for : {}"\
984 .format(symptom_uuid
, new_alarm_config
['alarm_uuid']))
988 def get_symptom_defination_details(self
, symptom_uuid
):
989 """Get symptom defination details
992 if symptom_uuid
is None:
993 self
.logger
.warn("get_symptom_defination_details: Symptom UUID not provided")
996 api_url
= '/suite-api/api/symptomdefinitions/'
997 headers
= {'Accept': 'application/json'}
999 resp
= requests
.get(self
.vrops_site
+ api_url
+ symptom_uuid
,
1000 auth
=(self
.vrops_user
, self
.vrops_password
),
1001 verify
= False, headers
= headers
)
1003 if resp
.status_code
is not 200:
1004 self
.logger
.warn("Symptom defination not found {} \nResponse code:{}\nResponse Content: {}"\
1005 .format(symptom_uuid
, resp
.status_code
, resp
.content
))
1008 symptom_details
= json
.loads(resp
.content
)
1009 #print "New symptom Details: {}".format(symptom_details)
1010 return symptom_details
1013 def reconfigure_alarm(self
, alarm_details_json
, new_alarm_config
):
1014 """Reconfigure alarm defination as per input
1016 if new_alarm_config
.has_key('severity') and new_alarm_config
['severity'] is not None:
1017 alarm_details_json
['states'][0]['severity'] = new_alarm_config
['severity']
1018 if new_alarm_config
.has_key('description') and new_alarm_config
['description'] is not None:
1019 alarm_details_json
['description'] = new_alarm_config
['description']
1021 api_url
= '/suite-api/api/alertdefinitions'
1022 headers
= {'Content-Type': 'application/json', 'Accept':'application/json'}
1023 data
= json
.dumps(alarm_details_json
)
1024 resp
= requests
.put(self
.vrops_site
+ api_url
,
1025 auth
=(self
.vrops_user
, self
.vrops_password
),
1030 if resp
.status_code
!= 200:
1031 self
.logger
.warn("Failed to update Alarm definition: {}, response code {}, response content: {}"\
1032 .format(alarm_details_json
['id'], resp
.status_code
, resp
.content
))
1035 parsed_alarm_details
= json
.loads(resp
.content
)
1036 alarm_def_uuid
= parsed_alarm_details
['id'].split('-', 1)[1]
1037 self
.logger
.info("Successfully updated Alarm definition: {}".format(alarm_def_uuid
))
1038 return alarm_def_uuid
1040 def delete_alarm_configuration(self
, delete_alarm_req_dict
):
1041 """Delete complete alarm configuration
1043 if delete_alarm_req_dict
['alarm_uuid'] is None:
1044 self
.logger
.info("delete_alarm_configuration: Alarm UUID not provided")
1046 #1)Get alarm & symptom definition details
1047 alarm_details_json
, alarm_details
= self
.get_alarm_defination_details(delete_alarm_req_dict
['alarm_uuid'])
1048 if alarm_details
is None or alarm_details_json
is None:
1051 #2) Delete alarm notification
1052 rule_id
= self
.delete_notification_rule(alarm_details
['alarm_name'])
1056 #3) Delete alarm configuration
1057 alarm_id
= self
.delete_alarm_defination(alarm_details
['alarm_id'])
1058 if alarm_id
is None:
1061 #4) Delete alarm symptom
1062 symptom_id
= self
.delete_symptom_definition(alarm_details
['symptom_definition_id'])
1063 if symptom_id
is None:
1066 self
.logger
.info("Completed deleting alarm configuration: {}"\
1067 .format(delete_alarm_req_dict
['alarm_uuid']))
1068 return delete_alarm_req_dict
['alarm_uuid']
1070 def delete_notification_rule(self
, alarm_name
):
1071 """Deleted notification rule defined for a particular alarm
1073 rule_id
= self
.get_notification_rule_id_by_alarm_name(alarm_name
)
1077 api_url
= '/suite-api/api/notifications/rules/'
1078 headers
= {'Accept':'application/json'}
1079 resp
= requests
.delete(self
.vrops_site
+ api_url
+ rule_id
,
1080 auth
=(self
.vrops_user
, self
.vrops_password
),
1081 verify
= False, headers
= headers
)
1082 if resp
.status_code
is not 204:
1083 self
.logger
.warn("Failed to delete notification rules for {}".format(alarm_name
))
1086 self
.logger
.info("Deleted notification rules for {}".format(alarm_name
))
1089 def get_notification_rule_id_by_alarm_name(self
, alarm_name
):
1090 """Find created Alarm notification rule id by alarm name
1092 alarm_notify_id
= 'notify_' + alarm_name
1093 api_url
= '/suite-api/api/notifications/rules'
1094 headers
= {'Content-Type': 'application/json', 'Accept':'application/json'}
1095 resp
= requests
.get(self
.vrops_site
+ api_url
,
1096 auth
=(self
.vrops_user
, self
.vrops_password
),
1097 verify
= False, headers
= headers
)
1099 if resp
.status_code
is not 200:
1100 self
.logger
.warn("Failed to get notification rules details for {}"\
1101 .format(alarm_name
))
1104 notifications
= json
.loads(resp
.content
)
1105 if notifications
is not None and notifications
.has_key('notification-rule'):
1106 notifications_list
= notifications
['notification-rule']
1107 for dict in notifications_list
:
1108 if dict['name'] is not None and dict['name'] == alarm_notify_id
:
1109 notification_id
= dict['id']
1110 self
.logger
.info("Found Notification id to be deleted: {} for {}"\
1111 .format(notification_id
, alarm_name
))
1112 return notification_id
1114 self
.logger
.warn("Notification id to be deleted not found for {}"\
1115 .format(alarm_name
))
1118 def delete_alarm_defination(self
, alarm_id
):
1119 """Delete created Alarm defination
1121 api_url
= '/suite-api/api/alertdefinitions/'
1122 headers
= {'Accept':'application/json'}
1123 resp
= requests
.delete(self
.vrops_site
+ api_url
+ alarm_id
,
1124 auth
=(self
.vrops_user
, self
.vrops_password
),
1125 verify
= False, headers
= headers
)
1126 if resp
.status_code
is not 204:
1127 self
.logger
.warn("Failed to delete alarm definition {}".format(alarm_id
))
1130 self
.logger
.info("Deleted alarm definition {}".format(alarm_id
))
1133 def delete_symptom_definition(self
, symptom_id
):
1134 """Delete symptom defination
1136 api_url
= '/suite-api/api/symptomdefinitions/'
1137 headers
= {'Accept':'application/json'}
1138 resp
= requests
.delete(self
.vrops_site
+ api_url
+ symptom_id
,
1139 auth
=(self
.vrops_user
, self
.vrops_password
),
1140 verify
= False, headers
= headers
)
1141 if resp
.status_code
is not 204:
1142 self
.logger
.warn("Failed to delete symptom definition {}".format(symptom_id
))
1145 self
.logger
.info("Deleted symptom definition {}".format(symptom_id
))
1149 def verify_metric_support(self
, metric_info
):
1150 """Verify, if Metric is supported by vROPs plugin, verify metric unit & return status
1152 status: True if supported, False if not supported
1155 if 'metric_name' not in metric_info
:
1156 self
.logger
.debug("Metric name not provided: {}".format(metric_info
))
1158 metric_key_params
= self
.get_default_Params(metric_info
['metric_name'])
1159 if not metric_key_params
:
1160 self
.logger
.warn("Metric not supported: {}".format(metric_info
['metric_name']))
1163 #If Metric is supported, verify optional metric unit & return status
1164 if 'metric_unit' in metric_info
:
1165 if metric_key_params
.get('unit') == metric_info
['metric_unit']:
1166 self
.logger
.info("Metric is supported with unit: {}".format(metric_info
['metric_name']))
1169 self
.logger
.debug("Metric supported but there is unit mismatch for: {}."\
1170 "Supported unit: {}"\
1171 .format(metric_info
['metric_name'],metric_key_params
['unit']))
1175 def get_triggered_alarms_list(self
, list_alarm_input
):
1176 """Get list of triggered alarms on a resource based on alarm input request.
1178 #TO Do - Need to add filtering of alarms based on Severity & alarm name
1180 triggered_alarms_list
= []
1181 if list_alarm_input
.get('resource_uuid') is None:
1182 self
.logger
.warn("Resource UUID is required to get triggered alarms list")
1183 return triggered_alarms_list
1185 #1)Find vROPs resource ID using RO resource UUID
1186 vrops_resource_id
= self
.get_vrops_resourceid_from_ro_uuid(list_alarm_input
['resource_uuid'])
1187 if vrops_resource_id
is None:
1188 return triggered_alarms_list
1190 #2)Get triggered alarms on particular resource
1191 triggered_alarms_list
= self
.get_triggered_alarms_on_resource(list_alarm_input
['resource_uuid'], vrops_resource_id
)
1192 return triggered_alarms_list
1194 def get_vrops_resourceid_from_ro_uuid(self
, ro_resource_uuid
):
1195 """Fetch vROPs resource ID using resource UUID from RO/SO
1197 #1) Find vm_moref_id from vApp uuid in vCD
1198 vm_moref_id
= self
.get_vm_moref_id(ro_resource_uuid
)
1199 if vm_moref_id
is None:
1200 self
.logger
.warn("Failed to find vm morefid for vApp in vCD: {}".format(ro_resource_uuid
))
1203 #2) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
1204 vrops_resource_id
= self
.get_vm_resource_id(vm_moref_id
)
1205 if vrops_resource_id
is None:
1206 self
.logger
.warn("Failed to find resource in vROPs: {}".format(ro_resource_uuid
))
1208 return vrops_resource_id
1211 def get_triggered_alarms_on_resource(self
, ro_resource_uuid
, vrops_resource_id
):
1212 """Get triggered alarms on particular resource & return list of dictionary of alarms
1214 resource_alarms
= []
1215 api_url
= '/suite-api/api/alerts?resourceId='
1216 headers
= {'Accept':'application/json'}
1217 resp
= requests
.get(self
.vrops_site
+ api_url
+ vrops_resource_id
,
1218 auth
=(self
.vrops_user
, self
.vrops_password
),
1219 verify
= False, headers
= headers
)
1221 if resp
.status_code
is not 200:
1222 self
.logger
.warn("Failed to get triggered alarms for {}"\
1223 .format(ro_resource_uuid
))
1226 all_alerts
= json
.loads(resp
.content
)
1227 if all_alerts
.has_key('alerts'):
1228 if not all_alerts
['alerts']:
1229 self
.logger
.info("No alarms present on resource {}".format(ro_resource_uuid
))
1230 return resource_alarms
1231 all_alerts_list
= all_alerts
['alerts']
1232 for alarm
in all_alerts_list
:
1233 #self.logger.info("Triggered Alarm {}".format(alarm))
1234 if alarm
['alertDefinitionName'] is not None and\
1235 len(alarm
['alertDefinitionName'].split('-', 1)) == 2:
1236 if alarm
['alertDefinitionName'].split('-', 1)[1] == ro_resource_uuid
:
1238 alarm_instance
['alarm_uuid'] = alarm
['alertDefinitionId'].split('-', 1)[1]
1239 alarm_instance
['resource_uuid'] = ro_resource_uuid
1240 alarm_instance
['alarm_instance_uuid'] = alarm
['alertId']
1241 alarm_instance
['vim_type'] = 'VMware'
1242 #find severity of alarm
1244 for key
,value
in severity_mano2vrops
.iteritems():
1245 if value
== alarm
['alertLevel']:
1247 if severity
is None:
1248 severity
= 'INDETERMINATE'
1249 alarm_instance
['severity'] = severity
1250 alarm_instance
['status'] = alarm
['status']
1251 alarm_instance
['start_date'] = self
.convert_date_time(alarm
['startTimeUTC'])
1252 alarm_instance
['update_date'] = self
.convert_date_time(alarm
['updateTimeUTC'])
1253 alarm_instance
['cancel_date'] = self
.convert_date_time(alarm
['cancelTimeUTC'])
1254 self
.logger
.info("Triggered Alarm on resource {}".format(alarm_instance
))
1255 resource_alarms
.append(alarm_instance
)
1256 if not resource_alarms
:
1257 self
.logger
.info("No alarms present on resource {}".format(ro_resource_uuid
))
1258 return resource_alarms
1260 def convert_date_time(self
, date_time
):
1261 """Convert the input UTC time in msec to OSM date time format
1263 date_time_formatted
= '0000-00-00T00:00:00'
1265 complete_datetime
= datetime
.datetime
.fromtimestamp(date_time
/1000.0).isoformat('T')
1266 date_time_formatted
= complete_datetime
.split('.',1)[0]
1267 return date_time_formatted