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
32 from pyvcloud
.vcd
.client
import BasicLoginCredentials
33 from pyvcloud
.vcd
.client
import Client
36 from xml
.etree
import ElementTree
as XmlElementTree
40 from OpenSSL
.crypto
import load_certificate
, FILETYPE_PEM
43 from socket
import getfqdn
46 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
48 OPERATION_MAPPING
= {'GE':'GT_EQ', 'LE':'LT_EQ', 'GT':'GT', 'LT':'LT', 'EQ':'EQ'}
49 severity_mano2vrops
= {'WARNING':'WARNING', 'MINOR':'WARNING', 'MAJOR':"IMMEDIATE",\
50 'CRITICAL':'CRITICAL', 'INDETERMINATE':'UNKNOWN'}
51 PERIOD_MSEC
= {'HR':3600000,'DAY':86400000,'WEEK':604800000,'MONTH':2678400000,'YEAR':31536000000}
53 #To Do - Add actual webhook url & certificate
54 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/www.vrops_webservice.com.cert'
55 #webhook_url = "https://mano-dev-1:8080/notify/" #for testing
56 webhook_url
= "https://" + getfqdn() + ":8080/notify/"
57 SSL_CERTIFICATE_FILE_NAME
= ('vROPs_Webservice/SSL_certificate/' + getfqdn() + ".cert")
58 #SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/10.172.137.214.cert' #for testing
60 MODULE_DIR
= os
.path
.dirname(__file__
)
61 CONFIG_FILE_NAME
= 'vrops_config.xml'
62 CONFIG_FILE_PATH
= os
.path
.join(MODULE_DIR
, CONFIG_FILE_NAME
)
63 SSL_CERTIFICATE_FILE_PATH
= os
.path
.join(MODULE_DIR
, SSL_CERTIFICATE_FILE_NAME
)
66 """MON Plugin class for vROPs telemetry plugin
69 """Constructor of MON plugin
71 'access_config': dictionary with VIM access information based on VIM type.
72 This contains a consolidate version of VIM & monitoring tool config at creation and
73 particular VIM config at their attachment.
74 For VIM type: 'vmware',
75 access_config - {'vrops_site':<>, 'vrops_user':<>, 'vrops_password':<>,
76 'vcloud-site':<>,'admin_username':<>,'admin_password':<>,
77 'nsx_manager':<>,'nsx_user':<>,'nsx_password':<>,
78 'vcenter_ip':<>,'vcenter_port':<>,'vcenter_user':<>,'vcenter_password':<>,
79 'vim_tenant_name':<>,'orgname':<>}
82 Returns: Raise an exception if some needed parameter is missing, but it must not do any connectivity
85 self
.logger
= logging
.getLogger('PluginReceiver.MonPlugin')
86 self
.logger
.setLevel(logging
.DEBUG
)
88 access_config
= self
.get_default_Params('Access_Config')
89 self
.access_config
= access_config
90 if not bool(access_config
):
91 self
.logger
.error("Access configuration not provided in vROPs Config file")
92 raise KeyError("Access configuration not provided in vROPs Config file")
95 self
.vrops_site
= access_config
['vrops_site']
96 self
.vrops_user
= access_config
['vrops_user']
97 self
.vrops_password
= access_config
['vrops_password']
98 self
.vcloud_site
= access_config
['vcloud-site']
99 self
.admin_username
= access_config
['admin_username']
100 self
.admin_password
= access_config
['admin_password']
101 self
.tenant_id
= access_config
['tenant_id']
102 except KeyError as exp
:
103 self
.logger
.error("Check Access configuration in vROPs Config file: {}".format(exp
))
104 raise KeyError("Check Access configuration in vROPs Config file: {}".format(exp
))
107 def configure_alarm(self
, config_dict
= {}):
108 """Configures or creates a new alarm using the input parameters in config_dict
110 "alarm_name": Alarm name in string format
111 "description": Description of alarm in string format
112 "resource_uuid": Resource UUID for which alarm needs to be configured. in string format
113 "Resource type": String resource type: 'VDU' or 'host'
114 "Severity": 'WARNING', 'MINOR', 'MAJOR', 'CRITICAL'
115 "metric_name": Metric key in string format
116 "operation": One of ('GE', 'LE', 'GT', 'LT', 'EQ')
117 "threshold_value": Defines the threshold (up to 2 fraction digits) that,
118 if crossed, will trigger the alarm.
119 "unit": Unit of measurement in string format
120 "statistic": AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM
122 Default parameters for each alarm are read from the plugin specific config file.
123 Dict of default parameters is as follows:
124 default_params keys = {'cancel_cycles','wait_cycles','resource_kind','adapter_kind',
125 'alarm_type','alarm_subType',impact}
127 Returns the UUID of created alarm or None
130 #1) get alarm & metrics parameters from plugin specific file
131 def_a_params
= self
.get_default_Params(config_dict
['alarm_name'])
133 self
.logger
.warning("Alarm not supported: {}".format(config_dict
['alarm_name']))
135 metric_key_params
= self
.get_default_Params(config_dict
['metric_name'])
136 if not metric_key_params
:
137 self
.logger
.warning("Metric not supported: {}".format(config_dict
['metric_name']))
140 #1.2) Check if alarm definition already exists
141 vrops_alarm_name
= def_a_params
['vrops_alarm']+ '-' + config_dict
['resource_uuid']
142 alert_def_list
= self
.get_alarm_defination_by_name(vrops_alarm_name
)
144 self
.logger
.warning("Alarm already exists: {}. Try updating by update_alarm_request"\
145 .format(vrops_alarm_name
))
148 #2) create symptom definition
149 symptom_params
={'cancel_cycles': (def_a_params
['cancel_period']/300)*def_a_params
['cancel_cycles'],
150 'wait_cycles': (def_a_params
['period']/300)*def_a_params
['evaluation'],
151 'resource_kind_key': def_a_params
['resource_kind'],
152 'adapter_kind_key': def_a_params
['adapter_kind'],
153 'symptom_name':vrops_alarm_name
,
154 'severity': severity_mano2vrops
[config_dict
['severity']],
155 'metric_key':metric_key_params
['metric_key'],
156 'operation':OPERATION_MAPPING
[config_dict
['operation']],
157 'threshold_value':config_dict
['threshold_value']}
158 symptom_uuid
= self
.create_symptom(symptom_params
)
159 if symptom_uuid
is not None:
160 self
.logger
.info("Symptom defined: {} with ID: {}".format(symptom_params
['symptom_name'],symptom_uuid
))
162 self
.logger
.warning("Failed to create Symptom: {}".format(symptom_params
['symptom_name']))
164 #3) create alert definition
165 #To Do - Get type & subtypes for all 5 alarms
166 alarm_params
= {'name':vrops_alarm_name
,
167 'description':config_dict
['description']\
168 if 'description' in config_dict
and config_dict
['description'] is not None else config_dict
['alarm_name'],
169 'adapterKindKey':def_a_params
['adapter_kind'],
170 'resourceKindKey':def_a_params
['resource_kind'],
171 'waitCycles':1, 'cancelCycles':1,
172 'type':def_a_params
['alarm_type'], 'subType':def_a_params
['alarm_subType'],
173 'severity':severity_mano2vrops
[config_dict
['severity']],
174 'symptomDefinitionId':symptom_uuid
,
175 'impact':def_a_params
['impact']}
177 alarm_def
= self
.create_alarm_definition(alarm_params
)
178 if alarm_def
is None:
179 self
.logger
.warning("Failed to create Alert: {}".format(alarm_params
['name']))
182 self
.logger
.info("Alarm defined: {} with ID: {}".format(alarm_params
['name'],alarm_def
))
184 #4) Find vm_moref_id from vApp uuid in vCD
185 vm_moref_id
= self
.get_vm_moref_id(config_dict
['resource_uuid'])
186 if vm_moref_id
is None:
187 self
.logger
.warning("Failed to find vm morefid for vApp in vCD: {}".format(config_dict
['resource_uuid']))
190 #5) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
191 resource_id
= self
.get_vm_resource_id(vm_moref_id
)
192 if resource_id
is None:
193 self
.logger
.warning("Failed to find resource in vROPs: {}".format(config_dict
['resource_uuid']))
196 #6) Configure alarm notification for a particular VM using it's resource_id
197 notification_id
= self
.create_alarm_notification_rule(vrops_alarm_name
, alarm_def
, resource_id
)
198 if notification_id
is None:
201 alarm_def_uuid
= alarm_def
.split('-', 1)[1]
202 self
.logger
.info("Alarm defination created with notification: {} with ID: {}"\
203 .format(alarm_params
['name'],alarm_def_uuid
))
204 #Return alarm defination UUID by removing 'AlertDefinition' from UUID
205 return (alarm_def_uuid
)
207 def get_default_Params(self
, metric_alarm_name
):
209 Read the default config parameters from plugin specific file stored with plugin file.
211 metric_alarm_name: Name of the alarm, whose config parameters to be read from the config file.
215 source
= open(CONFIG_FILE_PATH
, 'r')
216 except IOError as exp
:
217 msg
= ("Could not read Config file: {}, \nException: {}"\
218 .format(CONFIG_FILE_PATH
, exp
))
219 self
.logger
.error(msg
)
222 tree
= XmlElementTree
.parse(source
)
223 alarms
= tree
.getroot()
225 if alarm
.tag
== metric_alarm_name
:
227 if param
.tag
in ("period", "evaluation", "cancel_period", "alarm_type",\
228 "cancel_cycles", "alarm_subType"):
229 a_params
[param
.tag
] = int(param
.text
)
230 elif param
.tag
in ("enabled", "repeat"):
231 if(param
.text
.lower() == "true"):
232 a_params
[param
.tag
] = True
234 a_params
[param
.tag
] = False
236 a_params
[param
.tag
] = param
.text
241 def create_symptom(self
, symptom_params
):
242 """Create Symptom definition for an alarm
244 symptom_params: Dict of parameters required for defining a symptom as follows
247 resource_kind_key = "VirtualMachine"
248 adapter_kind_key = "VMWARE"
249 symptom_name = Test_Memory_Usage_TooHigh
254 Returns the uuid of Symptom definition
259 api_url
= '/suite-api/api/symptomdefinitions'
260 headers
= {'Content-Type': 'application/json','Accept': 'application/json'}
263 "name": symptom_params
['symptom_name'],
264 "adapterKindKey": symptom_params
['adapter_kind_key'],
265 "resourceKindKey": symptom_params
['resource_kind_key'],
266 "waitCycles": symptom_params
['wait_cycles'],
267 "cancelCycles": symptom_params
['cancel_cycles'],
269 "severity": symptom_params
['severity'],
271 "type": "CONDITION_HT",
272 "key": symptom_params
['metric_key'],
273 "operator": symptom_params
['operation'],
274 "value": symptom_params
['threshold_value'],
275 "valueType": "NUMERIC",
277 "thresholdType": "STATIC"
282 resp
= requests
.post(self
.vrops_site
+ api_url
,
283 auth
=(self
.vrops_user
, self
.vrops_password
),
286 data
=json
.dumps(data
))
288 if resp
.status_code
!= 201:
289 self
.logger
.warning("Failed to create Symptom definition: {}, response {}"\
290 .format(symptom_params
['symptom_name'], resp
.content
))
293 resp_data
= json
.loads(resp
.content
)
294 if resp_data
.get('id') is not None:
295 symptom_id
= resp_data
['id']
299 except Exception as exp
:
300 self
.logger
.warning("Error creating symptom definition : {}\n{}"\
301 .format(exp
, traceback
.format_exc()))
304 def create_alarm_definition(self
, alarm_params
):
306 Create an alarm definition in vROPs
309 'description':Alarm description,
310 'adapterKindKey': Adapter type in vROPs "VMWARE",
311 'resourceKindKey':Resource type in vROPs "VirtualMachine",
312 'waitCycles': No of wait cycles,
313 'cancelCycles': No of cancel cycles,
315 'subType': Alarm subtype,
316 'severity': Severity in vROPs "CRITICAL",
317 'symptomDefinitionId':symptom Definition uuid,
318 'impact': impact 'risk'
320 'alarm_uuid': returns alarm uuid
326 api_url
= '/suite-api/api/alertdefinitions'
327 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
329 "name": alarm_params
['name'],
330 "description": alarm_params
['description'],
331 "adapterKindKey": alarm_params
['adapterKindKey'],
332 "resourceKindKey": alarm_params
['resourceKindKey'],
335 "type": alarm_params
['type'],
336 "subType": alarm_params
['subType'],
339 "severity": alarm_params
['severity'],
342 "type": "SYMPTOM_SET",
344 "aggregation": "ALL",
345 "symptomSetOperator": "AND",
346 "symptomDefinitionIds": [alarm_params
['symptomDefinitionId']]
349 "impactType": "BADGE",
350 "detail": alarm_params
['impact']
356 resp
= requests
.post(self
.vrops_site
+ api_url
,
357 auth
=(self
.vrops_user
, self
.vrops_password
),
360 data
=json
.dumps(data
))
362 if resp
.status_code
!= 201:
363 self
.logger
.warning("Failed to create Alarm definition: {}, response {}"\
364 .format(alarm_params
['name'], resp
.content
))
367 resp_data
= json
.loads(resp
.content
)
368 if resp_data
.get('id') is not None:
369 alarm_uuid
= resp_data
['id']
373 except Exception as exp
:
374 self
.logger
.warning("Error creating alarm definition : {}\n{}".format(exp
, traceback
.format_exc()))
377 def configure_rest_plugin(self
):
379 Creates REST Plug-in for vROPs outbound alerts
384 plugin_name
= 'MON_module_REST_Plugin'
385 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
387 #If REST plugin not configured, configure it
388 if plugin_id
is not None:
392 cert_file_string
= open(SSL_CERTIFICATE_FILE_PATH
, "rb").read()
393 except IOError as exp
:
394 msg
= ("Could not read SSL certificate file: {}".format(SSL_CERTIFICATE_FILE_PATH
))
395 self
.logger
.error(msg
)
397 cert
= load_certificate(FILETYPE_PEM
, cert_file_string
)
398 certificate
= cert
.digest("sha1")
399 api_url
= '/suite-api/api/alertplugins'
400 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
402 "pluginTypeId": "RestPlugin",
410 "name": "Content-type",
411 "value": "application/json"
414 "name": "Certificate",
418 "name": "ConnectionCount",
424 resp
= requests
.post(self
.vrops_site
+ api_url
,
425 auth
=(self
.vrops_user
, self
.vrops_password
),
428 data
=json
.dumps(data
))
430 if resp
.status_code
is not 201:
431 self
.logger
.warning("Failed to create REST Plugin: {} for url: {}, \nresponse code: {},"\
432 "\nresponse content: {}".format(plugin_name
, webhook_url
,\
433 resp
.status_code
, resp
.content
))
436 resp_data
= json
.loads(resp
.content
)
437 if resp_data
.get('pluginId') is not None:
438 plugin_id
= resp_data
['pluginId']
440 if plugin_id
is None:
441 self
.logger
.warning("Failed to get REST Plugin ID for {}, url: {}".format(plugin_name
, webhook_url
))
444 self
.logger
.info("Created REST Plugin: {} with ID : {} for url: {}".format(plugin_name
, plugin_id
, webhook_url
))
445 status
= self
.enable_rest_plugin(plugin_id
, plugin_name
)
447 self
.logger
.warning("Failed to enable created REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
450 self
.logger
.info("Enabled REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
453 def check_if_plugin_configured(self
, plugin_name
):
454 """Check if the REST plugin is already created
455 Returns: plugin_id: if already created, None: if needs to be created
458 #Find the REST Plugin id details for - MON_module_REST_Plugin
459 api_url
= '/suite-api/api/alertplugins'
460 headers
= {'Accept': 'application/json'}
462 resp
= requests
.get(self
.vrops_site
+ api_url
,
463 auth
=(self
.vrops_user
, self
.vrops_password
),
464 verify
= False, headers
= headers
)
466 if resp
.status_code
is not 200:
467 self
.logger
.warning("Failed to REST GET Alarm plugin details \nResponse code: {}\nResponse content: {}"\
468 .format(resp
.status_code
, resp
.content
))
471 # Look for specific plugin & parse pluginId for 'MON_module_REST_Plugin'
472 plugins_list
= json
.loads(resp
.content
)
473 if plugins_list
.get('notificationPluginInstances') is not None:
474 for notify_plugin
in plugins_list
['notificationPluginInstances']:
475 if notify_plugin
.get('name') is not None and notify_plugin
['name'] == plugin_name
:
476 plugin_id
= notify_plugin
.get('pluginId')
478 if plugin_id
is None:
479 self
.logger
.warning("REST plugin {} not found".format(plugin_name
))
482 self
.logger
.info("Found REST Plugin: {}".format(plugin_name
))
486 def enable_rest_plugin(self
, plugin_id
, plugin_name
):
488 Enable the REST plugin using plugin_id
489 Params: plugin_id: plugin ID string that is to be enabled
490 Returns: status (Boolean) - True for success, False for failure
493 if plugin_id
is None or plugin_name
is None:
494 self
.logger
.debug("enable_rest_plugin() : Plugin ID or plugin_name not provided for {} plugin"\
495 .format(plugin_name
))
499 api_url
= "/suite-api/api/alertplugins/{}/enable/True".format(plugin_id
)
501 resp
= requests
.put(self
.vrops_site
+ api_url
,
502 auth
=(self
.vrops_user
, self
.vrops_password
),
505 if resp
.status_code
is not 204:
506 self
.logger
.warning("Failed to enable REST plugin {}. \nResponse code {}\nResponse Content: {}"\
507 .format(plugin_name
, resp
.status_code
, resp
.content
))
510 self
.logger
.info("Enabled REST plugin {}.".format(plugin_name
))
513 except Exception as exp
:
514 self
.logger
.warning("Error enabling REST plugin for {} plugin: Exception: {}\n{}"\
515 .format(plugin_name
, exp
, traceback
.format_exc()))
517 def create_alarm_notification_rule(self
, alarm_name
, alarm_id
, resource_id
):
519 Create notification rule for each alarm
526 notification_id: notification_id or None
528 notification_name
= 'notify_' + alarm_name
529 notification_id
= None
530 plugin_name
= 'MON_module_REST_Plugin'
532 #1) Find the REST Plugin id details for - MON_module_REST_Plugin
533 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
534 if plugin_id
is None:
535 self
.logger
.warning("Failed to get REST plugin_id for : {}".format('MON_module_REST_Plugin'))
538 #2) Create Alarm notification rule
539 api_url
= '/suite-api/api/notifications/rules'
540 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
542 "name" : notification_name
,
543 "pluginId" : plugin_id
,
545 "matchResourceIdOnly": True,
546 "resourceId": resource_id
548 "alertDefinitionIdFilters" : {
549 "values" : [ alarm_id
]
553 resp
= requests
.post(self
.vrops_site
+ api_url
,
554 auth
=(self
.vrops_user
, self
.vrops_password
),
557 data
=json
.dumps(data
))
559 if resp
.status_code
is not 201:
560 self
.logger
.warning("Failed to create Alarm notification rule {} for {} alarm."\
561 "\nResponse code: {}\nResponse content: {}"\
562 .format(notification_name
, alarm_name
, resp
.status_code
, resp
.content
))
565 #parse notification id from response
566 resp_data
= json
.loads(resp
.content
)
567 if resp_data
.get('id') is not None:
568 notification_id
= resp_data
['id']
570 self
.logger
.info("Created Alarm notification rule {} for {} alarm.".format(notification_name
, alarm_name
))
571 return notification_id
573 def get_vm_moref_id(self
, vapp_uuid
):
575 Get the moref_id of given VM
579 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
580 if vm_details
and "vm_vcenter_info" in vm_details
:
581 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
583 self
.logger
.info("Found vm_moref_id: {} for vApp UUID: {}".format(vm_moref_id
, vapp_uuid
))
586 except Exception as exp
:
587 self
.logger
.warning("Error occurred while getting VM moref ID for VM : {}\n{}"\
588 .format(exp
, traceback
.format_exc()))
591 def get_vapp_details_rest(self
, vapp_uuid
=None):
593 Method retrieve vapp detail from vCloud director
596 vapp_uuid - is vapp identifier.
599 Returns VM MOref ID or return None
605 if vapp_uuid
is None:
608 vca
= self
.connect_as_admin()
610 self
.logger
.warning("Failed to connect to vCD")
611 return parsed_respond
613 url_list
= [self
.vcloud_site
, '/api/vApp/vapp-', vapp_uuid
]
614 get_vapp_restcall
= ''.join(url_list
)
617 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
618 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
619 response
= requests
.get(get_vapp_restcall
,
623 if response
.status_code
!= 200:
624 self
.logger
.warning("REST API call {} failed. Return status code {}"\
625 .format(get_vapp_restcall
, response
.content
))
626 return parsed_respond
629 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
631 namespaces
= {'vm': 'http://www.vmware.com/vcloud/v1.5',
632 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
633 "xmlns":"http://www.vmware.com/vcloud/v1.5"
636 # parse children section for other attrib
637 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
638 if children_section
is not None:
639 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
640 if vCloud_extension_section
is not None:
642 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
643 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
644 if vmext
is not None:
645 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
646 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
648 except Exception as exp
:
649 self
.logger
.warning("Error occurred calling rest api for getting vApp details: {}\n{}"\
650 .format(exp
, traceback
.format_exc()))
652 return parsed_respond
655 def connect_as_admin(self
):
656 """ Method connect as pvdc admin user to vCloud director.
657 There are certain action that can be done only by provider vdc admin user.
658 Organization creation / provider network creation etc.
661 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
664 self
.logger
.debug("Logging into vCD org as admin.")
667 host
= self
.vcloud_site
669 client_as_admin
= Client(host
, verify_ssl_certs
=False)
670 client_as_admin
.set_credentials(BasicLoginCredentials(self
.admin_username
, org
,\
671 self
.admin_password
))
672 except Exception as e
:
673 self
.logger
.warning("Can't connect to a vCloud director as: {} with exception {}"\
674 .format(self
.admin_username
, e
))
676 return client_as_admin
679 def get_vm_resource_id(self
, vm_moref_id
):
680 """ Find resource ID in vROPs using vm_moref_id
682 if vm_moref_id
is None:
685 api_url
= '/suite-api/api/resources?resourceKind=VirtualMachine'
686 headers
= {'Accept': 'application/json'}
688 resp
= requests
.get(self
.vrops_site
+ api_url
,
689 auth
=(self
.vrops_user
, self
.vrops_password
),
690 verify
= False, headers
= headers
)
692 if resp
.status_code
is not 200:
693 self
.logger
.warning("Failed to get resource details from vROPs for {}"\
694 "\nResponse code:{}\nResponse Content: {}"\
695 .format(vm_moref_id
, resp
.status_code
, resp
.content
))
698 vm_resource_id
= None
700 resp_data
= json
.loads(resp
.content
)
701 if resp_data
.get('resourceList') is not None:
702 resource_list
= resp_data
.get('resourceList')
703 for resource
in resource_list
:
704 if resource
.get('resourceKey') is not None:
705 resource_details
= resource
['resourceKey']
706 if resource_details
.get('resourceIdentifiers') is not None:
707 resource_identifiers
= resource_details
['resourceIdentifiers']
708 for resource_identifier
in resource_identifiers
:
709 if resource_identifier
['identifierType']['name']=='VMEntityObjectID':
710 if resource_identifier
.get('value') is not None and \
711 resource_identifier
['value']==vm_moref_id
:
712 vm_resource_id
= resource
['identifier']
713 self
.logger
.info("Found VM resource ID: {} for vm_moref_id: {}"\
714 .format(vm_resource_id
, vm_moref_id
))
716 except Exception as exp
:
717 self
.logger
.warning("get_vm_resource_id: Error in parsing {}\n{}"\
718 .format(exp
, traceback
.format_exc()))
720 return vm_resource_id
723 def get_metrics_data(self
, metric
={}):
724 """Get an individual metric's data of a resource.
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'
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
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']
754 return_data
['tenant_uuid'] = None
755 return_data
['unit'] = None
756 #return_data['tenant_id'] = self.tenant_id
757 #self.logger.warning("return_data: {}".format(return_data))
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'])
762 if not metric_key_params
:
763 self
.logger
.warning("Metric not supported: {}".format(metric
['metric_name']))
764 #To Do: Return message
767 return_data
['unit'] = metric_key_params
['unit']
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
.warning("Failed to find vm morefid for vApp in vCD: {}".format(metric
['resource_uuid']))
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
.warning("Failed to find resource in vROPs: {}".format(metric
['resource_uuid']))
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']]
786 time_diff
= metric
['collection_period']* PERIOD_MSEC
[metric
['collection_unit']]
787 begin_time
= end_time
- time_diff
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
))
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'}
798 resp
= requests
.get(self
.vrops_site
+ api_url
,
799 auth
=(self
.vrops_user
, self
.vrops_password
),
800 verify
= False, headers
= headers
)
802 if resp
.status_code
is not 200:
803 self
.logger
.warning("Failed to retrive Metric data from vROPs for {}\nResponse code:{}\nResponse Content: {}"\
804 .format(metric
['metric_name'], resp
.status_code
, resp
.content
))
807 #5) Convert to required format
809 json_data
= json
.loads(resp
.content
)
810 for resp_key
,resp_val
in six
.iteritems(json_data
):
811 if resp_key
== 'values':
812 data
= json_data
['values'][0]
813 for data_k
,data_v
in six
.iteritems(data
):
814 if data_k
== 'stat-list':
816 for stat_list_k
,stat_list_v
in six
.iteritems(stat_list
):
817 for stat_keys
,stat_vals
in six
.iteritems(stat_list_v
[0]):
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']
823 return_data
['metrics_data'] = metrics_data
827 def update_alarm_configuration(self
, new_alarm_config
):
828 """Update alarm configuration (i.e. Symptom & alarm) as per request
830 if new_alarm_config
.get('alarm_uuid') is None:
831 self
.logger
.warning("alarm_uuid is required to update an Alarm")
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:
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']
843 self
.logger
.info("Symptom Defination ID not found for {}".format(new_alarm_config
['alarm_uuid']))
846 symptom_uuid
= self
.update_symptom_defination(symptom_defination_id
, new_alarm_config
)
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']))
854 alarm_uuid
= self
.reconfigure_alarm(alarm_details_json
, new_alarm_config
)
855 if alarm_uuid
is None:
860 self
.logger
.error("Exception while updating alarm: {}".format(traceback
.format_exc()))
862 def get_alarm_defination_details(self
, alarm_uuid
):
863 """Get alarm details based on alarm UUID
865 if alarm_uuid
is None:
866 self
.logger
.warning("get_alarm_defination_details: Alarm UUID not provided")
871 api_url
= '/suite-api/api/alertdefinitions/AlertDefinition-'
872 headers
= {'Accept': 'application/json'}
874 resp
= requests
.get(self
.vrops_site
+ api_url
+ alarm_uuid
,
875 auth
=(self
.vrops_user
, self
.vrops_password
),
876 verify
= False, headers
= headers
)
878 if resp
.status_code
is not 200:
879 self
.logger
.warning("Alarm to be updated not found: {}\nResponse code:{}\nResponse Content: {}"\
880 .format(alarm_uuid
, resp
.status_code
, resp
.content
))
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
.warning("Exception while retriving alarm defination details: {}".format(exp
))
897 return json_data
, alarm_details
900 def get_alarm_defination_by_name(self
, alarm_name
):
901 """Get alarm details based on alarm name
904 alert_match_list
= []
906 if alarm_name
is None:
907 self
.logger
.warning("get_alarm_defination_by_name: Alarm name not provided")
908 return alert_match_list
911 api_url
= '/suite-api/api/alertdefinitions'
912 headers
= {'Accept': 'application/json'}
914 resp
= requests
.get(self
.vrops_site
+ api_url
,
915 auth
=(self
.vrops_user
, self
.vrops_password
),
916 verify
= False, headers
= headers
)
918 if resp
.status_code
is not 200:
919 self
.logger
.warning("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
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
= 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))
931 return alert_match_list
933 except Exception as exp
:
934 self
.logger
.warning("Exception while searching alarm defination: {}".format(exp
))
935 return alert_match_list
938 def update_symptom_defination(self
, symptom_uuid
, new_alarm_config
):
939 """Update symptom defination based on new alarm input configuration
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:
947 if 'severity' in new_alarm_config
and new_alarm_config
['severity'] is not None:
948 symptom_details
['state']['severity'] = severity_mano2vrops
[new_alarm_config
['severity']]
949 if 'operation' in new_alarm_config
and new_alarm_config
['operation'] is not None:
950 symptom_details
['state']['condition']['operator'] = OPERATION_MAPPING
[new_alarm_config
['operation']]
951 if 'threshold_value' in new_alarm_config
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
955 if 'metric_name' in new_alarm_config 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.warning("Metric not supported: {}".format(config_dict['metric_name']))
960 symptom_details['state']['condition']['key'] = metric_key_params['metric_key']
962 self
.logger
.info("Fetched Symptom details : {}".format(symptom_details
))
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
),
973 if resp
.status_code
!= 200:
974 self
.logger
.warning("Failed to update Symptom definition: {}, response {}"\
975 .format(symptom_uuid
, resp
.content
))
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']))
984 self
.logger
.warning("Failed to update Symptom Defination {} for : {}"\
985 .format(symptom_uuid
, new_alarm_config
['alarm_uuid']))
989 def get_symptom_defination_details(self
, symptom_uuid
):
990 """Get symptom defination details
993 if symptom_uuid
is None:
994 self
.logger
.warning("get_symptom_defination_details: Symptom UUID not provided")
997 api_url
= '/suite-api/api/symptomdefinitions/'
998 headers
= {'Accept': 'application/json'}
1000 resp
= requests
.get(self
.vrops_site
+ api_url
+ symptom_uuid
,
1001 auth
=(self
.vrops_user
, self
.vrops_password
),
1002 verify
= False, headers
= headers
)
1004 if resp
.status_code
is not 200:
1005 self
.logger
.warning("Symptom defination not found {} \nResponse code:{}\nResponse Content: {}"\
1006 .format(symptom_uuid
, resp
.status_code
, resp
.content
))
1009 symptom_details
= json
.loads(resp
.content
)
1010 #print "New symptom Details: {}".format(symptom_details)
1011 return symptom_details
1014 def reconfigure_alarm(self
, alarm_details_json
, new_alarm_config
):
1015 """Reconfigure alarm defination as per input
1017 if 'severity' in new_alarm_config
and new_alarm_config
['severity'] is not None:
1018 alarm_details_json
['states'][0]['severity'] = new_alarm_config
['severity']
1019 if 'description' in new_alarm_config
and new_alarm_config
['description'] is not None:
1020 alarm_details_json
['description'] = new_alarm_config
['description']
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
),
1031 if resp
.status_code
!= 200:
1032 self
.logger
.warning("Failed to update Alarm definition: {}, response code {}, response content: {}"\
1033 .format(alarm_details_json
['id'], resp
.status_code
, resp
.content
))
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 definition: {}".format(alarm_def_uuid
))
1039 return alarm_def_uuid
1041 def delete_alarm_configuration(self
, delete_alarm_req_dict
):
1042 """Delete complete alarm configuration
1044 if delete_alarm_req_dict
['alarm_uuid'] is None:
1045 self
.logger
.info("delete_alarm_configuration: Alarm UUID not provided")
1047 #1)Get alarm & symptom definition 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:
1052 #2) Delete alarm notification
1053 rule_id
= self
.delete_notification_rule(alarm_details
['alarm_name'])
1057 #3) Delete alarm configuration
1058 alarm_id
= self
.delete_alarm_defination(alarm_details
['alarm_id'])
1059 if alarm_id
is None:
1062 #4) Delete alarm symptom
1063 symptom_id
= self
.delete_symptom_definition(alarm_details
['symptom_definition_id'])
1064 if symptom_id
is None:
1067 self
.logger
.info("Completed deleting alarm configuration: {}"\
1068 .format(delete_alarm_req_dict
['alarm_uuid']))
1069 return delete_alarm_req_dict
['alarm_uuid']
1071 def delete_notification_rule(self
, alarm_name
):
1072 """Deleted notification rule defined for a particular alarm
1074 rule_id
= self
.get_notification_rule_id_by_alarm_name(alarm_name
)
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
.warning("Failed to delete notification rules for {}".format(alarm_name
))
1087 self
.logger
.info("Deleted notification rules for {}".format(alarm_name
))
1090 def get_notification_rule_id_by_alarm_name(self
, alarm_name
):
1091 """Find created Alarm notification rule id by alarm name
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
)
1100 if resp
.status_code
is not 200:
1101 self
.logger
.warning("Failed to get notification rules details for {}"\
1102 .format(alarm_name
))
1105 notifications
= json
.loads(resp
.content
)
1106 if notifications
is not None and 'notification-rule' in notifications
:
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
1115 self
.logger
.warning("Notification id to be deleted not found for {}"\
1116 .format(alarm_name
))
1119 def delete_alarm_defination(self
, alarm_id
):
1120 """Delete created Alarm defination
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
.warning("Failed to delete alarm definition {}".format(alarm_id
))
1131 self
.logger
.info("Deleted alarm definition {}".format(alarm_id
))
1134 def delete_symptom_definition(self
, symptom_id
):
1135 """Delete symptom defination
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
.warning("Failed to delete symptom definition {}".format(symptom_id
))
1146 self
.logger
.info("Deleted symptom definition {}".format(symptom_id
))
1150 def verify_metric_support(self
, metric_info
):
1151 """Verify, if Metric is supported by vROPs plugin, verify metric unit & return status
1153 status: True if supported, False if not supported
1156 if 'metric_name' not in metric_info
:
1157 self
.logger
.debug("Metric name not provided: {}".format(metric_info
))
1159 metric_key_params
= self
.get_default_Params(metric_info
['metric_name'])
1160 if not metric_key_params
:
1161 self
.logger
.warning("Metric not supported: {}".format(metric_info
['metric_name']))
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']))
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']))
1176 def get_triggered_alarms_list(self
, list_alarm_input
):
1177 """Get list of triggered alarms on a resource based on alarm input request.
1179 #TO Do - Need to add filtering of alarms based on Severity & alarm name
1181 triggered_alarms_list
= []
1182 if list_alarm_input
.get('resource_uuid') is None:
1183 self
.logger
.warning("Resource UUID is required to get triggered alarms list")
1184 return triggered_alarms_list
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
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
1195 def get_vrops_resourceid_from_ro_uuid(self
, ro_resource_uuid
):
1196 """Fetch vROPs resource ID using resource UUID from RO/SO
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
.warning("Failed to find vm morefid for vApp in vCD: {}".format(ro_resource_uuid
))
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
.warning("Failed to find resource in vROPs: {}".format(ro_resource_uuid
))
1209 return vrops_resource_id
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
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
)
1222 if resp
.status_code
is not 200:
1223 self
.logger
.warning("Failed to get triggered alarms for {}"\
1224 .format(ro_resource_uuid
))
1227 all_alerts
= json
.loads(resp
.content
)
1228 if 'alerts' in all_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
:
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
1245 for key
,value
in six
.iteritems(severity_mano2vrops
):
1246 if value
== alarm
['alertLevel']:
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
1261 def convert_date_time(self
, date_time
):
1262 """Convert the input UTC time in msec to OSM date time format
1264 date_time_formatted
= '0000-00-00T00:00:00'
1266 complete_datetime
= datetime
.datetime
.fromtimestamp(date_time
/1000.0, tz
=pytz
.utc
).isoformat('T')
1267 date_time_formatted
= complete_datetime
.split('.',1)[0]
1268 return date_time_formatted