Remove OS::Heat::HARestarter

The OS::Heat::HARestarter has been deprecated since Kilo. Now is the
time to eliminate support and hide it from the documentation.

This replaces it with a placeholder resource (like OS::Heat::None) and
marks it as hidden.

Change-Id: I56cd1f2d0b3323399ef02c3a0a05d79cc69af956
This commit is contained in:
ricolin 2017-10-11 23:44:09 +08:00 committed by Rico Lin
parent 2c0797b73e
commit 7b56e0e008
5 changed files with 35 additions and 226 deletions

View File

@ -12,101 +12,38 @@
# under the License. # under the License.
from oslo_log import log as logging from oslo_log import log as logging
import six
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import attributes from heat.engine.resources.openstack.heat import none_resource
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources import signal_responder
from heat.engine import support from heat.engine import support
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class Restarter(signal_responder.SignalResponder): class Restarter(none_resource.NoneResource):
support_status = support.SupportStatus( support_status = support.SupportStatus(
support.DEPRECATED, status=support.HIDDEN,
_('The HARestarter resource type is deprecated and will be removed ' version='10.0.0',
'in a future release of Heat, once it has support for auto-healing ' message=_('The HARestarter resource type has been removed. Existing '
'any type of resource. Note that HARestarter does *not* actually ' 'stacks containing HARestarter resources can still be '
'restart servers - it deletes and then recreates them. It also does ' 'used, but the HARestarter resource will be a placeholder '
'the same to all dependent resources, and may therefore exhibit ' 'that does nothing.'),
'unexpected and undesirable behaviour. Instead, use the ' previous_status=support.SupportStatus(
'mark-unhealthy API to mark a resource as needing replacement, and ' status=support.DEPRECATED,
'then a stack update to perform the replacement while respecting ' message=_('The HARestarter resource type is deprecated and will '
'the dependencies and not deleting them unnecessarily.'), 'be removed in a future release of Heat, once it has '
version='2015.1' 'support for auto-healing any type of resource. Note '
) 'that HARestarter does *not* actually restart '
'servers - it deletes and then recreates them. It also '
PROPERTIES = ( 'does the same to all dependent resources, and may '
INSTANCE_ID, 'therefore exhibit unexpected and undesirable '
) = ( 'behaviour. Instead, use the mark-unhealthy API to '
'InstanceId', 'mark a resource as needing replacement, and then a '
) 'stack update to perform the replacement while '
'respecting the dependencies and not deleting them '
ATTRIBUTES = ( 'unnecessarily.'),
ALARM_URL, version='2015.1'))
) = (
'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())
def resource_mapping(): def resource_mapping():

View File

@ -1987,44 +1987,6 @@ class Stack(collections.Mapping):
action=self.RESTORE) action=self.RESTORE)
updater() 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): def get_availability_zones(self):
nova = self.clients.client('nova') nova = self.clients.client('nova')
if self._zones is None: if self._zones is None:

View File

@ -44,7 +44,6 @@ class ResourceTypeTest(common.HeatTestCase):
mock_is_service_available.return_value = (True, None) mock_is_service_available.return_value = (True, None)
resources = self.eng.list_resource_types(self.ctx, "DEPRECATED") resources = self.eng.list_resource_types(self.ctx, "DEPRECATED")
self.assertEqual(set(['OS::Aodh::Alarm', self.assertEqual(set(['OS::Aodh::Alarm',
'OS::Heat::HARestarter',
'OS::Magnum::Bay', 'OS::Magnum::Bay',
'OS::Magnum::BayModel', 'OS::Magnum::BayModel',
'OS::Glance::Image', 'OS::Glance::Image',

View File

@ -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)

View File

@ -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