Merge "Remove validations that moved to tripleo-validations"
This commit is contained in:
commit
78aa4bfa09
@ -129,17 +129,12 @@ mistral.actions =
|
||||
tripleo.templates.process = tripleo_common.actions.templates:ProcessTemplatesAction
|
||||
tripleo.templates.upload = tripleo_common.actions.templates:UploadTemplatesAction
|
||||
tripleo.templates.upload_plan_environment = tripleo_common.actions.templates:UploadPlanEnvironmentAction
|
||||
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.check_nodes_count = tripleo_common.actions.validations:CheckNodesCountAction
|
||||
tripleo.validations.get_pubkey = tripleo_common.actions.validations:GetPubkeyAction
|
||||
tripleo.validations.get_privkey = tripleo_common.actions.validations:GetPrivkeyAction
|
||||
tripleo.validations.enabled = tripleo_common.actions.validations:Enabled
|
||||
tripleo.validations.list_groups = tripleo_common.actions.validations:ListGroupsAction
|
||||
tripleo.validations.list_validations = tripleo_common.actions.validations:ListValidationsAction
|
||||
tripleo.validations.run_validation = tripleo_common.actions.validations:RunValidationAction
|
||||
tripleo.validations.verify_profiles = tripleo_common.actions.validations:VerifyProfilesAction
|
||||
tripleo.validations.upload = tripleo_common.actions.validations:UploadValidationsAction
|
||||
tripleo.files.file_exists = tripleo_common.actions.files:FileExists
|
||||
tripleo.files.make_temp_dir = tripleo_common.actions.files:MakeTempDir
|
||||
|
@ -18,7 +18,6 @@ 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
|
||||
|
||||
@ -141,347 +140,3 @@ class UploadValidationsAction(base.UploadDirectoryAction):
|
||||
def __init__(self, container=constants.VALIDATIONS_CONTAINER_NAME,
|
||||
dir_to_upload=constants.DEFAULT_VALIDATIONS_PATH):
|
||||
super(UploadValidationsAction, self).__init__(container, dir_to_upload)
|
||||
|
||||
|
||||
class CheckBootImagesAction(base.TripleOAction):
|
||||
"""Validate boot images"""
|
||||
|
||||
# TODO(bcrochet): The validation actions are temporary. This logic should
|
||||
# move to the tripleo-validations project eventually.
|
||||
def __init__(self, images,
|
||||
deploy_kernel_name=constants.DEFAULT_DEPLOY_KERNEL_NAME,
|
||||
deploy_ramdisk_name=constants.DEFAULT_DEPLOY_RAMDISK_NAME):
|
||||
super(CheckBootImagesAction, self).__init__()
|
||||
self.images = images
|
||||
self.deploy_kernel_name = deploy_kernel_name
|
||||
self.deploy_ramdisk_name = deploy_ramdisk_name
|
||||
|
||||
def run(self, context):
|
||||
messages = []
|
||||
kernel_id = self._check_for_image(self.deploy_kernel_name, messages)
|
||||
ramdisk_id = self._check_for_image(self.deploy_ramdisk_name, messages)
|
||||
|
||||
return_value = {
|
||||
'kernel_id': kernel_id,
|
||||
'ramdisk_id': ramdisk_id,
|
||||
'errors': messages,
|
||||
'warnings': []
|
||||
}
|
||||
|
||||
if messages:
|
||||
mistral_result = actions.Result(error=return_value)
|
||||
else:
|
||||
mistral_result = actions.Result(data=return_value)
|
||||
|
||||
return mistral_result
|
||||
|
||||
def _check_for_image(self, name, messages):
|
||||
multiple_message = ("Please make sure there is only one image named "
|
||||
"'{}' in glance.")
|
||||
missing_message = ("No image with the name '{}' found - make sure you "
|
||||
"have uploaded boot images.")
|
||||
|
||||
image_id = None
|
||||
found_images = [item['id'] for item in self.images
|
||||
if item['name'] == name]
|
||||
if len(found_images) > 1:
|
||||
messages.append(multiple_message.format(name))
|
||||
elif len(found_images) == 0:
|
||||
messages.append(missing_message.format(name))
|
||||
else:
|
||||
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, roles_info):
|
||||
super(CheckFlavorsAction, self).__init__()
|
||||
self.roles_info = roles_info
|
||||
|
||||
def run(self, context):
|
||||
"""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)
|
||||
"""
|
||||
compute_client = self.get_compute_client(context)
|
||||
flavors = {f.name: {'name': f.name, 'keys': f.get_keys()}
|
||||
for f in compute_client.flavors.list()}
|
||||
|
||||
result = {}
|
||||
warnings = []
|
||||
errors = []
|
||||
|
||||
message = "Flavor '{1}' provided for the role '{0}', does not exist"
|
||||
warning_message = (
|
||||
'Flavor {0} "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".')
|
||||
|
||||
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:
|
||||
keys = flavor.get('keys', None)
|
||||
if keys:
|
||||
if keys.get('capabilities:boot_option', '') \
|
||||
== 'netboot':
|
||||
warnings.append(
|
||||
warning_message.format(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 actions.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, context):
|
||||
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 actions.Result(**mistral_result)
|
||||
|
||||
|
||||
class VerifyProfilesAction(base.TripleOAction):
|
||||
"""Verify that the profiles have enough nodes"""
|
||||
|
||||
# TODO(bcrochet): The validation actions are temporary. This logic should
|
||||
# move to the tripleo-validations project eventually.
|
||||
def __init__(self, nodes, flavors):
|
||||
super(VerifyProfilesAction, self).__init__()
|
||||
|
||||
self.nodes = nodes
|
||||
self.flavors = flavors
|
||||
|
||||
def run(self, context):
|
||||
errors = []
|
||||
warnings = []
|
||||
|
||||
bm_nodes = {node['uuid']: node for node in self.nodes
|
||||
if node['provision_state'] in ('available', 'active')}
|
||||
|
||||
free_node_caps = {uu: self._node_get_capabilities(node)
|
||||
for uu, node in bm_nodes.items()}
|
||||
|
||||
profile_flavor_used = False
|
||||
for flavor_name, (flavor, scale) in self.flavors.items():
|
||||
if not scale:
|
||||
continue
|
||||
|
||||
profile = None
|
||||
keys = flavor.get('keys')
|
||||
if keys:
|
||||
profile = keys.get('capabilities:profile')
|
||||
|
||||
if not profile and len(self.flavors) > 1:
|
||||
message = ('Error: The {flavor} flavor has no profile '
|
||||
'associated.\n'
|
||||
'Recommendation: assign a profile with openstack '
|
||||
'flavor set --property '
|
||||
'"capabilities:profile"="PROFILE_NAME" {flavor}')
|
||||
|
||||
errors.append(message.format(flavor=flavor_name))
|
||||
continue
|
||||
|
||||
profile_flavor_used = True
|
||||
|
||||
assigned_nodes = [uu for uu, caps in free_node_caps.items()
|
||||
if caps.get('profile') == profile]
|
||||
required_count = scale - len(assigned_nodes)
|
||||
|
||||
if required_count < 0:
|
||||
warnings.append('%d nodes with profile %s won\'t be used '
|
||||
'for deployment now' % (-required_count,
|
||||
profile))
|
||||
required_count = 0
|
||||
|
||||
for uu in assigned_nodes:
|
||||
free_node_caps.pop(uu)
|
||||
|
||||
if required_count > 0:
|
||||
message = ('Error: only {total} of {scale} requested ironic '
|
||||
'nodes are tagged to profile {profile} (for flavor '
|
||||
'{flavor}).\n'
|
||||
'Recommendation: tag more nodes using openstack '
|
||||
'baremetal node set --property "capabilities='
|
||||
'profile:{profile},boot_option:local" <NODE ID>')
|
||||
errors.append(message.format(total=scale - required_count,
|
||||
scale=scale,
|
||||
profile=profile,
|
||||
flavor=flavor_name))
|
||||
|
||||
nodes_without_profile = [uu for uu, caps in free_node_caps.items()
|
||||
if not caps.get('profile')]
|
||||
if nodes_without_profile and profile_flavor_used:
|
||||
warnings.append("There are %d ironic nodes with no profile that "
|
||||
"will not be used: %s" % (
|
||||
len(nodes_without_profile),
|
||||
', '.join(nodes_without_profile)))
|
||||
|
||||
return_value = {
|
||||
'errors': errors,
|
||||
'warnings': warnings,
|
||||
}
|
||||
if errors:
|
||||
mistral_result = {'error': return_value}
|
||||
else:
|
||||
mistral_result = {'data': return_value}
|
||||
|
||||
return actions.Result(**mistral_result)
|
||||
|
||||
def _node_get_capabilities(self, node):
|
||||
"""Get node capabilities."""
|
||||
return nodeutils.capabilities_to_dict(
|
||||
node['properties'].get('capabilities'))
|
||||
|
||||
|
||||
class CheckNodesCountAction(base.TripleOAction):
|
||||
"""Validate hypervisor statistics"""
|
||||
|
||||
# TODO(bcrochet): The validation actions are temporary. This logic should
|
||||
# move to the tripleo-validations project eventually.
|
||||
def __init__(self, statistics, stack, associated_nodes, available_nodes,
|
||||
parameters, default_role_counts):
|
||||
super(CheckNodesCountAction, self).__init__()
|
||||
self.statistics = statistics
|
||||
self.stack = stack
|
||||
self.associated_nodes = associated_nodes
|
||||
self.available_nodes = available_nodes
|
||||
self.parameters = parameters
|
||||
self.default_role_counts = default_role_counts
|
||||
|
||||
def run(self, context):
|
||||
errors = []
|
||||
warnings = []
|
||||
|
||||
requested_count = 0
|
||||
|
||||
for param, default in self.default_role_counts.items():
|
||||
if self.stack:
|
||||
try:
|
||||
current = int(self.stack['parameters'][param])
|
||||
except KeyError:
|
||||
# We could be adding a new role on stack-update, so there's
|
||||
# no assumption the parameter exists in the stack.
|
||||
current = self.parameters.get(param, default)
|
||||
requested_count += self.parameters.get(param, current)
|
||||
else:
|
||||
requested_count += self.parameters.get(param, default)
|
||||
|
||||
# We get number of nodes usable for the stack by getting already
|
||||
# used (associated) nodes and number of nodes which can be used
|
||||
# (not in maintenance mode).
|
||||
# Assumption is that associated nodes are part of the stack (only
|
||||
# one overcloud is supported).
|
||||
associated = len(self.associated_nodes)
|
||||
available = len(self.available_nodes)
|
||||
|
||||
available_count = associated + available
|
||||
|
||||
if requested_count > available_count:
|
||||
errors.append('Not enough baremetal nodes - available: %d, '
|
||||
'requested: %d' %
|
||||
(available_count, requested_count))
|
||||
|
||||
if self.statistics['count'] < available_count:
|
||||
errors.append('Only %d nodes are exposed to Nova of %d requests. '
|
||||
'Check that enough nodes are in "available" state '
|
||||
'with maintenance mode off.' %
|
||||
(self.statistics['count'], available_count))
|
||||
|
||||
return_value = {
|
||||
'errors': errors,
|
||||
'warnings': warnings,
|
||||
'result': {
|
||||
'statistics': self.statistics,
|
||||
'enough_nodes': True,
|
||||
'requested_count': requested_count,
|
||||
'available_count': available_count,
|
||||
}
|
||||
}
|
||||
if errors:
|
||||
return_value['result']['enough_nodes'] = False
|
||||
mistral_result = {'error': return_value}
|
||||
else:
|
||||
mistral_result = {'data': return_value}
|
||||
|
||||
return actions.Result(**mistral_result)
|
||||
|
@ -14,7 +14,6 @@
|
||||
# under the License.
|
||||
import collections
|
||||
import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from mistral_lib import actions
|
||||
from oslo_concurrency.processutils import ProcessExecutionError
|
||||
@ -23,7 +22,6 @@ from tripleo_common.actions import validations
|
||||
from tripleo_common import constants
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.tests.utils import test_validations
|
||||
from tripleo_common.utils import nodes as nodeutils
|
||||
|
||||
|
||||
class GetPubkeyActionTest(base.TestCase):
|
||||
@ -215,667 +213,3 @@ class RunValidationActionTest(base.TestCase):
|
||||
mock_ctx)
|
||||
mock_cleanup_identity_file.assert_called_once_with(
|
||||
'identity_file_path')
|
||||
|
||||
|
||||
class TestCheckBootImagesAction(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCheckBootImagesAction, self).setUp()
|
||||
self.images = [
|
||||
{'id': '67890', 'name': 'ramdisk'},
|
||||
{'id': '12345', 'name': 'kernel'},
|
||||
]
|
||||
self.ctx = mock.MagicMock()
|
||||
|
||||
@mock.patch(
|
||||
'tripleo_common.actions.validations.CheckBootImagesAction'
|
||||
'._check_for_image')
|
||||
def test_run(self, mock_check_for_image):
|
||||
mock_check_for_image.side_effect = ['12345', '67890']
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'kernel_id': '12345',
|
||||
'ramdisk_id': '67890',
|
||||
'warnings': [],
|
||||
'errors': []})
|
||||
action_args = {
|
||||
'images': self.images,
|
||||
'deploy_kernel_name': 'kernel',
|
||||
'deploy_ramdisk_name': 'ramdisk'
|
||||
}
|
||||
action = validations.CheckBootImagesAction(**action_args)
|
||||
self.assertEqual(expected, action.run(self.ctx))
|
||||
mock_check_for_image.assert_has_calls([
|
||||
mock.call('kernel', []),
|
||||
mock.call('ramdisk', [])
|
||||
])
|
||||
|
||||
def test_check_for_image_success(self):
|
||||
expected = '12345'
|
||||
action_args = {
|
||||
'images': self.images,
|
||||
'deploy_kernel_name': 'kernel',
|
||||
'deploy_ramdisk_name': 'ramdisk'
|
||||
}
|
||||
|
||||
messages = mock.Mock()
|
||||
action = validations.CheckBootImagesAction(**action_args)
|
||||
self.assertEqual(expected, action._check_for_image('kernel', messages))
|
||||
messages.assert_not_called()
|
||||
|
||||
def test_check_for_image_missing(self):
|
||||
expected = None
|
||||
deploy_kernel_name = 'missing'
|
||||
action_args = {
|
||||
'images': self.images,
|
||||
'deploy_kernel_name': deploy_kernel_name
|
||||
}
|
||||
expected_message = ("No image with the name '%s' found - make sure "
|
||||
"you have uploaded boot images."
|
||||
% deploy_kernel_name)
|
||||
|
||||
messages = []
|
||||
action = validations.CheckBootImagesAction(**action_args)
|
||||
self.assertEqual(expected,
|
||||
action._check_for_image(deploy_kernel_name, messages))
|
||||
self.assertEqual(1, len(messages))
|
||||
self.assertIn(expected_message, messages)
|
||||
|
||||
def test_check_for_image_too_many(self):
|
||||
expected = None
|
||||
deploy_ramdisk_name = 'toomany'
|
||||
images = list(self.images)
|
||||
images.append({'id': 'abcde', 'name': deploy_ramdisk_name})
|
||||
images.append({'id': '45678', 'name': deploy_ramdisk_name})
|
||||
action_args = {
|
||||
'images': images,
|
||||
'deploy_ramdisk_name': deploy_ramdisk_name
|
||||
}
|
||||
expected_message = ("Please make sure there is only one image named "
|
||||
"'%s' in glance." % deploy_ramdisk_name)
|
||||
|
||||
messages = []
|
||||
action = validations.CheckBootImagesAction(**action_args)
|
||||
self.assertEqual(
|
||||
expected, action._check_for_image(deploy_ramdisk_name, messages))
|
||||
self.assertEqual(1, len(messages))
|
||||
self.assertIn(expected_message, messages)
|
||||
|
||||
|
||||
class FakeFlavor(object):
|
||||
name = ''
|
||||
uuid = ''
|
||||
|
||||
def __init__(self, name, keys={'capabilities:boot_option': 'local'}):
|
||||
self.uuid = uuid4()
|
||||
self.name = name
|
||||
self.keys = keys
|
||||
|
||||
def get_keys(self):
|
||||
return self.keys
|
||||
|
||||
|
||||
class TestCheckFlavorsAction(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCheckFlavorsAction, self).setUp()
|
||||
self.compute = mock.MagicMock()
|
||||
compute_patcher = mock.patch(
|
||||
'tripleo_common.actions.base.TripleOAction.get_compute_client',
|
||||
return_value=self.compute)
|
||||
self.mock_compute = compute_patcher.start()
|
||||
self.addCleanup(compute_patcher.stop)
|
||||
|
||||
self.mock_flavors = mock.Mock()
|
||||
self.compute.attach_mock(self.mock_flavors, 'flavors')
|
||||
self.mock_flavor_list = [
|
||||
FakeFlavor('flavor1'),
|
||||
FakeFlavor('flavor2',
|
||||
keys={'capabilities:boot_option': 'netboot'}),
|
||||
FakeFlavor('flavor3', None)
|
||||
]
|
||||
self.mock_flavors.attach_mock(
|
||||
mock.Mock(return_value=self.mock_flavor_list), 'list')
|
||||
self.ctx = mock.MagicMock()
|
||||
|
||||
def test_run_success(self):
|
||||
roles_info = {
|
||||
'role1': ('flavor1', 1),
|
||||
}
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'flavors': {
|
||||
'flavor1': (
|
||||
{
|
||||
'name': 'flavor1',
|
||||
'keys': {'capabilities:boot_option': 'local'}
|
||||
}, 1)
|
||||
},
|
||||
'warnings': [],
|
||||
'errors': [],
|
||||
}
|
||||
)
|
||||
|
||||
action_args = {
|
||||
'roles_info': roles_info
|
||||
}
|
||||
action = validations.CheckFlavorsAction(**action_args)
|
||||
self.assertEqual(expected, action.run(self.ctx))
|
||||
|
||||
def test_run_boot_option_is_netboot(self):
|
||||
roles_info = {
|
||||
'role2': ('flavor2', 1),
|
||||
'role3': ('flavor3', 1),
|
||||
}
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'flavors': {
|
||||
'flavor2': (
|
||||
{
|
||||
'name': 'flavor2',
|
||||
'keys': {'capabilities:boot_option': 'netboot'}
|
||||
}, 1),
|
||||
'flavor3': (
|
||||
{
|
||||
'name': 'flavor3',
|
||||
'keys': None
|
||||
}, 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 = {
|
||||
'roles_info': roles_info
|
||||
}
|
||||
action = validations.CheckFlavorsAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_run_flavor_does_not_exist(self):
|
||||
roles_info = {
|
||||
'role4': ('does_not_exist', 1),
|
||||
}
|
||||
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'errors': [
|
||||
"Flavor '%s' provided for the role '%s', does not "
|
||||
"exist" % ('does_not_exist', 'role4')
|
||||
],
|
||||
'warnings': [],
|
||||
'flavors': {},
|
||||
}
|
||||
)
|
||||
|
||||
action_args = {
|
||||
'roles_info': roles_info
|
||||
}
|
||||
action = validations.CheckFlavorsAction(**action_args)
|
||||
self.assertEqual(expected, action.run(self.ctx))
|
||||
|
||||
|
||||
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',
|
||||
}
|
||||
}
|
||||
self.ctx = mock.MagicMock()
|
||||
|
||||
def test_run_success(self):
|
||||
expected = actions.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(self.ctx))
|
||||
|
||||
def test_run_invalid_ramdisk(self):
|
||||
expected = actions.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(self.ctx))
|
||||
|
||||
def test_no_boot_option_local(self):
|
||||
expected = actions.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(self.ctx))
|
||||
|
||||
|
||||
class TestVerifyProfilesAction(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestVerifyProfilesAction, self).setUp()
|
||||
|
||||
self.nodes = []
|
||||
self.flavors = {name: (self._get_fake_flavor(name), 1)
|
||||
for name in ('compute', 'control')}
|
||||
self.ctx = mock.MagicMock()
|
||||
|
||||
def _get_fake_node(self, profile=None, possible_profiles=[],
|
||||
provision_state='available'):
|
||||
caps = {'%s_profile' % p: '1'
|
||||
for p in possible_profiles}
|
||||
if profile is not None:
|
||||
caps['profile'] = profile
|
||||
caps = nodeutils.dict_to_capabilities(caps)
|
||||
return {
|
||||
'uuid': str(uuid4()),
|
||||
'properties': {'capabilities': caps},
|
||||
'provision_state': provision_state,
|
||||
}
|
||||
|
||||
def _get_fake_flavor(self, name, profile=''):
|
||||
the_profile = profile or name
|
||||
return {
|
||||
'name': name,
|
||||
'profile': the_profile,
|
||||
'keys': {
|
||||
'capabilities:boot_option': 'local',
|
||||
'capabilities:profile': the_profile
|
||||
}
|
||||
}
|
||||
|
||||
def _test(self, expected_result):
|
||||
action = validations.VerifyProfilesAction(self.nodes, self.flavors)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def test_no_matching_without_scale(self):
|
||||
self.flavors = {name: (object(), 0)
|
||||
for name in self.flavors}
|
||||
self.nodes[:] = [self._get_fake_node(profile='fake'),
|
||||
self._get_fake_node(profile='fake')]
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
})
|
||||
self._test(expected)
|
||||
|
||||
def test_exact_match(self):
|
||||
self.nodes[:] = [self._get_fake_node(profile='compute'),
|
||||
self._get_fake_node(profile='control')]
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
})
|
||||
self._test(expected)
|
||||
|
||||
def test_nodes_with_no_profiles_present(self):
|
||||
self.nodes[:] = [self._get_fake_node(profile='compute'),
|
||||
self._get_fake_node(profile=None),
|
||||
self._get_fake_node(profile='foobar'),
|
||||
self._get_fake_node(profile='control')]
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'warnings': [
|
||||
'There are 1 ironic nodes with no profile that will not '
|
||||
'be used: %s' % self.nodes[1].get('uuid')
|
||||
],
|
||||
'errors': [],
|
||||
})
|
||||
self._test(expected)
|
||||
|
||||
def test_more_nodes_with_profiles_present(self):
|
||||
self.nodes[:] = [self._get_fake_node(profile='compute'),
|
||||
self._get_fake_node(profile='compute'),
|
||||
self._get_fake_node(profile='compute'),
|
||||
self._get_fake_node(profile='control')]
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'warnings': ["2 nodes with profile compute won't be used for "
|
||||
"deployment now"],
|
||||
'errors': [],
|
||||
})
|
||||
self._test(expected)
|
||||
|
||||
def test_no_nodes(self):
|
||||
# One error per each flavor
|
||||
expected = actions.Result(
|
||||
error={'errors': ['Error: only 0 of 1 requested ironic nodes are '
|
||||
'tagged to profile compute (for flavor '
|
||||
'compute)\n'
|
||||
'Recommendation: tag more nodes using openstack '
|
||||
'baremetal node set --property '
|
||||
'"capabilities=profile:compute,'
|
||||
'boot_option:local" <NODE ID>',
|
||||
'Error: only 0 of 1 requested ironic nodes are '
|
||||
'tagged to profile control (for flavor '
|
||||
'control).\n'
|
||||
'Recommendation: tag more nodes using openstack '
|
||||
'baremetal node set --property '
|
||||
'"capabilities=profile:control,'
|
||||
'boot_option:local" <NODE ID>'],
|
||||
'warnings': []})
|
||||
|
||||
action = validations.VerifyProfilesAction(self.nodes, self.flavors)
|
||||
result = action.run(self.ctx)
|
||||
self.assertEqual(expected.error['errors'].sort(),
|
||||
result.error['errors'].sort())
|
||||
self.assertEqual(expected.error['warnings'], result.error['warnings'])
|
||||
self.assertIsNone(result.data)
|
||||
|
||||
def test_not_enough_nodes(self):
|
||||
self.nodes[:] = [self._get_fake_node(profile='compute')]
|
||||
expected = actions.Result(
|
||||
error={'errors': ['Error: only 0 of 1 requested ironic nodes are '
|
||||
'tagged to profile control (for flavor '
|
||||
'control).\n'
|
||||
'Recommendation: tag more nodes using openstack '
|
||||
'baremetal node set --property '
|
||||
'"capabilities=profile:control,'
|
||||
'boot_option:local" <NODE ID>'],
|
||||
'warnings': []})
|
||||
self._test(expected)
|
||||
|
||||
def test_scale(self):
|
||||
# active nodes with assigned profiles are fine
|
||||
self.nodes[:] = [self._get_fake_node(profile='compute',
|
||||
provision_state='active'),
|
||||
self._get_fake_node(profile='control')]
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
}
|
||||
)
|
||||
self._test(expected)
|
||||
|
||||
def test_assign_profiles_wrong_state(self):
|
||||
# active nodes are not considered for assigning profiles
|
||||
self.nodes[:] = [self._get_fake_node(possible_profiles=['compute'],
|
||||
provision_state='active'),
|
||||
self._get_fake_node(possible_profiles=['control'],
|
||||
provision_state='cleaning'),
|
||||
self._get_fake_node(profile='compute',
|
||||
provision_state='error')]
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'warnings': [
|
||||
'There are 1 ironic nodes with no profile that will not '
|
||||
'be used: %s' % self.nodes[0].get('uuid')
|
||||
],
|
||||
'errors': [
|
||||
'Error: only 0 of 1 requested ironic nodes are tagged to '
|
||||
'profile control (for flavor control).\n'
|
||||
'Recommendation: tag more nodes using openstack baremetal '
|
||||
'node set --property "capabilities=profile:control,'
|
||||
'boot_option:local" <NODE ID>',
|
||||
'Error: only 0 of 1 requested ironic nodes are tagged to '
|
||||
'profile compute (for flavor compute).\n'
|
||||
'Recommendation: tag more nodes using openstack baremetal '
|
||||
'node set --property "capabilities=profile:compute,'
|
||||
'boot_option:local" <NODE ID>'
|
||||
]
|
||||
})
|
||||
|
||||
action = validations.VerifyProfilesAction(self.nodes, self.flavors)
|
||||
result = action.run(self.ctx)
|
||||
self.assertEqual(expected.error['errors'].sort(),
|
||||
result.error['errors'].sort())
|
||||
self.assertEqual(expected.error['warnings'], result.error['warnings'])
|
||||
self.assertIsNone(result.data)
|
||||
|
||||
def test_no_spurious_warnings(self):
|
||||
self.nodes[:] = [self._get_fake_node(profile=None)]
|
||||
self.flavors = {'baremetal': (
|
||||
self._get_fake_flavor('baremetal', None), 1)}
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'warnings': [
|
||||
'There are 1 ironic nodes with no profile that will not '
|
||||
'be used: %s' % self.nodes[0].get('uuid')
|
||||
],
|
||||
'errors': [
|
||||
'Error: only 0 of 1 requested ironic nodes are tagged to '
|
||||
'profile baremetal (for flavor baremetal).\n'
|
||||
'Recommendation: tag more nodes using openstack baremetal '
|
||||
'node set --property "capabilities=profile:baremetal,'
|
||||
'boot_option:local" <NODE ID>'
|
||||
]
|
||||
})
|
||||
self._test(expected)
|
||||
|
||||
|
||||
class TestCheckNodesCountAction(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCheckNodesCountAction, self).setUp()
|
||||
self.defaults = {
|
||||
'ControllerCount': 1,
|
||||
'ComputeCount': 1,
|
||||
'ObjectStorageCount': 0,
|
||||
'BlockStorageCount': 0,
|
||||
'CephStorageCount': 0,
|
||||
}
|
||||
self.stack = None
|
||||
self.action_args = {
|
||||
'stack': None,
|
||||
'associated_nodes': self._ironic_node_list(True, False),
|
||||
'available_nodes': self._ironic_node_list(False, True),
|
||||
'parameters': {},
|
||||
'default_role_counts': self.defaults,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1},
|
||||
}
|
||||
self.ctx = mock.MagicMock()
|
||||
|
||||
def _ironic_node_list(self, associated, maintenance):
|
||||
if associated:
|
||||
nodes = range(2)
|
||||
elif maintenance:
|
||||
nodes = range(1)
|
||||
return nodes
|
||||
|
||||
def test_run_check_hypervisor_stats(self):
|
||||
action_args = self.action_args.copy()
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'result': {
|
||||
'requested_count': 2,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'vcpus': 1, 'memory_mb': 1},
|
||||
'enough_nodes': True
|
||||
},
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_run_check_hypervisor_stats_not_met(self):
|
||||
statistics = {'count': 0, 'memory_mb': 0, 'vcpus': 0}
|
||||
|
||||
action_args = self.action_args.copy()
|
||||
action_args.update({'statistics': statistics})
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'errors': [
|
||||
'Only 0 nodes are exposed to Nova of 3 requests. Check '
|
||||
'that enough nodes are in "available" state with '
|
||||
'maintenance mode off.'],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'statistics': statistics,
|
||||
'enough_nodes': False,
|
||||
'requested_count': 2,
|
||||
'available_count': 3,
|
||||
}
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_check_nodes_count_deploy_enough_nodes(self):
|
||||
action_args = self.action_args.copy()
|
||||
action_args['parameters'] = {'ControllerCount': 2}
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'enough_nodes': True,
|
||||
'requested_count': 3,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1}
|
||||
}
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_check_nodes_count_deploy_too_much(self):
|
||||
action_args = self.action_args.copy()
|
||||
action_args['parameters'] = {'ControllerCount': 3}
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'errors': [
|
||||
"Not enough baremetal nodes - available: 3, requested: 4"],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'enough_nodes': False,
|
||||
'requested_count': 4,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1}
|
||||
}
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_check_nodes_count_scale_enough_nodes(self):
|
||||
action_args = self.action_args.copy()
|
||||
action_args['parameters'] = {'ControllerCount': 2}
|
||||
action_args['stack'] = {'parameters': self.defaults.copy()}
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
data={
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'enough_nodes': True,
|
||||
'requested_count': 3,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1}
|
||||
},
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_check_nodes_count_scale_too_much(self):
|
||||
action_args = self.action_args.copy()
|
||||
action_args['parameters'] = {'ControllerCount': 3}
|
||||
action_args['stack'] = {'parameters': self.defaults.copy()}
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'errors': [
|
||||
'Not enough baremetal nodes - available: 3, requested: 4'],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'enough_nodes': False,
|
||||
'requested_count': 4,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1}
|
||||
}
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_check_default_param_not_in_stack(self):
|
||||
missing_param = 'CephStorageCount'
|
||||
action_args = self.action_args.copy()
|
||||
action_args['parameters'] = {'ControllerCount': 3}
|
||||
action_args['stack'] = {'parameters': self.defaults.copy()}
|
||||
del action_args['stack']['parameters'][missing_param]
|
||||
|
||||
action = validations.CheckNodesCountAction(**action_args)
|
||||
result = action.run(self.ctx)
|
||||
|
||||
expected = actions.Result(
|
||||
error={
|
||||
'errors': [
|
||||
'Not enough baremetal nodes - available: 3, requested: 4'],
|
||||
'warnings': [],
|
||||
'result': {
|
||||
'enough_nodes': False,
|
||||
'requested_count': 4,
|
||||
'available_count': 3,
|
||||
'statistics': {'count': 3, 'memory_mb': 1, 'vcpus': 1}
|
||||
}
|
||||
})
|
||||
self.assertEqual(expected, result)
|
||||
|
@ -275,511 +275,3 @@ workflows:
|
||||
config_name: copy_ssh_key
|
||||
group: script
|
||||
queue_name: <% $.queue_name %>
|
||||
|
||||
check_boot_images:
|
||||
input:
|
||||
- deploy_kernel_name: 'bm-deploy-kernel'
|
||||
- deploy_ramdisk_name: 'bm-deploy-ramdisk'
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
output:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
kernel_id: <% $.kernel_id %>
|
||||
ramdisk_id: <% $.ramdisk_id %>
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
tasks:
|
||||
check_run_validations:
|
||||
on-complete:
|
||||
- get_images: <% $.run_validations %>
|
||||
- send_message: <% not $.run_validations %>
|
||||
|
||||
get_images:
|
||||
action: glance.images_list
|
||||
on-success: check_images
|
||||
publish:
|
||||
images: <% task().result %>
|
||||
|
||||
check_images:
|
||||
action: tripleo.validations.check_boot_images
|
||||
input:
|
||||
images: <% $.images %>
|
||||
deploy_kernel_name: <% $.deploy_kernel_name %>
|
||||
deploy_ramdisk_name: <% $.deploy_ramdisk_name %>
|
||||
on-success: send_message
|
||||
publish:
|
||||
kernel_id: <% task().result.kernel_id %>
|
||||
ramdisk_id: <% task().result.ramdisk_id %>
|
||||
warnings: <% task().result.warnings %>
|
||||
errors: <% task().result.errors %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
kernel_id: <% task().result.kernel_id %>
|
||||
ramdisk_id: <% task().result.ramdisk_id %>
|
||||
warnings: <% task().result.warnings %>
|
||||
errors: <% task().result.errors %>
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
kernel_id: <% $.kernel_id %>
|
||||
ramdisk_id: <% $.ramdisk_id %>
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
collect_flavors:
|
||||
input:
|
||||
- roles_info: {}
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
output:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
flavors: <% $.flavors %>
|
||||
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
|
||||
tasks:
|
||||
check_run_validations:
|
||||
on-complete:
|
||||
- check_flavors: <% $.run_validations %>
|
||||
- send_message: <% not $.run_validations %>
|
||||
|
||||
check_flavors:
|
||||
action: tripleo.validations.check_flavors
|
||||
input:
|
||||
roles_info: <% $.roles_info %>
|
||||
on-success: send_message
|
||||
publish:
|
||||
flavors: <% task().result.flavors %>
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
flavors: {}
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
flavors: <% $.flavors %>
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
check_ironic_boot_configuration:
|
||||
input:
|
||||
- kernel_id: null
|
||||
- ramdisk_id: null
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
output:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
|
||||
tasks:
|
||||
check_run_validations:
|
||||
on-complete:
|
||||
- get_ironic_nodes: <% $.run_validations %>
|
||||
- send_message: <% not $.run_validations %>
|
||||
|
||||
get_ironic_nodes:
|
||||
action: ironic.node_list
|
||||
input:
|
||||
provision_state: available
|
||||
maintenance: false
|
||||
detail: true
|
||||
on-success: check_node_boot_configuration
|
||||
publish:
|
||||
nodes: <% task().result %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().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
|
||||
publish:
|
||||
errors: <% task().result.errors.flatten() %>
|
||||
warnings: <% task().result.warnings.flatten() %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
errors: <% task().result.errors.flatten() %>
|
||||
warnings: <% task().result.warnings.flatten() %>
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
verify_profiles:
|
||||
input:
|
||||
- flavors: []
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
output:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
|
||||
tasks:
|
||||
check_run_validations:
|
||||
on-complete:
|
||||
- get_ironic_nodes: <% $.run_validations %>
|
||||
- send_message: <% not $.run_validations %>
|
||||
|
||||
get_ironic_nodes:
|
||||
action: ironic.node_list
|
||||
input:
|
||||
maintenance: false
|
||||
detail: true
|
||||
on-success: verify_profiles
|
||||
publish:
|
||||
nodes: <% task().result %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
|
||||
verify_profiles:
|
||||
action: tripleo.validations.verify_profiles
|
||||
input:
|
||||
nodes: <% $.nodes %>
|
||||
flavors: <% $.flavors %>
|
||||
on-success: send_message
|
||||
publish:
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
check_default_nodes_count:
|
||||
input:
|
||||
- stack_id: overcloud
|
||||
- parameters: {}
|
||||
- default_role_counts: {}
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
output:
|
||||
statistics: <% $.statistics %>
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
|
||||
tasks:
|
||||
check_run_validations:
|
||||
on-complete:
|
||||
- get_hypervisor_statistics: <% $.run_validations %>
|
||||
- send_message: <% not $.run_validations %>
|
||||
|
||||
get_hypervisor_statistics:
|
||||
action: nova.hypervisors_statistics
|
||||
on-success: get_stack
|
||||
publish:
|
||||
statistics: <% task().result %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
errors: []
|
||||
warnings: []
|
||||
statistics: null
|
||||
|
||||
get_stack:
|
||||
action: heat.stacks_get
|
||||
input:
|
||||
stack_id: <% $.stack_id %>
|
||||
resolve_outputs: false
|
||||
on-success: get_associated_nodes
|
||||
publish:
|
||||
stack: <% task().result %>
|
||||
on-error: get_associated_nodes
|
||||
publish-on-error:
|
||||
stack: null
|
||||
|
||||
get_associated_nodes:
|
||||
action: ironic.node_list
|
||||
input:
|
||||
associated: true
|
||||
on-success: get_available_nodes
|
||||
publish:
|
||||
associated_nodes: <% task().result %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
errors: []
|
||||
warnings: []
|
||||
|
||||
get_available_nodes:
|
||||
action: ironic.node_list
|
||||
input:
|
||||
provision_state: available
|
||||
associated: false
|
||||
maintenance: false
|
||||
on-success: check_nodes_count
|
||||
publish:
|
||||
available_nodes: <% task().result %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
errors: []
|
||||
warnings: []
|
||||
|
||||
check_nodes_count:
|
||||
action: tripleo.validations.check_nodes_count
|
||||
input:
|
||||
statistics: <% $.statistics %>
|
||||
stack: <% $.stack %>
|
||||
associated_nodes: <% $.associated_nodes %>
|
||||
available_nodes: <% $.available_nodes %>
|
||||
parameters: <% $.parameters %>
|
||||
default_role_counts: <% $.default_role_counts %>
|
||||
on-success: send_message
|
||||
publish:
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
on-error: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
statistics: null
|
||||
errors: <% task().result.errors %>
|
||||
warnings: <% task().result.warnings %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
statistics: <% $.statistics %>
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
check_pre_deployment_validations:
|
||||
input:
|
||||
- deploy_kernel_name: 'bm-deploy-kernel'
|
||||
- deploy_ramdisk_name: 'bm-deploy-ramdisk'
|
||||
- roles_info: {}
|
||||
- stack_id: overcloud
|
||||
- parameters: {}
|
||||
- default_role_counts: {}
|
||||
- run_validations: true
|
||||
- queue_name: tripleo
|
||||
|
||||
output:
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
kernel_id: <% $.kernel_id %>
|
||||
ramdisk_id: <% $.ramdisk_id %>
|
||||
flavors: <% $.flavors %>
|
||||
statistics: <% $.statistics %>
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
tasks:
|
||||
init_messages:
|
||||
on-success: check_boot_images
|
||||
publish:
|
||||
errors: []
|
||||
warnings: []
|
||||
|
||||
check_boot_images:
|
||||
workflow: check_boot_images
|
||||
input:
|
||||
deploy_kernel_name: <% $.deploy_kernel_name %>
|
||||
deploy_ramdisk_name: <% $.deploy_ramdisk_name %>
|
||||
run_validations: <% $.run_validations %>
|
||||
queue_name: <% $.queue_name %>
|
||||
publish:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
kernel_id: <% task().result.get('kernel_id') %>
|
||||
ramdisk_id: <% task().result.get('ramdisk_id') %>
|
||||
publish-on-error:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
kernel_id: <% task().result.get('kernel_id') %>
|
||||
ramdisk_id: <% task().result.get('ramdisk_id') %>
|
||||
status: FAILED
|
||||
on-success: collect_flavors
|
||||
on-error: collect_flavors
|
||||
|
||||
collect_flavors:
|
||||
workflow: collect_flavors
|
||||
input:
|
||||
roles_info: <% $.roles_info %>
|
||||
run_validations: <% $.run_validations %>
|
||||
queue_name: <% $.queue_name %>
|
||||
publish:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
flavors: <% task().result.get('flavors') %>
|
||||
publish-on-error:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
flavors: <% task().result.get('flavors') %>
|
||||
status: FAILED
|
||||
on-success: check_ironic_boot_configuration
|
||||
on-error: check_ironic_boot_configuration
|
||||
|
||||
check_ironic_boot_configuration:
|
||||
workflow: check_ironic_boot_configuration
|
||||
input:
|
||||
kernel_id: <% $.kernel_id %>
|
||||
ramdisk_id: <% $.ramdisk_id %>
|
||||
run_validations: <% $.run_validations %>
|
||||
queue_name: <% $.queue_name %>
|
||||
publish:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
publish-on-error:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
status: FAILED
|
||||
on-success: check_default_nodes_count
|
||||
on-error: check_default_nodes_count
|
||||
|
||||
check_default_nodes_count:
|
||||
workflow: check_default_nodes_count
|
||||
# ironic-nova sync happens once in two minutes
|
||||
retry: count=12 delay=10
|
||||
input:
|
||||
stack_id: <% $.stack_id %>
|
||||
parameters: <% $.parameters %>
|
||||
default_role_counts: <% $.default_role_counts %>
|
||||
run_validations: <% $.run_validations %>
|
||||
queue_name: <% $.queue_name %>
|
||||
publish:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
statistics: <% task().result.get('statistics') %>
|
||||
publish-on-error:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
statistics: <% task().result.get('statistics') %>
|
||||
status: FAILED
|
||||
on-success: verify_profiles
|
||||
# Do not confuse user with info about profiles if the nodes
|
||||
# count is off in the first place. Skip directly to
|
||||
# send_message. (bug 1703942)
|
||||
on-error: send_message
|
||||
|
||||
verify_profiles:
|
||||
workflow: verify_profiles
|
||||
input:
|
||||
flavors: <% $.flavors %>
|
||||
run_validations: <% $.run_validations %>
|
||||
queue_name: <% $.queue_name %>
|
||||
publish:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
publish-on-error:
|
||||
errors: <% $.errors + task().result.get('errors', []) %>
|
||||
warnings: <% $.warnings + task().result.get('warnings', []) %>
|
||||
status: FAILED
|
||||
on-success: send_message
|
||||
on-error: send_message
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.get('status', 'SUCCESS') %>
|
||||
message: <% $.get('message', '') %>
|
||||
execution: <% execution() %>
|
||||
payload:
|
||||
kernel_id: <% $.kernel_id %>
|
||||
ramdisk_id: <% $.ramdisk_id %>
|
||||
flavors: <% $.flavors %>
|
||||
statistics: <% $.statistics %>
|
||||
errors: <% $.errors %>
|
||||
warnings: <% $.warnings %>
|
||||
|
||||
upload_validations:
|
||||
tags:
|
||||
- tripleo-common-managed
|
||||
|
||||
input:
|
||||
- queue_name: tripleo
|
||||
|
||||
tasks:
|
||||
upload_validations:
|
||||
action: tripleo.validations.upload
|
||||
on-complete: send_message
|
||||
publish-on-error:
|
||||
status: FAILED
|
||||
message: <% task().result %>
|
||||
publish:
|
||||
status: SUCCESS
|
||||
message: <% task().result %>
|
||||
|
||||
send_message:
|
||||
workflow: tripleo.messaging.v1.send
|
||||
input:
|
||||
queue_name: <% $.queue_name %>
|
||||
type: <% execution().name %>
|
||||
status: <% $.status %>
|
||||
execution: <% execution() %>
|
||||
message: <% $.get('message', '') %>
|
||||
|
Loading…
x
Reference in New Issue
Block a user