diff --git a/cloudkitty/collector/monasca.py b/cloudkitty/collector/monasca.py index 2ba2070b..d14906be 100644 --- a/cloudkitty/collector/monasca.py +++ b/cloudkitty/collector/monasca.py @@ -67,6 +67,9 @@ MONASCA_EXTRA_SCHEMA = { All(str, Length(min=1)), Required('aggregation_method', default='max'): In(['max', 'mean', 'min']), + # In case the metrics in Monasca do not belong to the project + # cloudkitty is identified in + Required('forced_project_id', default=''): str, }, } @@ -177,6 +180,10 @@ class MonascaCollector(collector.BaseCollector): period = end - start extra_args = self.conf[metric_name]['extra_args'] + kwargs = {} + if extra_args['forced_project_id']: + kwargs['tenant_id'] = extra_args['forced_project_id'] + return self._conn.metrics.list_statistics( name=metric_name, merge_metrics=True, @@ -185,7 +192,8 @@ class MonascaCollector(collector.BaseCollector): end_time=ck_utils.ts2dt(end), period=period, statistics=extra_args['aggregation_method'], - group_by=group_by) + group_by=group_by, + **kwargs) def _fetch_metrics(self, metric_name, start, end, project_id=None, q_filter=None): diff --git a/cloudkitty/tests/__init__.py b/cloudkitty/tests/__init__.py index d86cbca5..6a38fa59 100644 --- a/cloudkitty/tests/__init__.py +++ b/cloudkitty/tests/__init__.py @@ -17,6 +17,7 @@ # import decimal +from keystoneauth1 import session as ks_sess import mock from oslo_config import fixture as config_fixture from oslotest import base @@ -82,7 +83,7 @@ class TestCase(testscenarios.TestWithScenarios, base.BaseTestCase): self.auth = auth session = mock.patch( 'keystoneauth1.loading.load_session_from_conf_options', - return_value=dict()) + return_value=ks_sess.Session()) session.start() self.session = session diff --git a/cloudkitty/tests/collectors/test_monasca.py b/cloudkitty/tests/collectors/test_monasca.py new file mode 100644 index 00000000..05fdb599 --- /dev/null +++ b/cloudkitty/tests/collectors/test_monasca.py @@ -0,0 +1,100 @@ +# Copyright 2019 Objectif Libre +# +# 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 datetime + +import mock + +from cloudkitty.collector import monasca as mon_collector +from cloudkitty import tests +from cloudkitty import transformer +from cloudkitty import utils + + +class MonascaCollectorTest(tests.TestCase): + + def setUp(self): + super(MonascaCollectorTest, self).setUp() + self.conf.set_override('collector', 'monasca', 'collect') + conf = { + 'metrics': { + 'metric_one': { + 'unit': 'GiB', + 'groupby': ['project_id'], + 'extra_args': { + 'aggregation_method': 'max', + }, + }, + 'metric_two': { + 'unit': 'MiB', + 'groupby': ['project_id'], + 'extra_args': { + 'aggregation_method': 'max', + 'forced_project_id': 'project_x' + }, + }, + } + } + with mock.patch( + 'cloudkitty.collector.monasca.' + 'MonascaCollector._get_monasca_endpoint', + return_value='http://noop'): + self.collector = mon_collector.MonascaCollector( + transformer.get_transformers(), + period=3600, + conf=conf, + ) + + def test_fetch_measures_kwargs_no_forced_project(self): + with mock.patch.object(self.collector._conn.metrics, + 'list_statistics') as m: + start = datetime.datetime(2019, 1, 1) + end = datetime.datetime(2019, 1, 1, 1) + self.collector._fetch_measures( + 'metric_one', + utils.dt2ts(start), + utils.dt2ts(end), + ) + m.assert_called_once_with( + name='metric_one', + merge_metrics=True, + dimensions={}, + start_time=start, + end_time=end, + period=3600, + statistics='max', + group_by=['project_id', 'resource_id'], + ) + + def test_fetch_measures_kwargs_with_forced_project(self): + with mock.patch.object(self.collector._conn.metrics, + 'list_statistics') as m: + start = datetime.datetime(2019, 1, 1) + end = datetime.datetime(2019, 1, 1, 1) + self.collector._fetch_measures( + 'metric_two', + utils.dt2ts(start), + utils.dt2ts(end), + ) + m.assert_called_once_with( + name='metric_two', + merge_metrics=True, + dimensions={}, + start_time=start, + end_time=end, + period=3600, + statistics='max', + group_by=['project_id', 'resource_id'], + tenant_id='project_x', + ) diff --git a/cloudkitty/tests/collectors/test_validation.py b/cloudkitty/tests/collectors/test_validation.py index 031eef36..725baa1b 100644 --- a/cloudkitty/tests/collectors/test_validation.py +++ b/cloudkitty/tests/collectors/test_validation.py @@ -97,6 +97,7 @@ class MetricConfigValidationTest(tests.TestCase): expected_output['metric_one']['extra_args'] = { 'aggregation_method': 'max', 'resource_key': 'resource_id', + 'forced_project_id': '', } self.assertEqual( diff --git a/doc/source/admin/configuration/collector.rst b/doc/source/admin/configuration/collector.rst index 7f0c6587..5fa6a1c1 100644 --- a/doc/source/admin/configuration/collector.rst +++ b/doc/source/admin/configuration/collector.rst @@ -64,6 +64,13 @@ Section: ``collector_monasca``. * ``monasca_service_name``: Defaults to ``monasca``. Name of the Monasca service in Keystone. +.. note:: By default, cloudkitty retrieves all metrics from Monasca in the + project it is identified in. However, some metrics may need to be + fetched from another tenant (for example if ceilometer is publishing + metrics to monasca in the ``service`` tenant but monasca-agent is + publishing metrics to the ``admin`` tenant). See the monasca-specific + section in "Metric collection" below for details on how to configure + this. Prometheus ---------- @@ -259,6 +266,12 @@ Monasca when retrieving measures from monasca. Must be one of ``min``, ``max``, ``mean``. +* ``forced_project_id``: Defaults to None. Force the given metric to be + fetched from a specific tenant instead of the one cloudkitty is identified + in. For example, if cloudkitty is identified in the ``service`` project, but + needs to fetch a metric from the ``admin`` project, its ID should be + specified through this option. + Prometheus ~~~~~~~~~~ diff --git a/releasenotes/notes/force-project-id-monasca-collector-cb30ed073d36d40e.yaml b/releasenotes/notes/force-project-id-monasca-collector-cb30ed073d36d40e.yaml new file mode 100644 index 00000000..fb7a94af --- /dev/null +++ b/releasenotes/notes/force-project-id-monasca-collector-cb30ed073d36d40e.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + It is now possible to force a project_id to retrieve a specific metric + from it with the monasca collector.