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
35 from xml
.etree
import ElementTree
as XmlElementTree
39 from OpenSSL
.crypto
import load_certificate
, FILETYPE_PEM
43 from socket
import getfqdn
47 from osm_mon
.core
.settings
import Config
49 sys
.path
.append(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), '..', '..', '..'))
50 from osm_mon
.core
.database
import DatabaseManager
54 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
55 OPERATION_MAPPING
= {'GE': 'GT_EQ', 'LE': 'LT_EQ', 'GT': 'GT', 'LT': 'LT', 'EQ': 'EQ'}
56 severity_mano2vrops
= {'WARNING': 'WARNING', 'MINOR': 'WARNING', 'MAJOR': "IMMEDIATE",
57 'CRITICAL': 'CRITICAL', 'INDETERMINATE': 'UNKNOWN'}
58 PERIOD_MSEC
= {'HR': 3600000, 'DAY': 86400000, 'WEEK': 604800000, 'MONTH': 2678400000, 'YEAR': 31536000000}
60 # To Do - Add actual webhook url & certificate
61 # SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/www.vrops_webservice.com.cert'
62 # webhook_url = "https://mano-dev-1:8080/notify/" #for testing
63 webhook_url
= "https://" + getfqdn() + ":8080/notify/"
64 SSL_CERTIFICATE_FILE_NAME
= ('vROPs_Webservice/SSL_certificate/' + getfqdn() + ".cert")
65 # SSL_CERTIFICATE_FILE_NAME = 'vROPs_Webservice/SSL_certificate/10.172.137.214.cert' #for testing
67 MODULE_DIR
= os
.path
.dirname(__file__
)
68 CONFIG_FILE_NAME
= 'vrops_config.xml'
69 CONFIG_FILE_PATH
= os
.path
.join(MODULE_DIR
, CONFIG_FILE_NAME
)
70 SSL_CERTIFICATE_FILE_PATH
= os
.path
.join(MODULE_DIR
, SSL_CERTIFICATE_FILE_NAME
)
72 cfg
= Config
.instance()
73 logging
.basicConfig(stream
=sys
.stdout
,
74 format
='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
75 datefmt
='%m/%d/%Y %I:%M:%S %p',
76 level
=logging
.getLevelName(cfg
.OSMMON_LOG_LEVEL
))
78 logger
= logging
.getLogger(__name__
)
82 """MON Plugin class for vROPs telemetry plugin
85 def __init__(self
, access_config
=None):
86 """Constructor of MON plugin
88 'access_config': dictionary with VIM access information based on VIM type.
89 This contains a consolidate version of VIM & monitoring tool config at creation and
90 particular VIM config at their attachment.
91 For VIM type: 'vmware',
92 access_config - {'vrops_site':<>, 'vrops_user':<>, 'vrops_password':<>,
93 'vcloud-site':<>,'admin_username':<>,'admin_password':<>,
94 'nsx_manager':<>,'nsx_user':<>,'nsx_password':<>,
95 'vcenter_ip':<>,'vcenter_port':<>,'vcenter_user':<>,'vcenter_password':<>,
96 'vim_tenant_name':<>,'orgname':<>}
99 Returns: Raise an exception if some needed parameter is missing, but it must not do any connectivity
100 check against the VIM
103 if access_config
is None:
104 logger
.error("VIM Access Configuration not provided")
105 raise KeyError("VIM Access Configuration not provided")
107 self
.database_manager
= DatabaseManager()
109 self
.access_config
= access_config
110 if not bool(access_config
):
111 logger
.error("VIM Account details are not added. Please add a VIM account")
112 raise KeyError("VIM Account details are not added. Please add a VIM account")
115 self
.vrops_site
= access_config
['vrops_site']
116 self
.vrops_user
= access_config
['vrops_user']
117 self
.vrops_password
= access_config
['vrops_password']
118 self
.vcloud_site
= access_config
['vim_url']
119 self
.admin_username
= access_config
['admin_username']
120 self
.admin_password
= access_config
['admin_password']
121 # self.tenant_id = access_config['tenant_id']
122 self
.vim_uuid
= access_config
['vim_uuid']
124 except KeyError as exp
:
125 logger
.error("Required VIM account details not provided: {}".format(exp
))
126 raise KeyError("Required VIM account details not provided: {}".format(exp
))
128 def configure_alarm(self
, config_dict
={}):
129 """Configures or creates a new alarm using the input parameters in config_dict
131 "alarm_name": Alarm name in string format
132 "description": Description of alarm in string format
133 "resource_uuid": Resource UUID for which alarm needs to be configured. in string format
134 "Resource type": String resource type: 'VDU' or 'host'
135 "Severity": 'WARNING', 'MINOR', 'MAJOR', 'CRITICAL'
136 "metric_name": Metric key in string format
137 "operation": One of ('GE', 'LE', 'GT', 'LT', 'EQ')
138 "threshold_value": Defines the threshold (up to 2 fraction digits) that,
139 if crossed, will trigger the alarm.
140 "statistic": AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM
142 Default parameters for each alarm are read from the plugin specific config file.
143 Dict of default parameters is as follows:
144 default_params keys = {'cancel_cycles','wait_cycles','resource_kind','adapter_kind',
145 'alarm_type','alarm_subType',impact}
147 Returns the UUID of created alarm or None
149 # 1) get alarm & metrics parameters from plugin specific file
150 def_a_params
= self
.get_default_Params(config_dict
['alarm_name'])
152 logger
.warning("Alarm not supported: {}".format(config_dict
['alarm_name']))
154 metric_key_params
= self
.get_default_Params(config_dict
['metric_name'])
155 if not metric_key_params
:
156 logger
.warning("Metric not supported: {}".format(config_dict
['metric_name']))
159 # 1.2) Check if alarm definition already exists
160 vrops_alarm_name
= def_a_params
['vrops_alarm'] + '-' + config_dict
['resource_uuid']
161 alert_def_list
= self
.get_alarm_defination_by_name(vrops_alarm_name
)
163 logger
.warning("Alarm already exists: {}. Try updating by update_alarm_request"
164 .format(vrops_alarm_name
))
167 # 2) create symptom definition
168 symptom_params
= {'cancel_cycles': (def_a_params
['cancel_period'] / 300) * def_a_params
['cancel_cycles'],
169 'wait_cycles': (def_a_params
['period'] / 300) * def_a_params
['evaluation'],
170 'resource_kind_key': def_a_params
['resource_kind'],
171 'adapter_kind_key': def_a_params
['adapter_kind'],
172 'symptom_name': vrops_alarm_name
,
173 'severity': severity_mano2vrops
[config_dict
['severity'].upper()],
174 'metric_key': metric_key_params
['metric_key'],
175 'operation': OPERATION_MAPPING
[config_dict
['operation']],
176 'threshold_value': config_dict
['threshold_value']}
178 symptom_uuid
= self
.create_symptom(symptom_params
)
179 if symptom_uuid
is not None:
180 logger
.info("Symptom defined: {} with ID: {}".format(symptom_params
['symptom_name'], symptom_uuid
))
182 logger
.warning("Failed to create Symptom: {}".format(symptom_params
['symptom_name']))
184 # 3) create alert definition
185 # To Do - Get type & subtypes for all 5 alarms
186 alarm_params
= {'name': vrops_alarm_name
,
187 'description': config_dict
['description']
188 if 'description' in config_dict
and config_dict
['description'] is not None else config_dict
[
190 'adapterKindKey': def_a_params
['adapter_kind'],
191 'resourceKindKey': def_a_params
['resource_kind'],
192 'waitCycles': 1, 'cancelCycles': 1,
193 'type': def_a_params
['alarm_type'], 'subType': def_a_params
['alarm_subType'],
194 'severity': severity_mano2vrops
[config_dict
['severity'].upper()],
195 'symptomDefinitionId': symptom_uuid
,
196 'impact': def_a_params
['impact']}
198 alarm_def
= self
.create_alarm_definition(alarm_params
)
199 if alarm_def
is None:
200 logger
.warning("Failed to create Alert: {}".format(alarm_params
['name']))
203 logger
.info("Alarm defined: {} with ID: {}".format(alarm_params
['name'], alarm_def
))
205 # 4) Find vm_moref_id from vApp uuid in vCD
206 vm_moref_id
= self
.get_vm_moref_id(config_dict
['resource_uuid'])
207 if vm_moref_id
is None:
208 logger
.warning("Failed to find vm morefid for vApp in vCD: {}".format(config_dict
['resource_uuid']))
211 # 5) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
212 resource_id
= self
.get_vm_resource_id(vm_moref_id
)
213 if resource_id
is None:
214 logger
.warning("Failed to find resource in vROPs: {}".format(config_dict
['resource_uuid']))
217 # 6) Configure alarm notification for a particular VM using it's resource_id
218 notification_id
= self
.create_alarm_notification_rule(vrops_alarm_name
, alarm_def
, resource_id
)
219 if notification_id
is None:
222 alarm_def_uuid
= alarm_def
.split('-', 1)[1]
223 logger
.info("Alarm definition created with notification: {} with ID: {}"
224 .format(alarm_params
['name'], alarm_def_uuid
))
225 self
.database_manager
.save_alarm(alarm_params
['name'],
227 config_dict
['threshold_value'],
228 config_dict
['operation'],
229 config_dict
['metric_name'].lower(),
230 config_dict
['vdu_name'].lower(),
231 config_dict
['vnf_member_index'].lower(),
232 config_dict
['ns_id'].lower()
235 # Return alarm definition UUID by removing 'AlertDefinition' from UUID
236 return (alarm_def_uuid
)
238 def get_default_Params(self
, metric_alarm_name
):
240 Read the default config parameters from plugin specific file stored with plugin file.
242 metric_alarm_name: Name of the alarm, whose config parameters to be read from the config file.
246 source
= open(CONFIG_FILE_PATH
, 'r')
247 except IOError as exp
:
248 msg
= ("Could not read Config file: {}, nException: {}"
249 .format(CONFIG_FILE_PATH
, exp
))
253 tree
= XmlElementTree
.parse(source
)
254 alarms
= tree
.getroot()
256 if alarm
.tag
.lower() == metric_alarm_name
.lower():
258 if param
.tag
in ("period", "evaluation", "cancel_period", "alarm_type",
259 "cancel_cycles", "alarm_subType"):
260 a_params
[param
.tag
] = int(param
.text
)
261 elif param
.tag
in ("enabled", "repeat"):
262 if param
.text
.lower() == "true":
263 a_params
[param
.tag
] = True
265 a_params
[param
.tag
] = False
267 a_params
[param
.tag
] = param
.text
271 def create_symptom(self
, symptom_params
):
272 """Create Symptom definition for an alarm
274 symptom_params: Dict of parameters required for defining a symptom as follows
277 resource_kind_key = "VirtualMachine"
278 adapter_kind_key = "VMWARE"
279 symptom_name = Test_Memory_Usage_TooHigh
284 Returns the uuid of Symptom definition
289 api_url
= '/suite-api/api/symptomdefinitions'
290 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
293 "name": symptom_params
['symptom_name'],
294 "adapterKindKey": symptom_params
['adapter_kind_key'],
295 "resourceKindKey": symptom_params
['resource_kind_key'],
296 "waitCycles": symptom_params
['wait_cycles'],
297 "cancelCycles": symptom_params
['cancel_cycles'],
299 "severity": symptom_params
['severity'],
301 "type": "CONDITION_HT",
302 "key": symptom_params
['metric_key'],
303 "operator": symptom_params
['operation'],
304 "value": symptom_params
['threshold_value'],
305 "valueType": "NUMERIC",
307 "thresholdType": "STATIC"
312 resp
= requests
.post(self
.vrops_site
+ api_url
,
313 auth
=(self
.vrops_user
, self
.vrops_password
),
316 data
=json
.dumps(data
))
318 if resp
.status_code
!= 201:
319 logger
.warning("Failed to create Symptom definition: {}, response {}"
320 .format(symptom_params
['symptom_name'], resp
.content
))
323 resp_data
= json
.loads(resp
.content
)
324 if resp_data
.get('id') is not None:
325 symptom_id
= resp_data
['id']
329 except Exception as exp
:
330 logger
.warning("Error creating symptom definition : {} n{}"
331 .format(exp
, traceback
.format_exc()))
333 def create_alarm_definition(self
, alarm_params
):
335 Create an alarm definition in vROPs
338 'description':Alarm description,
339 'adapterKindKey': Adapter type in vROPs "VMWARE",
340 'resourceKindKey':Resource type in vROPs "VirtualMachine",
341 'waitCycles': No of wait cycles,
342 'cancelCycles': No of cancel cycles,
344 'subType': Alarm subtype,
345 'severity': Severity in vROPs "CRITICAL",
346 'symptomDefinitionId':symptom Definition uuid,
347 'impact': impact 'risk'
349 'alarm_uuid': returns alarm uuid
355 api_url
= '/suite-api/api/alertdefinitions'
356 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
358 "name": alarm_params
['name'],
359 "description": alarm_params
['description'],
360 "adapterKindKey": alarm_params
['adapterKindKey'],
361 "resourceKindKey": alarm_params
['resourceKindKey'],
364 "type": alarm_params
['type'],
365 "subType": alarm_params
['subType'],
368 "severity": alarm_params
['severity'],
371 "type": "SYMPTOM_SET",
373 "aggregation": "ALL",
374 "symptomSetOperator": "AND",
375 "symptomDefinitionIds": [alarm_params
['symptomDefinitionId']]
378 "impactType": "BADGE",
379 "detail": alarm_params
['impact']
385 resp
= requests
.post(self
.vrops_site
+ api_url
,
386 auth
=(self
.vrops_user
, self
.vrops_password
),
389 data
=json
.dumps(data
))
391 if resp
.status_code
!= 201:
392 logger
.warning("Failed to create Alarm definition: {}, response {}"
393 .format(alarm_params
['name'], resp
.content
))
396 resp_data
= json
.loads(resp
.content
)
397 if resp_data
.get('id') is not None:
398 alarm_uuid
= resp_data
['id']
402 except Exception as exp
:
403 logger
.warning("Error creating alarm definition : {} n{}".format(exp
, traceback
.format_exc()))
405 def configure_rest_plugin(self
):
407 Creates REST Plug-in for vROPs outbound alerts
412 plugin_name
= 'MON_module_REST_Plugin'
413 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
415 # If REST plugin not configured, configure it
416 if plugin_id
is not None:
420 cert_file_string
= open(SSL_CERTIFICATE_FILE_PATH
, "rb").read()
421 except IOError as exp
:
422 msg
= ("Could not read SSL certificate file: {}".format(SSL_CERTIFICATE_FILE_PATH
))
425 cert
= load_certificate(FILETYPE_PEM
, cert_file_string
)
426 certificate
= cert
.digest("sha1")
427 api_url
= '/suite-api/api/alertplugins'
428 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
430 "pluginTypeId": "RestPlugin",
438 "name": "Content-type",
439 "value": "application/json"
442 "name": "Certificate",
446 "name": "ConnectionCount",
452 resp
= requests
.post(self
.vrops_site
+ api_url
,
453 auth
=(self
.vrops_user
, self
.vrops_password
),
456 data
=json
.dumps(data
))
458 if resp
.status_code
is not 201:
459 logger
.warning("Failed to create REST Plugin: {} for url: {}, nresponse code: {},"
460 " nresponse content: {}".format(plugin_name
, webhook_url
,
461 resp
.status_code
, resp
.content
))
464 resp_data
= json
.loads(resp
.content
)
465 if resp_data
.get('pluginId') is not None:
466 plugin_id
= resp_data
['pluginId']
468 if plugin_id
is None:
469 logger
.warning("Failed to get REST Plugin ID for {}, url: {}".format(plugin_name
, webhook_url
))
473 "Created REST Plugin: {} with ID : {} for url: {}".format(plugin_name
, plugin_id
, webhook_url
))
474 status
= self
.enable_rest_plugin(plugin_id
, plugin_name
)
477 "Failed to enable created REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
480 logger
.info("Enabled REST Plugin: {} for url: {}".format(plugin_name
, webhook_url
))
483 def check_if_plugin_configured(self
, plugin_name
):
484 """Check if the REST plugin is already created
485 Returns: plugin_id: if already created, None: if needs to be created
488 # Find the REST Plugin id details for - MON_module_REST_Plugin
489 api_url
= '/suite-api/api/alertplugins'
490 headers
= {'Accept': 'application/json'}
492 resp
= requests
.get(self
.vrops_site
+ api_url
,
493 auth
=(self
.vrops_user
, self
.vrops_password
),
494 verify
=False, headers
=headers
)
496 if resp
.status_code
is not 200:
497 logger
.warning("Failed to REST GET Alarm plugin details nResponse code: {} nResponse content: {}"
498 .format(resp
.status_code
, resp
.content
))
501 # Look for specific plugin & parse pluginId for 'MON_module_REST_Plugin'
502 plugins_list
= json
.loads(resp
.content
)
503 if plugins_list
.get('notificationPluginInstances') is not None:
504 for notify_plugin
in plugins_list
['notificationPluginInstances']:
505 if notify_plugin
.get('name') is not None and notify_plugin
['name'] == plugin_name
:
506 plugin_id
= notify_plugin
.get('pluginId')
508 if plugin_id
is None:
509 logger
.warning("REST plugin {} not found".format(plugin_name
))
512 logger
.info("Found REST Plugin: {}".format(plugin_name
))
515 def enable_rest_plugin(self
, plugin_id
, plugin_name
):
517 Enable the REST plugin using plugin_id
518 Params: plugin_id: plugin ID string that is to be enabled
519 Returns: status (Boolean) - True for success, False for failure
522 if plugin_id
is None or plugin_name
is None:
523 logger
.debug("enable_rest_plugin() : Plugin ID or plugin_name not provided for {} plugin"
524 .format(plugin_name
))
528 api_url
= "/suite-api/api/alertplugins/{}/enable/True".format(plugin_id
)
530 resp
= requests
.put(self
.vrops_site
+ api_url
,
531 auth
=(self
.vrops_user
, self
.vrops_password
),
534 if resp
.status_code
is not 204:
535 logger
.warning("Failed to enable REST plugin {}. nResponse code {} nResponse Content: {}"
536 .format(plugin_name
, resp
.status_code
, resp
.content
))
539 logger
.info("Enabled REST plugin {}.".format(plugin_name
))
542 except Exception as exp
:
543 logger
.warning("Error enabling REST plugin for {} plugin: Exception: {} n{}"
544 .format(plugin_name
, exp
, traceback
.format_exc()))
546 def create_alarm_notification_rule(self
, alarm_name
, alarm_id
, resource_id
):
548 Create notification rule for each alarm
555 notification_id: notification_id or None
557 notification_name
= 'notify_' + alarm_name
558 notification_id
= None
559 plugin_name
= 'MON_module_REST_Plugin'
561 # 1) Find the REST Plugin id details for - MON_module_REST_Plugin
562 plugin_id
= self
.check_if_plugin_configured(plugin_name
)
563 if plugin_id
is None:
564 logger
.warning("Failed to get REST plugin_id for : {}".format('MON_module_REST_Plugin'))
567 # 2) Create Alarm notification rule
568 api_url
= '/suite-api/api/notifications/rules'
569 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
571 "name": notification_name
,
572 "pluginId": plugin_id
,
574 "matchResourceIdOnly": True,
575 "resourceId": resource_id
577 "alertDefinitionIdFilters": {
582 resp
= requests
.post(self
.vrops_site
+ api_url
,
583 auth
=(self
.vrops_user
, self
.vrops_password
),
586 data
=json
.dumps(data
))
588 if resp
.status_code
is not 201:
589 logger
.warning("Failed to create Alarm notification rule {} for {} alarm."
590 " nResponse code: {} nResponse content: {}"
591 .format(notification_name
, alarm_name
, resp
.status_code
, resp
.content
))
594 # parse notification id from response
595 resp_data
= json
.loads(resp
.content
)
596 if resp_data
.get('id') is not None:
597 notification_id
= resp_data
['id']
599 logger
.info("Created Alarm notification rule {} for {} alarm.".format(notification_name
, alarm_name
))
600 return notification_id
602 def get_vm_moref_id(self
, vapp_uuid
):
604 Get the moref_id of given VM
608 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
609 if vm_details
and "vm_vcenter_info" in vm_details
:
610 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
611 logger
.info("Found vm_moref_id: {} for vApp UUID: {}".format(vm_moref_id
, vapp_uuid
))
614 except Exception as exp
:
615 logger
.warning("Error occurred while getting VM moref ID for VM : {} n{}"
616 .format(exp
, traceback
.format_exc()))
618 def get_vapp_details_rest(self
, vapp_uuid
=None):
620 Method retrieve vapp detail from vCloud director
623 vapp_uuid - is vapp identifier.
626 Returns VM MOref ID or return None
632 if vapp_uuid
is None:
633 return parsed_respond
635 vca
= self
.connect_as_admin()
637 logger
.warning("Failed to connect to vCD")
638 return parsed_respond
640 url_list
= [self
.vcloud_site
, '/api/vApp/vapp-', vapp_uuid
]
641 get_vapp_restcall
= ''.join(url_list
)
644 headers
= {'Accept': 'application/*+xml;version=' + API_VERSION
,
645 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
646 response
= requests
.get(get_vapp_restcall
,
650 if response
.status_code
!= 200:
651 logger
.warning("REST API call {} failed. Return status code {}"
652 .format(get_vapp_restcall
, response
.content
))
653 return parsed_respond
656 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
658 namespaces
= {'vm': 'http://www.vmware.com/vcloud/v1.5',
659 "vmext": "http://www.vmware.com/vcloud/extension/v1.5",
660 "xmlns": "http://www.vmware.com/vcloud/v1.5"
663 # parse children section for other attrib
664 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
665 if children_section
is not None:
666 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
667 if vCloud_extension_section
is not None:
669 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
670 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
671 if vmext
is not None:
672 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
673 parsed_respond
["vm_vcenter_info"] = vm_vcenter_info
675 except Exception as exp
:
676 logger
.warning("Error occurred calling rest api for getting vApp details: {} n{}"
677 .format(exp
, traceback
.format_exc()))
679 return parsed_respond
681 def connect_as_admin(self
):
682 """ Method connect as pvdc admin user to vCloud director.
683 There are certain action that can be done only by provider vdc admin user.
684 Organization creation / provider network creation etc.
687 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
690 logger
.debug("Logging into vCD org as admin.")
693 host
= self
.vcloud_site
695 client_as_admin
= Client(host
, verify_ssl_certs
=False)
696 client_as_admin
.set_credentials(BasicLoginCredentials(self
.admin_username
, org
,
697 self
.admin_password
))
698 return client_as_admin
700 except Exception as e
:
701 logger
.warning("Can't connect to a vCloud director as: {} with exception {}"
702 .format(self
.admin_username
, e
))
704 def get_vm_resource_id(self
, vm_moref_id
):
705 """ Find resource ID in vROPs using vm_moref_id
707 if vm_moref_id
is None:
710 api_url
= '/suite-api/api/resources?resourceKind=VirtualMachine'
711 headers
= {'Accept': 'application/json'}
713 resp
= requests
.get(self
.vrops_site
+ api_url
,
714 auth
=(self
.vrops_user
, self
.vrops_password
),
715 verify
=False, headers
=headers
)
717 if resp
.status_code
is not 200:
718 logger
.warning("Failed to get resource details from vROPs for {}"
719 " nResponse code:{} nResponse Content: {}"
720 .format(vm_moref_id
, resp
.status_code
, resp
.content
))
723 vm_resource_id
= None
725 resp_data
= json
.loads(resp
.content
)
726 if resp_data
.get('resourceList') is not None:
727 resource_list
= resp_data
.get('resourceList')
728 for resource
in resource_list
:
729 if resource
.get('resourceKey') is not None:
730 resource_details
= resource
['resourceKey']
731 if resource_details
.get('resourceIdentifiers') is not None:
732 resource_identifiers
= resource_details
['resourceIdentifiers']
733 for resource_identifier
in resource_identifiers
:
734 if resource_identifier
['identifierType']['name'] == 'VMEntityObjectID':
735 if resource_identifier
.get('value') is not None and \
736 resource_identifier
['value'] == vm_moref_id
:
737 vm_resource_id
= resource
['identifier']
738 logger
.info("Found VM resource ID: {} for vm_moref_id: {}"
739 .format(vm_resource_id
, vm_moref_id
))
741 except Exception as exp
:
742 logger
.warning("get_vm_resource_id: Error in parsing {} n{}"
743 .format(exp
, traceback
.format_exc()))
745 return vm_resource_id
747 def get_metrics_data(self
, metric
={}):
748 """Get an individual metric's data of a resource.
750 'metric_name': Normalized name of metric (string)
751 'resource_uuid': Resource UUID (string)
752 'period': Time period in Period Unit for which metrics data to be collected from
753 Monitoring tool from now.
754 'period_unit': Period measurement unit can be one of 'HR', 'DAY', 'MONTH', 'YEAR'
756 Return a dict that contains:
757 'metric_name': Normalized name of metric (string)
758 'resource_uuid': Resource UUID (string)
759 'tenant_id': tenent id name in which the resource is present in string format
760 'metrics_data': Dictionary containing time_series & metrics_series data.
761 'time_series': List of individual time stamp values in msec
762 'metrics_series': List of individual metrics data values
763 Raises an exception upon error or when network is not found
766 return_data
['schema_version'] = "1.0"
767 return_data
['schema_type'] = 'read_metric_data_response'
768 return_data
['vim_uuid'] = metric
['vim_uuid']
769 return_data
['metric_name'] = metric
['metric_name']
770 # To do - No metric_uuid in vROPs, thus returning '0'
771 return_data
['metric_uuid'] = '0'
772 return_data
['correlation_id'] = metric
['correlation_id']
773 return_data
['resource_uuid'] = metric
['resource_uuid']
774 return_data
['metrics_data'] = {'time_series': [], 'metrics_series': []}
775 # To do - Need confirmation about uuid & id
776 ##if 'tenant_uuid' in metric and metric['tenant_uuid'] is not None:
777 ## return_data['tenant_uuid'] = metric['tenant_uuid']
779 ## return_data['tenant_uuid'] = None
780 return_data
['unit'] = None
781 # return_data['tenant_id'] = self.tenant_id
782 # logger.warning("return_data: {}".format(return_data))
784 # 1) Get metric details from plugin specific file & format it into vROPs metrics
785 metric_key_params
= self
.get_default_Params(metric
['metric_name'])
787 if not metric_key_params
:
788 logger
.warning("Metric not supported: {}".format(metric
['metric_name']))
789 # To Do: Return message
792 return_data
['unit'] = metric_key_params
['unit']
794 # 2) Find the resource id in vROPs based on OSM resource_uuid
795 # 2.a) Find vm_moref_id from vApp uuid in vCD
796 vm_moref_id
= self
.get_vm_moref_id(metric
['resource_uuid'])
797 if vm_moref_id
is None:
798 logger
.warning("Failed to find vm morefid for vApp in vCD: {}".format(metric
['resource_uuid']))
800 # 2.b) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
801 resource_id
= self
.get_vm_resource_id(vm_moref_id
)
802 if resource_id
is None:
803 logger
.warning("Failed to find resource in vROPs: {}".format(metric
['resource_uuid']))
806 # 3) Calculate begin & end time for period & period unit
807 end_time
= int(round(time
.time() * 1000))
808 if metric
['collection_unit'] == 'YR':
809 time_diff
= PERIOD_MSEC
[metric
['collection_unit']]
811 time_diff
= metric
['collection_period'] * PERIOD_MSEC
[metric
['collection_unit']]
812 begin_time
= end_time
- time_diff
814 # 4) Get the metrics data
815 logger
.info("metric_key_params['metric_key'] = {}".format(metric_key_params
['metric_key']))
816 logger
.info("end_time: {}, begin_time: {}".format(end_time
, begin_time
))
818 url_list
= ['/suite-api/api/resources/', resource_id
, '/stats?statKey=',
819 metric_key_params
['metric_key'], '&begin=', str(begin_time
), '&end=', str(end_time
)]
820 api_url
= ''.join(url_list
)
821 headers
= {'Accept': 'application/json'}
823 resp
= requests
.get(self
.vrops_site
+ api_url
,
824 auth
=(self
.vrops_user
, self
.vrops_password
),
825 verify
=False, headers
=headers
)
827 if resp
.status_code
is not 200:
829 "Failed to retrieve Metric data from vROPs for {} nResponse code:{} nResponse Content: {}"
830 .format(metric
['metric_name'], resp
.status_code
, resp
.content
))
833 # 5) Convert to required format
835 json_data
= json
.loads(resp
.content
)
836 for resp_key
, resp_val
in six
.iteritems(json_data
):
837 if resp_key
== 'values':
838 data
= json_data
['values'][0]
839 for data_k
, data_v
in six
.iteritems(data
):
840 if data_k
== 'stat-list':
842 for stat_list_k
, stat_list_v
in six
.iteritems(stat_list
):
843 for stat_keys
, stat_vals
in six
.iteritems(stat_list_v
[0]):
844 if stat_keys
== 'timestamps':
845 metrics_data
['time_series'] = stat_list_v
[0]['timestamps']
846 if stat_keys
== 'data':
847 metrics_data
['metrics_series'] = stat_list_v
[0]['data']
849 return_data
['metrics_data'] = metrics_data
853 def update_alarm_configuration(self
, new_alarm_config
):
854 """Update alarm configuration (i.e. Symptom & alarm) as per request
856 if new_alarm_config
.get('alarm_uuid') is None:
857 logger
.warning("alarm_uuid is required to update an Alarm")
859 # 1) Get Alarm details from it's uuid & find the symptom definition
860 alarm_details_json
, alarm_details
= self
.get_alarm_defination_details(new_alarm_config
['alarm_uuid'])
861 if alarm_details_json
is None:
865 # 2) Update the symptom definition
866 if alarm_details
['alarm_id'] is not None and alarm_details
['symptom_definition_id'] is not None:
867 symptom_defination_id
= alarm_details
['symptom_definition_id']
869 logger
.info("Symptom Definition ID not found for {}".format(new_alarm_config
['alarm_uuid']))
872 symptom_uuid
= self
.update_symptom_defination(symptom_defination_id
, new_alarm_config
)
874 # 3) Update the alarm definition & Return UUID if successful update
875 if symptom_uuid
is None:
876 logger
.info("Symptom Definition details not found for {}"
877 .format(new_alarm_config
['alarm_uuid']))
880 alarm_uuid
= self
.reconfigure_alarm(alarm_details_json
, new_alarm_config
)
881 if alarm_uuid
is None:
886 logger
.error("Exception while updating alarm: {}".format(traceback
.format_exc()))
888 def get_alarm_defination_details(self
, alarm_uuid
):
889 """Get alarm details based on alarm UUID
891 if alarm_uuid
is None:
892 logger
.warning("get_alarm_defination_details: Alarm UUID not provided")
897 api_url
= '/suite-api/api/alertdefinitions/AlertDefinition-'
898 headers
= {'Accept': 'application/json'}
900 resp
= requests
.get(self
.vrops_site
+ api_url
+ alarm_uuid
,
901 auth
=(self
.vrops_user
, self
.vrops_password
),
902 verify
=False, headers
=headers
)
904 if resp
.status_code
is not 200:
905 logger
.warning("Alarm to be updated not found: {} nResponse code:{} nResponse Content: {}"
906 .format(alarm_uuid
, resp
.status_code
, resp
.content
))
910 json_data
= json
.loads(resp
.content
)
911 if json_data
['id'] is not None:
912 alarm_details
['alarm_id'] = json_data
['id']
913 alarm_details
['alarm_name'] = json_data
['name']
914 alarm_details
['adapter_kind'] = json_data
['adapterKindKey']
915 alarm_details
['resource_kind'] = json_data
['resourceKindKey']
916 alarm_details
['type'] = json_data
['type']
917 alarm_details
['sub_type'] = json_data
['subType']
918 alarm_details
['symptom_definition_id'] = \
919 json_data
['states'][0]['base-symptom-set']['symptomDefinitionIds'][0]
920 except Exception as exp
:
921 logger
.warning("Exception while retrieving alarm definition details: {}".format(exp
))
924 return json_data
, alarm_details
926 def get_alarm_defination_by_name(self
, alarm_name
):
927 """Get alarm details based on alarm name
930 alert_match_list
= []
932 if alarm_name
is None:
933 logger
.warning("get_alarm_defination_by_name: Alarm name not provided")
934 return alert_match_list
937 api_url
= '/suite-api/api/alertdefinitions'
938 headers
= {'Accept': 'application/json'}
940 resp
= requests
.get(self
.vrops_site
+ api_url
,
941 auth
=(self
.vrops_user
, self
.vrops_password
),
942 verify
=False, headers
=headers
)
944 if resp
.status_code
is not 200:
945 logger
.warning("get_alarm_defination_by_name: Error in response: {} nResponse code:{}"
946 " nResponse Content: {}".format(alarm_name
, resp
.status_code
, resp
.content
))
947 return alert_match_list
950 json_data
= json
.loads(resp
.content
)
951 if json_data
['alertDefinitions'] is not None:
952 alerts_list
= json_data
['alertDefinitions']
953 alert_match_list
= list(filter(lambda alert
: alert
['name'] == alarm_name
, alerts_list
))
954 status
= False if not alert_match_list
else True
955 # logger.debug("Found alert_match_list: {}for larm_name: {}, nstatus: {}".format(alert_match_list, alarm_name,status))
957 return alert_match_list
959 except Exception as exp
:
960 logger
.warning("Exception while searching alarm definition: {}".format(exp
))
961 return alert_match_list
963 def update_symptom_defination(self
, symptom_uuid
, new_alarm_config
):
964 """Update symptom definition based on new alarm input configuration
966 # 1) Get symptom definition details
967 symptom_details
= self
.get_symptom_defination_details(symptom_uuid
)
968 # print " n nsymptom_details: {}".format(symptom_details)
969 if symptom_details
is None:
972 if 'severity' in new_alarm_config
and new_alarm_config
['severity'] is not None:
973 symptom_details
['state']['severity'] = severity_mano2vrops
[new_alarm_config
['severity']]
974 if 'operation' in new_alarm_config
and new_alarm_config
['operation'] is not None:
975 symptom_details
['state']['condition']['operator'] = OPERATION_MAPPING
[new_alarm_config
['operation']]
976 if 'threshold_value' in new_alarm_config
and new_alarm_config
['threshold_value'] is not None:
977 symptom_details
['state']['condition']['value'] = new_alarm_config
['threshold_value']
978 # Find vrops metric key from metric_name, if required
980 if 'metric_name' in new_alarm_config and new_alarm_config['metric_name'] is not None:
981 metric_key_params = self.get_default_Params(new_alarm_config['metric_name'])
982 if not metric_key_params:
983 logger.warning("Metric not supported: {}".format(config_dict['metric_name']))
985 symptom_details['state']['condition']['key'] = metric_key_params['metric_key']
987 logger
.info("Fetched Symptom details : {}".format(symptom_details
))
989 api_url
= '/suite-api/api/symptomdefinitions'
990 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
991 data
= json
.dumps(symptom_details
)
992 resp
= requests
.put(self
.vrops_site
+ api_url
,
993 auth
=(self
.vrops_user
, self
.vrops_password
),
998 if resp
.status_code
!= 200:
999 logger
.warning("Failed to update Symptom definition: {}, response {}"
1000 .format(symptom_uuid
, resp
.content
))
1003 if symptom_uuid
is not None:
1004 logger
.info("Symptom definition updated {} for alarm: {}"
1005 .format(symptom_uuid
, new_alarm_config
['alarm_uuid']))
1008 logger
.warning("Failed to update Symptom Definition {} for : {}"
1009 .format(symptom_uuid
, new_alarm_config
['alarm_uuid']))
1012 def get_symptom_defination_details(self
, symptom_uuid
):
1013 """Get symptom definition details
1015 symptom_details
= {}
1016 if symptom_uuid
is None:
1017 logger
.warning("get_symptom_defination_details: Symptom UUID not provided")
1020 api_url
= '/suite-api/api/symptomdefinitions/'
1021 headers
= {'Accept': 'application/json'}
1023 resp
= requests
.get(self
.vrops_site
+ api_url
+ symptom_uuid
,
1024 auth
=(self
.vrops_user
, self
.vrops_password
),
1025 verify
=False, headers
=headers
)
1027 if resp
.status_code
is not 200:
1028 logger
.warning("Symptom definition not found {} nResponse code:{} nResponse Content: {}"
1029 .format(symptom_uuid
, resp
.status_code
, resp
.content
))
1032 symptom_details
= json
.loads(resp
.content
)
1033 # print "New symptom Details: {}".format(symptom_details)
1034 return symptom_details
1036 def reconfigure_alarm(self
, alarm_details_json
, new_alarm_config
):
1037 """Reconfigure alarm definition as per input
1039 if 'severity' in new_alarm_config
and new_alarm_config
['severity'] is not None:
1040 alarm_details_json
['states'][0]['severity'] = new_alarm_config
['severity']
1041 if 'description' in new_alarm_config
and new_alarm_config
['description'] is not None:
1042 alarm_details_json
['description'] = new_alarm_config
['description']
1044 api_url
= '/suite-api/api/alertdefinitions'
1045 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
1046 data
= json
.dumps(alarm_details_json
)
1047 resp
= requests
.put(self
.vrops_site
+ api_url
,
1048 auth
=(self
.vrops_user
, self
.vrops_password
),
1053 if resp
.status_code
!= 200:
1054 logger
.warning("Failed to update Alarm definition: {}, response code {}, response content: {}"
1055 .format(alarm_details_json
['id'], resp
.status_code
, resp
.content
))
1058 parsed_alarm_details
= json
.loads(resp
.content
)
1059 alarm_def_uuid
= parsed_alarm_details
['id'].split('-', 1)[1]
1060 logger
.info("Successfully updated Alarm definition: {}".format(alarm_def_uuid
))
1061 return alarm_def_uuid
1063 def delete_alarm_configuration(self
, delete_alarm_req_dict
):
1064 """Delete complete alarm configuration
1066 if delete_alarm_req_dict
['alarm_uuid'] is None:
1067 logger
.info("delete_alarm_configuration: Alarm UUID not provided")
1069 # 1)Get alarm & symptom definition details
1070 alarm_details_json
, alarm_details
= self
.get_alarm_defination_details(delete_alarm_req_dict
['alarm_uuid'])
1071 if alarm_details
is None or alarm_details_json
is None:
1074 # 2) Delete alarm notification
1075 rule_id
= self
.delete_notification_rule(alarm_details
['alarm_name'])
1079 # 3) Delete alarm configuration
1080 alarm_id
= self
.delete_alarm_defination(alarm_details
['alarm_id'])
1081 if alarm_id
is None:
1084 # 4) Delete alarm symptom
1085 symptom_id
= self
.delete_symptom_definition(alarm_details
['symptom_definition_id'])
1086 if symptom_id
is None:
1089 logger
.info("Completed deleting alarm configuration: {}"
1090 .format(delete_alarm_req_dict
['alarm_uuid']))
1091 return delete_alarm_req_dict
['alarm_uuid']
1093 def delete_notification_rule(self
, alarm_name
):
1094 """Deleted notification rule defined for a particular alarm
1096 rule_id
= self
.get_notification_rule_id_by_alarm_name(alarm_name
)
1100 api_url
= '/suite-api/api/notifications/rules/'
1101 headers
= {'Accept': 'application/json'}
1102 resp
= requests
.delete(self
.vrops_site
+ api_url
+ rule_id
,
1103 auth
=(self
.vrops_user
, self
.vrops_password
),
1104 verify
=False, headers
=headers
)
1105 if resp
.status_code
is not 204:
1106 logger
.warning("Failed to delete notification rules for {}".format(alarm_name
))
1109 logger
.info("Deleted notification rules for {}".format(alarm_name
))
1112 def get_notification_rule_id_by_alarm_name(self
, alarm_name
):
1113 """Find created Alarm notification rule id by alarm name
1115 alarm_notify_id
= 'notify_' + alarm_name
1116 api_url
= '/suite-api/api/notifications/rules'
1117 headers
= {'Content-Type': 'application/json', 'Accept': 'application/json'}
1118 resp
= requests
.get(self
.vrops_site
+ api_url
,
1119 auth
=(self
.vrops_user
, self
.vrops_password
),
1120 verify
=False, headers
=headers
)
1122 if resp
.status_code
is not 200:
1123 logger
.warning("Failed to get notification rules details for {}"
1124 .format(alarm_name
))
1127 notifications
= json
.loads(resp
.content
)
1128 if notifications
is not None and 'notification-rule' in notifications
:
1129 notifications_list
= notifications
['notification-rule']
1130 for dict in notifications_list
:
1131 if dict['name'] is not None and dict['name'] == alarm_notify_id
:
1132 notification_id
= dict['id']
1133 logger
.info("Found Notification id to be deleted: {} for {}"
1134 .format(notification_id
, alarm_name
))
1135 return notification_id
1137 logger
.warning("Notification id to be deleted not found for {}"
1138 .format(alarm_name
))
1141 def delete_alarm_defination(self
, alarm_id
):
1142 """Delete created Alarm definition
1144 api_url
= '/suite-api/api/alertdefinitions/'
1145 headers
= {'Accept': 'application/json'}
1146 resp
= requests
.delete(self
.vrops_site
+ api_url
+ alarm_id
,
1147 auth
=(self
.vrops_user
, self
.vrops_password
),
1148 verify
=False, headers
=headers
)
1149 if resp
.status_code
is not 204:
1150 logger
.warning("Failed to delete alarm definition {}".format(alarm_id
))
1153 logger
.info("Deleted alarm definition {}".format(alarm_id
))
1156 def delete_symptom_definition(self
, symptom_id
):
1157 """Delete symptom definition
1159 api_url
= '/suite-api/api/symptomdefinitions/'
1160 headers
= {'Accept': 'application/json'}
1161 resp
= requests
.delete(self
.vrops_site
+ api_url
+ symptom_id
,
1162 auth
=(self
.vrops_user
, self
.vrops_password
),
1163 verify
=False, headers
=headers
)
1164 if resp
.status_code
is not 204:
1165 logger
.warning("Failed to delete symptom definition {}".format(symptom_id
))
1168 logger
.info("Deleted symptom definition {}".format(symptom_id
))
1171 def verify_metric_support(self
, metric_info
):
1172 """Verify, if Metric is supported by vROPs plugin, verify metric unit & return status
1174 status: True if supported, False if not supported
1177 if 'metric_name' not in metric_info
:
1178 logger
.debug("Metric name not provided: {}".format(metric_info
))
1180 metric_key_params
= self
.get_default_Params(metric_info
['metric_name'].lower())
1181 if not metric_key_params
:
1182 logger
.warning("Metric not supported: {}".format(metric_info
['metric_name']))
1185 # If Metric is supported, verify optional metric unit & return status
1186 if 'metric_unit' in metric_info
:
1187 if metric_key_params
.get('unit') == metric_info
['metric_unit']:
1188 logger
.info("Metric is supported with unit: {}".format(metric_info
['metric_name']))
1191 logger
.debug("Metric supported but there is unit mismatch for: {}."
1192 "Supported unit: {}"
1193 .format(metric_info
['metric_name'], metric_key_params
['unit']))
1197 def get_triggered_alarms_list(self
, list_alarm_input
):
1198 """Get list of triggered alarms on a resource based on alarm input request.
1200 # TO Do - Need to add filtering of alarms based on Severity & alarm name
1202 triggered_alarms_list
= []
1203 if list_alarm_input
.get('resource_uuid') is None:
1204 logger
.warning("Resource UUID is required to get triggered alarms list")
1205 return triggered_alarms_list
1207 # 1)Find vROPs resource ID using RO resource UUID
1208 vrops_resource_id
= self
.get_vrops_resourceid_from_ro_uuid(list_alarm_input
['resource_uuid'])
1209 if vrops_resource_id
is None:
1210 return triggered_alarms_list
1212 # 2)Get triggered alarms on particular resource
1213 triggered_alarms_list
= self
.get_triggered_alarms_on_resource(list_alarm_input
['resource_uuid'],
1215 return triggered_alarms_list
1217 def get_vrops_resourceid_from_ro_uuid(self
, ro_resource_uuid
):
1218 """Fetch vROPs resource ID using resource UUID from RO/SO
1220 # 1) Find vm_moref_id from vApp uuid in vCD
1221 vm_moref_id
= self
.get_vm_moref_id(ro_resource_uuid
)
1222 if vm_moref_id
is None:
1223 logger
.warning("Failed to find vm morefid for vApp in vCD: {}".format(ro_resource_uuid
))
1226 # 2) Based on vm_moref_id, find VM's corresponding resource_id in vROPs to set notification
1227 vrops_resource_id
= self
.get_vm_resource_id(vm_moref_id
)
1228 if vrops_resource_id
is None:
1229 logger
.warning("Failed to find resource in vROPs: {}".format(ro_resource_uuid
))
1231 return vrops_resource_id
1233 def get_triggered_alarms_on_resource(self
, ro_resource_uuid
, vrops_resource_id
):
1234 """Get triggered alarms on particular resource & return list of dictionary of alarms
1236 resource_alarms
= []
1237 api_url
= '/suite-api/api/alerts?resourceId='
1238 headers
= {'Accept': 'application/json'}
1239 resp
= requests
.get(self
.vrops_site
+ api_url
+ vrops_resource_id
,
1240 auth
=(self
.vrops_user
, self
.vrops_password
),
1241 verify
=False, headers
=headers
)
1243 if resp
.status_code
is not 200:
1244 logger
.warning("Failed to get triggered alarms for {}"
1245 .format(ro_resource_uuid
))
1248 all_alerts
= json
.loads(resp
.content
)
1249 if 'alerts' in all_alerts
:
1250 if not all_alerts
['alerts']:
1251 logger
.info("No alarms present on resource {}".format(ro_resource_uuid
))
1252 return resource_alarms
1253 all_alerts_list
= all_alerts
['alerts']
1254 for alarm
in all_alerts_list
:
1255 # logger.info("Triggered Alarm {}".format(alarm))
1256 if alarm
['alertDefinitionName'] is not None and \
1257 len(alarm
['alertDefinitionName'].split('-', 1)) == 2:
1258 if alarm
['alertDefinitionName'].split('-', 1)[1] == ro_resource_uuid
:
1259 alarm_instance
= {'alarm_uuid': alarm
['alertDefinitionId'].split('-', 1)[1],
1260 'resource_uuid': ro_resource_uuid
,
1261 'alarm_instance_uuid': alarm
['alertId'],
1262 'vim_type': 'VMware'}
1263 # find severity of alarm
1265 for key
, value
in six
.iteritems(severity_mano2vrops
):
1266 if value
== alarm
['alertLevel']:
1268 if severity
is None:
1269 severity
= 'INDETERMINATE'
1270 alarm_instance
['severity'] = severity
1271 alarm_instance
['status'] = alarm
['status']
1272 alarm_instance
['start_date'] = self
.convert_date_time(alarm
['startTimeUTC'])
1273 alarm_instance
['update_date'] = self
.convert_date_time(alarm
['updateTimeUTC'])
1274 alarm_instance
['cancel_date'] = self
.convert_date_time(alarm
['cancelTimeUTC'])
1275 logger
.info("Triggered Alarm on resource {}".format(alarm_instance
))
1276 resource_alarms
.append(alarm_instance
)
1277 if not resource_alarms
:
1278 logger
.info("No alarms present on resource {}".format(ro_resource_uuid
))
1279 return resource_alarms
1281 def convert_date_time(self
, date_time
):
1282 """Convert the input UTC time in msec to OSM date time format
1284 date_time_formatted
= '0000-00-00T00:00:00'
1286 complete_datetime
= datetime
.datetime
.fromtimestamp(date_time
/ 1000.0, tz
=pytz
.utc
).isoformat('T')
1287 date_time_formatted
= complete_datetime
.split('.', 1)[0]
1288 return date_time_formatted