Merge "Invokes specified workflows in plan env file"
This commit is contained in:
commit
ce1a06fb56
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- Workflows, associated with plan-environment.yaml using
|
||||
workflow_parameters, are invoked before the actual
|
||||
deployment starts.
|
|
@ -36,6 +36,10 @@ class DeploymentError(RuntimeError):
|
|||
"""Deployment failed"""
|
||||
|
||||
|
||||
class PlanEnvWorkflowError(RuntimeError):
|
||||
"""Plan Environment workflow has failed"""
|
||||
|
||||
|
||||
class StackInProgress(RuntimeError):
|
||||
"""Unable to deploy as the stack is busy"""
|
||||
|
||||
|
|
|
@ -157,6 +157,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
|
||||
mock_create_tempest_deployer_input.assert_called_with()
|
||||
|
||||
@mock.patch('tripleoclient.workflows.parameters.invoke_plan_env_workflows',
|
||||
autospec=True)
|
||||
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_deploy_postconfig', autospec=True)
|
||||
|
@ -189,7 +191,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
mock_create_tempest_deployer_input,
|
||||
mock_create_parameters_env, mock_validate_args,
|
||||
mock_breakpoints_cleanup, mock_tarball,
|
||||
mock_postconfig, mock_get_overcloud_endpoint):
|
||||
mock_postconfig, mock_get_overcloud_endpoint,
|
||||
mock_invoke_plan_env_wf):
|
||||
|
||||
arglist = ['--templates', '--ceph-storage-scale', '3']
|
||||
verifylist = [
|
||||
|
@ -274,7 +277,10 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
'/tmp/tht/tripleo-heat-templates', mock.ANY)
|
||||
mock_tarball.tarball_extract_to_swift_container.assert_called_with(
|
||||
clients.tripleoclient.object_store, mock.ANY, 'overcloud')
|
||||
self.assertFalse(mock_invoke_plan_env_wf.called)
|
||||
|
||||
@mock.patch('tripleoclient.workflows.parameters.invoke_plan_env_workflows',
|
||||
autospec=True)
|
||||
@mock.patch('shutil.rmtree', autospec=True)
|
||||
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
|
@ -308,7 +314,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
mock_create_tempest_deployer, mock_create_parameters_env,
|
||||
mock_validate_args, mock_breakpoints_cleanup,
|
||||
mock_tarball, mock_postconfig,
|
||||
mock_get_overcloud_endpoint, mock_shutil_rmtree):
|
||||
mock_get_overcloud_endpoint, mock_shutil_rmtree,
|
||||
mock_invoke_plan_env_wf):
|
||||
arglist = ['--templates', '-p', 'the-plan-environment.yaml']
|
||||
verifylist = [
|
||||
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
||||
|
@ -402,6 +409,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
mock_open_context.assert_has_calls(
|
||||
[mock.call('the-plan-environment.yaml')])
|
||||
clients.tripleoclient.object_store.put_object.assert_called()
|
||||
self.assertTrue(mock_invoke_plan_env_wf.called)
|
||||
|
||||
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
|
@ -647,7 +655,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
def _fake_heat_deploy(self, stack, stack_name, template_path,
|
||||
parameters, environments, timeout, tht_root,
|
||||
env, update_plan_only, run_validations,
|
||||
skip_deploy_identifier):
|
||||
skip_deploy_identifier, plan_env_file):
|
||||
assertEqual(
|
||||
{'parameter_defaults': {},
|
||||
'resource_registry': {
|
||||
|
@ -708,7 +716,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
def _fake_heat_deploy(self, stack, stack_name, template_path,
|
||||
parameters, environments, timeout, tht_root,
|
||||
env, update_plan_only, run_validations,
|
||||
skip_deploy_identifier):
|
||||
skip_deploy_identifier, plan_env_file):
|
||||
# Should be no breakpoint cleanup because utils.get_stack = None
|
||||
assertEqual(
|
||||
{'parameter_defaults': {},
|
||||
|
@ -936,13 +944,14 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
self, mock_heat_deploy_func):
|
||||
result = self.cmd._try_overcloud_deploy_with_compat_yaml(
|
||||
'/fake/path', {}, 'overcloud', {}, ['~/overcloud-env.json'], 1,
|
||||
{}, False, True, False)
|
||||
{}, False, True, False, None)
|
||||
# If it returns None it succeeded
|
||||
self.assertIsNone(result)
|
||||
mock_heat_deploy_func.assert_called_once_with(
|
||||
self.cmd, {}, 'overcloud',
|
||||
'/fake/path/' + constants.OVERCLOUD_YAML_NAME, {},
|
||||
['~/overcloud-env.json'], 1, '/fake/path', {}, False, True, False)
|
||||
['~/overcloud-env.json'], 1, '/fake/path', {}, False, True, False,
|
||||
None)
|
||||
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_heat_deploy', autospec=True)
|
||||
|
@ -953,7 +962,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
self.cmd._try_overcloud_deploy_with_compat_yaml,
|
||||
'/fake/path', mock.ANY, mock.ANY, mock.ANY,
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY,
|
||||
mock.ANY)
|
||||
mock.ANY, None)
|
||||
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_heat_deploy', autospec=True)
|
||||
|
@ -964,7 +973,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
try:
|
||||
self.cmd._try_overcloud_deploy_with_compat_yaml(
|
||||
'/fake/path', mock.ANY, mock.ANY, mock.ANY,
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY,
|
||||
None)
|
||||
except ValueError as value_error:
|
||||
self.assertIn('/fake/path', str(value_error))
|
||||
|
||||
|
@ -1315,7 +1325,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
mock_get_template_contents.return_value = [{}, {}]
|
||||
|
||||
self.cmd._heat_deploy(mock_stack, 'mock_stack', '/tmp', {},
|
||||
{}, 1, '/tmp', {}, True, False, False)
|
||||
{}, 1, '/tmp', {}, True, False, False, None)
|
||||
|
||||
self.assertFalse(mock_deploy_and_wait.called)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import uuid
|
|||
|
||||
from osc_lib.tests import utils
|
||||
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient.workflows import parameters
|
||||
|
||||
|
||||
|
@ -52,3 +53,99 @@ class TestParameterWorkflows(utils.TestCommand):
|
|||
'tripleo.plan_management.v1.get_passwords',
|
||||
workflow_input={'queue_name': 'UUID4',
|
||||
'container': 'container-name'})
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows(self, mock_open,
|
||||
mock_safe_load):
|
||||
plan_env_data = {
|
||||
'name': 'overcloud',
|
||||
'workflow_parameters': {
|
||||
'tripleo.derive_params.v1.derive_parameters': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
"execution": {"id": "IDID"},
|
||||
"status": "SUCCESS",
|
||||
"message": "",
|
||||
"result": {}
|
||||
}])
|
||||
|
||||
parameters.invoke_plan_env_workflows(
|
||||
self.app.client_manager,
|
||||
'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.derive_params.v1.derive_parameters',
|
||||
workflow_input={
|
||||
'plan': 'overcloud',
|
||||
'queue_name': 'UUID4',
|
||||
'user_inputs': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2}})
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflow_failed(self, mock_open,
|
||||
mock_safe_load):
|
||||
plan_env_data = {
|
||||
'name': 'overcloud',
|
||||
'workflow_parameters': {
|
||||
'tripleo.derive_params.v1.derive_parameters': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
"execution": {"id": "IDID"},
|
||||
"status": "FAILED",
|
||||
"message": "workflow failure",
|
||||
"result": ""
|
||||
}])
|
||||
|
||||
self.assertRaises(exceptions.PlanEnvWorkflowError,
|
||||
parameters.invoke_plan_env_workflows,
|
||||
self.app.client_manager, 'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.derive_params.v1.derive_parameters',
|
||||
workflow_input={
|
||||
'plan': 'overcloud',
|
||||
'queue_name': 'UUID4',
|
||||
'user_inputs': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2}})
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows_no_workflow_params(
|
||||
self, mock_open, mock_safe_load):
|
||||
plan_env_data = {'name': 'overcloud'}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
parameters.invoke_plan_env_workflows(
|
||||
self.app.client_manager,
|
||||
'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_not_called()
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows_no_plan_env_file(
|
||||
self, mock_open, mock_safe_load):
|
||||
|
||||
mock_open.side_effect = IOError('')
|
||||
|
||||
self.assertRaises(exceptions.PlanEnvWorkflowError,
|
||||
parameters.invoke_plan_env_workflows,
|
||||
self.app.client_manager, 'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_not_called()
|
||||
|
|
|
@ -193,7 +193,7 @@ class DeployOvercloud(command.Command):
|
|||
|
||||
def _heat_deploy(self, stack, stack_name, template_path, parameters,
|
||||
env_files, timeout, tht_root, env, update_plan_only,
|
||||
run_validations, skip_deploy_identifier):
|
||||
run_validations, skip_deploy_identifier, plan_env_file):
|
||||
"""Verify the Baremetal nodes are available and do a stack update"""
|
||||
|
||||
self.log.debug("Getting template contents from plan %s" % stack_name)
|
||||
|
@ -220,7 +220,14 @@ class DeployOvercloud(command.Command):
|
|||
self._process_and_upload_environment(
|
||||
stack_name, env, moved_files, tht_root)
|
||||
|
||||
# Invokes the workflows specified in plan environment file
|
||||
if plan_env_file:
|
||||
workflow_params.invoke_plan_env_workflows(self.clients,
|
||||
stack_name,
|
||||
plan_env_file)
|
||||
if not update_plan_only:
|
||||
print("Deploying templates in the directory {0}".format(
|
||||
os.path.abspath(tht_root)))
|
||||
deployment.deploy_and_wait(
|
||||
self.log, self.clients, stack,
|
||||
stack_name, self.app_args.verbose_level,
|
||||
|
@ -398,7 +405,7 @@ class DeployOvercloud(command.Command):
|
|||
self._download_missing_files_from_plan(
|
||||
tht_root, parsed_args.stack)
|
||||
|
||||
print("Deploying templates in the directory {0}".format(
|
||||
print("Processing templates in the directory {0}".format(
|
||||
os.path.abspath(tht_root)))
|
||||
|
||||
self.log.debug("Creating Environment files")
|
||||
|
@ -439,20 +446,23 @@ class DeployOvercloud(command.Command):
|
|||
self._try_overcloud_deploy_with_compat_yaml(
|
||||
tht_root, stack, parsed_args.stack, parameters, env_files,
|
||||
parsed_args.timeout, env, parsed_args.update_plan_only,
|
||||
parsed_args.run_validations, parsed_args.skip_deploy_identifier)
|
||||
parsed_args.run_validations, parsed_args.skip_deploy_identifier,
|
||||
parsed_args.plan_environment_file)
|
||||
|
||||
def _try_overcloud_deploy_with_compat_yaml(self, tht_root, stack,
|
||||
stack_name, parameters,
|
||||
env_files, timeout,
|
||||
env, update_plan_only,
|
||||
run_validations,
|
||||
skip_deploy_identifier):
|
||||
skip_deploy_identifier,
|
||||
plan_env_file):
|
||||
overcloud_yaml = os.path.join(tht_root, constants.OVERCLOUD_YAML_NAME)
|
||||
try:
|
||||
self._heat_deploy(stack, stack_name, overcloud_yaml,
|
||||
parameters, env_files, timeout,
|
||||
tht_root, env, update_plan_only,
|
||||
run_validations, skip_deploy_identifier)
|
||||
run_validations, skip_deploy_identifier,
|
||||
plan_env_file)
|
||||
except ClientException as e:
|
||||
messages = 'Failed to deploy: %s' % str(e)
|
||||
raise ValueError(messages)
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient.workflows import base
|
||||
|
||||
|
||||
|
@ -49,3 +53,50 @@ def get_overcloud_passwords(clients, **workflow_input):
|
|||
assert payload['status'] == "SUCCESS"
|
||||
|
||||
return payload['message']
|
||||
|
||||
|
||||
def invoke_plan_env_workflows(clients, stack_name, plan_env_file):
|
||||
"""Invokes the workflows in plan environment file"""
|
||||
|
||||
try:
|
||||
with open(plan_env_file) as pf:
|
||||
plan_env_data = yaml.safe_load(pf.read())
|
||||
except IOError as exc:
|
||||
raise exceptions.PlanEnvWorkflowError('File (%s) is not found: '
|
||||
'%s' % (plan_env_file, exc))
|
||||
|
||||
if plan_env_data and "workflow_parameters" in plan_env_data:
|
||||
for wf_name, wf_inputs in plan_env_data["workflow_parameters"].items():
|
||||
print('Invoking workflow (%s) specified in plan-environment '
|
||||
'file' % wf_name)
|
||||
inputs = {}
|
||||
inputs['plan'] = stack_name
|
||||
queue_name = str(uuid.uuid4())
|
||||
inputs['queue_name'] = queue_name
|
||||
inputs['user_inputs'] = wf_inputs
|
||||
workflow_client = clients.workflow_engine
|
||||
tripleoclients = clients.tripleoclient
|
||||
with tripleoclients.messaging_websocket(queue_name) as ws:
|
||||
execution = base.start_workflow(
|
||||
workflow_client,
|
||||
wf_name,
|
||||
workflow_input=inputs
|
||||
)
|
||||
|
||||
# Getting the derive parameters timeout after 600 seconds.
|
||||
for payload in base.wait_for_messages(workflow_client,
|
||||
ws, execution, 600):
|
||||
if ('message' in payload and
|
||||
(payload.get('status', 'RUNNING') == "RUNNING")):
|
||||
print(payload['message'])
|
||||
|
||||
if payload.get('status', 'FAILED') == 'SUCCESS':
|
||||
result = payload.get('result', '')
|
||||
# Prints the workflow result
|
||||
if result:
|
||||
print('Workflow execution is completed. result:')
|
||||
print(yaml.safe_dump(result, default_flow_style=False))
|
||||
else:
|
||||
message = payload.get('message', '')
|
||||
msg = ('Workflow execution is failed: %s' % (message))
|
||||
raise exceptions.PlanEnvWorkflowError(msg)
|
||||
|
|
Loading…
Reference in New Issue