Implement check flavors as a custom action

Adds a custom action to check the flavors passed against the defined
flavors.

Change-Id: Ia0751ec4c63692ceda97d3845edcde1c26f9a3a8
Partial-Bug: #1638697
This commit is contained in:
Brad P. Crochet 2017-02-01 11:11:36 -05:00
parent bc99748ff0
commit c33b81aa31
5 changed files with 231 additions and 0 deletions

View File

@ -0,0 +1,4 @@
---
features:
- Adds an action and workflow used to check the status of
the defined and passed flavors in Nova.

View File

@ -96,6 +96,7 @@ mistral.actions =
tripleo.templates.process = tripleo_common.actions.templates:ProcessTemplatesAction
tripleo.templates.upload = tripleo_common.actions.templates:UploadTemplatesAction
tripleo.validations.check_boot_images = tripleo_common.actions.validations:CheckBootImagesAction
tripleo.validations.check_flavors = tripleo_common.actions.validations:CheckFlavorsAction
tripleo.validations.get_pubkey = tripleo_common.actions.validations:GetPubkeyAction
tripleo.validations.enabled = tripleo_common.actions.validations:Enabled
tripleo.validations.list_groups = tripleo_common.actions.validations:ListGroupsAction

View File

@ -173,3 +173,70 @@ class CheckBootImagesAction(base.TripleOAction):
image_id = found_images[0]
return image_id
class CheckFlavorsAction(base.TripleOAction):
"""Validate and collect nova flavors in use.
Ensure that selected flavors (--ROLE-flavor) are valid in nova.
Issue a warning if local boot is not set for a flavor.
"""
# TODO(bcrochet): The validation actions are temporary. This logic should
# move to the tripleo-validations project eventually.
def __init__(self, flavors, roles_info):
super(CheckFlavorsAction, self).__init__()
self.flavors = flavors
self.roles_info = roles_info
def run(self):
"""Validate and collect nova flavors in use.
Ensure that selected flavors (--ROLE-flavor) are valid in nova.
Issue a warning if local boot is not set for a flavor.
:returns: dictionary flavor name -> (flavor object, scale)
"""
flavors = {f['name']: f for f in self.flavors}
result = {}
warnings = []
errors = []
message = "Flavor '{1}' provided for the role '{0}', does not exist"
for target, (flavor_name, scale) in self.roles_info.items():
if flavor_name is None or not scale:
continue
old_flavor_name, old_scale = result.get(flavor_name, (None, None))
if old_flavor_name:
result[flavor_name] = (old_flavor_name, old_scale + scale)
else:
flavor = flavors.get(flavor_name)
if flavor:
if flavor.get('capabilities:boot_option', '') == 'netboot':
warnings.append(
'Flavor %s "capabilities:boot_option" is set to '
'"netboot". Nodes will PXE boot from the ironic '
'conductor instead of using a local bootloader. '
'Make sure that enough nodes are marked with the '
'"boot_option" capability set to "netboot".'
% flavor_name)
result[flavor_name] = (flavor, scale)
else:
errors.append(message.format(target, flavor_name))
return_value = {
'flavors': result,
'errors': errors,
'warnings': warnings,
}
if errors:
mistral_result = {'error': return_value}
else:
mistral_result = {'data': return_value}
return mistral_workflow_utils.Result(**mistral_result)

View File

@ -283,3 +283,100 @@ class TestCheckBootImagesAction(base.TestCase):
expected, action._check_for_image(deploy_ramdisk_name, messages))
self.assertEqual(1, len(messages))
self.assertIn(expected_message, messages)
class TestCheckFlavorsAction(base.TestCase):
def setUp(self):
super(TestCheckFlavorsAction, self).setUp()
self.flavors = [
{'name': 'flavor1', 'capabilities:boot_option': 'local'},
{'name': 'flavor2', 'capabilities:boot_option': 'netboot'},
{'name': 'flavor3'}
]
def test_run_success(self):
roles_info = {
'role1': ('flavor1', 1),
}
expected = mistral_workflow_utils.Result(
data={
'flavors': {
'flavor1': (
{
'name': 'flavor1',
'capabilities:boot_option': 'local'
}, 1)
},
'warnings': [],
'errors': [],
}
)
action_args = {
'flavors': self.flavors,
'roles_info': roles_info
}
action = validations.CheckFlavorsAction(**action_args)
self.assertEqual(expected, action.run())
def test_run_boot_option_is_netboot(self):
roles_info = {
'role2': ('flavor2', 1),
'role3': ('flavor3', 1),
}
expected = mistral_workflow_utils.Result(
data={
'flavors': {
'flavor2': (
{
'name': 'flavor2',
'capabilities:boot_option': 'netboot'
}, 1),
'flavor3': (
{
'name': 'flavor3',
}, 1),
},
'warnings': [
('Flavor %s "capabilities:boot_option" is set to '
'"netboot". Nodes will PXE boot from the ironic '
'conductor instead of using a local bootloader. Make '
'sure that enough nodes are marked with the '
'"boot_option" capability set to "netboot".' % 'flavor2')
],
'errors': []
}
)
action_args = {
'flavors': self.flavors,
'roles_info': roles_info
}
action = validations.CheckFlavorsAction(**action_args)
result = action.run()
self.assertEqual(expected, result)
def test_run_flavor_does_not_exist(self):
roles_info = {
'role4': ('does_not_exist', 1),
}
expected = mistral_workflow_utils.Result(
error={
'errors': [
"Flavor '%s' provided for the role '%s', does not "
"exist" % ('does_not_exist', 'role4')
],
'warnings': [],
'flavors': {},
}
)
action_args = {
'flavors': self.flavors,
'roles_info': roles_info
}
action = validations.CheckFlavorsAction(**action_args)
self.assertEqual(expected, action.run())

View File

@ -316,3 +316,65 @@ workflows:
warnings: <% $.warnings %>
on-success:
- fail: <% $.get('status') = "FAILED" %>
collect_flavors:
input:
- roles_info: {}
- run_validations: true
- queue_name: tripleo
output:
errors: <% $.errors %>
warnings: <% $.warnings %>
flavors: <% $.flavors %>
tasks:
check_run_validations:
on-complete:
- get_flavors: <% $.run_validations %>
- send_message: <% not $.run_validations %>
get_flavors:
action: nova.flavors_list
on-success: check_flavors
publish:
nova_flavors: <% task(get_flavors).result %>
check_flavors:
action: tripleo.validations.check_flavors
input:
flavors: <% $.nova_flavors %>
roles_info: <% $.roles_info %>
on-success: send_message
on-error: fail_check_flavors
publish:
flavors: <% task(check_flavors).result.flavors %>
errors: <% task(check_flavors).result.errors %>
warnings: <% task(check_flavors).result.warnings %>
publish-on-error:
flavors: {}
errors: <% task(check_flavors).result.errors %>
warnings: <% task(check_flavors).result.warnings %>
fail_check_flavors:
on-success: send_message
publish:
status: FAILED
message: <% task(check_flavors).result %>
send_message:
action: zaqar.queue_post
retry: count=5 delay=1
input:
queue_name: <% $.queue_name %>
messages:
body:
type: tripleo.validations.v1.collect_flavors
payload:
status: <% $.get('status', 'SUCCESS') %>
message: <% $.get('message', '') %>
execution: <% execution() %>
flavors: <% $.flavors %>
errors: <% $.errors %>
warnings: <% $.warnings %>
on-success:
- fail: <% $.get('status') = "FAILED" %>