Merge "Invokes specified workflows in plan env file"

This commit is contained in:
Jenkins 2017-07-07 17:30:20 +00:00 committed by Gerrit Code Review
commit ce1a06fb56
6 changed files with 191 additions and 14 deletions

View File

@ -0,0 +1,5 @@
---
features:
- Workflows, associated with plan-environment.yaml using
workflow_parameters, are invoked before the actual
deployment starts.

View File

@ -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"""

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)