From c0ea9ffa27ca2362c2a060bb16f7133555e57e41 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Fri, 17 Jul 2015 10:29:14 +0530 Subject: [PATCH] Monasca client plugin Adds monasca client plugin 'monasca' and notification constraint 'monasca.notification' implements blueprint support-monasca-alarm-notification Co-authored-by: Sergey Kraynev Change-Id: I4533ee8ff67c6cc608c366bc66f30c20df2a56a5 --- heat/engine/clients/os/monasca.py | 71 +++++++++++ heat/tests/clients/test_monasca_client.py | 146 ++++++++++++++++++++++ setup.cfg | 2 + 3 files changed, 219 insertions(+) create mode 100644 heat/engine/clients/os/monasca.py create mode 100644 heat/tests/clients/test_monasca_client.py diff --git a/heat/engine/clients/os/monasca.py b/heat/engine/clients/os/monasca.py new file mode 100644 index 0000000000..376ac3bf28 --- /dev/null +++ b/heat/engine/clients/os/monasca.py @@ -0,0 +1,71 @@ +# +# 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. + +from oslo_utils import importutils + +from heat.common import exception as heat_exc +from heat.engine.clients import client_plugin +from heat.engine import constraints + +client = importutils.try_import('monascaclient.client') +monasca_exc = importutils.try_import('monascaclient.exc') + +SERVICE_NAME = 'monasca' + + +class MonascaClientPlugin(client_plugin.ClientPlugin): + exceptions_module = [monasca_exc] + service_types = [MONITORING] = ['monitoring'] + + VERSION = '2_0' + + @staticmethod + def is_available(): + return client is not None + + def _create(self): + args = self._get_client_args(service_name=SERVICE_NAME, + service_type=self.MONITORING) + + return client.Client( + self.VERSION, + endpoint=args['os_endpoint'], + endpoint_type=args['endpoint_type'], + auth_url=args['auth_url'], + token=args['token'](), + project_id=args['project_id'], + service_type=args['service_type'], + os_cacert=args['cacert'], + cert_file=args['cert_file'], + key_file=args['key_file'], + insecure=args['insecure'] + ) + + def is_not_found(self, ex): + return isinstance(ex, monasca_exc.NotFound) + + def get_notification(self, notification): + try: + return self.client().notifications.get( + notification_id=notification)['id'] + except monasca_exc.NotFound: + raise heat_exc.EntityNotFound(entity='Monasca Notification', + name=notification) + + +class MonascaNotificationConstraint(constraints.BaseCustomConstraint): + + expected_exceptions = (heat_exc.EntityNotFound) + + def validate_with_client(self, client, notification): + client.client_plugin(SERVICE_NAME).get_notification(notification) diff --git a/heat/tests/clients/test_monasca_client.py b/heat/tests/clients/test_monasca_client.py new file mode 100644 index 0000000000..1ee725b680 --- /dev/null +++ b/heat/tests/clients/test_monasca_client.py @@ -0,0 +1,146 @@ +# +# 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 +import six + +from heat.common import exception as heat_exception +from heat.engine.clients.os import monasca as client_plugin +from heat.tests import common + + +class MonascaNotificationConstraintTest(common.HeatTestCase): + def test_expected_exceptions(self): + self.assertEqual( + (heat_exception.EntityNotFound), + client_plugin.MonascaNotificationConstraint.expected_exceptions, + "MonascaNotificationConstraint expected exceptions error") + + def test_constraint(self): + constraint = client_plugin.MonascaNotificationConstraint() + client_mock = mock.MagicMock() + client_plugin_mock = mock.MagicMock() + client_plugin_mock.get_notification.return_value = None + client_mock.client_plugin.return_value = client_plugin_mock + + self.assertIsNone(constraint.validate_with_client(client_mock, + 'notification_1')) + client_plugin_mock.get_notification.assert_called_once_with( + 'notification_1') + + +class MonascaClientPluginTest(common.HeatTestCase): + @mock.patch('heat.engine.clients.os.monasca.client') + @mock.patch.object(client_plugin.MonascaClientPlugin, + '_get_client_args') + def test_client(self, + mock_get_client_args, + mock_monasca_client): + with mock.patch.object(mock_monasca_client, + 'Client') as mock_client: + args = dict( + os_endpoint='endpoint', + endpoint_type='endpoint_type', + auth_url='auth_url', + project_id='project_id', + token=lambda: '', + service_type='service_type', + cacert='os_cacert', + cert_file='cert_file', + insecure='insecure', + key_file='key_file' + ) + + mock_get_client_args.return_value = args + + _plugin = client_plugin.MonascaClientPlugin( + context=mock.MagicMock() + ) + _plugin.client() + + # Make sure the right args are created + mock_get_client_args.assert_called_once_with( + service_name='monasca', + service_type='monitoring' + ) + + # Make sure proper client_plugin is created with expected args + mock_client.assert_called_once_with( + '2_0', + endpoint=args['os_endpoint'], + endpoint_type=args['endpoint_type'], + auth_url=args['auth_url'], + token=args['token'](), + project_id=args['project_id'], + service_type=args['service_type'], + os_cacert=args['cacert'], + cert_file=args['cert_file'], + key_file=args['key_file'], + insecure=args['insecure'] + ) + + +# TODO(skraynev): remove it when monasca client will be +# merged in global requirements +class NotFound(Exception): + pass + + +class MonascaClientPluginNotificationTest(common.HeatTestCase): + + sample_uuid = '477e8273-60a7-4c41-b683-fdb0bc7cd152' + sample_name = 'test-notification' + + def _get_mock_notification(self): + notification = dict() + notification['id'] = self.sample_uuid + notification['name'] = self.sample_name + return notification + + def setUp(self): + super(MonascaClientPluginNotificationTest, self).setUp() + self._client = mock.MagicMock() + client_plugin.monasca_exc = mock.Mock() + # TODO(skraynev): remove it when monasca client will be + # merged in global requirements + client_plugin.monasca_exc.NotFound = NotFound + self.client_plugin = client_plugin.MonascaClientPlugin( + context=mock.MagicMock() + ) + + @mock.patch.object(client_plugin.MonascaClientPlugin, 'client') + def test_get_notification(self, client_monasca): + mock_notification = self._get_mock_notification() + self._client.notifications.get.return_value = mock_notification + client_monasca.return_value = self._client + + self.assertEqual(self.sample_uuid, + self.client_plugin.get_notification( + self.sample_uuid)) + self._client.notifications.get.assert_called_once_with( + notification_id=self.sample_uuid) + + @mock.patch.object(client_plugin.MonascaClientPlugin, 'client') + def test_get_notification_not_found(self, client_monasca): + self._client.notifications.get.side_effect = ( + client_plugin.monasca_exc.NotFound) + client_monasca.return_value = self._client + + ex = self.assertRaises(heat_exception.EntityNotFound, + self.client_plugin.get_notification, + self.sample_uuid) + msg = ("The Monasca Notification (%(name)s) could not be found." % + {'name': self.sample_uuid}) + self.assertEqual(msg, six.text_type(ex)) + self._client.notifications.get.assert_called_once_with( + notification_id=self.sample_uuid) diff --git a/setup.cfg b/setup.cfg index d9cc16955c..7c9afe35cb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -57,6 +57,7 @@ heat.clients = magnum = heat.engine.clients.os.magnum:MagnumClientPlugin manila = heat.engine.clients.os.manila:ManilaClientPlugin mistral = heat.engine.clients.os.mistral:MistralClientPlugin + monasca = heat.engine.clients.os.monasca:MonascaClientPlugin nova = heat.engine.clients.os.nova:NovaClientPlugin neutron = heat.engine.clients.os.neutron:NeutronClientPlugin swift = heat.engine.clients.os.swift:SwiftClientPlugin @@ -95,6 +96,7 @@ heat.constraints = designate.domain = heat.engine.clients.os.designate:DesignateDomainConstraint timezone = heat.engine.constraint.common_constraints:TimezoneConstraint cron_expression = heat.engine.constraint.common_constraints:CRONExpressionConstraint + monasca.notification = heat.engine.clients.os.monasca:MonascaNotificationConstraint heat.stack_lifecycle_plugins =