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:
parent
076df58f30
commit
7a31915c49
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixes `bug 1614928
|
||||
<https://bugs.launchpad.net/tripleo/+bug/1614928>`__ Moves the package
|
||||
update command to use a workflow.
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue