Adds vdu, ns, threshold and operation info to alarm notification
[osm/MON.git] / osm_mon / plugins / OpenStack / Aodh / alarming.py
index b0602bd..13b6541 100644 (file)
@@ -27,37 +27,16 @@ import logging
 import six
 import yaml
 
+from osm_mon.core.auth import AuthManager
 from osm_mon.core.database import DatabaseManager
 from osm_mon.core.message_bus.producer import KafkaProducer
+from osm_mon.core.settings import Config
+from osm_mon.plugins.OpenStack.Gnocchi.metrics import METRIC_MAPPINGS
 from osm_mon.plugins.OpenStack.common import Common
 from osm_mon.plugins.OpenStack.response import OpenStack_Response
-from osm_mon.plugins.OpenStack.settings import Config
 
 log = logging.getLogger(__name__)
 
-ALARM_NAMES = {
-    "average_memory_usage_above_threshold": "average_memory_utilization",
-    "disk_read_ops": "disk_read_ops",
-    "disk_write_ops": "disk_write_ops",
-    "disk_read_bytes": "disk_read_bytes",
-    "disk_write_bytes": "disk_write_bytes",
-    "net_packets_dropped": "packets_dropped",
-    "packets_in_above_threshold": "packets_received",
-    "packets_out_above_threshold": "packets_sent",
-    "cpu_utilization_above_threshold": "cpu_utilization"}
-
-METRIC_MAPPINGS = {
-    "average_memory_utilization": "memory.percent",
-    "disk_read_ops": "disk.read.requests",
-    "disk_write_ops": "disk.write.requests",
-    "disk_read_bytes": "disk.read.bytes",
-    "disk_write_bytes": "disk.write.bytes",
-    "packets_dropped": "interface.if_dropped",
-    "packets_received": "interface.if_packets",
-    "packets_sent": "interface.if_packets",
-    "cpu_utilization": "cpu_util",
-}
-
 SEVERITIES = {
     "warning": "low",
     "minor": "low",
@@ -83,6 +62,7 @@ class Alarming(object):
         config.read_environ()
 
         self._database_manager = DatabaseManager()
+        self._auth_manager = AuthManager()
 
         # Use the Response class to generate valid json response messages
         self._response = OpenStack_Response()
@@ -90,7 +70,41 @@ class Alarming(object):
         # Initializer a producer to send responses back to SO
         self._producer = KafkaProducer("alarm_response")
 
-    def alarming(self, message):
+    def configure_alarm(self, alarm_endpoint, metric_endpoint, auth_token, values, vim_config):
+        """Create requested alarm in Aodh."""
+        url = "{}/v2/alarms/".format(alarm_endpoint)
+
+        # Check if the desired alarm is supported
+        alarm_name = values['alarm_name'].lower()
+        metric_name = values['metric_name'].lower()
+        resource_id = values['resource_uuid']
+
+        if metric_name not in METRIC_MAPPINGS.keys():
+            log.warning("This metric is not supported.")
+            return None, False
+
+        # Check for the required metric
+        metric_id = self.check_for_metric(auth_token, metric_endpoint, metric_name, resource_id)
+
+        try:
+            if metric_id is not None:
+                # Create the alarm if metric is available
+                if 'granularity' in vim_config and 'granularity' not in values:
+                    values['granularity'] = vim_config['granularity']
+                payload = self.check_payload(values, metric_name, resource_id,
+                                             alarm_name)
+                new_alarm = Common.perform_request(
+                    url, auth_token, req_type="post", payload=payload)
+                return json.loads(new_alarm.text)['alarm_id'], True
+            else:
+                log.warning("The required Gnocchi metric does not exist.")
+                return None, False
+
+        except Exception as exc:
+            log.warning("Failed to create the alarm: %s", exc)
+        return None, False
+
+    def alarming(self, message, vim_uuid):
         """Consume info from the message bus to manage alarms."""
         try:
             values = json.loads(message.value)
@@ -98,35 +112,43 @@ class Alarming(object):
             values = yaml.safe_load(message.value)
 
         log.info("OpenStack alarm action required.")
-        vim_uuid = values['vim_uuid']
 
         auth_token = Common.get_auth_token(vim_uuid)
 
         alarm_endpoint = Common.get_endpoint("alarming", vim_uuid)
         metric_endpoint = Common.get_endpoint("metric", vim_uuid)
 
+        vim_account = self._auth_manager.get_credentials(vim_uuid)
+        vim_config = json.loads(vim_account.config)
+
         if message.key == "create_alarm_request":
             # Configure/Update an alarm
             alarm_details = values['alarm_create_request']
 
             alarm_id, alarm_status = self.configure_alarm(
-                alarm_endpoint, metric_endpoint, auth_token, alarm_details)
+                alarm_endpoint, metric_endpoint, auth_token, alarm_details, vim_config)
 
             # Generate a valid response message, send via producer
+            if alarm_status is True:
+                log.info("Alarm successfully created")
+                self._database_manager.save_alarm(alarm_id,
+                                                  vim_uuid,
+                                                  alarm_details['threshold_value'],
+                                                  alarm_details['operation'].lower(),
+                                                  alarm_details['metric_name'].lower(),
+                                                  alarm_details['vdu_name'].lower(),
+                                                  alarm_details['vnf_member_index'].lower(),
+                                                  alarm_details['ns_id'].lower()
+                                                  )
             try:
-                if alarm_status is True:
-                    log.info("Alarm successfully created")
-                    self._database_manager.save_alarm(alarm_id, vim_uuid)
-
                 resp_message = self._response.generate_response(
                     'create_alarm_response', status=alarm_status,
                     alarm_id=alarm_id,
                     cor_id=alarm_details['correlation_id'])
                 log.info("Response Message: %s", resp_message)
                 self._producer.create_alarm_response(
-                    'create_alarm_response', resp_message,
-                    'alarm_response')
-            except Exception as exc:
+                    'create_alarm_response', resp_message)
+            except Exception:
                 log.exception("Response creation failed:")
 
         elif message.key == "list_alarm_request":
@@ -144,9 +166,8 @@ class Alarming(object):
                     cor_id=list_details['correlation_id'])
                 log.info("Response Message: %s", resp_message)
                 self._producer.list_alarm_response(
-                    'list_alarm_response', resp_message,
-                    'alarm_response')
-            except Exception as exc:
+                    'list_alarm_response', resp_message)
+            except Exception:
                 log.exception("Failed to send a valid response back.")
 
         elif message.key == "delete_alarm_request":
@@ -164,10 +185,9 @@ class Alarming(object):
                     cor_id=request_details['correlation_id'])
                 log.info("Response message: %s", resp_message)
                 self._producer.delete_alarm_response(
-                    'delete_alarm_response', resp_message,
-                    'alarm_response')
-            except Exception as exc:
-                log.warn("Failed to create delete response:%s", exc)
+                    'delete_alarm_response', resp_message)
+            except Exception:
+                log.exception("Failed to create delete response: ")
 
         elif message.key == "acknowledge_alarm":
             # Acknowledge that an alarm has been dealt with by the SO
@@ -180,14 +200,14 @@ class Alarming(object):
             if response is True:
                 log.info("Acknowledged the alarm and cleared it.")
             else:
-                log.warn("Failed to acknowledge/clear the alarm.")
+                log.warning("Failed to acknowledge/clear the alarm.")
 
         elif message.key == "update_alarm_request":
             # Update alarm configurations
             alarm_details = values['alarm_update_request']
 
             alarm_id, status = self.update_alarm(
-                alarm_endpoint, auth_token, alarm_details)
+                alarm_endpoint, auth_token, alarm_details, vim_config)
 
             # Generate a response for an update request
             try:
@@ -197,48 +217,15 @@ class Alarming(object):
                     status=status)
                 log.info("Response message: %s", resp_message)
                 self._producer.update_alarm_response(
-                    'update_alarm_response', resp_message,
-                    'alarm_response')
-            except Exception as exc:
-                log.warn("Failed to send an update response:%s", exc)
+                    'update_alarm_response', resp_message)
+            except Exception:
+                log.exception("Failed to send an update response: ")
 
         else:
             log.debug("Unknown key, no action will be performed")
 
         return
 
-    def configure_alarm(self, alarm_endpoint, metric_endpoint, auth_token, values):
-        """Create requested alarm in Aodh."""
-        url = "{}/v2/alarms/".format(alarm_endpoint)
-
-        # Check if the desired alarm is supported
-        alarm_name = values['alarm_name'].lower()
-        metric_name = values['metric_name'].lower()
-        resource_id = values['resource_uuid']
-
-        if metric_name not in METRIC_MAPPINGS.keys():
-            log.warn("This metric is not supported.")
-            return None, False
-
-        # Check for the required metric
-        metric_id = self.check_for_metric(auth_token, metric_endpoint, metric_name, resource_id)
-
-        try:
-            if metric_id is not None:
-                # Create the alarm if metric is available
-                payload = self.check_payload(values, metric_name, resource_id,
-                                             alarm_name)
-                new_alarm = Common.perform_request(
-                    url, auth_token, req_type="post", payload=payload)
-                return json.loads(new_alarm.text)['alarm_id'], True
-            else:
-                log.warn("The required Gnocchi metric does not exist.")
-                return None, False
-
-        except Exception as exc:
-            log.warn("Failed to create the alarm: %s", exc)
-        return None, False
-
     def delete_alarm(self, endpoint, auth_token, alarm_id):
         """Delete alarm function."""
         url = "{}/v2/alarms/%s".format(endpoint) % alarm_id
@@ -253,8 +240,8 @@ class Alarming(object):
             else:
                 return True
 
-        except Exception as exc:
-            log.warn("Failed to delete alarm: %s because %s.", alarm_id, exc)
+        except Exception:
+            log.exception("Failed to delete alarm %s :", alarm_id)
         return False
 
     def list_alarms(self, endpoint, auth_token, list_details):
@@ -267,15 +254,12 @@ class Alarming(object):
         try:
             resource = list_details['resource_uuid']
         except KeyError as exc:
-            log.warn("Resource id not specified for list request: %s", exc)
+            log.warning("Resource id not specified for list request: %s", exc)
             return None
 
         # Checking what fields are specified for a list request
         try:
             name = list_details['alarm_name'].lower()
-            if name not in ALARM_NAMES.keys():
-                log.warn("This alarm is not supported, won't be used!")
-                name = None
         except KeyError as exc:
             log.info("Alarm name isn't specified.")
             name = None
@@ -347,11 +331,11 @@ class Alarming(object):
             Common.perform_request(
                 url, auth_token, req_type="put", payload=payload)
             return True
-        except Exception as exc:
-            log.warn("Unable to update alarm state: %s", exc)
+        except Exception:
+            log.exception("Unable to update alarm state: ")
         return False
 
-    def update_alarm(self, endpoint, auth_token, values):
+    def update_alarm(self, endpoint, auth_token, values, vim_config):
         """Get alarm name for an alarm configuration update."""
         # Get already existing alarm details
         url = "{}/v2/alarms/%s".format(endpoint) % values['alarm_uuid']
@@ -366,11 +350,12 @@ class Alarming(object):
             resource_id = rule['resource_id']
             metric_name = [key for key, value in six.iteritems(METRIC_MAPPINGS) if value == rule['metric']][0]
         except Exception as exc:
-            log.warn("Failed to retrieve existing alarm info: %s.\
-                     Can only update OSM alarms.", exc)
+            log.exception("Failed to retrieve existing alarm info. Can only update OSM alarms.")
             return None, False
 
         # Generates and check payload configuration for alarm update
+        if 'granularity' in vim_config and 'granularity' not in values:
+            values['granularity'] = vim_config['granularity']
         payload = self.check_payload(values, metric_name, resource_id,
                                      alarm_name, alarm_state=alarm_state)
 
@@ -382,8 +367,7 @@ class Alarming(object):
 
                 return json.loads(update_alarm.text)['alarm_id'], True
             except Exception as exc:
-                log.warn("Alarm update could not be performed: %s", exc)
-                return None, False
+                log.exception("Alarm update could not be performed: ")
         return None, False
 
     def check_payload(self, values, metric_name, resource_id,
@@ -404,7 +388,7 @@ class Alarming(object):
 
             statistic = values['statistic'].lower()
 
-            granularity = '300'
+            granularity = cfg.OS_DEFAULT_GRANULARITY
             if 'granularity' in values:
                 granularity = values['granularity']
 
@@ -430,7 +414,7 @@ class Alarming(object):
                                   'alarm_actions': [cfg.OS_NOTIFIER_URI], })
             return payload
         except KeyError as exc:
-            log.warn("Alarm is not configured correctly: %s", exc)
+            log.warning("Alarm is not configured correctly: %s", exc)
         return None
 
     def get_alarm_state(self, endpoint, auth_token, alarm_id):
@@ -442,7 +426,7 @@ class Alarming(object):
                 url, auth_token, req_type="get")
             return json.loads(alarm_state.text)
         except Exception as exc:
-            log.warn("Failed to get the state of the alarm:%s", exc)
+            log.warning("Failed to get the state of the alarm:%s", exc)
         return None
 
     def check_for_metric(self, auth_token, metric_endpoint, m_name, r_id):