Derive Params (part 1): Initial workflow to start
This initial workflow which starts the derive parameters workflow is responsible to get the list of role names from the flattened heat resource tree. Once the role names list is obtained, derive parameters per role workflow is invoked to get introspection data of first matching node for all role names. Implements: blueprint tripleo-derive-parameters Co-Authored-By: Jaganathan Palanisamy <jpalanis@redhat.com> Co-Authored-By: Alan Bishop <abishop@redhat.com> Change-Id: I113f3e6f67c7dbdad74264afb17dfca0612008c4
This commit is contained in:
parent
58a385a238
commit
6bdc5fafb0
@ -86,6 +86,7 @@ mistral.actions =
|
||||
tripleo.parameters.update_role = tripleo_common.actions.parameters:UpdateRoleParametersAction
|
||||
tripleo.parameters.generate_passwords = tripleo_common.actions.parameters:GeneratePasswordsAction
|
||||
tripleo.parameters.get_passwords = tripleo_common.actions.parameters:GetPasswordsAction
|
||||
tripleo.parameters.get_profile_of_flavor = tripleo_common.actions.parameters:GetProfileOfFlavorAction
|
||||
tripleo.parameters.generate_fencing = tripleo_common.actions.parameters:GenerateFencingParametersAction
|
||||
tripleo.plan.create = tripleo_common.actions.plan:CreatePlanAction
|
||||
tripleo.plan.update = tripleo_common.actions.plan:UpdatePlanAction
|
||||
|
@ -35,6 +35,7 @@ from mistral.workflow import utils as mistral_workflow_utils
|
||||
from tripleo_common.actions import base
|
||||
from tripleo_common.actions import templates
|
||||
from tripleo_common import constants
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.utils import nodes
|
||||
from tripleo_common.utils import parameters
|
||||
from tripleo_common.utils import passwords as password_utils
|
||||
@ -396,3 +397,29 @@ class GetFlattenedParametersAction(GetParametersAction):
|
||||
processed_data['heat_resource_tree'] = flattened
|
||||
|
||||
return processed_data
|
||||
|
||||
|
||||
class GetProfileOfFlavorAction(base.TripleOAction):
|
||||
"""Gets the profile name for a given flavor name.
|
||||
|
||||
Need flavor object to get profile name since get_keys method is
|
||||
not available for external access. so we have created an action
|
||||
to get profile name from flavor name.
|
||||
|
||||
:param flavor_name: Flavor name
|
||||
|
||||
:return: profile name
|
||||
"""
|
||||
|
||||
def __init__(self, flavor_name):
|
||||
super(GetProfileOfFlavorAction, self).__init__()
|
||||
self.flavor_name = flavor_name
|
||||
|
||||
def run(self, context):
|
||||
compute_client = self.get_compute_client(context)
|
||||
try:
|
||||
return parameters.get_profile_of_flavor(self.flavor_name,
|
||||
compute_client)
|
||||
except exception.DeriveParamsError as err:
|
||||
LOG.error('Derive Params Error: %s', err)
|
||||
return mistral_workflow_utils.Result(error=str(err))
|
||||
|
@ -106,3 +106,7 @@ class RootDeviceDetectionError(Exception):
|
||||
|
||||
class PlanOperationError(Exception):
|
||||
"""Error while performing a deployment plan operation"""
|
||||
|
||||
|
||||
class DeriveParamsError(Exception):
|
||||
"""Error while performing a derive parameters operation"""
|
||||
|
@ -18,6 +18,7 @@ from swiftclient import exceptions as swiftexceptions
|
||||
|
||||
from tripleo_common.actions import parameters
|
||||
from tripleo_common import constants
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.tests import base
|
||||
|
||||
_EXISTING_PASSWORDS = {
|
||||
@ -823,3 +824,33 @@ class GetFlattenedParametersActionTest(base.TestCase):
|
||||
action = parameters.GetFlattenedParametersAction()
|
||||
result = action.run(mock_ctx)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
|
||||
class GetProfileOfFlavorActionTest(base.TestCase):
|
||||
|
||||
@mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
|
||||
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||
'get_compute_client')
|
||||
@mock.patch('mistral.context.ctx')
|
||||
def test_profile_found(self, mock_ctx, mock_get_compute_client,
|
||||
mock_get_profile_of_flavor):
|
||||
mock_ctx = mock.MagicMock()
|
||||
mock_get_profile_of_flavor.return_value = 'compute'
|
||||
action = parameters.GetProfileOfFlavorAction('oooq_compute')
|
||||
result = action.run(mock_ctx)
|
||||
expected_result = "compute"
|
||||
self.assertEqual(result, expected_result)
|
||||
|
||||
@mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
|
||||
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||
'get_compute_client')
|
||||
@mock.patch('mistral.context.ctx')
|
||||
def test_profile_not_found(self, mock_ctx, mock_get_compute_client,
|
||||
mock_get_profile_of_flavor):
|
||||
mock_ctx = mock.MagicMock()
|
||||
profile = (exception.DeriveParamsError, )
|
||||
mock_get_profile_of_flavor.side_effect = profile
|
||||
action = parameters.GetProfileOfFlavorAction('no_profile')
|
||||
result = action.run(mock_ctx)
|
||||
self.assertTrue(result.is_error())
|
||||
mock_get_profile_of_flavor.assert_called_once()
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.utils import parameters
|
||||
|
||||
@ -121,3 +122,68 @@ class ParametersTest(base.TestCase):
|
||||
# the 'compute' flavor.
|
||||
self.assertEqual(parameters.get_flavor('compute', compute_client),
|
||||
'compute')
|
||||
|
||||
def test_profile_flavor_found(self):
|
||||
compute_client = mock.MagicMock()
|
||||
|
||||
# Mock for a compute_client.flavors.find result item
|
||||
flavor = mock.MagicMock()
|
||||
flavor.id = 1
|
||||
flavor.name = 'oooq_compute'
|
||||
|
||||
# Mock result of <flavor instance>.get_keys()
|
||||
flavor_keys = mock.MagicMock()
|
||||
flavor_keys.get.side_effect = ('compute', )
|
||||
|
||||
# Connecting the mock instances...
|
||||
flavor.get_keys.side_effect = (flavor_keys, )
|
||||
compute_client.flavors.find.side_effect = (flavor, )
|
||||
|
||||
# Calling `get_profile_of_flavor` with a 'oooq_compute' flavor
|
||||
# should return profile 'compute'.
|
||||
profile = parameters.get_profile_of_flavor('oooq_compute',
|
||||
compute_client)
|
||||
self.assertEqual(profile, 'compute')
|
||||
|
||||
def test_profile_flavor_not_found_exception(self):
|
||||
compute_client = mock.MagicMock()
|
||||
flavor = (Exception, )
|
||||
compute_client.flavors.find.side_effect = flavor
|
||||
|
||||
# Calling `get_profile_of_flavor` with a 'oooq_compute' flavor
|
||||
# should raises DeriveParamsError exception
|
||||
self.assertRaises(exception.DeriveParamsError,
|
||||
parameters.get_profile_of_flavor,
|
||||
'oooq_compute', compute_client)
|
||||
|
||||
def test_profile_flavor_not_found(self):
|
||||
compute_client = mock.MagicMock()
|
||||
compute_client.flavors.find.return_value = None
|
||||
|
||||
# Calling `get_profile_of_flavor` with a 'oooq_compute' flavor
|
||||
# should raises DeriveParamsError exception
|
||||
self.assertRaises(exception.DeriveParamsError,
|
||||
parameters.get_profile_of_flavor,
|
||||
'oooq_compute', compute_client)
|
||||
|
||||
def test_profile_not_found_flavor_found(self):
|
||||
compute_client = mock.MagicMock()
|
||||
|
||||
# Mock for a compute_client.flavors.find result item
|
||||
flavor = mock.MagicMock()
|
||||
flavor.id = 1
|
||||
flavor.name = 'oooq_compute'
|
||||
|
||||
# Mock result of <flavor instance>.get_keys()
|
||||
flavor_keys = mock.MagicMock()
|
||||
flavor_keys.get.side_effect = (exception.DeriveParamsError, )
|
||||
|
||||
# Connecting the mock instances...
|
||||
flavor.get_keys.side_effect = (flavor_keys, )
|
||||
compute_client.flavors.find.side_effect = (flavor, )
|
||||
|
||||
# Calling `get_profile_of_flavor` with a 'no_profile' flavor
|
||||
# should raises DeriveParamsError exception
|
||||
self.assertRaises(exception.DeriveParamsError,
|
||||
parameters.get_profile_of_flavor,
|
||||
'no_profile', compute_client)
|
||||
|
@ -14,6 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.utils import nodes
|
||||
|
||||
|
||||
@ -91,3 +92,34 @@ def set_count_and_flavor_params(role, baremetal_client, compute_client):
|
||||
_get_count_key(role): node_count,
|
||||
_get_flavor_key(role): flavor
|
||||
}
|
||||
|
||||
|
||||
def get_profile_of_flavor(flavor_name, compute_client):
|
||||
"""Returns profile name for a given flavor name.
|
||||
|
||||
:param flavor_name: Flavor name
|
||||
:param compute_client: Compute client object
|
||||
:raises: exception.DeriveParamsError: Derive parameters error
|
||||
|
||||
:return: profile name
|
||||
"""
|
||||
|
||||
try:
|
||||
flavor = compute_client.flavors.find(name=flavor_name)
|
||||
except Exception as err:
|
||||
raise exception.DeriveParamsError(
|
||||
'Unable to determine flavor for flavor name: '
|
||||
'%(flavor_name)s. Error:%(err)s' % {'flavor_name': flavor_name,
|
||||
'err': err})
|
||||
if flavor:
|
||||
profile = flavor.get_keys().get('capabilities:profile', '')
|
||||
if profile:
|
||||
return profile
|
||||
else:
|
||||
raise exception.DeriveParamsError(
|
||||
'Unable to determine profile for flavor (flavor name: '
|
||||
'%s)' % flavor_name)
|
||||
else:
|
||||
raise exception.DeriveParamsError(
|
||||
'Unable to determine flavor for flavor name: '
|
||||
'%s' % flavor_name)
|
||||
|
181
workbooks/derive_params.yaml
Normal file
181
workbooks/derive_params.yaml
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
version: '2.0'
|
||||
name: tripleo.derive_params.v1
|
||||
description: TripleO Workflows to derive deployment parameters from the introspected data
|
||||
|
||||
workflows:
|
||||
|
||||
derive_parameters:
|
||||
description: The main workflow for deriving parameters from the introspected data
|
||||
|
||||
input:
|
||||
- plan: overcloud
|
||||
- queue_name: tripleo
|
||||
|
||||
tasks:
|
||||
get_flattened_parameters:
|
||||
action: tripleo.parameters.get_flatten container=<% $.plan %>
|
||||
publish:
|
||||
environment_parameters: <% task().result.mistral_environment_parameters %>
|
||||
heat_resource_tree: <% task().result.heat_resource_tree %>
|
||||
on-success:
|
||||
- get_roles: <% $.environment_parameters and $.heat_resource_tree %>
|
||||
- set_status_failed_get_flattened_parameters: <% (not $.environment_parameters) or (not $.heat_resource_tree) %>
|
||||
on-error: set_status_failed_get_flattened_parameters
|
||||
|
||||
get_roles:
|
||||
action: tripleo.role.list container=<% $.plan %>
|
||||
publish:
|
||||
role_name_list: <% task().result %>
|
||||
on-success:
|
||||
- get_valid_roles: <% $.role_name_list %>
|
||||
- set_status_failed_get_roles: <% not $.role_name_list %>
|
||||
on-error: set_status_failed_on_error_get_roles
|
||||
|
||||
# Obtain only the roles which has count > 0, by checking <RoleName>Count parameter, like ComputeCount
|
||||
get_valid_roles:
|
||||
publish:
|
||||
valid_role_name_list: <% let(hr => $.heat_resource_tree.parameters) -> $.role_name_list.where(int($hr.get(concat($, 'Count'), {}).get('default', 0)) > 0) %>
|
||||
on-success:
|
||||
- for_each_role: <% $.valid_role_name_list %>
|
||||
- set_status_failed_get_valid_roles: <% not $.valid_role_name_list %>
|
||||
|
||||
# Execute the basic preparation workflow for each role to get introspection data
|
||||
for_each_role:
|
||||
with-items: role_name in <% $.valid_role_name_list %>
|
||||
concurrency: 1
|
||||
workflow: _derive_parameters_per_role
|
||||
input:
|
||||
role_name: <% $.role_name %>
|
||||
environment_parameters: <% $.environment_parameters %>
|
||||
heat_resource_tree: <% $.heat_resource_tree %>
|
||||
on-success: send_message
|
||||
on-error: set_status_failed_for_each_role
|
||||
|
||||
set_status_failed_get_flattened_parameters:
|
||||
on-success: send_message
|
||||
publish:
|
||||
status: FAILED
|
||||
message: <% task(get_flattened_parameters).result %>
|
||||
|
||||
set_status_failed_get_roles:
|
||||
on-success: send_message
|
||||
publish:
|
||||
status: FAILED
|
||||
message: "Unable to determine the list of roles in the deployment plan"
|
||||
|
||||
set_status_failed_on_error_get_roles:
|
||||
on-success: send_message
|
||||
publish:
|
||||
status: FAILED
|
||||
message: <% task(get_roles).result %>
|
||||
|
||||
set_status_failed_get_valid_roles:
|
||||
on-success: send_message
|
||||
publish:
|
||||
status: FAILED
|
||||
message: 'Unable to determine the list of valid roles in the deployment plan.'
|
||||
|
||||
set_status_failed_for_each_role:
|
||||
on-success: send_message
|
||||
publish:
|
||||
status: FAILED
|
||||
message: <% task(for_each_role).result.select(dict('role_name' => $.role_name, 'status' => $.get('status', 'SUCCESS'), 'message' => $.get('message', ''))) %>
|
||||
|
||||
send_message:
|
||||
action: zaqar.queue_post
|
||||
retry: count=5 delay=1
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
messages:
|
||||
body:
|
||||
type: tripleo.derive_params.v1.derive_parameters
|
||||
payload:
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
on-success:
|
||||
- fail: <% $.get('status') = 'FAILED' %>
|
||||
|
||||
|
||||
_derive_parameters_per_role:
|
||||
description: >
|
||||
Workflow which runs per role to get the introspection data on the first matching node assigned to role.
|
||||
Once introspection data is fetched, this worklow will trigger the actual derive parameters workflow
|
||||
input:
|
||||
- role_name
|
||||
- environment_parameters
|
||||
- heat_resource_tree
|
||||
|
||||
tasks:
|
||||
# Getting introspection data workflow, which will take care of
|
||||
# 1) profile and flavor based mapping
|
||||
# 2) Nova placement api based mapping
|
||||
# Currently we have implemented profile and flavor based mapping
|
||||
# TODO-Nova placement api based mapping is pending, we will enchance it later.
|
||||
get_flavor_name:
|
||||
publish:
|
||||
flavor_name: <% let(param_name => concat('Overcloud', $.role_name, 'Flavor').replace('OvercloudControllerFlavor', 'OvercloudControlFlavor')) -> $.heat_resource_tree.parameters.get($param_name, {}).get('default', '') %>
|
||||
on-success:
|
||||
- get_profile_name: <% $.flavor_name %>
|
||||
- set_status_failed_get_flavor_name: <% not $.flavor_name %>
|
||||
|
||||
get_profile_name:
|
||||
action: tripleo.parameters.get_profile_of_flavor flavor_name=<% $.flavor_name %>
|
||||
publish:
|
||||
profile_name: <% task().result %>
|
||||
on-success: get_profile_node
|
||||
on-error: set_status_failed_get_profile_name
|
||||
|
||||
get_profile_node:
|
||||
workflow: tripleo.baremetal.v1.nodes_with_profile
|
||||
input:
|
||||
profile: <% $.profile_name %>
|
||||
publish:
|
||||
profile_node_uuid: <% task().result.matching_nodes.first('') %>
|
||||
on-success:
|
||||
- get_introspection_data: <% $.profile_node_uuid %>
|
||||
- set_status_failed_no_matching_node_get_profile_node: <% not $.profile_node_uuid %>
|
||||
on-error: set_status_failed_on_error_get_profile_node
|
||||
|
||||
get_introspection_data:
|
||||
on-error: set_status_failed_get_introspection_data
|
||||
action: baremetal_introspection.get_data uuid=<% $.profile_node_uuid %>
|
||||
publish:
|
||||
introspection_data: <% task().result %>
|
||||
# TODO-Follow up patches workflows will actually be used here to derive parameters for each role
|
||||
|
||||
set_status_failed_get_flavor_name:
|
||||
publish:
|
||||
role_name: <% $.role_name %>
|
||||
status: FAILED
|
||||
message: <% "Unable to determine flavor for role '{0}'".format($.role_name) %>
|
||||
on-success: fail
|
||||
|
||||
set_status_failed_get_profile_name:
|
||||
publish:
|
||||
role_name: <% $.role_name %>
|
||||
status: FAILED
|
||||
message: <% task(get_profile_name).result %>
|
||||
on-success: fail
|
||||
|
||||
set_status_failed_no_matching_node_get_profile_node:
|
||||
publish:
|
||||
role_name: <% $.role_name %>
|
||||
status: FAILED
|
||||
message: <% "Unable to determine matching node for profile '{0}'".format($.profile_name) %>
|
||||
on-success: fail
|
||||
|
||||
set_status_failed_on_error_get_profile_node:
|
||||
publish:
|
||||
role_name: <% $.role_name %>
|
||||
status: FAILED
|
||||
message: <% task(get_profile_node).result %>
|
||||
on-success: fail
|
||||
|
||||
set_status_failed_get_introspection_data:
|
||||
publish:
|
||||
role_name: <% $.role_name %>
|
||||
status: FAILED
|
||||
message: <% task(get_introspection_data).result %>
|
||||
on-success: fail
|
Loading…
Reference in New Issue
Block a user