Sync state if needed during retrieval

Update the deployment status if needed when retrieving the status. We do
this since tripleoclient does not yet use a single API for overcloud
deployment. Since there is no long running process to make sure the
status is updated, we instead update the status if needed when we get it
with this action.

The logic to update the status is detailed in a comment in the code.

Also as part of this commit the status is kept as deploying in
deploy_play when not triggering config_download, since the client will
trigger config_download on it's own. It makes more sense to keep the
status as deploying in that scenario.

Change-Id: I6d329e974965edf28d6f5b12e6854319cfb683f4
Closes-Bug: #1798193
(cherry picked from commit de06f9373a)
This commit is contained in:
James Slagle 2018-10-16 16:28:15 -04:00
parent 6613d0fe4d
commit 79c687cd32
4 changed files with 312 additions and 6 deletions

View File

@ -88,6 +88,7 @@ mistral.actions =
tripleo.deployment.config = tripleo_common.actions.deployment:OrchestrationDeployAction
tripleo.deployment.deploy = tripleo_common.actions.deployment:DeployStackAction
tripleo.deployment.get_deployment_failures = tripleo_common.actions.deployment:DeploymentFailuresAction
tripleo.deployment.get_deployment_status = tripleo_common.actions.deployment:DeploymentStatusAction
tripleo.deployment.convert_status = tripleo_common.actions.deployment:ConvertStatusAction
tripleo.deployment.overcloudrc = tripleo_common.actions.deployment:OvercloudRcAction
tripleo.derive_params.convert_number_to_range_list = tripleo_common.actions.derive_params:ConvertNumberToRangeListAction

View File

@ -16,6 +16,7 @@ import json
import logging
import os
import time
import yaml
from heatclient.common import deployment_utils
from heatclient import exc as heat_exc
@ -321,6 +322,89 @@ class DeploymentFailuresAction(base.TripleOAction):
'Ansible errors file not found at %s' % failures_file)
class DeploymentStatusAction(base.TripleOAction):
"""Get the deployment status and update it if necessary
The status will be set based off of the stack status and any running
config_download workflow.
"""
def __init__(self,
plan=constants.DEFAULT_CONTAINER_NAME):
super(DeploymentStatusAction, self).__init__()
self.plan = plan
def run(self, context):
orchestration_client = self.get_orchestration_client(context)
workflow_client = self.get_workflow_client(context)
swift_client = self.get_object_client(context)
try:
stack = orchestration_client.stacks.get(self.plan)
except heat_exc.HTTPNotFound:
return dict(status_update=None,
deployment_status=None)
try:
headers, body = swift_client.get_object(
'%s-messages' % self.plan,
'deployment_status.yaml')
deployment_status = yaml.safe_load(body)['deployment_status']
except swiftexceptions.ClientException:
deployment_status = None
stack_status = stack.stack_status
cd_status = None
ansible_status = None
# Will get set to new status if an update is required
status_update = None
cd_execs = workflow_client.executions.find(
workflow_name='tripleo.deployment.v1.config_download_deploy')
cd_execs.sort(key=lambda x: x.updated_at)
if cd_execs:
cd_exec = workflow_client.executions.get(cd_execs[-1].id)
cd_status = cd_exec.state
ansible_status = json.loads(cd_exec.output)['deployment_status']
def update_status(status):
# If we need to update the status return it
if deployment_status != status:
return status
# Update the status if needed. We do this since tripleoclient does not
# yet use a single API for overcloud deployment. Since there is no long
# running process to make sure the status is updated, we instead update
# the status if needed when we get it with this action.
#
# The logic here is:
#
# If stack or config_download is in progress, then the status is
# deploying.
#
# Else if stack is failed or config_download is failed or ansible is
# failed, then the status is failed.
#
# Else if config_download status is success and ansible is success
# then status is success.
#
# Else, we just return the read deployment_status from earlier.
if stack_status.endswith('IN_PROGRESS') or cd_status == 'RUNNING':
status_update = update_status('DEPLOYING')
elif stack_status.endswith('FAILED') or cd_status == 'FAILED' \
or ansible_status == 'DEPLOY_FAILED':
status_update = update_status('DEPLOY_FAILED')
elif cd_status == 'SUCCESS' and ansible_status == 'DEPLOY_SUCCESS':
status_update = update_status('DEPLOY_SUCCESS')
return dict(cd_status=cd_status,
stack_status=stack_status,
deployment_status=deployment_status,
ansible_status=ansible_status,
status_update=status_update)
class ConvertStatusAction(base.TripleOAction):
"""Translate a Heat stack status into a config-download deployment status

View File

@ -503,6 +503,198 @@ class OvercloudRcActionTestCase(base.TestCase):
self.assertEqual(result, {"overcloudrc": "fake overcloudrc"})
class DeploymentStatusActionTest(base.TestCase):
def setUp(self):
super(DeploymentStatusActionTest, self).setUp()
self.plan = 'overcloud'
self.ctx = mock.MagicMock()
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'COMPLETE'
heat().stacks.get.return_value = mock_stack
body = 'deployment_status: DEPLOY_SUCCESS'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_SUCCESS"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['stack_status'], 'COMPLETE')
self.assertEqual(result['cd_status'], 'SUCCESS')
self.assertEqual(result['deployment_status'], 'DEPLOY_SUCCESS')
self.assertEqual(result['status_update'], None)
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status_update_failed(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'FAILED'
heat().stacks.get.return_value = mock_stack
body = 'deployment_status: DEPLOY_SUCCESS'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_SUCCESS"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['stack_status'], 'FAILED')
self.assertEqual(result['cd_status'], 'SUCCESS')
self.assertEqual(result['deployment_status'], 'DEPLOY_SUCCESS')
self.assertEqual(result['status_update'], 'DEPLOY_FAILED')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status_update_deploying(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'IN_PROGRESS'
heat().stacks.get.return_value = mock_stack
body = 'deployment_status: DEPLOY_SUCCESS'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_SUCCESS"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['stack_status'], 'IN_PROGRESS')
self.assertEqual(result['cd_status'], 'SUCCESS')
self.assertEqual(result['deployment_status'], 'DEPLOY_SUCCESS')
self.assertEqual(result['status_update'], 'DEPLOYING')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status_update_success(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'COMPLETE'
heat().stacks.get.return_value = mock_stack
body = 'deployment_status: DEPLOYING'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_SUCCESS"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['stack_status'], 'COMPLETE')
self.assertEqual(result['cd_status'], 'SUCCESS')
self.assertEqual(result['deployment_status'], 'DEPLOYING')
self.assertEqual(result['status_update'], 'DEPLOY_SUCCESS')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status_ansible_failed(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'COMPLETE'
heat().stacks.get.return_value = mock_stack
body = 'deployment_status: DEPLOYING'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_FAILED"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['stack_status'], 'COMPLETE')
self.assertEqual(result['cd_status'], 'SUCCESS')
self.assertEqual(result['deployment_status'], 'DEPLOYING')
self.assertEqual(result['status_update'], 'DEPLOY_FAILED')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_get_deployment_status_no_heat_stack(
self, heat, mistral, swift):
mock_stack = mock.Mock()
mock_stack.stack_status = 'COMPLETE'
heat().stacks.get.side_effect = heat_exc.HTTPNotFound()
body = 'deployment_status: DEPLOY_SUCCESS'
swift().get_object.return_value = [mock.Mock(), body]
execution = mock.Mock()
execution.updated_at = 1
execution.state = 'SUCCESS'
execution.output = '{"deployment_status":"DEPLOY_SUCCESS"}'
mistral().executions.get.return_value = execution
mistral().executions.find.return_value = [execution]
action = deployment.DeploymentStatusAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['status_update'], None)
self.assertEqual(result['deployment_status'], None)
class DeploymentFailuresActionTest(base.TestCase):
def setUp(self):

View File

@ -232,7 +232,7 @@ workflows:
message: <% task().result %>
on-success:
- wait_for_stack_complete: <% $.config_download %>
- set_deployment_success: <% not $.config_download %>
- set_deployment_deploying: <% not $.config_download %>
on-error: set_deployment_failed
wait_for_stack_complete:
@ -316,6 +316,12 @@ workflows:
status: SUCCESS
deployment_status: DEPLOY_SUCCESS
set_deployment_deploying:
on-success: send_message
publish:
status: SUCCESS
deployment_status: DEPLOYING
send_message:
workflow: tripleo.messaging.v1.send
input:
@ -696,7 +702,8 @@ workflows:
get_deployment_status:
description: >
Get deployment status
Get deployment status and update it if needed based on stack and
config_downlooad status.
tags:
- tripleo-common-managed
@ -711,6 +718,32 @@ workflows:
tasks:
get_deployment_status:
action: tripleo.deployment.get_deployment_status
input:
plan: <% $.plan %>
publish:
status_update: <% yaml_parse(coalesce(task().result.status_update, '')) %>
deployment_status: <% task().result.deployment_status %>
on-complete:
- reload_deployment_status: <% $.status_update = null and $.deployment_status != null %>
- update_status: <% $.status_update != null and $.deployment_status != null %>
- send_message: <% $.deployment_status = null %>
publish-on-error:
message: No deployment status found for plan <% $.plan %>
deployment_status: ""
update_status:
workflow: tripleo.messaging.v1.send
input:
queue_name: <% $.queue_name %>
type: <% execution().name %>
status: RUNNING
execution: <% execution() %>
plan_name: <% $.plan %>
deployment_status: <% $.status_update %>
on-complete: reload_deployment_status
reload_deployment_status:
action: swift.get_object
input:
container: <% $.plan %>-messages
@ -718,9 +751,6 @@ workflows:
publish:
deployment_status: <% yaml_parse(task().result.last()) %>
on-complete: send_message
publish-on-error:
message: No deployment status found for plan <% $.plan %>
deployment_status: ""
send_message:
workflow: tripleo.messaging.v1.send
@ -729,7 +759,6 @@ workflows:
type: <% execution().name %>
execution: <% execution() %>
status: <% $.get("status", "SUCCESS") %>
message: <% $.get("message", "") %>
payload:
deployment_status: <% $.get(deployment_status, "") %>