Allow mark-unhealthy by physical resource ID

If the name passed into mark-unhealthy is not a valid resource name,
check if it is a valid resource id and retrieve the resource via id
instead of name.

Change-Id: Ie28ed102665b2c6379d1f55b7a02b76d05e38ddd
Co-Authored-By: Zane Bitter <zbitter@redhat.com>
Closes-Bug: #1635295
This commit is contained in:
Tanvir Talukder 2016-12-05 14:58:20 -05:00 committed by Zane Bitter
parent f310a1f6bc
commit 4e465402d0
5 changed files with 66 additions and 10 deletions

View File

@ -54,6 +54,13 @@ resource_name_url:
in: path
required: false
type: string
resource_name_or_physical_id_url:
description: |
The name of a resource in the stack, or the ID of its underlying physical
resource.
in: path
required: true
type: string
snapshot_id_url:
description: |
The UUID of the snapshot.

View File

@ -263,7 +263,7 @@ This operation does not return a response body.
Mark a resource as unhealthy
============================
.. rest_method:: PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}
.. rest_method:: PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name_or_physical_id}
Mark the specified resource in the stack as unhealthy.
@ -288,7 +288,7 @@ Request Parameters
- tenant_id: tenant_id
- stack_name: stack_name_url
- stack_id: stack_id_url
- resource_name: resource_name_url
- resource_name_or_physical_id: resource_name_or_physical_id_url
- mark_unhealthy: mark_unhealthy
- resource_status_reason: resource_update_status_reason

View File

@ -1866,6 +1866,8 @@ class EngineService(service.ServiceBase):
is false and the resource is in CHECK_FAILED state.
Otherwise, make no change.
:param resource_name: either the logical name of the resource or the
physical resource ID.
:param mark_unhealthy: indicates whether the resource is unhealthy.
:param resource_status_reason: reason for health change.
"""
@ -1877,22 +1879,21 @@ class EngineService(service.ServiceBase):
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]
s = self._get_stack(cnxt, stack_identity)
stack = parser.Stack.load(cnxt, stack=s)
rsrc = self._find_resource_in_stack(cnxt, resource_name, stack)
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)
if rsrc.action != rsrc.DELETE:
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)
@ -1900,6 +1901,29 @@ class EngineService(service.ServiceBase):
raise exception.ActionInProgress(stack_name=stack.name,
action=stack.action)
@staticmethod
def _find_resource_in_stack(cnxt, resource_name, stack):
"""Find a resource in a stack by either name or physical ID."""
if resource_name in stack:
return stack[resource_name]
rsrcs = resource_objects.Resource.get_all_by_physical_resource_id(
cnxt,
resource_name)
def in_stack(rs):
return rs.stack_id == stack.id and stack[rs.name].id == rs.id
matches = [stack[rs.name] for rs in rsrcs if in_stack(rs)]
if not matches:
raise exception.ResourceNotFound(resource_name=resource_name,
stack_name=stack.name)
if len(matches) > 1:
raise exception.PhysicalResourceIDAmbiguity(phys_id=resource_name)
return matches[0]
@context.request_context
def find_physical_resource(self, cnxt, physical_resource_id):
"""Return an identifier for the specified resource.

View File

@ -536,6 +536,28 @@ class StackResourcesServiceTest(common.HeatTestCase):
self.assertIsInstance(stack_dependencies, dependencies.Dependencies)
self.assertEqual(2, len(stack_dependencies.graph()))
@tools.stack_context('service_find_resource_logical_name')
def test_find_resource_logical_name(self):
rsrc = self.stack['WebServer']
physical_rsrc = self.eng._find_resource_in_stack(self.ctx,
'WebServer',
self.stack)
self.assertEqual(rsrc.id, physical_rsrc.id)
@tools.stack_context('service_find_resource_physical_id')
def test_find_resource_physical_id(self):
rsrc = self.stack['WebServer']
physical_rsrc = self.eng._find_resource_in_stack(self.ctx,
rsrc.resource_id,
self.stack)
self.assertEqual(rsrc.id, physical_rsrc.id)
@tools.stack_context('service_find_resource_not_found')
def test_find_resource_nonexist(self):
self.assertRaises(exception.ResourceNotFound,
self.eng._find_resource_in_stack,
self.ctx, 'wibble', self.stack)
@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(),

View File

@ -0,0 +1,3 @@
---
features:
- The ``resource mark unhealthy`` command now accepts either a logical resource name (as it did previously) or a physical resource ID to identify the resource to be marked unhealthy.