Implement check ironic boot configuration as a custom action

Add a custom action and workflow for checking the configuration of the
available Ironic nodes.

Change-Id: I74d1c242d0d06c5aff8f6d3533399745d6d8d9f3
Partial-Bug: #1638697
This commit is contained in:
Brad P. Crochet 2017-02-02 10:47:54 -05:00
parent c33b81aa31
commit f6f1ac9838
5 changed files with 206 additions and 0 deletions

View File

@ -0,0 +1,4 @@
---
features:
- Adds an action and workflow used to check the ironic boot
configuration.

View File

@ -97,6 +97,7 @@ mistral.actions =
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.check_node_boot_configuration = tripleo_common.actions.validations:CheckNodeBootConfigurationAction
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

@ -18,6 +18,7 @@ from oslo_concurrency.processutils import ProcessExecutionError
from tripleo_common.actions import base
from tripleo_common import constants
from tripleo_common.utils import nodes as nodeutils
from tripleo_common.utils import passwords as password_utils
from tripleo_common.utils import validations as utils
@ -240,3 +241,57 @@ class CheckFlavorsAction(base.TripleOAction):
mistral_result = {'data': return_value}
return mistral_workflow_utils.Result(**mistral_result)
class CheckNodeBootConfigurationAction(base.TripleOAction):
"""Check the boot configuration of the baremetal nodes"""
# TODO(bcrochet): The validation actions are temporary. This logic should
# move to the tripleo-validations project eventually.
def __init__(self, node, kernel_id, ramdisk_id):
super(CheckNodeBootConfigurationAction, self).__init__()
self.node = node
self.kernel_id = kernel_id
self.ramdisk_id = ramdisk_id
def run(self):
warnings = []
errors = []
message = ("Node {uuid} has an incorrectly configured "
"{property}. Expected \"{expected}\" but got "
"\"{actual}\".")
if self.node['driver_info'].get('deploy_ramdisk') != self.ramdisk_id:
errors.append(message.format(
uuid=self.node['uuid'],
property='driver_info/deploy_ramdisk',
expected=self.ramdisk_id,
actual=self.node['driver_info'].get('deploy_ramdisk')
))
if self.node['driver_info'].get('deploy_kernel') != self.kernel_id:
errors.append(message.format(
uuid=self.node['uuid'],
property='driver_info/deploy_kernel',
expected=self.kernel_id,
actual=self.node['driver_info'].get('deploy_kernel')
))
capabilities = nodeutils.capabilities_to_dict(
self.node['properties'].get('capabilities', ''))
if capabilities.get('boot_option') != 'local':
boot_option_message = ("Node {uuid} is not configured to use "
"boot_option:local in capabilities. It "
"will not be used for deployment with "
"flavors that require boot_option:local.")
warnings.append(boot_option_message.format(uuid=self.node['uuid']))
return_value = {
'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

@ -380,3 +380,78 @@ class TestCheckFlavorsAction(base.TestCase):
}
action = validations.CheckFlavorsAction(**action_args)
self.assertEqual(expected, action.run())
class TestCheckNodeBootConfigurationAction(base.TestCase):
def setUp(self):
super(TestCheckNodeBootConfigurationAction, self).setUp()
self.kernel_id = '12345'
self.ramdisk_id = '67890'
self.node = {
'uuid': '100f2cf6-06de-480e-a73e-6fdf6c9962b7',
'driver_info': {
'deploy_kernel': '12345',
'deploy_ramdisk': '67890',
},
'properties': {
'capabilities': 'boot_option:local',
}
}
def test_run_success(self):
expected = mistral_workflow_utils.Result(
data={'errors': [], 'warnings': []}
)
action_args = {
'node': self.node,
'kernel_id': self.kernel_id,
'ramdisk_id': self.ramdisk_id,
}
action = validations.CheckNodeBootConfigurationAction(**action_args)
self.assertEqual(expected, action.run())
def test_run_invalid_ramdisk(self):
expected = mistral_workflow_utils.Result(
error={
'errors': [
'Node 100f2cf6-06de-480e-a73e-6fdf6c9962b7 has an '
'incorrectly configured driver_info/deploy_ramdisk. '
'Expected "67890" but got "98760".'
],
'warnings': []})
node = self.node.copy()
node['driver_info']['deploy_ramdisk'] = '98760'
action_args = {
'node': node,
'kernel_id': self.kernel_id,
'ramdisk_id': self.ramdisk_id,
}
action = validations.CheckNodeBootConfigurationAction(**action_args)
self.assertEqual(expected, action.run())
def test_no_boot_option_local(self):
expected = mistral_workflow_utils.Result(
data={
'errors': [],
'warnings': [
'Node 100f2cf6-06de-480e-a73e-6fdf6c9962b7 is not '
'configured to use boot_option:local in capabilities. '
'It will not be used for deployment with flavors that '
'require boot_option:local.'
]
}
)
node = self.node.copy()
node['properties']['capabilities'] = 'boot_option:not_local'
action_args = {
'node': node,
'kernel_id': self.kernel_id,
'ramdisk_id': self.ramdisk_id,
}
action = validations.CheckNodeBootConfigurationAction(**action_args)
self.assertEqual(expected, action.run())

View File

@ -378,3 +378,74 @@ workflows:
warnings: <% $.warnings %>
on-success:
- fail: <% $.get('status') = "FAILED" %>
check_ironic_boot_configuration:
input:
- kernel_id: null
- ramdisk_id: null
- run_validations: true
- queue_name: tripleo
output:
errors: <% $.errors %>
warnings: <% $.warnings %>
tasks:
check_run_validations:
on-complete:
- get_ironic_nodes: <% $.run_validations %>
- send_message: <% not $.run_validations %>
get_ironic_nodes:
action: ironic.node_list
on-success: check_node_boot_configuration
on-error: failed_get_ironic_nodes
input:
maintenance: false
detail: true
publish:
nodes: <% task(get_ironic_nodes).result %>
failed_get_ironic_nodes:
on-success: send_message
publish:
status: FAILED
message: <% task(get_ironic_nodes).result %>
check_node_boot_configuration:
action: tripleo.validations.check_node_boot_configuration
input:
node: <% $.node %>
kernel_id: <% $.kernel_id %>
ramdisk_id: <% $.ramdisk_id %>
with-items: node in <% $.nodes %>
on-success: send_message
on-error: fail_check_node_boot_configuration
publish:
errors: <% task(check_node_boot_configuration).result.errors.flatten() %>
warnings: <% task(check_node_boot_configuration).result.warnings.flatten() %>
publish-on-error:
errors: <% task(check_node_boot_configuration).result.errors.flatten() %>
warnings: <% task(check_node_boot_configuration).result.warnings.flatten() %>
fail_check_node_boot_configuration:
on-success: send_message
publish:
status: FAILED
message: <% task(check_node_boot_configuration).result %>
send_message:
action: zaqar.queue_post
retry: count=5 delay=1
input:
queue_name: <% $.queue_name %>
messages:
body:
type: tripleo.validations.v1.check_ironic_boot_configuration
payload:
status: <% $.get('status', 'SUCCESS') %>
message: <% $.get('message', '') %>
execution: <% execution() %>
errors: <% $.errors %>
warnings: <% $.warnings %>
on-success:
- fail: <% $.get('status') = "FAILED" %>