Openstack-Aodh plugin 80/2080/1
authorHelena McGough <helena.mcgough@intel.com>
Wed, 23 Aug 2017 10:11:42 +0000 (10:11 +0000)
committerHelena McGough <helena.mcgough@intel.com>
Wed, 23 Aug 2017 10:11:42 +0000 (10:11 +0000)
 - Initial Aodh plugin work for MON

Change-Id: I9525f37871390609f6a02e7acb87f62e643ee615
Signed-off-by: Helena McGough <helena.mcgough@intel.com>
plugins/OpenStack/Aodh/__init__.py [new file with mode: 0644]
plugins/OpenStack/Aodh/alarming.py [new file with mode: 0644]
plugins/OpenStack/Aodh/aodh_common.py [new file with mode: 0644]
plugins/OpenStack/Aodh/plugin_instance.py [new file with mode: 0644]
plugins/OpenStack/__init__.py [new file with mode: 0644]
plugins/OpenStack/settings.py [new file with mode: 0644]
plugins/OpenStack/singleton.py [new file with mode: 0644]

diff --git a/plugins/OpenStack/Aodh/__init__.py b/plugins/OpenStack/Aodh/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/OpenStack/Aodh/alarming.py b/plugins/OpenStack/Aodh/alarming.py
new file mode 100644 (file)
index 0000000..3ae31ba
--- /dev/null
@@ -0,0 +1,62 @@
+"""Send alarm info from Aodh to SO via MON"""
+
+import json
+from plugins.Openstack.Aodh.aodh_common import Aodh_Common
+
+
+class Alarming(object):
+    """Receives alarm info from Aodh."""
+
+    def __init__(self):
+        """Create the aodh_receiver instance."""
+        self._aodh_common = Aodh_Common()
+
+    def alarming(self):
+        """Receive payload from Aodh."""
+        auth_token = self._aodh_common._authenticate()
+        endpoint = self._aodh_common.get_endpoint()
+
+        alarm_list = self._get_alarm_list(endpoint, auth_token)
+        # Confirm communication with Aodh by listing alarms
+        print("Alarm List ", alarm_list.text)
+
+        alarm_id = self._create_alarm(endpoint, auth_token)
+        print(alarm_id)
+
+#        alarm_info = self._get_alarm_info(endpoint,
+#            auth_token, "372af0e2-5c36-4e4d-8ce9-ca92d97d07d0")
+#        print("Alarm info", alarm_info.text)
+        return
+
+    def _get_alarm_list(self, endpoint, auth_token):
+        """Get a list of alarms that exist in Aodh."""
+        url = "{}/v2/alarms/".format(endpoint)
+
+        alarm_list = self._aodh_common._perform_request(url, auth_token,
+                                                        req_type="get")
+        return alarm_list
+
+    def _get_alarm_info(self, endpoint, auth_token, alarmID):
+        """Get information about a specific alarm from Aodh."""
+        url = "{}/v2/alarms/%s".format(endpoint) % (alarmID)
+
+        alarm_details = self._aodh_common._perform_request(url, auth_token,
+                                                           req_type="get")
+        return alarm_details
+
+    def _create_alarm(self, endpoint, auth_token):
+        """Get a list of alarms that exist in Aodh."""
+        url = "{}/v2/alarms/".format(endpoint)
+
+        rule = {'event_type': "threshold",}
+        payload = json.dumps({'state': 'alarm',
+                              'name': 'my_alarm',
+                              'severity': 'moderate',
+                              'type': 'event',
+                              'event_rule': rule,})
+
+        new_alarm = self._aodh_common._perform_request(url, auth_token,
+                                                        req_type="post", payload=payload)
+        alarm_id =json.loads(new_alarm.text)['alarm_id']
+        return alarm_id
+
diff --git a/plugins/OpenStack/Aodh/aodh_common.py b/plugins/OpenStack/Aodh/aodh_common.py
new file mode 100644 (file)
index 0000000..28e44fc
--- /dev/null
@@ -0,0 +1,82 @@
+"""Common methods for the Aodh Sender/Receiver."""
+
+import threading
+import os
+import requests
+
+from keystoneauth1.identity import v3
+from keystoneauth1.identity.v3 import AuthMethod
+from keystoneauth1 import session
+from keystoneclient.v3 import client
+from keystoneclient.service_catalog import ServiceCatalog
+
+from plugins.Openstack.settings import Config
+
+
+class Aodh_Common(object):
+    """Common calls for Aodh Sender/Receiver."""
+
+    def __init__(self):
+        """Create the common instance."""
+        self._auth_token = None
+        self._endpoint = None
+        self._ks = None
+
+    def _authenticate(self):
+        """Authenticate and/or renew the authentication token"""
+
+        if self._auth_token is not None:
+            return self._auth_token
+
+        try:
+            cfg = Config.instance()
+            self._ks = client.Client(auth_url=cfg.OS_AUTH_URL,
+                                     username=cfg.OS_USERNAME,
+                                     password=cfg.OS_PASSWORD,
+                                     tenant_name=cfg.OS_TENANT_NAME)
+            self._auth_token = self._ks.auth_token
+        except Exception as exc:
+
+            # TODO: Log errors
+            self._auth_token = None
+
+        return self._auth_token
+
+    def get_endpoint(self):
+        endpoint = self._ks.service_catalog.url_for(
+            service_type='alarming',
+            endpoint_type='internalURL',
+            region_name='RegionOne')
+        return endpoint
+
+    @classmethod
+    def _perform_request(cls, url, auth_token, req_type="get", payload=None):
+        """Perform the POST/PUT/GET/DELETE request."""
+
+        # request headers
+        headers = {'X-Auth-Token': auth_token,
+                   'Content-type': 'application/json'}
+        # perform request and return its result
+        try:
+            if req_type == "put":
+                response = requests.put(
+                    url, data=payload, headers=headers,
+                    timeout=1)
+            elif req_type == "post":
+                response = requests.post(
+                    url, data=payload, headers=headers,
+                    timeout=1)
+            elif req_type == "get":
+                response = requests.get(
+                    url, headers=headers, timeout=1)
+            elif req_type == "delete":
+                response = requests.delete(
+                    url, headers=headers, timeout=1)
+            else:
+                print("Invalid request type")
+
+        except Exception as e:
+            # Log info later
+            print("Exception thrown on request", e)
+
+        return response
diff --git a/plugins/OpenStack/Aodh/plugin_instance.py b/plugins/OpenStack/Aodh/plugin_instance.py
new file mode 100644 (file)
index 0000000..8096f3f
--- /dev/null
@@ -0,0 +1,38 @@
+"""Aodh plugin for the OSM monitoring module."""
+
+import sys
+import logging
+
+path = "/home/stack/MON"
+if path not in sys.path:
+    sys.path.append(path)
+
+from plugins.Openstack.Aodh.alarming import Alarming
+from plugins.Openstack.settings import Config
+
+
+def register_plugin():
+    """Register the plugin."""
+    config = Config.instance()
+    instance = Plugin(config=config)
+    instance.config()
+    instance.alarm()
+
+
+class Plugin(object):
+    """Aodh plugin for OSM MON."""
+
+    def __init__(self, config):
+        """Plugin instance."""
+        self._config = config
+        self._alarm = Alarming()
+
+    def config(self):
+        """Configure plugin."""
+        self._config.read_environ()
+
+    def alarm(self):
+        """Allow alarm info to be received from Aodh."""
+        self._alarm.alarming()
+
+register_plugin()
diff --git a/plugins/OpenStack/__init__.py b/plugins/OpenStack/__init__.py
new file mode 100644 (file)
index 0000000..805dce3
Binary files /dev/null and b/plugins/OpenStack/__init__.py differ
diff --git a/plugins/OpenStack/settings.py b/plugins/OpenStack/settings.py
new file mode 100644 (file)
index 0000000..4dacef9
--- /dev/null
@@ -0,0 +1,63 @@
+"""Configurations for the Aodh plugin."""
+
+from __future__ import unicode_literals
+
+from plugins.Openstack.singleton import Singleton
+
+from collections import namedtuple
+import six
+import os
+
+
+class BadConfigError(Exception):
+    """Configuration exception"""
+    pass
+
+
+class CfgParam(namedtuple('CfgParam', ['key', 'default', 'data_type'])):
+    """Configuration parameter definition"""
+
+    def value(self, data):
+        """Convert a string to the parameter type"""
+
+        try:
+            return self.data_type(data)
+        except (ValueError, TypeError) as exc:
+            raise BadConfigError(
+                'Invalid value "%s" for configuration parameter "%s"' % (
+                    data, self.key))
+
+
+@Singleton
+class Config(object):
+    """Plugin confguration"""
+
+    _configuration = [
+        CfgParam('OS_AUTH_URL', None, six.text_type),
+        CfgParam('OS_IDENTITY_API_VERSION', "3", six.text_type),
+        CfgParam('OS_USERNAME', "aodh", six.text_type),
+        CfgParam('OS_PASSWORD', "password", six.text_type),
+        CfgParam('OS_TENANT_NAME', "service", six.text_type),
+    ]
+
+    _config_dict = {cfg.key: cfg for cfg in _configuration}
+    _config_keys = _config_dict.keys()
+
+    def __init__(self):
+        """Set the default values"""
+        for cfg in self._configuration:
+            setattr(self, cfg.key, cfg.default)
+
+    def read_environ(self):
+        """Check the appropriate environment variables and update defaults."""
+
+        for key in self._config_keys:
+            if (key == "OS_IDENTITY_API_VERSION" or key == "OS_PASSWORD"):
+                val = str(os.environ[key])
+                setattr(self, key, val)
+            elif (key == "OS_AUTH_URL"):
+                val = str(os.environ[key]) + "/v3"
+                setattr(self, key, val)
+            else:
+                # TODO: Log errors and no config updates required
+                return
diff --git a/plugins/OpenStack/singleton.py b/plugins/OpenStack/singleton.py
new file mode 100644 (file)
index 0000000..2edc20b
--- /dev/null
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+
+
+class Singleton(object):
+    """Simple singleton class"""
+
+    def __init__(self, decorated):
+        self._decorated = decorated
+
+    def instance(self):
+        """Return singleton instance"""
+        try:
+            return self._instance
+        except AttributeError:
+            self._instance = self._decorated()
+            return self._instance