Updated Openstack plugin logging and tests
[osm/MON.git] / plugins / OpenStack / Gnocchi / metrics.py
index ca6f47a..a1d58fd 100644 (file)
@@ -23,7 +23,9 @@
 
 import datetime
 import json
-import logging as log
+import logging
+log = logging.getLogger(__name__)
+
 import time
 
 from core.message_bus.producer import KafkaProducer
@@ -36,15 +38,15 @@ from plugins.OpenStack.response import OpenStack_Response
 __author__ = "Helena McGough"
 
 METRIC_MAPPINGS = {
-    "AVERAGE_MEMORY_UTILIZATION": "memory.percent",
-    "DISK_READ_OPS": "disk.disk_ops",
-    "DISK_WRITE_OPS": "disk.disk_ops",
-    "DISK_READ_BYTES": "disk.disk_octets",
-    "DISK_WRITE_BYTES": "disk.disk_octets",
-    "PACKETS_DROPPED": "interface.if_dropped",
-    "PACKETS_RECEIVED": "interface.if_packets",
-    "PACKETS_SENT": "interface.if_packets",
-    "CPU_UTILIZATION": "cpu.percent",
+    "average_memory_utilization": "memory.percent",
+    "disk_read_ops": "disk.disk_ops",
+    "disk_write_ops": "disk.disk_ops",
+    "disk_read_bytes": "disk.disk_octets",
+    "disk_write_bytes": "disk.disk_octets",
+    "packets_dropped": "interface.if_dropped",
+    "packets_received": "interface.if_packets",
+    "packets_sent": "interface.if_packets",
+    "cpu_utilization": "cpu.percent",
 }
 
 PERIOD_MS = {
@@ -86,7 +88,7 @@ class Metrics(object):
 
             if vim_type == "openstack":
                 # Generate auth_token and endpoint
-                auth_token, endpoint = self.authenticate(values)
+                auth_token, endpoint = self.authenticate()
 
                 if message.key == "create_metric_request":
                     # Configure metric
@@ -100,6 +102,7 @@ class Metrics(object):
                             'create_metric_response', status=status,
                             cor_id=values['correlation_id'],
                             metric_id=metric_id, r_id=resource_id)
+                        log.info("Response messages: %s", resp_message)
                         self._producer.create_metrics_resp(
                             'create_metric_response', resp_message,
                             'metric_response')
@@ -120,6 +123,7 @@ class Metrics(object):
                             r_id=values['resource_uuid'],
                             cor_id=values['correlation_id'],
                             times=timestamps, metrics=metric_data)
+                        log.info("Response message: %s", resp_message)
                         self._producer.read_metric_data_response(
                             'read_metric_data_response', resp_message,
                             'metric_response')
@@ -139,6 +143,7 @@ class Metrics(object):
                             m_name=values['metric_name'],
                             status=status, r_id=values['resource_uuid'],
                             cor_id=values['correlation_id'])
+                        log.info("Response message: %s", resp_message)
                         self._producer.delete_metric_response(
                             'delete_metric_response', resp_message,
                             'metric_response')
@@ -162,6 +167,7 @@ class Metrics(object):
                             'update_metric_response', status=False,
                             cor_id=values['correlation_id'],
                             r_id=resource_id, m_id=metric_id)
+                        log.info("Response message: %s", resp_message)
                         self._producer.update_metric_response(
                             'update_metric_response', resp_message,
                             'metric_response')
@@ -179,6 +185,7 @@ class Metrics(object):
                         resp_message = self._response.generate_response(
                             'list_metric_response', m_list=metric_list,
                             cor_id=list_details['correlation_id'])
+                        log.info("Response message: %s", resp_message)
                         self._producer.list_metric_response(
                             'list_metric_response', resp_message,
                             'metric_response')
@@ -211,41 +218,55 @@ class Metrics(object):
             endpoint, auth_token, metric_name, resource_id)
 
         if metric_id is None:
-            # Need to create a new version of the resource for gnocchi to
-            # create the new metric based on that resource
-            url = "{}/v1/resource/generic".format(endpoint)
+            # Try appending metric to existing resource
             try:
-                # Try to create a new resource for the new metric
-                metric = {'name': metric_name,
-                          'archive_policy_name': 'high',
-                          'unit': values['metric_unit'], }
-
-                resource_payload = json.dumps({'id': resource_id,
-                                               'metrics': {
-                                                   metric_name: metric}})
-
-                new_resource = self._common._perform_request(
-                    url, auth_token, req_type="post", payload=resource_payload)
-
-                resource_id = json.loads(new_resource.text)['id']
-            except Exception as exc:
-                # Append new metric to existing resource
-                log.debug("This resource already exists:%s, appending metric.",
-                          exc)
                 base_url = "{}/v1/resource/generic/%s/metric"
                 res_url = base_url.format(endpoint) % resource_id
                 payload = {metric_name: {'archive_policy_name': 'high',
                                          'unit': values['metric_unit']}}
-                self._common._perform_request(
+                result = self._common._perform_request(
                     res_url, auth_token, req_type="post",
                     payload=json.dumps(payload))
+                # Get id of newly created metric
+                for row in json.loads(result.text):
+                    if row['name'] == metric_name:
+                        metric_id = row['id']
+                log.info("Appended metric to existing resource.")
 
-            metric_id = self.get_metric_id(
-                endpoint, auth_token, metric_name, resource_id)
-            return metric_id, resource_id, True
+                return metric_id, resource_id, True
+            except Exception as exc:
+                # Gnocchi version of resource does not exist creating a new one
+                log.info("Failed to append metric to existing resource:%s",
+                         exc)
+                try:
+                    url = "{}/v1/resource/generic".format(endpoint)
+                    metric = {'name': metric_name,
+                              'archive_policy_name': 'high',
+                              'unit': values['metric_unit'], }
+
+                    resource_payload = json.dumps({'id': resource_id,
+                                                   'metrics': {
+                                                       metric_name: metric}})
+
+                    resource = self._common._perform_request(
+                        url, auth_token, req_type="post",
+                        payload=resource_payload)
+
+                    # Return the newly created resource_id for creating alarms
+                    new_resource_id = json.loads(resource.text)['id']
+                    log.info("Created new resource for metric: %s",
+                             new_resource_id)
+
+                    metric_id = self.get_metric_id(
+                        endpoint, auth_token, metric_name, new_resource_id)
+
+                    return metric_id, new_resource_id, True
+                except Exception as exc:
+                    log.warn("Failed to create a new resource:%s", exc)
+            return None, None, False
 
         else:
-            log.debug("This metric already exists for this resource.")
+            log.info("This metric already exists for this resource.")
 
         return metric_id, resource_id, False
 
@@ -269,43 +290,53 @@ class Metrics(object):
         """List all metrics."""
         url = "{}/v1/metric/".format(endpoint)
 
+        # Check for a specified list
         try:
             # Check if the metric_name was specified for the list
-            metric_name = values['metric_name']
-            result = self._common._perform_request(
-                url, auth_token, req_type="get")
-            metric_list = json.loads(result.text)
+            metric_name = values['metric_name'].lower()
+            if metric_name not in METRIC_MAPPINGS.keys():
+                log.warn("This metric is not supported, won't be listed.")
+                metric_name = None
+        except KeyError as exc:
+            log.info("Metric name is not specified: %s", exc)
+            metric_name = None
 
-            # Format the list response
-            metrics = self.response_list(
-                metric_list, metric_name=metric_name)
-            return metrics
-        except KeyError:
-            log.debug("Metric name is not specified for this list.")
+        try:
+            resource = values['resource_uuid']
+        except KeyError as exc:
+            log.info("Resource is not specified:%s", exc)
+            resource = None
 
         try:
-            # Check if a resource_id was specified
-            resource_id = values['resource_uuid']
             result = self._common._perform_request(
                 url, auth_token, req_type="get")
-            metric_list = json.loads(result.text)
-            # Format the list response
-            metrics = self.response_list(
-                metric_list, resource=resource_id)
-            return metrics
-        except KeyError:
-            log.debug("Resource id not specificed either, will return a\
-                       complete list.")
-            try:
-                result = self._common._perform_request(
-                    url, auth_token, req_type="get")
-                metric_list = json.loads(result.text)
+            metrics = json.loads(result.text)
+
+            if metrics is not None:
                 # Format the list response
-                metrics = self.response_list(metric_list)
-                return metrics
+                if metric_name is not None and resource is not None:
+                    metric_list = self.response_list(
+                        metrics, metric_name=metric_name, resource=resource)
+                    log.info("Returning an %s resource list for %s metrics",
+                             metric_name, resource)
+                elif metric_name is not None:
+                    metric_list = self.response_list(
+                        metrics, metric_name=metric_name)
+                    log.info("Returning a list of %s metrics", metric_name)
+                elif resource is not None:
+                    metric_list = self.response_list(
+                        metrics, resource=resource)
+                    log.info("Return a list of %s resource metrics", resource)
+                else:
+                    metric_list = self.response_list(metrics)
+                    log.info("Returning a complete list of metrics")
 
-            except Exception as exc:
-                log.warn("Failed to generate any metric list. %s", exc)
+                return metric_list
+            else:
+                log.info("There are no metrics available")
+                return []
+        except Exception as exc:
+            log.warn("Failed to generate any metric list. %s", exc)
         return None
 
     def get_metric_id(self, endpoint, auth_token, metric_name, resource_id):
@@ -318,17 +349,17 @@ class Metrics(object):
                 url, auth_token, req_type="get")
             return json.loads(result.text)['metrics'][metric_name]
         except Exception:
-            log.debug("Metric doesn't exist. No metric_id available")
+            log.info("Metric doesn't exist. No metric_id available")
         return None
 
     def get_metric_name(self, values):
         """Check metric name configuration and normalize."""
         try:
             # Normalize metric name
-            metric_name = values['metric_name']
+            metric_name = values['metric_name'].lower()
             return metric_name, METRIC_MAPPINGS[metric_name]
         except KeyError:
-            log.warn("Metric name %s is invalid.", metric_name)
+            log.info("Metric name %s is invalid.", metric_name)
         return metric_name, None
 
     def read_metric_data(self, endpoint, auth_token, values):
@@ -348,7 +379,7 @@ class Metrics(object):
                 diff = PERIOD_MS[collection_unit]
             else:
                 diff = collection_period * PERIOD_MS[collection_unit]
-            s_time = (end_time - diff)/1000.0
+            s_time = (end_time - diff) / 1000.0
             start_time = datetime.datetime.fromtimestamp(s_time).strftime(
                 '%Y-%m-%dT%H:%M:%S.%f')
             base_url = "{}/v1/metric/%(0)s/measures?start=%(1)s&stop=%(2)s"
@@ -370,44 +401,54 @@ class Metrics(object):
             log.warn("Failed to gather specified measures: %s", exc)
         return timestamps, data
 
-    def authenticate(self, values):
+    def authenticate(self):
         """Generate an authentication token and endpoint for metric request."""
         try:
             # Check for a tenant_id
-            auth_token = self._common._authenticate(
-                tenant_id=values['tenant_uuid'])
-            endpoint = self._common.get_endpoint("metric")
-        except KeyError:
-            log.warn("Tenant ID is not specified. Will use a generic\
-                      authentication.")
             auth_token = self._common._authenticate()
             endpoint = self._common.get_endpoint("metric")
+            return auth_token, endpoint
+        except Exception as exc:
+            log.warn("Authentication to Keystone failed: %s", exc)
 
-        return auth_token, endpoint
+        return None, None
 
     def response_list(self, metric_list, metric_name=None, resource=None):
         """Create the appropriate lists for a list response."""
-        resp_list = []
+        resp_list, name_list, res_list = [], [], []
 
+        # Create required lists
         for row in metric_list:
+            # Only list OSM metrics
+            if row['name'] in METRIC_MAPPINGS.keys():
+                metric = {"metric_name": row['name'],
+                          "metric_uuid": row['id'],
+                          "metric_unit": row['unit'],
+                          "resource_uuid": row['resource_id']}
+                resp_list.append(str(metric))
+            # Generate metric_name specific list
             if metric_name is not None:
                 if row['name'] == metric_name:
                     metric = {"metric_name": row['name'],
                               "metric_uuid": row['id'],
                               "metric_unit": row['unit'],
                               "resource_uuid": row['resource_id']}
-                    resp_list.append(metric)
-            elif resource is not None:
+                    name_list.append(str(metric))
+            # Generate resource specific list
+            if resource is not None:
                 if row['resource_id'] == resource:
                     metric = {"metric_name": row['name'],
                               "metric_uuid": row['id'],
                               "metric_unit": row['unit'],
                               "resource_uuid": row['resource_id']}
-                    resp_list.append(metric)
-            else:
-                metric = {"metric_name": row['name'],
-                          "metric_uuid": row['id'],
-                          "metric_unit": row['unit'],
-                          "resource_uuid": row['resource_id']}
-                resp_list.append(metric)
-        return resp_list
+                    res_list.append(str(metric))
+
+        # Join required lists
+        if metric_name is not None and resource is not None:
+            return list(set(res_list).intersection(name_list))
+        elif metric_name is not None:
+            return name_list
+        elif resource is not None:
+            return list(set(res_list).intersection(resp_list))
+        else:
+            return resp_list