Files
watcher/watcher/tests/datasources/test_manager.py
Dantali0n 0541d8c25b Grafana proxy datasource to retrieve metrics
New datasource to retrieve metrics that can be configured in a
flexible way depending on the deployment. Current implemenation only
works with InfluxDB. Slight changes to datasource manager were
necessary because grafana metric_map can only be built at runtime.

The yaml configuration file can still be used to define metrics
but will require that five different attributes are specified per
metric.

Specific databases accesible through grafana can be defined by
creating 'translators' for these specific databases. This patch
introduces a base class for these translators and their methods.

In addition the first translator specific for InfluxDB is
created.

Depends-on: I68475883529610e514aa82f1881105ab0cf24ec3
Depends-on: If1f27dc01e853c5b24bdb21f1e810f64eaee2e5c
Implements: blueprint grafana-proxy-datasource
Change-Id: Ib12b6a7882703e84a27c301e821c1a034b192508
2019-07-09 16:22:00 +02:00

157 lines
6.5 KiB
Python

# -*- encoding: utf-8 -*-
# Copyright (c) 2017 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import mock
from mock import MagicMock
from watcher.common import exception
from watcher.datasources import gnocchi
from watcher.datasources import grafana
from watcher.datasources import manager as ds_manager
from watcher.datasources import monasca
from watcher.tests import base
class TestDataSourceManager(base.BaseTestCase):
def _dsm_config(self, **kwargs):
dss = ['gnocchi', 'ceilometer', 'monasca']
opts = dict(datasources=dss, metric_map_path=None)
opts.update(kwargs)
return MagicMock(**opts)
def _dsm(self, **kwargs):
opts = dict(config=self._dsm_config(), osc=mock.MagicMock())
opts.update(kwargs)
return ds_manager.DataSourceManager(**opts)
def test_metric_file_path_not_exists(self):
manager = self._dsm()
expected = ds_manager.DataSourceManager.metric_map
actual = manager.metric_map
self.assertEqual(expected, actual)
self.assertEqual({}, manager.load_metric_map('/nope/nope/nope.yaml'))
def test_metric_file_metric_override(self):
path = 'watcher.datasources.manager.DataSourceManager.load_metric_map'
retval = {
monasca.MonascaHelper.NAME: {"host_airflow": "host_fnspid"}
}
with mock.patch(path, return_value=retval):
dsmcfg = self._dsm_config(datasources=['monasca'])
manager = self._dsm(config=dsmcfg)
backend = manager.get_backend(['host_airflow'])
self.assertEqual("host_fnspid", backend.METRIC_MAP['host_airflow'])
@mock.patch.object(grafana, 'CONF')
def test_metric_file_metric_override_grafana(self, m_config):
"""Grafana requires a different structure in the metric map"""
m_config.grafana_client.token = \
"eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk=="
m_config.grafana_client.base_url = "https://grafana.proxy/api/"
path = 'watcher.datasources.manager.DataSourceManager.load_metric_map'
metric_map = {
'db': 'production_cloud',
'project': '7485',
'attribute': 'hostname',
'translator': 'influxdb',
'query': 'SHOW SERIES'
}
retval = {
grafana.GrafanaHelper.NAME: {"host_airflow": metric_map}
}
with mock.patch(path, return_value=retval):
dsmcfg = self._dsm_config(datasources=['grafana'])
manager = self._dsm(config=dsmcfg)
backend = manager.get_backend(['host_airflow'])
self.assertEqual(metric_map, backend.METRIC_MAP['host_airflow'])
def test_metric_file_invalid_ds(self):
with mock.patch('yaml.safe_load') as mo:
mo.return_value = {"newds": {"metric_one": "i_am_metric_one"}}
mgr = self._dsm()
self.assertNotIn('newds', mgr.metric_map.keys())
def test_get_backend(self):
manager = self._dsm()
backend = manager.get_backend(['host_cpu_usage', 'instance_cpu_usage'])
self.assertEqual(backend, manager.gnocchi)
def test_get_backend_order(self):
dss = ['monasca', 'ceilometer', 'gnocchi']
dsmcfg = self._dsm_config(datasources=dss)
manager = self._dsm(config=dsmcfg)
backend = manager.get_backend(['host_cpu_usage', 'instance_cpu_usage'])
self.assertEqual(backend, manager.monasca)
def test_get_backend_wrong_metric(self):
manager = self._dsm()
self.assertRaises(exception.MetricNotAvailable, manager.get_backend,
['host_cpu', 'instance_cpu_usage'])
@mock.patch.object(gnocchi, 'GnocchiHelper')
def test_get_backend_error_datasource(self, m_gnocchi):
m_gnocchi.side_effect = exception.DataSourceNotAvailable
manager = self._dsm()
backend = manager.get_backend(['host_cpu_usage', 'instance_cpu_usage'])
self.assertEqual(backend, manager.ceilometer)
@mock.patch.object(grafana.GrafanaHelper, 'METRIC_MAP',
{'host_cpu_usage': 'test'})
def test_get_backend_grafana(self):
dss = ['grafana', 'ceilometer', 'gnocchi']
dsmcfg = self._dsm_config(datasources=dss)
manager = self._dsm(config=dsmcfg)
backend = manager.get_backend(['host_cpu_usage'])
self.assertEqual(backend, manager.grafana)
@mock.patch.object(grafana, 'CONF')
def test_dynamic_metric_map_grafana(self, m_config):
m_config.grafana_client.token = \
"eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk=="
m_config.grafana_client.base_url = "https://grafana.proxy/api/"
m_config.grafana_client.project_id_map = {'host_cpu_usage': 7221}
m_config.grafana_client.attribute_map = {'host_cpu_usage': 'hostname'}
m_config.grafana_client.database_map = {'host_cpu_usage': 'mock_db'}
m_config.grafana_client.translator_map = {'host_cpu_usage': 'influxdb'}
m_config.grafana_client.query_map = {
'host_cpu_usage': 'SHOW SERIES'
}
dss = ['grafana', 'ceilometer', 'gnocchi']
dsmcfg = self._dsm_config(datasources=dss)
manager = self._dsm(config=dsmcfg)
backend = manager.get_backend(['host_cpu_usage'])
self.assertEqual(backend, manager.grafana)
def test_get_backend_no_datasources(self):
dsmcfg = self._dsm_config(datasources=[])
manager = self._dsm(config=dsmcfg)
self.assertRaises(exception.NoDatasourceAvailable, manager.get_backend,
['host_cpu_usage', 'instance_cpu_usage'])
dsmcfg = self._dsm_config(datasources=None)
manager = self._dsm(config=dsmcfg)
self.assertRaises(exception.NoDatasourceAvailable, manager.get_backend,
['host_cpu_usage', 'instance_cpu_usage'])
def test_get_backend_no_metrics(self):
manager = self._dsm()
self.assertRaises(exception.InvalidParameter, manager.get_backend, [])
self.assertRaises(exception.InvalidParameter, manager.get_backend,
None)