Merge "Resource mark unhealthy RPC and ReST API"
This commit is contained in:
commit
fb3ad4983f
@ -36,6 +36,7 @@
|
|||||||
"resource:index": "rule:deny_stack_user",
|
"resource:index": "rule:deny_stack_user",
|
||||||
"resource:metadata": "",
|
"resource:metadata": "",
|
||||||
"resource:signal": "",
|
"resource:signal": "",
|
||||||
|
"resource:mark_unhealthy": "rule:deny_stack_user",
|
||||||
"resource:show": "rule:deny_stack_user",
|
"resource:show": "rule:deny_stack_user",
|
||||||
"stacks:abandon": "rule:deny_stack_user",
|
"stacks:abandon": "rule:deny_stack_user",
|
||||||
"stacks:create": "rule:deny_stack_user",
|
"stacks:create": "rule:deny_stack_user",
|
||||||
|
@ -320,6 +320,12 @@ class API(wsgi.Router):
|
|||||||
'url': '/resources/{resource_name}/signal',
|
'url': '/resources/{resource_name}/signal',
|
||||||
'action': 'signal',
|
'action': 'signal',
|
||||||
'method': 'POST'
|
'method': 'POST'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'resource_mark_unhealthy',
|
||||||
|
'url': '/resources/{resource_name}',
|
||||||
|
'action': 'mark_unhealthy',
|
||||||
|
'method': 'PATCH'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import six
|
|||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from heat.api.openstack.v1 import util
|
from heat.api.openstack.v1 import util
|
||||||
|
from heat.common.i18n import _
|
||||||
from heat.common import identifier
|
from heat.common import identifier
|
||||||
from heat.common import param_utils
|
from heat.common import param_utils
|
||||||
from heat.common import serializers
|
from heat.common import serializers
|
||||||
@ -155,6 +156,36 @@ class ResourceController(object):
|
|||||||
resource_name=resource_name,
|
resource_name=resource_name,
|
||||||
details=body)
|
details=body)
|
||||||
|
|
||||||
|
@util.identified_stack
|
||||||
|
def mark_unhealthy(self, req, identity, resource_name, body):
|
||||||
|
"""Mark a resource as healthy or unhealthy."""
|
||||||
|
data = dict()
|
||||||
|
VALID_KEYS = (RES_UPDATE_MARK_UNHEALTHY, RES_UPDATE_STATUS_REASON) = (
|
||||||
|
'mark_unhealthy', rpc_api.RES_STATUS_DATA)
|
||||||
|
|
||||||
|
invalid_keys = set(body) - set(VALID_KEYS)
|
||||||
|
if invalid_keys:
|
||||||
|
raise exc.HTTPBadRequest(_("Invalid keys in resource "
|
||||||
|
"mark unhealthy %s") % invalid_keys)
|
||||||
|
|
||||||
|
if RES_UPDATE_MARK_UNHEALTHY not in body:
|
||||||
|
raise exc.HTTPBadRequest(
|
||||||
|
_("Missing mandatory (%s) key from mark unhealthy "
|
||||||
|
"request") % RES_UPDATE_MARK_UNHEALTHY)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data[RES_UPDATE_MARK_UNHEALTHY] = param_utils.extract_bool(
|
||||||
|
RES_UPDATE_MARK_UNHEALTHY,
|
||||||
|
body[RES_UPDATE_MARK_UNHEALTHY])
|
||||||
|
except ValueError as e:
|
||||||
|
raise exc.HTTPBadRequest(six.text_type(e))
|
||||||
|
|
||||||
|
data[RES_UPDATE_STATUS_REASON] = body.get(RES_UPDATE_STATUS_REASON, "")
|
||||||
|
self.rpc_client.resource_mark_unhealthy(req.context,
|
||||||
|
stack_identity=identity,
|
||||||
|
resource_name=resource_name,
|
||||||
|
**data)
|
||||||
|
|
||||||
|
|
||||||
def create_resource(options):
|
def create_resource(options):
|
||||||
"""Resources resource factory method."""
|
"""Resources resource factory method."""
|
||||||
|
@ -294,7 +294,7 @@ class EngineService(service.Service):
|
|||||||
by the RPC caller.
|
by the RPC caller.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.25'
|
RPC_API_VERSION = '1.26'
|
||||||
|
|
||||||
def __init__(self, host, topic):
|
def __init__(self, host, topic):
|
||||||
super(EngineService, self).__init__()
|
super(EngineService, self).__init__()
|
||||||
@ -1643,6 +1643,50 @@ class EngineService(service.Service):
|
|||||||
self.thread_group_mgr.start(stack.id, _resource_signal,
|
self.thread_group_mgr.start(stack.id, _resource_signal,
|
||||||
stack, rsrc, details, False)
|
stack, rsrc, details, False)
|
||||||
|
|
||||||
|
@context.request_context
|
||||||
|
def resource_mark_unhealthy(self, cnxt, stack_identity, resource_name,
|
||||||
|
mark_unhealthy, resource_status_reason=None):
|
||||||
|
"""Mark the resource as healthy or unhealthy.
|
||||||
|
|
||||||
|
Put the resource in CHECK_FAILED state if 'mark_unhealthy'
|
||||||
|
is true. Put the resource in CHECK_COMPLETE if 'mark_unhealthy'
|
||||||
|
is false and the resource is in CHECK_FAILED state.
|
||||||
|
Otherwise, make no change.
|
||||||
|
|
||||||
|
:param mark_unhealthy: indicates whether the resource is unhealthy.
|
||||||
|
:param resource_status_reason: reason for health change.
|
||||||
|
"""
|
||||||
|
def lock(rsrc):
|
||||||
|
if rsrc.stack.convergence:
|
||||||
|
return rsrc.lock(self.engine_id)
|
||||||
|
else:
|
||||||
|
return stack_lock.StackLock(cnxt,
|
||||||
|
rsrc.stack.id,
|
||||||
|
self.engine_id)
|
||||||
|
|
||||||
|
s = self._get_stack(cnxt, stack_identity)
|
||||||
|
stack = parser.Stack.load(cnxt, stack=s)
|
||||||
|
if resource_name not in stack:
|
||||||
|
raise exception.ResourceNotFound(resource_name=resource_name,
|
||||||
|
stack_name=stack.name)
|
||||||
|
|
||||||
|
if not isinstance(mark_unhealthy, bool):
|
||||||
|
raise exception.Invalid(reason="mark_unhealthy is not a boolean")
|
||||||
|
|
||||||
|
rsrc = stack[resource_name]
|
||||||
|
reason = (resource_status_reason or
|
||||||
|
"state changed by resource_mark_unhealthy api")
|
||||||
|
try:
|
||||||
|
with lock(rsrc):
|
||||||
|
if mark_unhealthy:
|
||||||
|
rsrc.state_set(rsrc.CHECK, rsrc.FAILED, reason=reason)
|
||||||
|
elif rsrc.state == (rsrc.CHECK, rsrc.FAILED):
|
||||||
|
rsrc.state_set(rsrc.CHECK, rsrc.COMPLETE, reason=reason)
|
||||||
|
|
||||||
|
except exception.UpdateInProgress:
|
||||||
|
raise exception.ActionInProgress(stack_name=stack.name,
|
||||||
|
action=stack.action)
|
||||||
|
|
||||||
@context.request_context
|
@context.request_context
|
||||||
def find_physical_resource(self, cnxt, physical_resource_id):
|
def find_physical_resource(self, cnxt, physical_resource_id):
|
||||||
"""Return an identifier for the specified resource.
|
"""Return an identifier for the specified resource.
|
||||||
|
@ -44,6 +44,7 @@ class EngineClient(object):
|
|||||||
1.23 - Add environment_files to create/update/preview/validate
|
1.23 - Add environment_files to create/update/preview/validate
|
||||||
1.24 - Adds ignorable_errors to validate_template
|
1.24 - Adds ignorable_errors to validate_template
|
||||||
1.25 - list_stack_resource filter update
|
1.25 - list_stack_resource filter update
|
||||||
|
1.26 - Add mark_unhealthy
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BASE_RPC_API_VERSION = '1.0'
|
BASE_RPC_API_VERSION = '1.0'
|
||||||
@ -560,6 +561,25 @@ class EngineClient(object):
|
|||||||
|
|
||||||
version='1.3')
|
version='1.3')
|
||||||
|
|
||||||
|
def resource_mark_unhealthy(self, ctxt, stack_identity, resource_name,
|
||||||
|
mark_unhealthy, resource_status_reason=None):
|
||||||
|
"""Mark the resource as unhealthy or healthy.
|
||||||
|
|
||||||
|
:param ctxt: RPC context.
|
||||||
|
:param stack_identity: Name of the stack.
|
||||||
|
:param resource_name: the Resource.
|
||||||
|
:param mark_unhealthy: indicates whether the resource is unhealthy.
|
||||||
|
:param resource_status_reason: reason for health change.
|
||||||
|
"""
|
||||||
|
return self.call(
|
||||||
|
ctxt,
|
||||||
|
self.make_msg('resource_mark_unhealthy',
|
||||||
|
stack_identity=stack_identity,
|
||||||
|
resource_name=resource_name,
|
||||||
|
mark_unhealthy=mark_unhealthy,
|
||||||
|
resource_status_reason=resource_status_reason),
|
||||||
|
version='1.26')
|
||||||
|
|
||||||
def create_watch_data(self, ctxt, watch_name, stats_data):
|
def create_watch_data(self, ctxt, watch_name, stats_data):
|
||||||
"""Creates data for CloudWatch and WaitConditions.
|
"""Creates data for CloudWatch and WaitConditions.
|
||||||
|
|
||||||
|
@ -707,3 +707,140 @@ class ResourceControllerTest(tools.ControllerTest, common.HeatTestCase):
|
|||||||
|
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
self.m.VerifyAll()
|
self.m.VerifyAll()
|
||||||
|
|
||||||
|
def test_mark_unhealthy_valid_request(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'mark_unhealthy', True)
|
||||||
|
res_name = 'WebServer'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant,
|
||||||
|
'wordpress', '1')
|
||||||
|
|
||||||
|
req = self._get(stack_identity._tenant_path())
|
||||||
|
|
||||||
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
||||||
|
body = {'mark_unhealthy': True,
|
||||||
|
rpc_api.RES_STATUS_DATA: 'Anything'}
|
||||||
|
params = {'stack_identity': stack_identity,
|
||||||
|
'resource_name': res_name}
|
||||||
|
params.update(body)
|
||||||
|
|
||||||
|
rpc_client.EngineClient.call(
|
||||||
|
req.context,
|
||||||
|
('resource_mark_unhealthy', params),
|
||||||
|
version='1.26')
|
||||||
|
self.m.ReplayAll()
|
||||||
|
|
||||||
|
result = self.controller.mark_unhealthy(
|
||||||
|
req, tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name,
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
self.assertIsNone(result)
|
||||||
|
self.m.VerifyAll()
|
||||||
|
|
||||||
|
def test_mark_unhealthy_without_reason(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'mark_unhealthy', True)
|
||||||
|
res_name = 'WebServer'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant,
|
||||||
|
'wordpress', '1')
|
||||||
|
|
||||||
|
req = self._get(stack_identity._tenant_path())
|
||||||
|
|
||||||
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
||||||
|
body = {'mark_unhealthy': True, rpc_api.RES_STATUS_DATA: ''}
|
||||||
|
params = {'stack_identity': stack_identity,
|
||||||
|
'resource_name': res_name}
|
||||||
|
params.update(body)
|
||||||
|
|
||||||
|
rpc_client.EngineClient.call(
|
||||||
|
req.context,
|
||||||
|
('resource_mark_unhealthy', params),
|
||||||
|
version='1.26')
|
||||||
|
self.m.ReplayAll()
|
||||||
|
|
||||||
|
del body[rpc_api.RES_STATUS_DATA]
|
||||||
|
|
||||||
|
result = self.controller.mark_unhealthy(
|
||||||
|
req, tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name,
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
self.assertIsNone(result)
|
||||||
|
self.m.VerifyAll()
|
||||||
|
|
||||||
|
def test_mark_unhealthy_with_invalid_keys(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'mark_unhealthy', True)
|
||||||
|
res_name = 'WebServer'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant,
|
||||||
|
'wordpress', '1')
|
||||||
|
|
||||||
|
req = self._get(stack_identity._tenant_path())
|
||||||
|
|
||||||
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
||||||
|
body = {'mark_unhealthy': True,
|
||||||
|
rpc_api.RES_STATUS_DATA: 'Any',
|
||||||
|
'invalid_key1': 1, 'invalid_key2': 2}
|
||||||
|
expected = "Invalid keys in resource mark unhealthy"
|
||||||
|
actual = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.mark_unhealthy, req,
|
||||||
|
tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name,
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
self.assertIn(expected, six.text_type(actual))
|
||||||
|
self.assertIn('invalid_key1', six.text_type(actual))
|
||||||
|
self.assertIn('invalid_key2', six.text_type(actual))
|
||||||
|
|
||||||
|
def test_mark_unhealthy_with_invalid_value(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'mark_unhealthy', True)
|
||||||
|
res_name = 'WebServer'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant,
|
||||||
|
'wordpress', '1')
|
||||||
|
|
||||||
|
req = self._get(stack_identity._tenant_path())
|
||||||
|
|
||||||
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
||||||
|
body = {'mark_unhealthy': 'XYZ',
|
||||||
|
rpc_api.RES_STATUS_DATA: 'Any'}
|
||||||
|
|
||||||
|
expected = ('Unrecognized value "XYZ" for "mark_unhealthy", '
|
||||||
|
'acceptable values are: true, false')
|
||||||
|
|
||||||
|
actual = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.mark_unhealthy, req,
|
||||||
|
tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name,
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
self.assertIn(expected, six.text_type(actual))
|
||||||
|
|
||||||
|
def test_mark_unhealthy_without_mark_unhealthy_key(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'mark_unhealthy', True)
|
||||||
|
res_name = 'WebServer'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant,
|
||||||
|
'wordpress', '1')
|
||||||
|
|
||||||
|
req = self._get(stack_identity._tenant_path())
|
||||||
|
|
||||||
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
||||||
|
body = {rpc_api.RES_STATUS_DATA: 'Any'}
|
||||||
|
|
||||||
|
expected = ("Missing mandatory (%s) key from "
|
||||||
|
"mark unhealthy request" % 'mark_unhealthy')
|
||||||
|
|
||||||
|
actual = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.mark_unhealthy, req,
|
||||||
|
tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name,
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
self.assertIn(expected, six.text_type(actual))
|
||||||
|
@ -40,7 +40,7 @@ class ServiceEngineTest(common.HeatTestCase):
|
|||||||
|
|
||||||
def test_make_sure_rpc_version(self):
|
def test_make_sure_rpc_version(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'1.25',
|
'1.26',
|
||||||
service.EngineService.RPC_API_VERSION,
|
service.EngineService.RPC_API_VERSION,
|
||||||
('RPC version is changed, please update this test to new version '
|
('RPC version is changed, please update this test to new version '
|
||||||
'and make sure additional test cases are added for RPC APIs '
|
'and make sure additional test cases are added for RPC APIs '
|
||||||
|
@ -22,6 +22,7 @@ from heat.engine import dependencies
|
|||||||
from heat.engine import resource as res
|
from heat.engine import resource as res
|
||||||
from heat.engine import service
|
from heat.engine import service
|
||||||
from heat.engine import stack
|
from heat.engine import stack
|
||||||
|
from heat.engine import stack_lock
|
||||||
from heat.engine import template as templatem
|
from heat.engine import template as templatem
|
||||||
from heat.objects import stack as stack_object
|
from heat.objects import stack as stack_object
|
||||||
from heat.tests import common
|
from heat.tests import common
|
||||||
@ -497,3 +498,183 @@ class StackResourcesServiceTest(common.HeatTestCase):
|
|||||||
stack_dependencies = stk.dependencies
|
stack_dependencies = stk.dependencies
|
||||||
self.assertIsInstance(stack_dependencies, dependencies.Dependencies)
|
self.assertIsInstance(stack_dependencies, dependencies.Dependencies)
|
||||||
self.assertEqual(2, len(stack_dependencies.graph()))
|
self.assertEqual(2, len(stack_dependencies.graph()))
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_healthy_create_complete_test_stk')
|
||||||
|
def test_mark_healthy_in_create_complete(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', False,
|
||||||
|
resource_status_reason='noop')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
self.assertIn('resource_action', r)
|
||||||
|
self.assertIn('resource_status', r)
|
||||||
|
self.assertIn('resource_status_reason', r)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CREATE')
|
||||||
|
self.assertEqual(r['resource_status'], 'COMPLETE')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'state changed')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_create_complete_test_stk')
|
||||||
|
def test_mark_unhealthy_in_create_complete(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason='Some Reason')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'Some Reason')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_healthy_check_failed_test_stk')
|
||||||
|
def test_mark_healthy_check_failed(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason='Some Reason')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'Some Reason')
|
||||||
|
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', False,
|
||||||
|
resource_status_reason='Good Reason')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'COMPLETE')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'Good Reason')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_check_failed_test_stack')
|
||||||
|
def test_mark_unhealthy_check_failed(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason='Some Reason')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'Some Reason')
|
||||||
|
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason='New Reason')
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'], 'New Reason')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_invalid_value_test_stk')
|
||||||
|
def test_mark_unhealthy_invalid_value(self):
|
||||||
|
ex = self.assertRaises(dispatcher.ExpectedException,
|
||||||
|
self.eng.resource_mark_unhealthy,
|
||||||
|
self.ctx,
|
||||||
|
self.stack.identifier(),
|
||||||
|
'WebServer', "This is wrong",
|
||||||
|
resource_status_reason="Some Reason")
|
||||||
|
self.assertEqual(exception.Invalid, ex.exc_info[0])
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_none_reason_test_stk')
|
||||||
|
def test_mark_unhealthy_none_reason(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True)
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'],
|
||||||
|
'state changed by resource_mark_unhealthy api')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_empty_reason_test_stk')
|
||||||
|
def test_mark_unhealthy_empty_reason(self):
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason="")
|
||||||
|
|
||||||
|
r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', with_attr=None)
|
||||||
|
|
||||||
|
self.assertEqual(r['resource_action'], 'CHECK')
|
||||||
|
self.assertEqual(r['resource_status'], 'FAILED')
|
||||||
|
self.assertEqual(r['resource_status_reason'],
|
||||||
|
'state changed by resource_mark_unhealthy api')
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_lock_no_converge_test_stk')
|
||||||
|
def test_mark_unhealthy_lock_no_convergence(self):
|
||||||
|
mock_acquire = self.patchobject(stack_lock.StackLock,
|
||||||
|
'acquire',
|
||||||
|
return_value=None)
|
||||||
|
|
||||||
|
mock_release = self.patchobject(stack_lock.StackLock,
|
||||||
|
'release',
|
||||||
|
return_value=None)
|
||||||
|
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason="")
|
||||||
|
|
||||||
|
mock_acquire.assert_called_once_with()
|
||||||
|
mock_release.assert_called_once_with()
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_lock_converge_test_stk',
|
||||||
|
convergence=True)
|
||||||
|
def test_mark_unhealthy_stack_lock_convergence(self):
|
||||||
|
mock_acquire = self.patchobject(res.Resource,
|
||||||
|
'_acquire',
|
||||||
|
return_value=None)
|
||||||
|
|
||||||
|
self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason="")
|
||||||
|
|
||||||
|
mock_acquire.assert_called_once_with(self.eng.engine_id)
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_lockexc_converge_test_stk',
|
||||||
|
convergence=True)
|
||||||
|
def test_mark_unhealthy_stack_lock_exc_convergence(self):
|
||||||
|
def _acquire(*args, **kwargs):
|
||||||
|
raise exception.UpdateInProgress(self.stack.name)
|
||||||
|
|
||||||
|
self.patchobject(
|
||||||
|
res.Resource,
|
||||||
|
'_acquire',
|
||||||
|
return_value=None,
|
||||||
|
side_effect=exception.UpdateInProgress(self.stack.name))
|
||||||
|
ex = self.assertRaises(dispatcher.ExpectedException,
|
||||||
|
self.eng.resource_mark_unhealthy,
|
||||||
|
self.ctx,
|
||||||
|
self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason="")
|
||||||
|
self.assertEqual(exception.ActionInProgress, ex.exc_info[0])
|
||||||
|
|
||||||
|
@tools.stack_context('service_mark_unhealthy_lockexc_no_converge_test_stk')
|
||||||
|
def test_mark_unhealthy_stack_lock_exc_no_convergence(self):
|
||||||
|
self.patchobject(
|
||||||
|
stack_lock.StackLock,
|
||||||
|
'acquire',
|
||||||
|
return_value=None,
|
||||||
|
side_effect=exception.ActionInProgress(
|
||||||
|
stack_name=self.stack.name,
|
||||||
|
action=self.stack.action))
|
||||||
|
ex = self.assertRaises(dispatcher.ExpectedException,
|
||||||
|
self.eng.resource_mark_unhealthy,
|
||||||
|
self.ctx,
|
||||||
|
self.stack.identifier(),
|
||||||
|
'WebServer', True,
|
||||||
|
resource_status_reason="")
|
||||||
|
self.assertEqual(exception.ActionInProgress, ex.exc_info[0])
|
||||||
|
@ -391,3 +391,11 @@ class EngineRpcAPITestCase(common.HeatTestCase):
|
|||||||
'call',
|
'call',
|
||||||
stack_identity=self.identity,
|
stack_identity=self.identity,
|
||||||
version='1.22')
|
version='1.22')
|
||||||
|
|
||||||
|
def test_resource_mark_unhealthy(self):
|
||||||
|
self._test_engine_api('resource_mark_unhealthy', 'call',
|
||||||
|
stack_identity=self.identity,
|
||||||
|
resource_name='LogicalResourceId',
|
||||||
|
mark_unhealthy=True,
|
||||||
|
resource_status_reason="Any reason",
|
||||||
|
version='1.26')
|
||||||
|
Loading…
Reference in New Issue
Block a user