Add support for custom validations

This patch introduces support for running custom validations by changing
the behavior of the validations actions ListValidationsAction,
ListGroupsAction and RunValidationAction.

Until now, these actions sourced validations from a directory on disk.
Now, these action are sourcing validations from the plan container
subdirectory (custom validations), or, if this is not available, from
the Swift container holding the default validations.

Change-Id: I9e9131b355312c53f12d154976d5d9cd706cc338
Implements: blueprint custom-validations
Depends-On: I338e139fa770ebb7bdcc1c0afb79eec062fada8b
This commit is contained in:
Ana Krivokapic
2018-05-18 22:11:10 +02:00
parent 0e009ece98
commit 8f88e78778
7 changed files with 272 additions and 77 deletions

View File

@@ -12,9 +12,12 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import six
from mistral_lib import actions from mistral_lib import actions
from mistralclient.api import base as mistralclient_api from mistralclient.api import base as mistralclient_api
from oslo_concurrency.processutils import ProcessExecutionError from oslo_concurrency.processutils import ProcessExecutionError
from swiftclient import exceptions as swiftexceptions
from tripleo_common.actions import base from tripleo_common.actions import base
from tripleo_common import constants from tripleo_common import constants
@@ -82,19 +85,34 @@ class Enabled(base.TripleOAction):
class ListValidationsAction(base.TripleOAction): class ListValidationsAction(base.TripleOAction):
"""Return a set of TripleO validations""" """Return a set of TripleO validations"""
def __init__(self, groups=None): def __init__(self, plan=constants.DEFAULT_CONTAINER_NAME, groups=None):
super(ListValidationsAction, self).__init__() super(ListValidationsAction, self).__init__()
self.groups = groups self.groups = groups
self.plan = plan
def run(self, context): def run(self, context):
return utils.load_validations(groups=self.groups) swift = self.get_object_client(context)
try:
return utils.load_validations(
swift, plan=self.plan, groups=self.groups)
except swiftexceptions.ClientException as err:
msg = "Error loading validations from Swift: %s" % err
return actions.Result(error={"msg": six.text_type(msg)})
class ListGroupsAction(base.TripleOAction): class ListGroupsAction(base.TripleOAction):
"""Return a set of TripleO validation groups""" """Return a set of TripleO validation groups"""
def __init__(self, plan=constants.DEFAULT_CONTAINER_NAME):
super(ListGroupsAction, self).__init__()
self.plan = plan
def run(self, context): def run(self, context):
validations = utils.load_validations() swift = self.get_object_client(context)
try:
validations = utils.load_validations(swift, plan=self.plan)
except swiftexceptions.ClientException as err:
msg = "Error loading validations from Swift: %s" % err
return actions.Result(error={"msg": six.text_type(msg)})
return { return {
group for validation in validations group for validation in validations
for group in validation['groups'] for group in validation['groups']
@@ -110,13 +128,16 @@ class RunValidationAction(base.TripleOAction):
def run(self, context): def run(self, context):
mc = self.get_workflow_client(context) mc = self.get_workflow_client(context)
swift = self.get_object_client(context)
identity_file = None identity_file = None
try: try:
env = mc.environments.get('ssh_keys') env = mc.environments.get('ssh_keys')
private_key = env.variables['private_key'] private_key = env.variables['private_key']
identity_file = utils.write_identity_file(private_key) identity_file = utils.write_identity_file(private_key)
stdout, stderr = utils.run_validation(self.validation, stdout, stderr = utils.run_validation(swift,
self.validation,
identity_file, identity_file,
self.plan, self.plan,
context) context)

View File

@@ -47,6 +47,9 @@ CONFIG_CONTAINER_NAME = 'overcloud-config'
#: The default name to use for the container for validations #: The default name to use for the container for validations
VALIDATIONS_CONTAINER_NAME = 'tripleo-validations' VALIDATIONS_CONTAINER_NAME = 'tripleo-validations'
#: The name of the plan subdirectory that holds custom validations
CUSTOM_VALIDATIONS_FOLDER = 'custom-validations'
#: The default key to use for updating parameters in plan environment. #: The default key to use for updating parameters in plan environment.
DEFAULT_PLAN_ENV_KEY = 'parameter_defaults' DEFAULT_PLAN_ENV_KEY = 'parameter_defaults'

View File

@@ -113,47 +113,64 @@ class Enabled(base.TestCase):
class ListValidationsActionTest(base.TestCase): class ListValidationsActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.validations.load_validations') @mock.patch('tripleo_common.utils.validations.load_validations')
def test_run_default(self, mock_load_validations): @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run_default(self, mock_get_object_client, mock_load_validations):
mock_ctx = mock.MagicMock() mock_ctx = mock.MagicMock()
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
mock_load_validations.return_value = 'list of validations' mock_load_validations.return_value = 'list of validations'
action = validations.ListValidationsAction()
action = validations.ListValidationsAction(plan='overcloud')
self.assertEqual('list of validations', action.run(mock_ctx)) self.assertEqual('list of validations', action.run(mock_ctx))
mock_load_validations.assert_called_once_with(groups=None) mock_load_validations.assert_called_once_with(
mock_get_object_client(), plan='overcloud', groups=None)
@mock.patch('tripleo_common.utils.validations.load_validations') @mock.patch('tripleo_common.utils.validations.load_validations')
def test_run_groups(self, mock_load_validations): @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run_groups(self, mock_get_object_client, mock_load_validations):
mock_ctx = mock.MagicMock() mock_ctx = mock.MagicMock()
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
mock_load_validations.return_value = 'list of validations' mock_load_validations.return_value = 'list of validations'
action = validations.ListValidationsAction(groups=['group1',
'group2']) action = validations.ListValidationsAction(
plan='overcloud', groups=['group1', 'group2'])
self.assertEqual('list of validations', action.run(mock_ctx)) self.assertEqual('list of validations', action.run(mock_ctx))
mock_load_validations.assert_called_once_with(groups=['group1', mock_load_validations.assert_called_once_with(
'group2']) mock_get_object_client(), plan='overcloud',
groups=['group1', 'group2'])
class ListGroupsActionTest(base.TestCase): class ListGroupsActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.validations.load_validations') @mock.patch('tripleo_common.utils.validations.load_validations')
def test_run(self, mock_load_validations): @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run(self, mock_get_object_client, mock_load_validations):
mock_ctx = mock.MagicMock() mock_ctx = mock.MagicMock()
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
mock_load_validations.return_value = [ mock_load_validations.return_value = [
test_validations.VALIDATION_GROUPS_1_2_PARSED, test_validations.VALIDATION_GROUPS_1_2_PARSED,
test_validations.VALIDATION_GROUP_1_PARSED, test_validations.VALIDATION_GROUP_1_PARSED,
test_validations.VALIDATION_WITH_METADATA_PARSED] test_validations.VALIDATION_WITH_METADATA_PARSED]
action = validations.ListGroupsAction()
self.assertEqual(set(['group1', 'group2']), action.run(mock_ctx)) action = validations.ListGroupsAction(plan='overcloud')
mock_load_validations.assert_called_once_with() self.assertEqual({'group1', 'group2'}, action.run(mock_ctx))
mock_load_validations.assert_called_once_with(
mock_get_object_client(), plan='overcloud')
class RunValidationActionTest(base.TestCase): class RunValidationActionTest(base.TestCase):
@mock.patch( @mock.patch(
'tripleo_common.actions.base.TripleOAction.get_workflow_client') 'tripleo_common.actions.base.TripleOAction.get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.utils.validations.write_identity_file') @mock.patch('tripleo_common.utils.validations.write_identity_file')
@mock.patch('tripleo_common.utils.validations.cleanup_identity_file') @mock.patch('tripleo_common.utils.validations.cleanup_identity_file')
@mock.patch('tripleo_common.utils.validations.run_validation') @mock.patch('tripleo_common.utils.validations.run_validation')
def test_run(self, mock_run_validation, mock_cleanup_identity_file, def test_run(self, mock_run_validation, mock_cleanup_identity_file,
mock_write_identity_file, get_workflow_client_mock): mock_write_identity_file, mock_get_object_client,
get_workflow_client_mock):
mock_ctx = mock.MagicMock() mock_ctx = mock.MagicMock()
mistral = mock.MagicMock() mistral = mock.MagicMock()
get_workflow_client_mock.return_value = mistral get_workflow_client_mock.return_value = mistral
@@ -161,6 +178,8 @@ class RunValidationActionTest(base.TestCase):
mistral.environments.get.return_value = environment(variables={ mistral.environments.get.return_value = environment(variables={
'private_key': 'shhhh' 'private_key': 'shhhh'
}) })
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
mock_write_identity_file.return_value = 'identity_file_path' mock_write_identity_file.return_value = 'identity_file_path'
mock_run_validation.return_value = 'output', 'error' mock_run_validation.return_value = 'output', 'error'
action = validations.RunValidationAction('validation') action = validations.RunValidationAction('validation')
@@ -173,6 +192,7 @@ class RunValidationActionTest(base.TestCase):
self.assertEqual(expected, action.run(mock_ctx)) self.assertEqual(expected, action.run(mock_ctx))
mock_write_identity_file.assert_called_once_with('shhhh') mock_write_identity_file.assert_called_once_with('shhhh')
mock_run_validation.assert_called_once_with( mock_run_validation.assert_called_once_with(
mock_get_object_client(),
'validation', 'validation',
'identity_file_path', 'identity_file_path',
constants.DEFAULT_CONTAINER_NAME, constants.DEFAULT_CONTAINER_NAME,
@@ -182,11 +202,13 @@ class RunValidationActionTest(base.TestCase):
@mock.patch( @mock.patch(
'tripleo_common.actions.base.TripleOAction.get_workflow_client') 'tripleo_common.actions.base.TripleOAction.get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.utils.validations.write_identity_file') @mock.patch('tripleo_common.utils.validations.write_identity_file')
@mock.patch('tripleo_common.utils.validations.cleanup_identity_file') @mock.patch('tripleo_common.utils.validations.cleanup_identity_file')
@mock.patch('tripleo_common.utils.validations.run_validation') @mock.patch('tripleo_common.utils.validations.run_validation')
def test_run_failing(self, mock_run_validation, mock_cleanup_identity_file, def test_run_failing(self, mock_run_validation, mock_cleanup_identity_file,
mock_write_identity_file, get_workflow_client_mock): mock_write_identity_file, mock_get_object_client,
get_workflow_client_mock):
mock_ctx = mock.MagicMock() mock_ctx = mock.MagicMock()
mistral = mock.MagicMock() mistral = mock.MagicMock()
get_workflow_client_mock.return_value = mistral get_workflow_client_mock.return_value = mistral
@@ -194,6 +216,8 @@ class RunValidationActionTest(base.TestCase):
mistral.environments.get.return_value = environment(variables={ mistral.environments.get.return_value = environment(variables={
'private_key': 'shhhh' 'private_key': 'shhhh'
}) })
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
mock_write_identity_file.return_value = 'identity_file_path' mock_write_identity_file.return_value = 'identity_file_path'
mock_run_validation.side_effect = ProcessExecutionError( mock_run_validation.side_effect = ProcessExecutionError(
stdout='output', stderr='error') stdout='output', stderr='error')
@@ -207,6 +231,7 @@ class RunValidationActionTest(base.TestCase):
self.assertEqual(expected, action.run(mock_ctx)) self.assertEqual(expected, action.run(mock_ctx))
mock_write_identity_file.assert_called_once_with('shhhh') mock_write_identity_file.assert_called_once_with('shhhh')
mock_run_validation.assert_called_once_with( mock_run_validation.assert_called_once_with(
mock_get_object_client(),
'validation', 'validation',
'identity_file_path', 'identity_file_path',
constants.DEFAULT_CONTAINER_NAME, constants.DEFAULT_CONTAINER_NAME,

View File

@@ -22,6 +22,28 @@ from tripleo_common.tests import base
from tripleo_common.utils import validations from tripleo_common.utils import validations
VALIDATION_DEFAULT = """---
- hosts: overcloud
vars:
metadata:
name: First validation
description: Default validation
tasks:
- name: Ping the nodes
ping:
"""
VALIDATION_CUSTOM = """---
- hosts: overcloud
vars:
metadata:
name: First validation
description: Custom validation
tasks:
- name: Ping the nodes
ping:
"""
VALIDATION_GROUP_1 = """--- VALIDATION_GROUP_1 = """---
- hosts: overcloud - hosts: overcloud
vars: vars:
@@ -138,45 +160,83 @@ class LoadValidationsTest(base.TestCase):
value = validations.get_remaining_metadata(validation) value = validations.get_remaining_metadata(validation)
self.assertEqual({}, value) self.assertEqual({}, value)
@mock.patch('glob.glob') @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_load_validations_no_group(self, mock_glob): def test_load_validations_no_group(self, mock_get_object_client):
mock_glob.return_value = ['VALIDATION_GROUP_1', swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
'VALIDATION_WITH_METADATA'] swiftclient.get_container.side_effect = (
mock_open_context = mock.mock_open() ({}, []), # no custom validations
mock_open_context().read.side_effect = [VALIDATION_GROUP_1, ({},
VALIDATION_WITH_METADATA] [{'name': 'VALIDATION_GROUP_1.yaml', 'groups': ['group1']},
{'name': 'VALIDATION_WITH_METADATA.yaml'}]))
swiftclient.get_object.side_effect = (
({}, VALIDATION_GROUP_1),
({}, VALIDATION_WITH_METADATA),
)
mock_get_object_client.return_value = swiftclient
with mock.patch('tripleo_common.utils.validations.open', my_validations = validations.load_validations(
mock_open_context): mock_get_object_client(), plan='overcloud')
my_validations = validations.load_validations()
expected = [VALIDATION_GROUP_1_PARSED, VALIDATION_WITH_METADATA_PARSED] expected = [VALIDATION_GROUP_1_PARSED, VALIDATION_WITH_METADATA_PARSED]
self.assertEqual(expected, my_validations) self.assertEqual(expected, my_validations)
@mock.patch('glob.glob') @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_load_validations_group(self, mock_glob): def test_load_validations_group(self, mock_get_object_client):
mock_glob.return_value = ['VALIDATION_GROUPS_1_2', swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
'VALIDATION_GROUP_1', swiftclient.get_container.side_effect = (
'VALIDATION_WITH_METADATA'] ({}, []), # no custom validations
mock_open_context = mock.mock_open() ({},
mock_open_context().read.side_effect = [VALIDATION_GROUPS_1_2, [
VALIDATION_GROUP_1, {'name': 'VALIDATION_GROUPS_1_2.yaml',
VALIDATION_WITH_METADATA] 'groups': ['group1', 'group2']},
{'name': 'VALIDATION_GROUP_1.yaml', 'groups': ['group1']},
{'name': 'VALIDATION_WITH_METADATA.yaml'}
]
)
)
swiftclient.get_object.side_effect = (
({}, VALIDATION_GROUPS_1_2),
({}, VALIDATION_GROUP_1),
({}, VALIDATION_WITH_METADATA),
)
mock_get_object_client.return_value = swiftclient
with mock.patch('tripleo_common.utils.validations.open', my_validations = validations.load_validations(
mock_open_context): mock_get_object_client(), plan='overcloud', groups=['group1'])
my_validations = validations.load_validations(groups=['group1'])
expected = [VALIDATION_GROUPS_1_2_PARSED, VALIDATION_GROUP_1_PARSED] expected = [VALIDATION_GROUPS_1_2_PARSED, VALIDATION_GROUP_1_PARSED]
self.assertEqual(expected, my_validations) self.assertEqual(expected, my_validations)
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_load_validations_custom_gets_picked_over_default(
self, mock_get_object_client):
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
swiftclient.get_container.side_effect = (
({}, [{'name': 'FIRST_VALIDATION.yaml'}]),
({}, [{'name': 'FIRST_VALIDATION.yaml'}])
)
swiftclient.get_object.side_effect = (
({}, VALIDATION_CUSTOM),
({}, VALIDATION_DEFAULT)
)
mock_get_object_client.return_value = swiftclient
my_validations = validations.load_validations(
mock_get_object_client(), plan='overcloud')
self.assertEqual(len(my_validations), 1)
self.assertEqual('Custom validation', my_validations[0]['description'])
class RunValidationTest(base.TestCase): class RunValidationTest(base.TestCase):
@mock.patch('tripleo_common.utils.validations.find_validation') @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.utils.validations.download_validation')
@mock.patch('oslo_concurrency.processutils.execute') @mock.patch('oslo_concurrency.processutils.execute')
def test_run_validation(self, mock_execute, def test_run_validation(self, mock_execute,
mock_find_validation): mock_download_validation, mock_get_object_client):
swiftclient = mock.MagicMock(url='http://swift:8080/v1/AUTH_test')
mock_get_object_client.return_value = swiftclient
Ctx = namedtuple('Ctx', 'auth_uri user_name auth_token project_name') Ctx = namedtuple('Ctx', 'auth_uri user_name auth_token project_name')
mock_ctx = Ctx( mock_ctx = Ctx(
auth_uri='auth_uri', auth_uri='auth_uri',
@@ -185,9 +245,10 @@ class RunValidationTest(base.TestCase):
project_name='project_name' project_name='project_name'
) )
mock_execute.return_value = 'output' mock_execute.return_value = 'output'
mock_find_validation.return_value = 'validation_path' mock_download_validation.return_value = 'validation_path'
result = validations.run_validation('validation', 'identity_file', result = validations.run_validation(mock_get_object_client(),
'validation', 'identity_file',
'plan', mock_ctx) 'plan', mock_ctx)
self.assertEqual('output', result) self.assertEqual('output', result)
mock_execute.assert_called_once_with( mock_execute.assert_called_once_with(
@@ -201,7 +262,8 @@ class RunValidationTest(base.TestCase):
'identity_file', 'identity_file',
'plan' 'plan'
) )
mock_find_validation.assert_called_once_with('validation') mock_download_validation.assert_called_once_with(
mock_get_object_client(), 'plan', 'validation')
class RunPatternValidatorTest(base.TestCase): class RunPatternValidatorTest(base.TestCase):

View File

@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import dateutil.parser
import logging import logging
import os import os
import tempfile import tempfile
@@ -53,22 +54,39 @@ def delete_container(swiftclient, name):
LOG.info(six.text_type(e)) LOG.info(six.text_type(e))
def download_container(swiftclient, container, dest): def download_container(swiftclient, container, dest,
overwrite_only_newer=False):
"""Download the contents of a Swift container to a directory""" """Download the contents of a Swift container to a directory"""
objects = swiftclient.get_container(container)[1] objects = swiftclient.get_container(container)[1]
for obj in objects: for obj in objects:
is_newer = False
filename = obj['name'] filename = obj['name']
contents = swiftclient.get_object(container, filename)[1] contents = swiftclient.get_object(container, filename)[1]
path = os.path.join(dest, filename) path = os.path.join(dest, filename)
dirname = os.path.dirname(path) dirname = os.path.dirname(path)
already_exists = os.path.exists(path)
if not os.path.exists(dirname): if already_exists:
os.makedirs(dirname) last_mod_swift = int(dateutil.parser.parse(
obj['last_modified']).strftime('%s'))
last_mod_disk = int(os.path.getmtime(path))
with open(path, 'w') as f: if last_mod_swift > last_mod_disk:
f.write(contents) is_newer = True
# write file if `overwrite_only_newer` is not set,
# or if file does not exist at destination,
# or if we found a newer file at source
if (not overwrite_only_newer
or not already_exists
or (overwrite_only_newer and is_newer)):
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(path, 'w') as f:
f.write(contents)
def create_container(swiftclient, container): def create_container(swiftclient, container):

View File

@@ -12,7 +12,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import glob
import logging import logging
import os import os
import re import re
@@ -20,8 +19,10 @@ import tempfile
import yaml import yaml
from oslo_concurrency import processutils from oslo_concurrency import processutils
from swiftclient import exceptions as swiftexceptions
from tripleo_common import constants from tripleo_common import constants
import tripleo_common.utils.swift as swift_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -42,26 +43,63 @@ def get_validation_metadata(validation, key):
LOG.exception("Failed to get validation metadata.") LOG.exception("Failed to get validation metadata.")
def load_validations(groups=None): def _get_validations_from_swift(swift, container, objects, groups, results,
'''Loads all validations.''' skip_existing=False):
paths = glob.glob('{}/*.yaml'.format(constants.DEFAULT_VALIDATIONS_PATH)) existing_ids = [validation['id'] for validation in results]
for obj in objects:
validation_id, ext = os.path.splitext(obj['name'])
if ext != '.yaml':
continue
if skip_existing and validation_id in existing_ids:
continue
contents = swift.get_object(container, obj['name'])[1]
validation = yaml.safe_load(contents)
validation_groups = get_validation_metadata(validation, 'groups') or []
if not groups or set.intersection(set(groups), set(validation_groups)):
results.append({
'id': validation_id,
'name': get_validation_metadata(validation, 'name'),
'groups': get_validation_metadata(validation, 'groups'),
'description': get_validation_metadata(validation,
'description'),
'metadata': get_remaining_metadata(validation)
})
return results
def load_validations(swift, plan, groups=None):
"""Loads all validations.
Retrieves all of default and custom validations for a given plan and
returns a list of dicts, with each dict representing a single validation.
If both a default and a custom validation with the same name are found,
the custom validation is picked.
"""
results = [] results = []
for validation_path in sorted(paths):
with open(validation_path) as f: # Get custom validations first
validation = yaml.safe_load(f.read()) container = plan
validation_groups = get_validation_metadata(validation, 'groups') \
or [] try:
if not groups or \ objects = swift.get_container(
set.intersection(set(groups), set(validation_groups)): container, prefix=constants.CUSTOM_VALIDATIONS_FOLDER)[1]
results.append({ except swiftexceptions.ClientException:
'id': os.path.splitext( pass
os.path.basename(validation_path))[0], else:
'name': get_validation_metadata(validation, 'name'), results = _get_validations_from_swift(
'groups': get_validation_metadata(validation, 'groups'), swift, container, objects, groups, results)
'description': get_validation_metadata(validation,
'description'), # Get default validations
'metadata': get_remaining_metadata(validation) container = constants.VALIDATIONS_CONTAINER_NAME
}) objects = swift.get_container(container)[1]
results = _get_validations_from_swift(swift, container, objects, groups,
results, skip_existing=True)
return results return results
@@ -73,11 +111,36 @@ def get_remaining_metadata(validation):
return dict() return dict()
def find_validation(validation): def download_validation(swift, plan, validation):
return '{}/{}.yaml'.format(constants.DEFAULT_VALIDATIONS_PATH, validation) """Downloads validations from Swift to a temporary location"""
dst_dir = '/tmp/{}-validations'.format(plan)
# Download the whole default validations container
swift_utils.download_container(
swift,
constants.VALIDATIONS_CONTAINER_NAME,
dst_dir,
overwrite_only_newer=True
)
filename = '{}.yaml'.format(validation)
swift_path = os.path.join(constants.CUSTOM_VALIDATIONS_FOLDER, filename)
dst_path = os.path.join(dst_dir, filename)
# If a custom validation with that name exists, get it from the plan
# container and override. Otherwise, the default one will be used.
try:
contents = swift.get_object(plan, swift_path)[1]
except swiftexceptions.ClientException:
pass
else:
with open(dst_path, 'w') as f:
f.write(contents)
return dst_path
def run_validation(validation, identity_file, plan, context): def run_validation(swift, validation, identity_file, plan, context):
return processutils.execute( return processutils.execute(
'/usr/bin/sudo', '-u', 'validations', '/usr/bin/sudo', '-u', 'validations',
'OS_AUTH_URL={}'.format(context.auth_uri), 'OS_AUTH_URL={}'.format(context.auth_uri),
@@ -85,7 +148,7 @@ def run_validation(validation, identity_file, plan, context):
'OS_AUTH_TOKEN={}'.format(context.auth_token), 'OS_AUTH_TOKEN={}'.format(context.auth_token),
'OS_TENANT_NAME={}'.format(context.project_name), 'OS_TENANT_NAME={}'.format(context.project_name),
'/usr/bin/run-validation', '/usr/bin/run-validation',
find_validation(validation), download_validation(swift, plan, validation),
identity_file, identity_file,
plan plan
) )

View File

@@ -121,7 +121,7 @@ workflows:
find_validations: find_validations:
on-success: notify_running on-success: notify_running
action: tripleo.validations.list_validations groups=<% $.group_names %> action: tripleo.validations.list_validations plan=<% $.plan %> groups=<% $.group_names %>
publish: publish:
validations: <% task().result %> validations: <% task().result %>
@@ -168,6 +168,7 @@ workflows:
list: list:
input: input:
- group_names: [] - group_names: []
- plan: overcloud
output: output:
validations: <% $.validations %> validations: <% $.validations %>
tags: tags:
@@ -175,7 +176,7 @@ workflows:
tasks: tasks:
find_validations: find_validations:
on-success: send_message on-success: send_message
action: tripleo.validations.list_validations groups=<% $.group_names %> action: tripleo.validations.list_validations plan=<% $.plan %> groups=<% $.group_names %>
publish: publish:
status: SUCCESS status: SUCCESS
message: <% task().result %> message: <% task().result %>
@@ -195,13 +196,15 @@ workflows:
validations: <% $.get('validations', []) %> validations: <% $.get('validations', []) %>
list_groups: list_groups:
input:
- plan: overcloud
output: output:
groups: <% task(find_groups).result %> groups: <% task(find_groups).result %>
tags: tags:
- tripleo-common-managed - tripleo-common-managed
tasks: tasks:
find_groups: find_groups:
action: tripleo.validations.list_groups action: tripleo.validations.list_groups plan=<% $.plan %>
add_validation_ssh_key_parameter: add_validation_ssh_key_parameter:
input: input: