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_match = tripleoclient.v1.overcloud_profiles:MatchProfiles
overcloud_profiles_list = tripleoclient.v1.overcloud_profiles:ListProfiles overcloud_profiles_list = tripleoclient.v1.overcloud_profiles:ListProfiles
overcloud_raid_create = tripleoclient.v1.overcloud_raid:CreateRAID 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_update_stack = tripleoclient.v1.overcloud_update:UpdateOvercloud
overcloud_execute = tripleoclient.v1.overcloud_execute:RemoteExecute overcloud_execute = tripleoclient.v1.overcloud_execute:RemoteExecute
undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud

View File

@ -26,14 +26,23 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
super(TestOvercloudUpdate, self).setUp() super(TestOvercloudUpdate, self).setUp()
# Get the command object to test # 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) autospec=True)
@mock.patch('tripleo_common.update.PackageUpdateManager') @mock.patch('tripleoclient.v1.overcloud_update.UpdateOvercloud.log',
def test_update_out(self, update_manager, mock_process_templates): autospec=True)
update_manager.return_value.get_status.return_value = ( @mock.patch('tripleoclient.workflows.package_update.update_and_wait',
'COMPLETE', {}) 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'] argslist = ['overcloud', '-i', '--templates']
verifylist = [ verifylist = [
('stack', 'overcloud'), ('stack', 'overcloud'),
@ -41,24 +50,17 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
('templates', '/usr/share/openstack-tripleo-heat-templates/') ('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) parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
update_manager.get_status.called_once() mock_update_wait.assert_called_once_with(
update_manager.update.called_once() mock_logger,
update_manager.do_interactive_update.called_once() 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) autospec=True)
@mock.patch('tripleo_common.update.PackageUpdateManager') def test_update_failed(self, mock_update_wait):
def test_update_failed(self, update_manager, mock_process_templates): mock_update_wait.return_value = 'FAILED'
update_manager.return_value.get_status.return_value = (
'FAILED', {})
argslist = ['overcloud', '-i', '--templates'] argslist = ['overcloud', '-i', '--templates']
verifylist = [ verifylist = [
('stack', 'overcloud'), ('stack', 'overcloud'),
@ -66,12 +68,6 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
('templates', '/usr/share/openstack-tripleo-heat-templates/') ('templates', '/usr/share/openstack-tripleo-heat-templates/')
] ]
parsed_args = self.check_parser(self.cmd, argslist, verifylist) 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.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args) self.cmd.take_action, parsed_args)

View File

@ -14,15 +14,16 @@
# #
import logging import logging
import uuid
from osc_lib.command import command from osc_lib.command import command
from osc_lib.i18n import _ from osc_lib.i18n import _
from osc_lib import utils from osc_lib import utils
from tripleo_common import update
from tripleoclient import constants from tripleoclient import constants
from tripleoclient import exceptions 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): class UpdateOvercloud(command.Command):
@ -47,7 +48,9 @@ class UpdateOvercloud(command.Command):
parser.add_argument('-i', '--interactive', dest='interactive', parser.add_argument('-i', '--interactive', dest='interactive',
action='store_true') action='store_true')
parser.add_argument('-a', '--abort', dest='abort_update', 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( parser.add_argument(
'-e', '--environment-file', metavar='<HEAT ENVIRONMENT FILE>', '-e', '--environment-file', metavar='<HEAT ENVIRONMENT FILE>',
action='append', dest='environment_files', action='append', dest='environment_files',
@ -60,7 +63,9 @@ class UpdateOvercloud(command.Command):
) )
parser.add_argument( parser.add_argument(
'--answers-file', '--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 return parser
@ -68,32 +73,75 @@ class UpdateOvercloud(command.Command):
self.log.debug("take_action(%s)" % parsed_args) self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager clients = self.app.client_manager
workflow = clients.workflow_engine stack = oooutils.get_stack(clients.orchestration,
stack_fields = templates.process_templates( parsed_args.stack)
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_name = stack.stack_name
if parsed_args.interactive: if parsed_args.interactive:
update_manager.do_interactive_update() timeout = 0
status, _ = update_manager.get_status()
status = package_update.update_and_wait(
self.log, clients, stack, stack_name,
self.app_args.verbose_level, timeout)
if status not in ['COMPLETE']: if status not in ['COMPLETE']:
raise exceptions.DeploymentError("Stack update failed.") raise exceptions.DeploymentError("Stack update failed.")
else: else:
status, _ = update_manager.get_status() status = package_update.update(clients, container=stack_name,
print("stack {0} status: {1}".format(parsed_args.stack, queue_name=str(uuid.uuid4()))
status)) 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)