diff --git a/heat/engine/resources/openstack/heat/ha_restarter.py b/heat/engine/resources/openstack/heat/ha_restarter.py index 438a3205b6..c8f9618beb 100644 --- a/heat/engine/resources/openstack/heat/ha_restarter.py +++ b/heat/engine/resources/openstack/heat/ha_restarter.py @@ -12,101 +12,38 @@ # under the License. from oslo_log import log as logging -import six from heat.common.i18n import _ -from heat.engine import attributes -from heat.engine import constraints -from heat.engine import properties -from heat.engine.resources import signal_responder +from heat.engine.resources.openstack.heat import none_resource from heat.engine import support LOG = logging.getLogger(__name__) -class Restarter(signal_responder.SignalResponder): +class Restarter(none_resource.NoneResource): support_status = support.SupportStatus( - support.DEPRECATED, - _('The HARestarter resource type is deprecated and will be removed ' - 'in a future release of Heat, once it has support for auto-healing ' - 'any type of resource. Note that HARestarter does *not* actually ' - 'restart servers - it deletes and then recreates them. It also does ' - 'the same to all dependent resources, and may therefore exhibit ' - 'unexpected and undesirable behaviour. Instead, use the ' - 'mark-unhealthy API to mark a resource as needing replacement, and ' - 'then a stack update to perform the replacement while respecting ' - 'the dependencies and not deleting them unnecessarily.'), - version='2015.1' - ) - - PROPERTIES = ( - INSTANCE_ID, - ) = ( - 'InstanceId', - ) - - ATTRIBUTES = ( - ALARM_URL, - ) = ( - 'AlarmUrl', - ) - - properties_schema = { - INSTANCE_ID: properties.Schema( - properties.Schema.STRING, - _('Instance ID to be restarted.'), - required=True, - constraints=[ - constraints.CustomConstraint('nova.server') - ] - ), - } - - attributes_schema = { - ALARM_URL: attributes.Schema( - _("A signed url to handle the alarm (Heat extension)."), - type=attributes.Schema.STRING - ), - } - - def handle_create(self): - super(Restarter, self).handle_create() - self.resource_id_set(self._get_user_id()) - - def handle_signal(self, details=None): - if details is None: - alarm_state = 'alarm' - else: - alarm_state = details.get('state', 'alarm').lower() - - LOG.info('%(name)s Alarm, new state %(state)s', - {'name': self.name, 'state': alarm_state}) - - if alarm_state != 'alarm': - return - - target_id = self.properties[self.INSTANCE_ID] - victim = self.stack.resource_by_refid(target_id) - if victim is None: - LOG.info('%(name)s Alarm, can not find instance ' - '%(instance)s', - {'name': self.name, - 'instance': target_id}) - return - - LOG.info('%(name)s Alarm, restarting resource: %(victim)s', - {'name': self.name, 'victim': victim.name}) - self.stack.restart_resource(victim.name) - - def _resolve_attribute(self, name): - """Resolves the resource's attributes. - - Heat extension: "AlarmUrl" returns the url to post to the policy - when there is an alarm. - """ - if name == self.ALARM_URL and self.resource_id is not None: - return six.text_type(self._get_ec2_signed_url()) + status=support.HIDDEN, + version='10.0.0', + message=_('The HARestarter resource type has been removed. Existing ' + 'stacks containing HARestarter resources can still be ' + 'used, but the HARestarter resource will be a placeholder ' + 'that does nothing.'), + previous_status=support.SupportStatus( + status=support.DEPRECATED, + message=_('The HARestarter resource type is deprecated and will ' + 'be removed in a future release of Heat, once it has ' + 'support for auto-healing any type of resource. Note ' + 'that HARestarter does *not* actually restart ' + 'servers - it deletes and then recreates them. It also ' + 'does the same to all dependent resources, and may ' + 'therefore exhibit unexpected and undesirable ' + 'behaviour. Instead, use the mark-unhealthy API to ' + 'mark a resource as needing replacement, and then a ' + 'stack update to perform the replacement while ' + 'respecting the dependencies and not deleting them ' + 'unnecessarily.'), + version='2015.1')) def resource_mapping(): diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 0058e3082e..9ed192d2af 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -1987,44 +1987,6 @@ class Stack(collections.Mapping): action=self.RESTORE) updater() - def restart_resource(self, resource_name): - """Restart the resource specified by resource_name. - - stop resource_name and all that depend on it - start resource_name and all that depend on it - """ - warnings.warn("Stack.restart_resource() is horribly broken and will " - "never be fixed. If you're using it in a resource type " - "other than HARestarter, don't. And don't use " - "HARestarter either.", - DeprecationWarning) - - deps = self.dependencies[self[resource_name]] - failed = False - - for res in reversed(deps): - try: - scheduler.TaskRunner(res.destroy)() - except exception.ResourceFailure as ex: - failed = True - LOG.info('Resource %(name)s delete failed: %(ex)s', - {'name': res.name, 'ex': ex}) - - for res in deps: - if not failed: - try: - res.state_reset() - scheduler.TaskRunner(res.create)() - except exception.ResourceFailure as ex: - failed = True - LOG.info('Resource %(name)s create failed: ' - '%(ex)s', {'name': res.name, 'ex': ex}) - else: - res.state_set(res.CREATE, res.FAILED, - 'Resource restart aborted') - # TODO(asalkeld) if any of this fails we Should - # restart the whole stack - def get_availability_zones(self): nova = self.clients.client('nova') if self._zones is None: diff --git a/heat/tests/engine/test_resource_type.py b/heat/tests/engine/test_resource_type.py index 8d9837ba0b..25bbc05e03 100644 --- a/heat/tests/engine/test_resource_type.py +++ b/heat/tests/engine/test_resource_type.py @@ -44,7 +44,6 @@ class ResourceTypeTest(common.HeatTestCase): mock_is_service_available.return_value = (True, None) resources = self.eng.list_resource_types(self.ctx, "DEPRECATED") self.assertEqual(set(['OS::Aodh::Alarm', - 'OS::Heat::HARestarter', 'OS::Magnum::Bay', 'OS::Magnum::BayModel', 'OS::Glance::Image', diff --git a/heat/tests/openstack/heat/test_restarter.py b/heat/tests/openstack/heat/test_restarter.py deleted file mode 100644 index 94857b7362..0000000000 --- a/heat/tests/openstack/heat/test_restarter.py +++ /dev/null @@ -1,101 +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. - -import mock - -from heat.common import template_format -from heat.engine.clients.os import nova -from heat.tests import common -from heat.tests import utils - - -restarter_template = ''' -{ - "AWSTemplateFormatVersion" : "2010-09-09", - "Description" : "Template to test HARestarter", - "Parameters" : {}, - "Resources" : { - "instance": { - "Type": "OS::Heat::None" - }, - "restarter": { - "Type": "OS::Heat::HARestarter", - "Properties": { - "InstanceId": {"Ref": "instance"} - } - } - } -} -''' - -bogus_template = ''' -{ - "AWSTemplateFormatVersion" : "2010-09-09", - "Description" : "Template to test HARestarter", - "Parameters" : {}, - "Resources" : { - "restarter": { - "Type": "OS::Heat::HARestarter", - "Properties": { - "InstanceId": "instance" - } - } - } -} -''' - - -class RestarterTest(common.HeatTestCase): - def create_restarter(self, template=restarter_template): - snippet = template_format.parse(template) - self.stack = utils.parse_stack(snippet) - restarter = self.stack['restarter'] - self.patchobject(nova.NovaClientPlugin, 'get_server', - return_value=mock.MagicMock()) - restarter.handle_create = mock.Mock(return_value=None) - self.stack.create() - return restarter - - def test_create(self): - rsrc = self.create_restarter() - - self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) - rsrc.handle_create.assert_called_once_with() - - def test_handle_signal(self): - rsrc = self.create_restarter() - - with mock.patch.object(rsrc.stack, 'restart_resource') as rr: - self.assertIsNone(rsrc.handle_signal()) - rr.assert_called_once_with('instance') - - def test_handle_signal_alarm(self): - rsrc = self.create_restarter() - - with mock.patch.object(rsrc.stack, 'restart_resource') as rr: - self.assertIsNone(rsrc.handle_signal({'state': 'Alarm'})) - rr.assert_called_once_with('instance') - - def test_handle_signal_not_alarm(self): - rsrc = self.create_restarter() - - with mock.patch.object(rsrc.stack, 'restart_resource') as rr: - self.assertIsNone(rsrc.handle_signal({'state': 'spam'})) - self.assertEqual([], rr.mock_calls) - - def test_handle_signal_no_instance(self): - rsrc = self.create_restarter(bogus_template) - - with mock.patch.object(rsrc.stack, 'restart_resource') as rr: - self.assertIsNone(rsrc.handle_signal()) - self.assertEqual([], rr.mock_calls) diff --git a/releasenotes/notes/hidden-heat-harestarter-resource-a123479c317886a3.yaml b/releasenotes/notes/hidden-heat-harestarter-resource-a123479c317886a3.yaml new file mode 100644 index 0000000000..2fb5f20f6c --- /dev/null +++ b/releasenotes/notes/hidden-heat-harestarter-resource-a123479c317886a3.yaml @@ -0,0 +1,12 @@ +--- +upgrade: + - | + The ``OS::Heat::HARestarter`` resource type is no longer supported. This + resource type is now hidden from the documentation. HARestarter resources + in stacks, including pre-existing ones, are now only placeholders and will + no longer do anything. The recommended alternative is to mark a resource + unhealthy and then do a stack update to replace it. This still correctly + manages dependencies but, unlike HARestarter, also avoid replacing + dependent resources unnecessarily. An example of this technique can be + seen in the autohealing sample templates at + https://git.openstack.org/cgit/openstack/heat-templates/tree/hot/autohealing