d7cf33cf8de00b03316b459594625e3ee42b8944
[osm/MON.git] / osm_mon / plugins / vRealiseOps / vROPs_Webservice / vrops_webservice
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2016-2017 VMware Inc.
6 # This file is part of ETSI OSM
7 # All Rights Reserved.
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
12 #
13 #         http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact:  osslegalrouting@vmware.com
23 ##
24
25 """
26  Webservice for vRealize Operations (vROPs) to post/notify alarms details.
27
28 """
29 from osm_mon.core.settings import Config
30
31 __author__ = "Arpita Kate"
32 __date__ = "$15-Sept-2017 16:09:29$"
33 __version__ = '0.1'
34
35 import json
36 import logging
37 import os
38 import sys
39 from datetime import datetime
40 from socket import getfqdn
41
42 import requests
43 from bottle import (ServerAdapter, route, run, server_names, default_app,
44                     request, response)
45
46 sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', '..'))
47 from osm_mon.core.message_bus.producer import Producer
48
49 # from core.message_bus.producer import KafkaProducer
50
51 sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..'))
52 from osm_mon.core.database import DatabaseManager
53
54 try:
55     from cheroot.wsgi import Server as WSGIServer
56     from cheroot.ssl.pyopenssl import pyOpenSSLAdapter
57 except ImportError:
58     from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer
59     from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
60
61 # Set Constants
62 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
63 CERT_DIR = os.path.join(BASE_DIR, "SSL_certificate")
64 certificate_name = getfqdn() + ".cert"
65 key_name = getfqdn() + ".key"
66 CERTIFICATE = os.path.join(CERT_DIR, certificate_name)
67 KEY = os.path.join(CERT_DIR, key_name)
68 # CERTIFICATE = os.path.join(CERT_DIR, "www.vrops_webservice.com.cert")
69 # KEY = os.path.join(CERT_DIR, "www.vrops_webservice.com.key")
70 CONFIG_FILE = os.path.join(BASE_DIR, '../vrops_config.xml')
71 # Severity Mapping from vROPs to OSM
72 VROPS_SEVERITY_TO_OSM_MAPPING = {
73     "ALERT_CRITICALITY_LEVEL_CRITICAL": "CRITICAL",
74     "ALERT_CRITICALITY_LEVEL_WARNING": "WARNING",
75     "ALERT_CRITICALITY_LEVEL_IMMEDIATE": "MAJOR",
76     "ALERT_CRITICALITY_LEVEL_INFO": "INDETERMINATE",
77     "ALERT_CRITICALITY_LEVEL_AUTO": "INDETERMINATE",
78     "ALERT_CRITICALITY_LEVEL_UNKNOWN": "INDETERMINATE",
79     "ALERT_CRITICALITY_LEVEL_NONE": "INDETERMINATE"
80 }
81
82 # Set logger
83 cfg = Config.instance()
84 logging.basicConfig(stream=sys.stdout,
85                     format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
86                     datefmt='%m/%d/%Y %I:%M:%S %p',
87                     level=logging.getLevelName(cfg.OSMMON_LOG_LEVEL))
88
89 logger = logging.getLogger(__name__)
90
91
92 def format_datetime(str_date):
93     """
94         Method to format datetime
95         Args:
96             str_date - datetime string
97         Returns:
98            formated datetime
99     """
100     date_fromat = "%Y-%m-%dT%H:%M:%S"
101     formated_datetime = None
102     try:
103         datetime_obj = datetime.fromtimestamp(float(str_date) / 1000.)
104         formated_datetime = datetime_obj.strftime(date_fromat)
105     except Exception as exp:
106         logger.error('Exception: {} occured while converting date {} into format {}'.format(
107             exp, str_date, date_fromat))
108
109     return formated_datetime
110
111
112 def get_alarm_config(alarm_name):
113     """
114         Method to get configuration parameters
115         Args:
116             None
117         Returns:
118             dictionary of config parameters
119     """
120     vim_account = {}
121     database_manager = DatabaseManager()
122     alarm = database_manager.get_alarm(alarm_name, 'VMware')
123     vim_account_details = alarm.credentials
124
125     try:
126         if vim_account_details is not None:
127             vim_account['name'] = vim_account_details.name
128             vim_account['vim_tenant_name'] = vim_account_details.tenant_name
129             vim_account['vim_type'] = vim_account_details.type
130             vim_account['vim_uuid'] = vim_account_details.uuid
131             vim_account['vim_url'] = vim_account_details.url
132             vim_account['org_user'] = vim_account_details.user
133             vim_account['org_password'] = vim_account_details.password
134
135             vim_config = json.loads(vim_account_details.config)
136             vim_account['tenant_id'] = vim_config['tenant_id']
137             vim_account['admin_username'] = vim_config['admin_username']
138             vim_account['admin_password'] = vim_config['admin_password']
139             vim_account['vrops_site'] = vim_config['vrops_site']
140             vim_account['vrops_user'] = vim_config['vrops_user']
141             vim_account['vrops_password'] = vim_config['vrops_password']
142             vim_account['vcenter_ip'] = vim_config['vcenter_ip']
143             vim_account['vcenter_port'] = vim_config['vcenter_port']
144             vim_account['vcenter_user'] = vim_config['vcenter_user']
145             vim_account['vcenter_password'] = vim_config['vcenter_password']
146             if vim_config['nsx_manager'] is not None:
147                 vim_account['nsx_manager'] = vim_config['nsx_manager']
148             if vim_config['nsx_user'] is not None:
149                 vim_account['nsx_user'] = vim_config['nsx_user']
150             if vim_config['nsx_password'] is not None:
151                 vim_account['nsx_password'] = vim_config['nsx_password']
152             if vim_config['orgname'] is not None:
153                 vim_account['orgname'] = vim_config['orgname']
154     except Exception as exp:
155         logger.error("VIM account details not sufficient: {}".format(exp))
156     return vim_account
157
158
159 def get_alarm_definationID(alarm_instance_uuid, access_config):
160     """
161          Method to get alarm/alert defination ID
162             Args:
163                 alarm_instance_uuid : UUID of alarm
164             Returns:
165                 alarm defination ID
166     """
167     alarm_definationID = None
168     if alarm_instance_uuid:
169         try:
170             # access_config = get_alarm_config()
171             headers = {'Accept': 'application/json'}
172             api_url = '{}/suite-api/api/alerts/{}' \
173                 .format(access_config.get('vrops_site'), alarm_instance_uuid)
174             api_response = requests.get(api_url,
175                                         auth=(access_config.get('vrops_user'),
176                                               access_config.get('vrops_password')),
177                                         verify=False,
178                                         headers=headers
179                                         )
180
181             if api_response.status_code == 200:
182                 data = api_response.json()
183                 if data.get("alertDefinitionId") is not None:
184                     alarm_definationID = '-'.join(data.get("alertDefinitionId").split('-')[1:])
185             else:
186                 logger.error("Failed to get alert definition ID for alarm {}"
187                              .format(alarm_instance_uuid))
188         except Exception as exp:
189             logger.error("Exception occured while getting alert definition ID for alarm : {}"
190                          .format(exp, alarm_instance_uuid))
191
192     return alarm_definationID
193
194
195 @route('/notify/<alarmID>', method='POST')
196 def notify_alarm(alarmID):
197     """
198         Method notify alarm details by publishing message at Kafka message bus
199         Args:
200             alarmID - Name of alarm
201         Returns:
202            response code
203     """
204     logger.info("Request:{} from:{} {} {} " \
205                 .format(request, request.remote_addr, request.method, request.url))
206     response.headers['Content-Type'] = 'application/json'
207     try:
208         postdata = json.loads(request.body.read())
209         notify_details = {}
210         vim_access_config = get_alarm_config(postdata.get('alertName'))
211         # Parse notify data
212         notify_details['vim_uuid'] = vim_access_config.get('vim_uuid')
213         notify_details['alarm_uuid'] = get_alarm_definationID(postdata.get('alertId'),
214                                                               vim_access_config)
215         notify_details['description'] = postdata.get('info')
216         notify_details['alarm_instance_uuid'] = alarmID
217         notify_details['resource_uuid'] = '-'.join(postdata.get('alertName').split('-')[1:])
218         notify_details['tenant_uuid'] = vim_access_config.get('tenant_id')
219         notify_details['vim_type'] = "VMware"
220         notify_details['severity'] = VROPS_SEVERITY_TO_OSM_MAPPING.get(postdata.get('criticality'),
221                                                                        'INDETERMINATE')
222         notify_details['status'] = postdata.get('status')
223         if postdata.get('startDate'):
224             notify_details['start_date_time'] = format_datetime(postdata.get('startDate'))
225         if postdata.get('updateDate'):
226             notify_details['update_date_time'] = format_datetime(postdata.get('updateDate'))
227         if postdata.get('cancelDate'):
228             notify_details['cancel_date_time'] = format_datetime(postdata.get('cancelDate'))
229
230         alarm_details = {'schema_version': 1.0,
231                          'schema_type': "notify_alarm",
232                          'notify_details': notify_details
233                          }
234         alarm_data = json.dumps(alarm_details)
235         logger.info("Alarm details: {}".format(alarm_data))
236
237         # Publish Alarm details
238         producer = Producer()
239         producer.send(topic='alarm_response', key=notify_alarm, value=alarm_data)
240         producer.flush()
241
242         # return 201 on Success
243         response.status = 201
244
245     except Exception as exp:
246         logger.error('Exception: {} occured while notifying alarm {}.'.format(exp, alarmID))
247         # return 500 on Error
248         response.status = 500
249
250     return response
251
252
253 class SSLWebServer(ServerAdapter):
254     """
255     CherryPy web server with SSL support.
256     """
257
258     def run(self, handler):
259         """
260         Runs a CherryPy Server using the SSL certificate.
261         """
262         server = WSGIServer((self.host, self.port), handler)
263         server.ssl_adapter = pyOpenSSLAdapter(
264             certificate=CERTIFICATE,
265             private_key=KEY,
266             # certificate_chain="intermediate_cert.crt"
267         )
268
269         try:
270             server.start()
271             logger.info("Started vROPs Web Service")
272         except Exception as exp:
273             server.stop()
274             logger.error("Exception: {} Stopped vROPs Web Service".format(exp))
275
276
277 if __name__ == "__main__":
278     # Start SSL Web Service
279     logger.info("Start vROPs Web Service")
280     app = default_app()
281     server_names['sslwebserver'] = SSLWebServer
282     run(app=app, host=getfqdn(), port=8080, server='sslwebserver')