Migrate overcloud update to a mistral workflow

This moves the package update to using a mistral workflow.

Change-Id: I5176746ca4843202a385206289be66f7e6450f48
Depends-On: I8a1f2dbffa19c2d8c93684562d2fb16aef6667db
Closes-Bug: #1614928
This commit is contained in:
Brad P. Crochet 2016-10-03 12:52:54 -04:00
parent 076df58f30
commit 7a31915c49
5 changed files with 200 additions and 54 deletions

View File

@ -0,0 +1,5 @@
---
fixes:
- Fixes `bug 1614928
<https://bugs.launchpad.net/tripleo/+bug/1614928>`__ Moves the package
update command to use a workflow.

View File

@ -79,6 +79,8 @@ openstack.tripleoclient.v1 =
overcloud_profiles_match = tripleoclient.v1.overcloud_profiles:MatchProfiles
overcloud_profiles_list = tripleoclient.v1.overcloud_profiles:ListProfiles
overcloud_raid_create = tripleoclient.v1.overcloud_raid:CreateRAID
overcloud_update_abort = tripleoclient.v1.overcloud_update:AbortUpdateOvercloud
overcloud_update_clear_breakpoints = tripleoclient.v1.overcloud_update:ClearBreakpointsOvercloud
overcloud_update_stack = tripleoclient.v1.overcloud_update:UpdateOvercloud
overcloud_execute = tripleoclient.v1.overcloud_execute:RemoteExecute
undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud

View File

@ -26,14 +26,23 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
super(TestOvercloudUpdate, self).setUp()
# Get the command object to test
self.cmd = overcloud_update.UpdateOvercloud(self.app, None)
app_args = mock.Mock()
app_args.verbose_level = 1
self.cmd = overcloud_update.UpdateOvercloud(self.app, app_args)
@mock.patch('tripleoclient.workflows.templates.process_templates',
@mock.patch('tripleoclient.utils.get_stack',
autospec=True)
@mock.patch('tripleo_common.update.PackageUpdateManager')
def test_update_out(self, update_manager, mock_process_templates):
update_manager.return_value.get_status.return_value = (
'COMPLETE', {})
@mock.patch('tripleoclient.v1.overcloud_update.UpdateOvercloud.log',
autospec=True)
@mock.patch('tripleoclient.workflows.package_update.update_and_wait',
autospec=True)
def test_update_out(self, mock_update_wait, mock_logger, mock_get_stack):
mock_update_wait.return_value = 'COMPLETE'
mock_stack = mock.Mock()
mock_stack.stack_name = 'mystack'
mock_get_stack.return_value = mock_stack
# mock_logger.return_value = mock.Mock()
argslist = ['overcloud', '-i', '--templates']
verifylist = [
('stack', 'overcloud'),
@ -41,24 +50,17 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
('templates', '/usr/share/openstack-tripleo-heat-templates/')
]
mock_process_templates.return_value = {
'stack_name': 'mystack',
'environment': {},
'files': {},
'templates': 'template body',
}
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args)
update_manager.get_status.called_once()
update_manager.update.called_once()
update_manager.do_interactive_update.called_once()
mock_update_wait.assert_called_once_with(
mock_logger,
self.app.client_manager,
mock_stack, 'mystack', 1, 0)
@mock.patch('tripleoclient.workflows.templates.process_templates',
@mock.patch('tripleoclient.workflows.package_update.update_and_wait',
autospec=True)
@mock.patch('tripleo_common.update.PackageUpdateManager')
def test_update_failed(self, update_manager, mock_process_templates):
update_manager.return_value.get_status.return_value = (
'FAILED', {})
def test_update_failed(self, mock_update_wait):
mock_update_wait.return_value = 'FAILED'
argslist = ['overcloud', '-i', '--templates']
verifylist = [
('stack', 'overcloud'),
@ -66,12 +68,6 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
('templates', '/usr/share/openstack-tripleo-heat-templates/')
]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
mock_process_templates.return_value = {
'stack_name': 'mystack',
'environment': {},
'files': {},
'templates': 'template body',
}
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)

View File

@ -14,15 +14,16 @@
#
import logging
import uuid
from osc_lib.command import command
from osc_lib.i18n import _
from osc_lib import utils
from tripleo_common import update
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient.workflows import templates
from tripleoclient import utils as oooutils
from tripleoclient.workflows import package_update
class UpdateOvercloud(command.Command):
@ -47,7 +48,9 @@ class UpdateOvercloud(command.Command):
parser.add_argument('-i', '--interactive', dest='interactive',
action='store_true')
parser.add_argument('-a', '--abort', dest='abort_update',
action='store_true')
action='store_true',
help=_('DEPRECATED. Please use the command'
'"openstack overcloud update abort"'))
parser.add_argument(
'-e', '--environment-file', metavar='<HEAT ENVIRONMENT FILE>',
action='append', dest='environment_files',
@ -60,7 +63,9 @@ class UpdateOvercloud(command.Command):
)
parser.add_argument(
'--answers-file',
help=_('Path to a YAML file with arguments and parameters.')
help=_('Path to a YAML file with arguments and parameters. '
'DEPRECATED. Not necessary when used with a plan. Will '
'be silently ignored, and removed in the "P" release.')
)
return parser
@ -68,32 +73,75 @@ class UpdateOvercloud(command.Command):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
workflow = clients.workflow_engine
stack_fields = templates.process_templates(
workflow, container=parsed_args.stack)
update_manager = update.PackageUpdateManager(
heatclient=clients.orchestration,
novaclient=clients.compute,
stack_id=parsed_args.stack,
stack_fields=stack_fields)
if parsed_args.abort_update:
print("cancelling package update on stack {0}".format(
parsed_args.stack))
update_manager.cancel()
else:
status, resources = update_manager.get_status()
if status not in ['IN_PROGRESS', 'WAITING']:
print("starting package update on stack {0}".format(
parsed_args.stack))
update_manager.update()
stack = oooutils.get_stack(clients.orchestration,
parsed_args.stack)
stack_name = stack.stack_name
if parsed_args.interactive:
update_manager.do_interactive_update()
status, _ = update_manager.get_status()
timeout = 0
status = package_update.update_and_wait(
self.log, clients, stack, stack_name,
self.app_args.verbose_level, timeout)
if status not in ['COMPLETE']:
raise exceptions.DeploymentError("Stack update failed.")
else:
status, _ = update_manager.get_status()
print("stack {0} status: {1}".format(parsed_args.stack,
status))
status = package_update.update(clients, container=stack_name,
queue_name=str(uuid.uuid4()))
print("stack {0} status: {1}".format(parsed_args.stack, status))
class AbortUpdateOvercloud(command.Command):
"""Aborts a package update on overcloud nodes"""
log = logging.getLogger(__name__ + ".AbortUpdateOvercloud")
def get_parser(self, prog_name):
parser = super(AbortUpdateOvercloud, self).get_parser(prog_name)
parser.add_argument('stack', nargs='?',
help=_('Name or ID of heat stack to abort a '
'running update '
'(default=Env: OVERCLOUD_STACK_NAME)'),
default=utils.env('OVERCLOUD_STACK_NAME'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
heat = clients.orchestration
stack = oooutils.get_stack(heat, parsed_args.stack)
package_update.abort_update(clients, stack_id=stack.id)
class ClearBreakpointsOvercloud(command.Command):
"""Clears a set of breakpoints on a currently updating overcloud"""
log = logging.getLogger(__name__ + ".ClearBreakpointsOvercloud")
def get_parser(self, prog_name):
parser = super(ClearBreakpointsOvercloud, self).get_parser(prog_name)
parser.add_argument('stack', nargs='?',
help=_('Name or ID of heat stack to clear a '
'breakpoint or set of breakpoints '
'(default=Env: OVERCLOUD_STACK_NAME)'),
default=utils.env('OVERCLOUD_STACK_NAME'))
parser.add_argument('--ref',
action='append',
dest='refs',
help=_('Breakpoint to clear'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
heat = clients.orchestration
stack = oooutils.get_stack(heat, parsed_args.stack)
package_update.clear_breakpoints(clients, stack_id=stack.id,
refs=parsed_args.refs)

View File

@ -0,0 +1,95 @@
# 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.
from __future__ import print_function
import pprint
import uuid
from tripleo_common import update as update_common
from tripleoclient.workflows import base
def update(clients, **workflow_input):
workflow_client = clients.workflow_engine
tripleoclients = clients.tripleoclient
queue_name = workflow_input['queue_name']
execution = base.start_workflow(
workflow_client,
'tripleo.update.v1.update_plan',
workflow_input=workflow_input
)
with tripleoclients.messaging_websocket(queue_name) as ws:
message = ws.wait_for_message(execution.id)
assert message['status'] == "SUCCESS", pprint.pformat(message)
def update_and_wait(log, clients, stack, plan_name, verbose_level,
timeout=None):
"""Start the update and wait for it to give breakpoints or finish"""
log.info("Performing Heat stack update")
queue_name = str(uuid.uuid4())
workflow_input = {
"container": plan_name,
"queue_name": queue_name,
}
if timeout is not None:
workflow_input['timeout'] = timeout
update(clients, **workflow_input)
update_manager = update_common.PackageUpdateManager(
heatclient=clients.orchestration,
novaclient=clients.compute,
stack_id=stack)
update_manager.do_interactive_update()
def abort_update(clients, **workflow_input):
workflow_client = clients.workflow_engine
tripleoclients = clients.tripleoclient
workflow_input['queue_name'] = str(uuid.uuid4())
queue_name = workflow_input['queue_name']
execution = base.start_workflow(
workflow_client,
'tripleo.update.v1.cancel_stack_update',
workflow_input=workflow_input
)
with tripleoclients.messaging_websocket(queue_name) as ws:
message = ws.wait_for_message(execution.id)
assert message['status'] == "SUCCESS", pprint.pformat(message)
def clear_breakpoints(clients, **workflow_input):
workflow_client = clients.workflow_engine
tripleoclients = clients.tripleoclient
workflow_input['queue_name'] = str(uuid.uuid4())
queue_name = workflow_input['queue_name']
execution = base.start_workflow(
workflow_client,
'tripleo.update.v1.clear_breakpoints',
workflow_input=workflow_input
)
with tripleoclients.messaging_websocket(queue_name) as ws:
message = ws.wait_for_message(execution.id)
assert message['status'] == "SUCCESS", pprint.pformat(message)