From 75075223653d9e0cab33aeddda2363128b83e38b Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 9 Nov 2023 18:39:31 +0900 Subject: [PATCH] Remove Monasca support The monasca project was marked inactive during 2023.1 cycle[1] and is being formally retired. [1] https://review.opendev.org/c/openstack/governance/+/897520 Change-Id: I57f88f08bd0d160760703891b00aa9cb26dd1d98 Signed-off-by: Takashi Kajinami --- README.rst | 1 - heat/common/config.py | 7 +- heat/engine/clients/os/monasca.py | 61 ---- .../openstack/monasca/alarm_definition.py | 184 +---------- .../openstack/monasca/notification.py | 178 +---------- heat/tests/clients/test_monasca_client.py | 98 ------ heat/tests/openstack/monasca/__init__.py | 0 .../monasca/test_alarm_definition.py | 198 ------------ .../openstack/monasca/test_notification.py | 285 ------------------ .../remove-monasca-dece91171a385669.yaml | 11 + requirements.txt | 1 - setup.cfg | 2 - 12 files changed, 41 insertions(+), 985 deletions(-) delete mode 100644 heat/engine/clients/os/monasca.py delete mode 100644 heat/tests/clients/test_monasca_client.py delete mode 100644 heat/tests/openstack/monasca/__init__.py delete mode 100644 heat/tests/openstack/monasca/test_alarm_definition.py delete mode 100644 heat/tests/openstack/monasca/test_notification.py create mode 100644 releasenotes/notes/remove-monasca-dece91171a385669.yaml diff --git a/README.rst b/README.rst index da06879015..af5f9cd490 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,6 @@ We have integration with * https://opendev.org/openstack/python-manilaclient (shared file system service) * https://opendev.org/openstack/python-mistralclient (workflow service) * https://opendev.org/openstack/python-zaqarclient (messaging service) -* https://opendev.org/openstack/python-monascaclient (monitoring service) * https://opendev.org/openstack/python-zunclient (container management service) * https://opendev.org/openstack/python-blazarclient (reservation service) * https://opendev.org/openstack/python-octaviaclient (Load-balancer service) diff --git a/heat/common/config.py b/heat/common/config.py index d62a9f599a..c91c02dc79 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -482,10 +482,9 @@ def list_opts(): yield noauth_group.name, noauth_opts yield 'clients', default_clients_opts - for client in ('aodh', 'barbican', 'cinder', 'designate', - 'glance', 'heat', 'keystone', 'magnum', 'manila', 'mistral', - 'monasca', 'neutron', 'nova', 'octavia', - 'swift', 'trove', 'vitrage', 'zaqar' + for client in ('aodh', 'barbican', 'cinder', 'designate', 'glance', 'heat', + 'keystone', 'magnum', 'manila', 'mistral', 'neutron', + 'nova', 'octavia', 'swift', 'trove', 'vitrage', 'zaqar' ): client_specific_group = 'clients_' + client yield client_specific_group, clients_opts diff --git a/heat/engine/clients/os/monasca.py b/heat/engine/clients/os/monasca.py deleted file mode 100644 index 6dccdfe90a..0000000000 --- a/heat/engine/clients/os/monasca.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# 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 monascaclient import exc as monasca_exc -from monascaclient.v2_0 import client as monasca_client - -from heat.common import exception as heat_exc -from heat.engine.clients import client_plugin -from heat.engine import constraints - -CLIENT_NAME = 'monasca' - - -class MonascaClientPlugin(client_plugin.ClientPlugin): - exceptions_module = [monasca_exc] - - service_types = [MONITORING] = ['monitoring'] - - VERSION = '2_0' - - def _create(self): - interface = self._get_client_option(CLIENT_NAME, 'endpoint_type') - endpoint = self.url_for(service_type=self.MONITORING, - endpoint_type=interface) - - # Directly use v2_0 client to avoid dynamic import in monasca client, - # We can switch back once https://review.opendev.org/#/c/700989 fixed. - return monasca_client.Client( - session=self.context.keystone_session, - service_type='monitoring', - endpoint=endpoint) - - def is_not_found(self, ex): - return isinstance(ex, monasca_exc.NotFound) - - def is_un_processable(self, ex): - return isinstance(ex, monasca_exc.UnprocessableEntity) - - 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): - - resource_client_name = CLIENT_NAME - resource_getter_name = 'get_notification' diff --git a/heat/engine/resources/openstack/monasca/alarm_definition.py b/heat/engine/resources/openstack/monasca/alarm_definition.py index dea669eaef..f873a25ace 100644 --- a/heat/engine/resources/openstack/monasca/alarm_definition.py +++ b/heat/engine/resources/openstack/monasca/alarm_definition.py @@ -12,13 +12,11 @@ # under the License. from heat.common.i18n import _ -from heat.engine import constraints -from heat.engine import properties -from heat.engine import resource +from heat.engine.resources.openstack.heat import none_resource from heat.engine import support -class MonascaAlarmDefinition(resource.Resource): +class MonascaAlarmDefinition(none_resource.NoneResource): """Heat Template Resource for Monasca Alarm definition. Monasca Alarm definition helps to define the required expression for @@ -32,175 +30,19 @@ class MonascaAlarmDefinition(resource.Resource): """ support_status = support.SupportStatus( - version='22.0.0', - status=support.DEPRECATED, - message=_('Monasca project was marked inactive'), + version='25.0.0', + status=support.HIDDEN, + message=_('Monasca project was retired'), previous_status=support.SupportStatus( - version='7.0.0', + version='22.0.0', + status=support.DEPRECATED, + message=_('Monasca project was marked inactive'), previous_status=support.SupportStatus( - version='5.0.0', - status=support.UNSUPPORTED - ))) - - default_client_name = 'monasca' - - entity = 'alarm_definitions' - - SEVERITY_LEVELS = ( - LOW, MEDIUM, HIGH, CRITICAL - ) = ( - 'low', 'medium', 'high', 'critical' - ) - - PROPERTIES = ( - NAME, DESCRIPTION, EXPRESSION, MATCH_BY, SEVERITY, - OK_ACTIONS, ALARM_ACTIONS, UNDETERMINED_ACTIONS, - ACTIONS_ENABLED - ) = ( - 'name', 'description', 'expression', 'match_by', 'severity', - 'ok_actions', 'alarm_actions', 'undetermined_actions', - 'actions_enabled' - ) - - properties_schema = { - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the alarm. By default, physical resource name is ' - 'used.'), - update_allowed=True - ), - DESCRIPTION: properties.Schema( - properties.Schema.STRING, - _('Description of the alarm.'), - update_allowed=True - ), - EXPRESSION: properties.Schema( - properties.Schema.STRING, - _('Expression of the alarm to evaluate.'), - update_allowed=False, - required=True - ), - MATCH_BY: properties.Schema( - properties.Schema.LIST, - _('The metric dimensions to match to the alarm dimensions. ' - 'One or more dimension key names separated by a comma.'), - default=[], - ), - SEVERITY: properties.Schema( - properties.Schema.STRING, - _('Severity of the alarm.'), - update_allowed=True, - constraints=[constraints.AllowedValues( - SEVERITY_LEVELS - )], - default=LOW - ), - OK_ACTIONS: properties.Schema( - properties.Schema.LIST, - _('The notification methods to use when an alarm state is OK.'), - update_allowed=True, - schema=properties.Schema( - properties.Schema.STRING, - _('Monasca notification.'), - constraints=[constraints.CustomConstraint( - 'monasca.notification') - ] - ), - default=[], - ), - ALARM_ACTIONS: properties.Schema( - properties.Schema.LIST, - _('The notification methods to use when an alarm state is ALARM.'), - update_allowed=True, - schema=properties.Schema( - properties.Schema.STRING, - _('Monasca notification.'), - constraints=[constraints.CustomConstraint( - 'monasca.notification') - ] - ), - default=[], - ), - UNDETERMINED_ACTIONS: properties.Schema( - properties.Schema.LIST, - _('The notification methods to use when an alarm state is ' - 'UNDETERMINED.'), - update_allowed=True, - schema=properties.Schema( - properties.Schema.STRING, - _('Monasca notification.'), - constraints=[constraints.CustomConstraint( - 'monasca.notification') - ] - ), - default=[], - ), - ACTIONS_ENABLED: properties.Schema( - properties.Schema.BOOLEAN, - _('Whether to enable the actions or not.'), - update_allowed=True, - default=True, - ), - } - - def handle_create(self): - args = dict( - name=(self.properties[self.NAME] or - self.physical_resource_name()), - description=self.properties[self.DESCRIPTION], - expression=self.properties[self.EXPRESSION], - match_by=self.properties[self.MATCH_BY], - severity=self.properties[self.SEVERITY], - ok_actions=self.properties[self.OK_ACTIONS], - alarm_actions=self.properties[self.ALARM_ACTIONS], - undetermined_actions=self.properties[ - self.UNDETERMINED_ACTIONS] - ) - - alarm = self.client().alarm_definitions.create(**args) - self.resource_id_set(alarm['id']) - - # Monasca enables action by default - actions_enabled = self.properties[self.ACTIONS_ENABLED] - if not actions_enabled: - self.client().alarm_definitions.patch( - alarm_id=self.resource_id, - actions_enabled=actions_enabled - ) - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - args = dict(alarm_id=self.resource_id) - - if prop_diff.get(self.NAME): - args['name'] = prop_diff.get(self.NAME) - - if prop_diff.get(self.DESCRIPTION): - args['description'] = prop_diff.get(self.DESCRIPTION) - - if prop_diff.get(self.SEVERITY): - args['severity'] = prop_diff.get(self.SEVERITY) - - if prop_diff.get(self.OK_ACTIONS): - args['ok_actions'] = prop_diff.get(self.OK_ACTIONS) - - if prop_diff.get(self.ALARM_ACTIONS): - args['alarm_actions'] = prop_diff.get(self.ALARM_ACTIONS) - - if prop_diff.get(self.UNDETERMINED_ACTIONS): - args['undetermined_actions'] = prop_diff.get( - self.UNDETERMINED_ACTIONS - ) - - if prop_diff.get(self.ACTIONS_ENABLED): - args['actions_enabled'] = prop_diff.get(self.ACTIONS_ENABLED) - - self.client().alarm_definitions.patch(**args) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().alarm_definitions.delete( - alarm_id=self.resource_id) + version='7.0.0', + previous_status=support.SupportStatus( + version='5.0.0', + status=support.UNSUPPORTED + )))) def resource_mapping(): diff --git a/heat/engine/resources/openstack/monasca/notification.py b/heat/engine/resources/openstack/monasca/notification.py index 65a155da58..ead20afd52 100644 --- a/heat/engine/resources/openstack/monasca/notification.py +++ b/heat/engine/resources/openstack/monasca/notification.py @@ -11,18 +11,12 @@ # License for the specific language governing permissions and limitations # under the License. -import re -from urllib import parse - -from heat.common import exception from heat.common.i18n import _ -from heat.engine import constraints -from heat.engine import properties -from heat.engine import resource +from heat.engine.resources.openstack.heat import none_resource from heat.engine import support -class MonascaNotification(resource.Resource): +class MonascaNotification(none_resource.NoneResource): """Heat Template Resource for Monasca Notification. A resource which is used to notificate if there is some alarm. @@ -32,164 +26,20 @@ class MonascaNotification(resource.Resource): """ support_status = support.SupportStatus( - version='22.0.0', - status=support.DEPRECATED, - message=_('Monasca project was marked inactive'), + version='25.0.0', + status=support.HIDDEN, + message=_('Monasca project was retired'), previous_status=support.SupportStatus( - version='7.0.0', - status=support.SUPPORTED, + version='22.0.0', + status=support.DEPRECATED, + message=_('Monasca project was marked inactive'), previous_status=support.SupportStatus( - version='5.0.0', - status=support.SUPPORTED - ))) - - default_client_name = 'monasca' - - entity = 'notifications' - - # NOTE(sirushti): To conform to the autoscaling behaviour in heat, we set - # the default period interval during create/update to 60 for webhooks only. - _default_period_interval = 60 - - NOTIFICATION_TYPES = ( - EMAIL, WEBHOOK, PAGERDUTY - ) = ( - 'email', 'webhook', 'pagerduty' - ) - - PROPERTIES = ( - NAME, TYPE, ADDRESS, PERIOD - ) = ( - 'name', 'type', 'address', 'period' - ) - - properties_schema = { - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the notification. By default, physical resource name ' - 'is used.'), - update_allowed=True - ), - TYPE: properties.Schema( - properties.Schema.STRING, - _('Type of the notification.'), - update_allowed=True, - required=True, - constraints=[constraints.AllowedValues( - NOTIFICATION_TYPES - )] - ), - ADDRESS: properties.Schema( - properties.Schema.STRING, - _('Address of the notification. It could be a valid email ' - 'address, url or service key based on notification type.'), - update_allowed=True, - required=True, - constraints=[constraints.Length(max=512)] - ), - PERIOD: properties.Schema( - properties.Schema.INTEGER, - _('Interval in seconds to invoke webhooks if the alarm state ' - 'does not transition away from the defined trigger state. A ' - 'value of 0 will disable continuous notifications. This ' - 'property is only applicable for the webhook notification ' - 'type and has default period interval of 60 seconds.'), - support_status=support.SupportStatus(version='7.0.0'), - update_allowed=True, - constraints=[constraints.AllowedValues([0, 60])] - ) - } - - def _period_interval(self): - period = self.properties[self.PERIOD] - if period is None: - period = self._default_period_interval - return period - - def validate(self): - super(MonascaNotification, self).validate() - if self.properties[self.PERIOD] is not None and ( - self.properties[self.TYPE] != self.WEBHOOK): - msg = _('The period property can only be specified against a ' - 'Webhook Notification type.') - raise exception.StackValidationFailed(message=msg) - - address = self.properties[self.ADDRESS] - if not address: - return - - if self.properties[self.TYPE] == self.WEBHOOK: - try: - parsed_address = parse.urlparse(address) - except Exception: - msg = _('Address "%(addr)s" should have correct format ' - 'required by "%(wh)s" type of "%(type)s" ' - 'property') % { - 'addr': address, - 'wh': self.WEBHOOK, - 'type': self.TYPE} - raise exception.StackValidationFailed(message=msg) - if not parsed_address.scheme: - msg = _('Address "%s" doesn\'t have required URL ' - 'scheme') % address - raise exception.StackValidationFailed(message=msg) - if not parsed_address.netloc: - msg = _('Address "%s" doesn\'t have required network ' - 'location') % address - raise exception.StackValidationFailed(message=msg) - if parsed_address.scheme not in ['http', 'https']: - msg = _('Address "%(addr)s" doesn\'t satisfies ' - 'allowed schemes: %(schemes)s') % { - 'addr': address, - 'schemes': ', '.join(['http', 'https']) - } - raise exception.StackValidationFailed(message=msg) - elif (self.properties[self.TYPE] == self.EMAIL and - not re.match(r'^\S+@\S+$', address)): - msg = _('Address "%(addr)s" doesn\'t satisfies allowed format for ' - '"%(email)s" type of "%(type)s" property') % { - 'addr': address, - 'email': self.EMAIL, - 'type': self.TYPE} - raise exception.StackValidationFailed(message=msg) - - def handle_create(self): - args = dict( - name=(self.properties[self.NAME] or - self.physical_resource_name()), - type=self.properties[self.TYPE], - address=self.properties[self.ADDRESS], - ) - if args['type'] == self.WEBHOOK: - args['period'] = self._period_interval() - - notification = self.client().notifications.create(**args) - self.resource_id_set(notification['id']) - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - args = dict(notification_id=self.resource_id) - - args['name'] = (prop_diff.get(self.NAME) or - self.properties[self.NAME]) - - args['type'] = (prop_diff.get(self.TYPE) or - self.properties[self.TYPE]) - - args['address'] = (prop_diff.get(self.ADDRESS) or - self.properties[self.ADDRESS]) - - if args['type'] == self.WEBHOOK: - updated_period = prop_diff.get(self.PERIOD) - args['period'] = (updated_period if updated_period is not None - else self._period_interval()) - - self.client().notifications.update(**args) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().notifications.delete( - notification_id=self.resource_id) + version='7.0.0', + status=support.SUPPORTED, + previous_status=support.SupportStatus( + version='5.0.0', + status=support.SUPPORTED + )))) def resource_mapping(): diff --git a/heat/tests/clients/test_monasca_client.py b/heat/tests/clients/test_monasca_client.py deleted file mode 100644 index b5d566d6f9..0000000000 --- a/heat/tests/clients/test_monasca_client.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# 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 unittest import mock - -from heat.common import exception as heat_exception -from heat.engine.clients.os import monasca as client_plugin -from heat.tests import common -from heat.tests import utils - - -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): - def test_client(self): - context = utils.dummy_context() - plugin = context.clients.client_plugin('monasca') - client = plugin.client() - self.assertIsNotNone(client.metrics) - - def test_client_uses_session(self): - context = mock.MagicMock() - monasca_client = client_plugin.MonascaClientPlugin(context=context) - self.assertIsNotNone(monasca_client._create()) - - -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() - 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, str(ex)) - self._client.notifications.get.assert_called_once_with( - notification_id=self.sample_uuid) diff --git a/heat/tests/openstack/monasca/__init__.py b/heat/tests/openstack/monasca/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/heat/tests/openstack/monasca/test_alarm_definition.py b/heat/tests/openstack/monasca/test_alarm_definition.py deleted file mode 100644 index 8dba8d04de..0000000000 --- a/heat/tests/openstack/monasca/test_alarm_definition.py +++ /dev/null @@ -1,198 +0,0 @@ -# -# 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 unittest import mock - -from heat.engine.clients.os import monasca as client_plugin -from heat.engine.resources.openstack.monasca import alarm_definition -from heat.engine import stack -from heat.engine import template -from heat.tests import common -from heat.tests import utils - - -sample_template = { - 'heat_template_version': '2015-10-15', - 'resources': { - 'test_resource': { - 'type': 'OS::Monasca::AlarmDefinition', - 'properties': { - 'name': 'sample_alarm_id', - 'description': 'sample alarm def', - 'expression': 'sample expression', - 'match_by': ['match_by'], - 'severity': 'low', - 'ok_actions': ['sample_notification'], - 'alarm_actions': ['sample_notification'], - 'undetermined_actions': ['sample_notification'], - 'actions_enabled': False - } - } - } -} - - -class MonascaAlarmDefinitionTest(common.HeatTestCase): - - def setUp(self): - super(MonascaAlarmDefinitionTest, self).setUp() - - self.ctx = utils.dummy_context() - - self.stack = stack.Stack( - self.ctx, 'test_stack', - template.Template(sample_template) - ) - - self.test_resource = self.stack['test_resource'] - - # Mock client - self.test_client = mock.MagicMock() - self.test_resource.client = mock.MagicMock( - return_value=self.test_client) - - # Mock client plugin - self.test_client_plugin = client_plugin.MonascaClientPlugin(self.ctx) - self.test_client_plugin._create = mock.MagicMock( - return_value=self.test_client) - self.test_resource.client_plugin = mock.MagicMock( - return_value=self.test_client_plugin) - self.test_client_plugin.get_notification = mock.MagicMock( - return_value='sample_notification') - - def _get_mock_resource(self): - value = dict(id='477e8273-60a7-4c41-b683-fdb0bc7cd152') - - return value - - def test_resource_handle_create(self): - mock_alarm_create = self.test_client.alarm_definitions.create - mock_alarm_patch = self.test_client.alarm_definitions.patch - mock_resource = self._get_mock_resource() - mock_alarm_create.return_value = mock_resource - - # validate the properties - self.assertEqual( - 'sample_alarm_id', - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.NAME)) - self.assertEqual( - 'sample alarm def', - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.DESCRIPTION)) - self.assertEqual( - 'sample expression', - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.EXPRESSION)) - self.assertEqual( - ['match_by'], - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.MATCH_BY)) - self.assertEqual( - 'low', - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.SEVERITY)) - self.assertEqual( - ['sample_notification'], - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.OK_ACTIONS)) - self.assertEqual( - ['sample_notification'], - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.ALARM_ACTIONS)) - self.assertEqual( - ['sample_notification'], - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.UNDETERMINED_ACTIONS)) - self.assertEqual( - False, - self.test_resource.properties.get( - alarm_definition.MonascaAlarmDefinition.ACTIONS_ENABLED)) - - self.test_resource.data_set = mock.Mock() - self.test_resource.handle_create() - # validate physical resource id - self.assertEqual(mock_resource['id'], self.test_resource.resource_id) - - args = dict( - name='sample_alarm_id', - description='sample alarm def', - expression='sample expression', - match_by=['match_by'], - severity='low', - ok_actions=['sample_notification'], - alarm_actions=['sample_notification'], - undetermined_actions=['sample_notification'] - ) - - mock_alarm_create.assert_called_once_with(**args) - mock_alarm_patch.assert_called_once_with( - alarm_id=self.test_resource.resource_id, - actions_enabled=False) - - def test_resource_handle_update(self): - mock_alarm_patch = self.test_client.alarm_definitions.patch - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - - prop_diff = { - alarm_definition.MonascaAlarmDefinition.NAME: - 'name-updated', - alarm_definition.MonascaAlarmDefinition.DESCRIPTION: - 'description-updated', - alarm_definition.MonascaAlarmDefinition.ACTIONS_ENABLED: - True, - alarm_definition.MonascaAlarmDefinition.SEVERITY: - 'medium', - alarm_definition.MonascaAlarmDefinition.OK_ACTIONS: - ['sample_notification'], - alarm_definition.MonascaAlarmDefinition.ALARM_ACTIONS: - ['sample_notification'], - alarm_definition.MonascaAlarmDefinition.UNDETERMINED_ACTIONS: - ['sample_notification']} - - self.test_resource.handle_update(json_snippet=None, - tmpl_diff=None, - prop_diff=prop_diff) - - args = dict( - alarm_id=self.test_resource.resource_id, - name='name-updated', - description='description-updated', - actions_enabled=True, - severity='medium', - ok_actions=['sample_notification'], - alarm_actions=['sample_notification'], - undetermined_actions=['sample_notification'] - ) - - mock_alarm_patch.assert_called_once_with(**args) - - def test_resource_handle_delete(self): - mock_alarm_delete = self.test_client.alarm_definitions.delete - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - mock_alarm_delete.return_value = None - - self.assertIsNone(self.test_resource.handle_delete()) - mock_alarm_delete.assert_called_once_with( - alarm_id=self.test_resource.resource_id - ) - - def test_resource_handle_delete_resource_id_is_none(self): - self.test_resource.resource_id = None - self.assertIsNone(self.test_resource.handle_delete()) - - def test_resource_handle_delete_not_found(self): - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - mock_alarm_delete = self.test_client.alarm_definitions.delete - mock_alarm_delete.side_effect = client_plugin.monasca_exc.NotFound - self.assertIsNone(self.test_resource.handle_delete()) diff --git a/heat/tests/openstack/monasca/test_notification.py b/heat/tests/openstack/monasca/test_notification.py deleted file mode 100644 index 500597950c..0000000000 --- a/heat/tests/openstack/monasca/test_notification.py +++ /dev/null @@ -1,285 +0,0 @@ -# -# 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 unittest import mock - -from heat.common import exception -from heat.engine.cfn import functions as cfn_funcs -from heat.engine.clients.os import monasca as client_plugin -from heat.engine.resources.openstack.monasca import notification -from heat.engine import stack -from heat.engine import template -from heat.tests import common -from heat.tests import utils - - -sample_template = { - 'heat_template_version': '2015-10-15', - 'resources': { - 'test_resource': { - 'type': 'OS::Monasca::Notification', - 'properties': { - 'name': 'test-notification', - 'type': 'webhook', - 'address': 'http://localhost:80/', - 'period': 60 - } - } - } -} - - -class MonascaNotificationTest(common.HeatTestCase): - - def setUp(self): - super(MonascaNotificationTest, self).setUp() - - self.ctx = utils.dummy_context() - - self.stack = stack.Stack( - self.ctx, 'test_stack', - template.Template(sample_template) - ) - - self.test_resource = self.stack['test_resource'] - - # Mock client - self.test_client = mock.MagicMock() - self.test_resource.client = mock.MagicMock( - return_value=self.test_client) - - # Mock client plugin - self.test_client_plugin = client_plugin.MonascaClientPlugin(self.ctx) - self.test_client_plugin._create = mock.MagicMock( - return_value=self.test_client) - self.test_resource.client_plugin = mock.MagicMock( - return_value=self.test_client_plugin) - - def _get_mock_resource(self): - value = dict(id='477e8273-60a7-4c41-b683-fdb0bc7cd152') - - return value - - def test_validate_success_no_period(self): - self.test_resource.properties.data.pop('period') - self.test_resource.validate() - - def test_validate_invalid_type_with_period(self): - self.test_resource.properties.data['type'] = self.test_resource.EMAIL - self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - - def test_validate_no_scheme_address_for_get_attr(self): - self.test_resource.properties.data['type'] = self.test_resource.WEBHOOK - self.patchobject(cfn_funcs, 'GetAtt', return_value=None) - get_att = cfn_funcs.GetAtt(self.stack, 'Fn::GetAtt', - ["ResourceA", "abc"]) - self.test_resource.properties.data['address'] = get_att - self.assertIsNone(self.test_resource.validate()) - - def test_validate_no_scheme_address_for_webhook(self): - self.test_resource.properties.data['type'] = self.test_resource.WEBHOOK - self.test_resource.properties.data['address'] = 'abc@def.com' - ex = self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - self.assertEqual('Address "abc@def.com" doesn\'t have ' - 'required URL scheme', str(ex)) - - def test_validate_no_netloc_address_for_webhook(self): - self.test_resource.properties.data['type'] = self.test_resource.WEBHOOK - self.test_resource.properties.data['address'] = 'https://' - ex = self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - self.assertEqual('Address "https://" doesn\'t have ' - 'required network location', str(ex)) - - def test_validate_prohibited_address_for_webhook(self): - self.test_resource.properties.data['type'] = self.test_resource.WEBHOOK - self.test_resource.properties.data['address'] = 'ftp://127.0.0.1' - ex = self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - self.assertEqual('Address "ftp://127.0.0.1" doesn\'t satisfies ' - 'allowed schemes: http, https', str(ex)) - - def test_validate_incorrect_address_for_email(self): - self.test_resource.properties.data['type'] = self.test_resource.EMAIL - self.test_resource.properties.data['address'] = 'abc#def.com' - self.test_resource.properties.data.pop('period') - ex = self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - self.assertEqual('Address "abc#def.com" doesn\'t satisfies allowed ' - 'format for "email" type of "type" property', - str(ex)) - - def test_validate_invalid_address_parsing(self): - self.test_resource.properties.data['type'] = self.test_resource.WEBHOOK - self.test_resource.properties.data['address'] = "https://example.com]" - ex = self.assertRaises(exception.StackValidationFailed, - self.test_resource.validate) - self.assertEqual('Address "https://example.com]" should have correct ' - 'format required by "webhook" type of "type" ' - 'property', str(ex)) - - def test_resource_handle_create(self): - mock_notification_create = self.test_client.notifications.create - mock_resource = self._get_mock_resource() - mock_notification_create.return_value = mock_resource - - # validate the properties - self.assertEqual( - 'test-notification', - self.test_resource.properties.get( - notification.MonascaNotification.NAME)) - self.assertEqual( - 'webhook', - self.test_resource.properties.get( - notification.MonascaNotification.TYPE)) - self.assertEqual( - 'http://localhost:80/', - self.test_resource.properties.get( - notification.MonascaNotification.ADDRESS)) - self.assertEqual( - 60, self.test_resource.properties.get( - notification.MonascaNotification.PERIOD)) - - self.test_resource.data_set = mock.Mock() - self.test_resource.handle_create() - - args = dict( - name='test-notification', - type='webhook', - address='http://localhost:80/', - period=60 - ) - - mock_notification_create.assert_called_once_with(**args) - - # validate physical resource id - self.assertEqual(mock_resource['id'], self.test_resource.resource_id) - - def test_resource_handle_create_default_period(self): - self.test_resource.properties.data.pop('period') - mock_notification_create = self.test_client.notifications.create - self.test_resource.handle_create() - - args = dict( - name='test-notification', - type='webhook', - address='http://localhost:80/', - period=60 - ) - mock_notification_create.assert_called_once_with(**args) - - def test_resource_handle_create_no_period(self): - self.test_resource.properties.data.pop('period') - self.test_resource.properties.data['type'] = 'email' - self.test_resource.properties.data['address'] = 'abc@def.com' - mock_notification_create = self.test_client.notifications.create - self.test_resource.handle_create() - - args = dict( - name='test-notification', - type='email', - address='abc@def.com' - ) - mock_notification_create.assert_called_once_with(**args) - - def test_resource_handle_update(self): - mock_notification_update = self.test_client.notifications.update - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - - prop_diff = {notification.MonascaNotification.ADDRESS: - 'http://localhost:1234/', - notification.MonascaNotification.NAME: 'name-updated', - notification.MonascaNotification.TYPE: 'webhook', - notification.MonascaNotification.PERIOD: 0} - - self.test_resource.handle_update(json_snippet=None, - tmpl_diff=None, - prop_diff=prop_diff) - - args = dict( - notification_id=self.test_resource.resource_id, - name='name-updated', - type='webhook', - address='http://localhost:1234/', - period=0 - ) - mock_notification_update.assert_called_once_with(**args) - - def test_resource_handle_update_default_period(self): - mock_notification_update = self.test_client.notifications.update - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - self.test_resource.properties.data.pop('period') - - prop_diff = {notification.MonascaNotification.ADDRESS: - 'http://localhost:1234/', - notification.MonascaNotification.NAME: 'name-updated', - notification.MonascaNotification.TYPE: 'webhook'} - - self.test_resource.handle_update(json_snippet=None, - tmpl_diff=None, - prop_diff=prop_diff) - - args = dict( - notification_id=self.test_resource.resource_id, - name='name-updated', - type='webhook', - address='http://localhost:1234/', - period=60 - ) - mock_notification_update.assert_called_once_with(**args) - - def test_resource_handle_update_no_period(self): - mock_notification_update = self.test_client.notifications.update - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - self.test_resource.properties.data.pop('period') - - prop_diff = {notification.MonascaNotification.ADDRESS: - 'abc@def.com', - notification.MonascaNotification.NAME: 'name-updated', - notification.MonascaNotification.TYPE: 'email'} - - self.test_resource.handle_update(json_snippet=None, - tmpl_diff=None, - prop_diff=prop_diff) - - args = dict( - notification_id=self.test_resource.resource_id, - name='name-updated', - type='email', - address='abc@def.com' - ) - mock_notification_update.assert_called_once_with(**args) - - def test_resource_handle_delete(self): - mock_notification_delete = self.test_client.notifications.delete - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - mock_notification_delete.return_value = None - - self.assertIsNone(self.test_resource.handle_delete()) - mock_notification_delete.assert_called_once_with( - notification_id=self.test_resource.resource_id - ) - - def test_resource_handle_delete_resource_id_is_none(self): - self.test_resource.resource_id = None - self.assertIsNone(self.test_resource.handle_delete()) - - def test_resource_handle_delete_not_found(self): - self.test_resource.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' - mock_notification_delete = self.test_client.notifications.delete - mock_notification_delete.side_effect = ( - client_plugin.monasca_exc.NotFound) - - self.assertIsNone(self.test_resource.handle_delete()) diff --git a/releasenotes/notes/remove-monasca-dece91171a385669.yaml b/releasenotes/notes/remove-monasca-dece91171a385669.yaml new file mode 100644 index 0000000000..f9462ab11c --- /dev/null +++ b/releasenotes/notes/remove-monasca-dece91171a385669.yaml @@ -0,0 +1,11 @@ +--- +upgrade: + - | + Integration with monasca has been removed because the monasca project was + retired. Because of the removal, the following resource types are no longer + supported and now hidden. + + - ``OS::Monasca::AlarmDefinition`` + - ``OS::Monasca::Notifications`` + + Also, the options in ``[clients_monasca]`` section have been removed. diff --git a/requirements.txt b/requirements.txt index 45d500b98f..b5a7cb39fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,6 @@ python-keystoneclient>=3.8.0 # Apache-2.0 python-magnumclient>=2.3.0 # Apache-2.0 python-manilaclient>=1.16.0 # Apache-2.0 python-mistralclient>=3.1.0 # Apache-2.0 -python-monascaclient>=1.12.0 # Apache-2.0 python-neutronclient>=7.7.0 # Apache-2.0 python-novaclient>=9.1.0 # Apache-2.0 python-octaviaclient>=1.8.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 3c7325db56..a5806d632c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -82,7 +82,6 @@ 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 octavia = heat.engine.clients.os.octavia:OctaviaClientPlugin @@ -131,7 +130,6 @@ heat.constraints = manila.share_snapshot = heat.engine.clients.os.manila:ManilaShareSnapshotConstraint manila.share_type = heat.engine.clients.os.manila:ManilaShareTypeConstraint mistral.workflow = heat.engine.clients.os.mistral:WorkflowConstraint - monasca.notification = heat.engine.clients.os.monasca:MonascaNotificationConstraint neutron.address_scope = heat.engine.clients.os.neutron.neutron_constraints:AddressScopeConstraint neutron.flow_classifier = heat.engine.clients.os.neutron.neutron_constraints:FlowClassifierConstraint neutron.lbaas.listener = heat.engine.clients.os.neutron.lbaas_constraints:ListenerConstraint