Workflows to load validations

This commit adds workflow to list available validations and validation
groups.

Change-Id: I85fb6c1939e711887b2ee91c9cefa41a3da681a3
This commit is contained in:
Martin André 2016-08-10 14:48:35 +02:00
parent 81514076c8
commit ceecdadbf4
7 changed files with 255 additions and 1 deletions

View File

@ -72,3 +72,5 @@ mistral.actions =
tripleo.reset_parameters = tripleo_common.actions.parameters:ResetParametersAction
tripleo.deploy = tripleo_common.actions.deployment:DeployStackAction
tripleo.validations.get_pubkey = tripleo_common.actions.validations:GetPubkeyAction
tripleo.validations.list_validations = tripleo_common.actions.validations:ListValidationsAction
tripleo.validations.list_groups = tripleo_common.actions.validations:ListGroupsAction

View File

@ -53,3 +53,26 @@ class GetPubkeyAction(base.TripleOAction):
mc.environments.create(**workflow_env)
return public_key
class ListValidationsAction(base.TripleOAction):
"""Return a set of TripleO validations"""
def __init__(self, groups=None):
super(ListValidationsAction, self).__init__()
self.groups = groups
def run(self):
return utils.load_validations(groups=self.groups)
class ListGroupsAction(base.TripleOAction):
"""Return a set of TripleO validation groups"""
def __init__(self):
super(ListGroupsAction, self).__init__()
def run(self):
validations = utils.load_validations()
return {
group for validation in validations
for group in validation['groups']
}

View File

@ -32,6 +32,10 @@ DEFAULT_CONTAINER_NAME = 'overcloud'
#: The path to the tripleo heat templates installed on the undercloud
DEFAULT_TEMPLATES_PATH = '/usr/share/openstack-tripleo-heat-templates/'
# The path to the tripleo validations installed on the undercloud
DEFAULT_VALIDATIONS_PATH = \
'/usr/share/openstack-tripleo-validations/validations/'
# TRIPLEO_META_USAGE_KEY is inserted into metadata for containers created in
# Swift via SwiftPlanStorageBackend to identify them from other containers
TRIPLEO_META_USAGE_KEY = 'x-container-meta-usage-tripleo'

View File

@ -17,6 +17,7 @@ import mock
from tripleo_common.actions import validations
from tripleo_common.tests import base
from tripleo_common.tests.utils import test_validations
class GetPubkeyActionTest(base.TestCase):
@ -55,3 +56,35 @@ class GetPubkeyActionTest(base.TestCase):
mock_mkdtemp.assert_called_once()
mock_create_keypair.assert_called_once_with('/tmp_path/id_rsa')
mock_rmtree.asser_called_once_with('/tmp_path')
class ListValidationsActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.validations.load_validations')
def test_run_default(self, mock_load_validations):
mock_load_validations.return_value = 'list of validations'
action = validations.ListValidationsAction()
self.assertEqual('list of validations', action.run())
mock_load_validations.assert_called_once_with(groups=None)
@mock.patch('tripleo_common.utils.validations.load_validations')
def test_run_groups(self, mock_load_validations):
mock_load_validations.return_value = 'list of validations'
action = validations.ListValidationsAction(groups=['group1',
'group2'])
self.assertEqual('list of validations', action.run())
mock_load_validations.assert_called_once_with(groups=['group1',
'group2'])
class ListGroupsActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.validations.load_validations')
def test_run(self, mock_load_validations):
mock_load_validations.return_value = [
test_validations.VALIDATION_GROUPS_1_2_PARSED,
test_validations.VALIDATION_GROUP_1_PARSED,
test_validations.VALIDATION_WITH_METADATA_PARSED]
action = validations.ListGroupsAction()
self.assertEqual(set(['group1', 'group2']), action.run())
mock_load_validations.assert_called_once_with()

View File

@ -14,12 +14,78 @@
# limitations under the License.
import mock
import yaml
from tripleo_common.tests import base
from tripleo_common.utils import validations
class ValidationsTest(base.TestCase):
VALIDATION_GROUP_1 = """---
- hosts: overcloud
vars:
metadata:
name: First validation
description: A validation belonging to group1
groups:
- group1
tasks:
- name: Ping the nodes
ping:
"""
VALIDATION_WITH_METADATA = """---
- hosts: undercloud
vars:
metadata:
name: Validation with metadata
description: A validation with extra metadata
foo: a foo metadata
bar: 42
tasks:
- name: Do something useful
watch_tv:
"""
VALIDATION_GROUPS_1_2 = """---
- hosts: undercloud
vars:
metadata:
name: Validation from many groups
description: A validation belonging to groups 1 and 2
groups:
- group1
- group2
tasks:
- name: Do something useful
watch_tv:
"""
VALIDATION_GROUP_1_PARSED = {
'description': 'A validation belonging to group1',
'groups': ['group1'],
'id': 'VALIDATION_GROUP_1',
'metadata': {},
'name': 'First validation',
}
VALIDATION_WITH_METADATA_PARSED = {
'description': 'A validation with extra metadata',
'groups': [],
'id': 'VALIDATION_WITH_METADATA',
'metadata': {'foo': 'a foo metadata', 'bar': 42},
'name': 'Validation with metadata',
}
VALIDATION_GROUPS_1_2_PARSED = {
'description': 'A validation belonging to groups 1 and 2',
'groups': ['group1', 'group2'],
'id': 'VALIDATION_GROUPS_1_2',
'metadata': {},
'name': 'Validation from many groups',
}
class ValidationsKeyTest(base.TestCase):
@mock.patch("oslo_concurrency.processutils.execute")
def test_create_ssh_keypair(self, mock_execute):
@ -27,3 +93,63 @@ class ValidationsTest(base.TestCase):
mock_execute.assert_called_once_with(
'/usr/bin/ssh-keygen', '-t', 'rsa', '-N', '',
'-f', '/path/to/key', '-C', 'tripleo-validations')
class LoadValidationsTest(base.TestCase):
def test_get_validation_metadata(self):
validation = yaml.safe_load(VALIDATION_GROUP_1)
value = validations.get_validation_metadata(validation, 'name')
self.assertEqual('First validation', value)
@mock.patch('tripleo_common.utils.validations.DEFAULT_METADATA')
def test_get_validation_metadata_default_value(self, mock_metadata):
mock_metadata.get.return_value = 'default_value'
value = validations.get_validation_metadata({}, 'missing')
self.assertEqual('default_value', value)
def test_get_remaining_metadata(self):
validation = yaml.safe_load(VALIDATION_WITH_METADATA)
value = validations.get_remaining_metadata(validation)
expected = {
'foo': 'a foo metadata',
'bar': 42
}
self.assertEqual(expected, value)
def test_get_remaining_metadata_no_extra(self):
validation = yaml.safe_load(VALIDATION_GROUP_1)
value = validations.get_remaining_metadata(validation)
self.assertEqual({}, value)
@mock.patch('glob.glob')
def test_load_validations_no_group(self, mock_glob):
mock_glob.return_value = ['VALIDATION_GROUP_1',
'VALIDATION_WITH_METADATA']
mock_open_context = mock.mock_open()
mock_open_context().read.side_effect = [VALIDATION_GROUP_1,
VALIDATION_WITH_METADATA]
with mock.patch('tripleo_common.utils.validations.open',
mock_open_context):
my_validations = validations.load_validations()
expected = [VALIDATION_GROUP_1_PARSED, VALIDATION_WITH_METADATA_PARSED]
self.assertEqual(expected, my_validations)
@mock.patch('glob.glob')
def test_load_validations_group(self, mock_glob):
mock_glob.return_value = ['VALIDATION_GROUPS_1_2',
'VALIDATION_GROUP_1',
'VALIDATION_WITH_METADATA']
mock_open_context = mock.mock_open()
mock_open_context().read.side_effect = [VALIDATION_GROUPS_1_2,
VALIDATION_GROUP_1,
VALIDATION_WITH_METADATA]
with mock.patch('tripleo_common.utils.validations.open',
mock_open_context):
my_validations = validations.load_validations(groups=['group1'])
expected = [VALIDATION_GROUPS_1_2_PARSED, VALIDATION_GROUP_1_PARSED]
self.assertEqual(expected, my_validations)

View File

@ -12,12 +12,64 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import glob
import logging
import os
import yaml
from oslo_concurrency import processutils
from tripleo_common import constants
LOG = logging.getLogger(__name__)
DEFAULT_METADATA = {
'name': 'Unnamed',
'description': 'No description',
'stage': 'No stage',
'groups': [],
}
def get_validation_metadata(validation, key):
try:
return validation[0]['vars']['metadata'][key]
except KeyError:
return DEFAULT_METADATA.get(key)
except TypeError:
LOG.exception("Failed to get validation metadata.")
def load_validations(groups=None):
'''Loads all validations.'''
paths = glob.glob('{}/*.yaml'.format(constants.DEFAULT_VALIDATIONS_PATH))
results = []
for validation_path in sorted(paths):
with open(validation_path) as f:
validation = yaml.safe_load(f.read())
validation_groups = get_validation_metadata(validation, 'groups') \
or []
if not groups or \
set.intersection(set(groups), set(validation_groups)):
results.append({
'id': os.path.splitext(
os.path.basename(validation_path))[0],
'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 get_remaining_metadata(validation):
try:
return {k: v for k, v in validation[0]['vars']['metadata'].items()
if k not in ['name', 'description', 'groups']}
except KeyError:
return dict()
def create_ssh_keypair(key_path):
"""Create SSH keypair"""

View File

@ -5,6 +5,20 @@ description: TripleO Validations Workflows v1
workflows:
list:
type: direct
input:
- group_names: []
tasks:
find_validations:
action: tripleo.validations.list_validations groups=<% $.group_names %>
list_groups:
type: direct
tasks:
find_groups:
action: tripleo.validations.list_groups
copy_ssh_key:
type: direct
input: