Merge "Add the community validation paths"

This commit is contained in:
Zuul 2021-11-18 19:39:23 +00:00 committed by Gerrit Code Review
commit 1b94c03ee7
10 changed files with 386 additions and 70 deletions

View File

@ -126,8 +126,16 @@ class Ansible(object):
gathering_policy, module_path, key,
extra_env_variables, ansible_timeout,
callback_whitelist, base_dir, python_interpreter,
env={}):
env={}, validation_cfg_file=None):
"""Handle Ansible env var for Ansible config execution"""
community_roles = ""
community_library = ""
community_lookup = ""
if utils.community_validations_on(validation_cfg_file):
community_roles = f"{constants.COMMUNITY_ROLES_DIR}:"
community_library = f"{constants.COMMUNITY_LIBRARY_DIR}:"
community_lookup = f"{constants.COMMUNITY_LOOKUP_DIR}:"
cwd = os.getcwd()
env['ANSIBLE_SSH_ARGS'] = (
'-o UserKnownHostsFile={} '
@ -159,10 +167,12 @@ class Ansible(object):
'{}:{}:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/ceph-ansible/library:'
'{community_path}'
'{}/library'.format(
os.path.join(workdir, 'modules'),
os.path.join(cwd, 'modules'),
base_dir
base_dir,
community_path=community_library
)
)
env['ANSIBLE_LOOKUP_PLUGINS'] = os.path.expanduser(
@ -170,10 +180,12 @@ class Ansible(object):
'{}:{}:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/ceph-ansible/plugins/lookup:'
'{community_path}'
'{}/lookup_plugins'.format(
os.path.join(workdir, 'lookup'),
os.path.join(cwd, 'lookup'),
base_dir
base_dir,
community_path=community_lookup
)
)
env['ANSIBLE_CALLBACK_PLUGINS'] = os.path.expanduser(
@ -215,10 +227,12 @@ class Ansible(object):
'/usr/share/ansible/roles:'
'/usr/share/ceph-ansible/roles:'
'/etc/ansible/roles:'
'{community_path}'
'{}/roles'.format(
os.path.join(workdir, 'roles'),
os.path.join(cwd, 'roles'),
base_dir
base_dir,
community_path=community_roles
)
)
env['ANSIBLE_CALLBACK_WHITELIST'] = callback_whitelist
@ -421,7 +435,8 @@ class Ansible(object):
connection, gathering_policy,
module_path, key, extra_env_variables,
ansible_timeout, callback_whitelist,
base_dir, python_interpreter))
base_dir, python_interpreter,
validation_cfg_file=validation_cfg_file))
if 'ANSIBLE_CONFIG' not in env and not ansible_cfg_file:
ansible_cfg_file = os.path.join(ansible_artifact_path,

View File

@ -67,4 +67,5 @@ class ValidationList(BaseLister):
v_actions = ValidationActions(validation_path=validation_dir)
return (v_actions.list_validations(groups=group,
categories=category,
products=product))
products=product,
validation_config=self.base.config))

View File

@ -45,7 +45,8 @@ class Show(BaseShow):
validation_name = parsed_args.validation_name
v_actions = ValidationActions(validation_path=validation_dir)
data = v_actions.show_validations(validation_name)
data = v_actions.show_validations(
validation_name, validation_config=self.base.config)
if data:
return data.keys(), data.values()
@ -70,7 +71,9 @@ class ShowGroup(BaseLister):
self.base.set_argument_parser(self, parsed_args)
v_actions = ValidationActions(parsed_args.validation_dir)
return v_actions.group_information(constants.VALIDATION_GROUPS_INFO)
return v_actions.group_information(
constants.VALIDATION_GROUPS_INFO,
validation_config=self.base.config)
class ShowParameter(BaseShow):
@ -162,7 +165,8 @@ class ShowParameter(BaseShow):
categories=parsed_args.category,
products=parsed_args.product,
output_format=parsed_args.format_output,
download_file=parsed_args.download)
download_file=parsed_args.download,
validation_config=self.base.config)
if parsed_args.download:
self.app.LOG.info(

View File

@ -248,7 +248,8 @@ FAKE_PLAYBOOK = [{'hosts': 'undercloud',
'categories': ['os', 'storage'],
'products': ['product1'],
'name':
'Advanced Format 512e Support'}}}]
'Advanced Format 512e Support',
'path': '/tmp'}}}]
FAKE_PLAYBOOK2 = [{'hosts': 'undercloud',
'roles': ['advanced_format_512e_support'],
@ -274,14 +275,16 @@ FAKE_METADATA = {'id': 'foo',
'groups': ['prep', 'pre-deployment'],
'categories': ['os', 'storage'],
'products': ['product1'],
'name': 'Advanced Format 512e Support'}
'name': 'Advanced Format 512e Support',
'path': '/tmp'}
FORMATED_DATA = {'Description': 'foo',
'Groups': ['prep', 'pre-deployment'],
'Categories': ['os', 'storage'],
'Products': ['product1'],
'ID': 'foo',
'Name': 'Advanced Format 512e Support'}
'Name': 'Advanced Format 512e Support',
'Path': '/tmp'}
GROUP = {'no-op': [{'description': 'noop-foo'}],
'pre': [{'description': 'pre-foo'}],

View File

@ -21,6 +21,7 @@ except ImportError:
from unittest import TestCase
from ansible_runner import Runner
from validations_libs import constants
from validations_libs.ansible import Ansible
from validations_libs.tests import fakes
@ -175,6 +176,56 @@ class TestAnsible(TestCase):
'/tmp/foo/fact_cache'
))
def test_ansible_env_var_with_community_validations(self):
# AP No config file (use the default True)
env = self.run._ansible_env_var(
output_callback="", ssh_user="", workdir="", connection="",
gathering_policy="", module_path="", key="",
extra_env_variables="", ansible_timeout="",
callback_whitelist="", base_dir="", python_interpreter="",
env={}, validation_cfg_file=None)
assert(f"{constants.COMMUNITY_LIBRARY_DIR}:" in env["ANSIBLE_LIBRARY"])
assert(f"{constants.COMMUNITY_ROLES_DIR}:" in env["ANSIBLE_ROLES_PATH"])
assert(f"{constants.COMMUNITY_LOOKUP_DIR}:" in env["ANSIBLE_LOOKUP_PLUGINS"])
# AP config file with no settting (use the default True)
env = self.run._ansible_env_var(
output_callback="", ssh_user="", workdir="", connection="",
gathering_policy="", module_path="", key="",
extra_env_variables="", ansible_timeout="",
callback_whitelist="", base_dir="", python_interpreter="",
env={}, validation_cfg_file={"default": {}})
assert(f"{constants.COMMUNITY_LIBRARY_DIR}:" in env["ANSIBLE_LIBRARY"])
assert(f"{constants.COMMUNITY_ROLES_DIR}:" in env["ANSIBLE_ROLES_PATH"])
assert(f"{constants.COMMUNITY_LOOKUP_DIR}:" in env["ANSIBLE_LOOKUP_PLUGINS"])
# AP config file with settting True
env = self.run._ansible_env_var(
output_callback="", ssh_user="", workdir="", connection="",
gathering_policy="", module_path="", key="",
extra_env_variables="", ansible_timeout="",
callback_whitelist="", base_dir="", python_interpreter="",
env={}, validation_cfg_file={"default": {"enable_community_validations": True}})
assert(f"{constants.COMMUNITY_LIBRARY_DIR}:" in env["ANSIBLE_LIBRARY"])
assert(f"{constants.COMMUNITY_ROLES_DIR}:" in env["ANSIBLE_ROLES_PATH"])
assert(f"{constants.COMMUNITY_LOOKUP_DIR}:" in env["ANSIBLE_LOOKUP_PLUGINS"])
def test_ansible_env_var_without_community_validations(self):
# AP config file with settting False
env = self.run._ansible_env_var(
output_callback="", ssh_user="", workdir="", connection="",
gathering_policy="", module_path="", key="",
extra_env_variables="", ansible_timeout="",
callback_whitelist="", base_dir="", python_interpreter="",
env={}, validation_cfg_file={"default": {"enable_community_validations": False}})
assert(f"{constants.COMMUNITY_LIBRARY_DIR}:" not in env["ANSIBLE_LIBRARY"])
assert(f"{constants.COMMUNITY_ROLES_DIR}:" not in env["ANSIBLE_ROLES_PATH"])
assert(f"{constants.COMMUNITY_LOOKUP_DIR}:" not in env["ANSIBLE_LOOKUP_PLUGINS"])
def test_get_extra_vars_dict(self):
extra_vars = {
'foo': 'bar'

View File

@ -44,10 +44,48 @@ class TestUtils(TestCase):
'Categories': ['os', 'storage'],
'Products': ['product1'],
'ID': '512e',
'Parameters': {}}
'Parameters': {},
'Path': '/tmp'}
res = utils.get_validations_data('512e')
self.assertEqual(res, output)
@mock.patch('validations_libs.validation.Validation._get_content',
return_value=fakes.FAKE_PLAYBOOK[0])
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.exists', side_effect=(False, True))
def test_get_community_validations_data(self, mock_exists, mock_open, mock_data):
"""
The main difference between this test and test_get_validations_data
is that this one tries to load first the validations_commons validation
then it fails as os.path.exists returns false and then looks for it in the
community validations.
"""
output = {'Name': 'Advanced Format 512e Support',
'Description': 'foo', 'Groups': ['prep', 'pre-deployment'],
'Categories': ['os', 'storage'],
'Products': ['product1'],
'ID': '512e',
'Parameters': {},
'Path': '/tmp'}
res = utils.get_validations_data('512e')
self.assertEqual(res, output)
@mock.patch('validations_libs.validation.Validation._get_content',
return_value=fakes.FAKE_PLAYBOOK[0])
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.exists', side_effect=(False, True))
def test_get_community_disabled_validations_data(self, mock_exists, mock_open, mock_data):
"""
This test is similar to test_get_community_validations_data in the sense that it
doesn't find the validations_commons one and should look for community validations
but the setting is disabled by the config so it shouldn't find any validations
"""
output = {}
res = utils.get_validations_data(
'512e',
validation_config={'default': {"enable_community_validations": False}})
self.assertEqual(res, output)
@mock.patch('os.path.exists', return_value=True)
def test_get_validations_data_wrong_type(self, mock_exists):
validation = ['val1']
@ -60,11 +98,33 @@ class TestUtils(TestCase):
@mock.patch('glob.glob')
def test_parse_all_validations_on_disk(self, mock_glob, mock_open,
mock_load):
mock_glob.return_value = \
['/foo/playbook/foo.yaml']
mock_glob.side_effect = \
(['/foo/playbook/foo.yaml'], [])
result = utils.parse_all_validations_on_disk('/foo/playbook')
self.assertEqual(result, [fakes.FAKE_METADATA])
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
@mock.patch('glob.glob')
def test_parse_community_validations_on_disk(
self, mock_glob, mock_open, mock_load):
mock_glob.side_effect = \
([], ['/foo/playbook/foo.yaml'])
result = utils.parse_all_validations_on_disk('/foo/playbook')
self.assertEqual(result, [fakes.FAKE_METADATA])
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
@mock.patch('glob.glob')
def test_parse_all_community_disabled_validations_on_disk(
self, mock_glob, mock_open, mock_load):
mock_glob.side_effect = \
([], ['/foo/playbook/foo.yaml'])
result = utils.parse_all_validations_on_disk(
'/foo/playbook',
validation_config={'default': {"enable_community_validations": False}})
self.assertEqual(result, [])
def test_parse_all_validations_on_disk_wrong_path_type(self):
self.assertRaises(TypeError,
utils.parse_all_validations_on_disk,
@ -118,8 +178,8 @@ class TestUtils(TestCase):
def test_parse_all_validations_on_disk_by_group(self, mock_glob,
mock_open,
mock_load):
mock_glob.return_value = \
['/foo/playbook/foo.yaml']
mock_glob.side_effect = \
(['/foo/playbook/foo.yaml'], [])
result = utils.parse_all_validations_on_disk('/foo/playbook',
['prep'])
self.assertEqual(result, [fakes.FAKE_METADATA])
@ -130,8 +190,8 @@ class TestUtils(TestCase):
def test_parse_all_validations_on_disk_by_category(self, mock_glob,
mock_open,
mock_load):
mock_glob.return_value = \
['/foo/playbook/foo.yaml']
mock_glob.side_effect = \
(['/foo/playbook/foo.yaml'], [])
result = utils.parse_all_validations_on_disk('/foo/playbook',
categories=['os'])
self.assertEqual(result, [fakes.FAKE_METADATA])
@ -147,31 +207,80 @@ class TestUtils(TestCase):
def test_parse_all_validations_on_disk_by_product(self, mock_glob,
mock_open,
mock_load):
mock_glob.return_value = \
['/foo/playbook/foo.yaml']
mock_glob.side_effect = (['/foo/playbook/foo.yaml'], [])
result = utils.parse_all_validations_on_disk('/foo/playbook',
products=['product1'])
self.assertEqual(result, [fakes.FAKE_METADATA])
@mock.patch('os.path.isfile')
@mock.patch('os.listdir')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_validations_playbook_by_id(self, mock_open, mock_load,
mock_listdir, mock_isfile):
mock_listdir.return_value = ['foo.yaml']
mock_glob, mock_isfile):
mock_glob.side_effect = (['/foo/playbook/foo.yaml'], [])
mock_isfile.return_value = True
result = utils.get_validations_playbook('/foo/playbook',
validation_id=['foo'])
self.assertEqual(result, ['/foo/playbook/foo.yaml'])
@mock.patch('os.path.isfile')
@mock.patch('os.listdir')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_community_playbook_by_id(self, mock_open, mock_load,
mock_glob, mock_isfile):
mock_glob.side_effect = (
[],
['/home/foo/community-validations/playbooks/foo.yaml'])
mock_isfile.return_value = True
# AP this needs a bit of an explanation. We look at the explicity at
# the /foo/playbook directory but the community validation path is
# implicit and we find there the id that we are looking for.
result = utils.get_validations_playbook('/foo/playbook',
validation_id=['foo'])
self.assertEqual(result, ['/home/foo/community-validations/playbooks/foo.yaml'])
@mock.patch('os.path.isfile')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_community_disabled_playbook_by_id(
self, mock_open, mock_load, mock_glob, mock_isfile):
mock_glob.side_effect = (
[],
['/home/foo/community-validations/playbooks/foo.yaml'])
mock_isfile.return_value = True
# The validations_commons validation is not found and community_vals is disabled
# So no validation should be found.
result = utils.get_validations_playbook(
'/foo/playbook',
validation_id=['foo'],
validation_config={'default': {"enable_community_validations": False}})
self.assertEqual(result, [])
@mock.patch('os.path.isfile')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_community_playbook_by_id_not_found(
self, mock_open, mock_load, mock_glob, mock_isfile):
mock_glob.side_effect = (
[],
['/home/foo/community-validations/playbooks/foo.yaml/'])
# the is file fails
mock_isfile.return_value = False
result = utils.get_validations_playbook('/foo/playbook',
validation_id=['foo'])
self.assertEqual(result, [])
@mock.patch('os.path.isfile')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_validations_playbook_by_id_group(self, mock_open, mock_load,
mock_listdir, mock_isfile):
mock_listdir.return_value = ['foo.yaml']
mock_glob, mock_isfile):
mock_glob.side_effect = (['/foo/playbook/foo.yaml'], [])
mock_isfile.return_value = True
result = utils.get_validations_playbook('/foo/playbook', ['foo'], ['prep'])
self.assertEqual(result, ['/foo/playbook/foo.yaml',
@ -192,24 +301,24 @@ class TestUtils(TestCase):
self.assertEqual(result, [])
@mock.patch('os.path.isfile')
@mock.patch('os.listdir')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_validations_playbook_by_category(self, mock_open, mock_load,
mock_listdir, mock_isfile):
mock_listdir.return_value = ['foo.yaml']
mock_glob, mock_isfile):
mock_glob.side_effect = (['/foo/playbook/foo.yaml'], [])
mock_isfile.return_value = True
result = utils.get_validations_playbook('/foo/playbook',
categories=['os', 'storage'])
self.assertEqual(result, ['/foo/playbook/foo.yaml'])
@mock.patch('os.path.isfile')
@mock.patch('os.listdir')
@mock.patch('glob.glob')
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
@mock.patch('six.moves.builtins.open')
def test_get_validations_playbook_by_product(self, mock_open, mock_load,
mock_listdir, mock_isfile):
mock_listdir.return_value = ['foo.yaml']
mock_glob, mock_isfile):
mock_glob.side_effect = (['/foo/playbook/foo.yaml'], [])
mock_isfile.return_value = True
result = utils.get_validations_playbook('/foo/playbook',
products=['product1'])

View File

@ -15,8 +15,10 @@
try:
from unittest import mock
from unittest.mock import ANY
except ImportError:
import mock
from mock import ANY
import json
@ -209,7 +211,8 @@ class TestValidationActions(TestCase):
'groups': ['prep', 'pre-deployment'],
'id': 'foo',
'name': 'My Validition One Name',
'parameters': {}}]
'parameters': {},
'path': '/tmp/foobar/validation-playbooks'}]
mock_ansible_run.return_value = ('foo.yaml', 0, 'successful')
@ -224,6 +227,29 @@ class TestValidationActions(TestCase):
validations_dir='/tmp/foo')
self.assertEqual(run_return, expected_run_return)
mock_ansible_run.assert_called_with(
workdir=ANY,
playbook='/tmp/foobar/validation-playbooks/foo.yaml',
base_dir='/usr/share/ansible',
playbook_dir='/tmp/foobar/validation-playbooks',
parallel_run=True,
inventory='tmp/inventory.yaml',
output_callback='validation_stdout',
callback_whitelist=None,
quiet=True,
extra_vars=None,
limit_hosts=None,
extra_env_variables=None,
ansible_cfg_file=None,
gathering_policy='explicit',
ansible_artifact_path=ANY,
log_path=ANY,
run_async=False,
python_interpreter=None,
ssh_user=None,
validation_cfg_file=None
)
@mock.patch('validations_libs.utils.get_validations_playbook')
def test_validation_run_wrong_validation_name(self, mock_validation_play):
mock_validation_play.return_value = []
@ -234,6 +260,31 @@ class TestValidationActions(TestCase):
validations_dir='/tmp/foo'
)
@mock.patch('validations_libs.utils.get_validations_playbook')
def test_validation_run_not_all_found(self, mock_validation_play):
mock_validation_play.return_value = ['/tmp/foo/fake.yaml']
run = ValidationActions()
try:
run.run_validations(
validation_name=['fake', 'foo'],
validations_dir='/tmp/foo')
except RuntimeError as runtime_error:
self.assertEqual(
"Validation ['foo'] not found in /tmp/foo.",
str(runtime_error))
else:
self.fail("Runtime error exception should have been raised")
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
def test_validation_run_not_enough_params(self, mock_validation_play):
mock_validation_play.return_value = []
run = ValidationActions()
self.assertRaises(RuntimeError, run.run_validations,
validations_dir='/tmp/foo'
)
@mock.patch('validations_libs.utils.os.makedirs')
@mock.patch('validations_libs.utils.os.access', return_value=True)
@mock.patch('validations_libs.utils.os.path.exists', return_value=True)
@ -250,7 +301,8 @@ class TestValidationActions(TestCase):
'groups': ['prep', 'pre-deployment'],
'id': 'foo',
'name': 'My Validition One Name',
'parameters': {}}]
'parameters': {},
'path': '/usr/share/ansible/validation-playbooks'}]
mock_ansible_run.return_value = ('foo.yaml', 0, 'failed')
@ -295,7 +347,8 @@ class TestValidationActions(TestCase):
'groups': ['prep', 'pre-deployment'],
'id': 'foo',
'name': 'My Validition One Name',
'parameters': {}}]
'parameters': {},
'path': '/usr/share/ansible/validation-playbooks'}]
playbook = ['fake.yaml']
inventory = 'tmp/inventory.yaml'
@ -321,7 +374,8 @@ class TestValidationActions(TestCase):
'groups': ['prep', 'pre-deployment'],
'id': 'foo',
'name': 'My Validition One Name',
'parameters': {}}]
'parameters': {},
'path': '/usr/share/ansible/validation-playbooks'}]
playbook = ['fake.yaml']
inventory = 'tmp/inventory.yaml'
@ -357,7 +411,9 @@ class TestValidationActions(TestCase):
'Categories': ['os', 'storage'],
'Products': ['product1'],
'ID': '512e',
'Parameters': {}}
'Parameters': {},
'Path': '/tmp'
}
data.update({'Last execution date': '2019-11-25 13:40:14',
'Number of execution': 'Total: 1, Passed: 0, Failed: 1'})
validations_show = ValidationActions()

View File

@ -37,6 +37,21 @@ def current_time():
return '%sZ' % datetime.datetime.utcnow().isoformat()
def community_validations_on(validation_config):
"""Check for flag for community validations to be enabled
The default value is true
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: A boolean with the status of community validations flag
:rtype: `bool`
"""
if not validation_config:
return True
return validation_config.get("default", {}).get("enable_community_validations", True)
def create_log_dir(log_path=constants.VALIDATIONS_LOG_BASEDIR):
"""Check for presence of the selected validations log dir.
Create the directory if needed, and use fallback if that
@ -136,7 +151,8 @@ def create_artifacts_dir(log_path=constants.VALIDATIONS_LOG_BASEDIR,
def parse_all_validations_on_disk(path,
groups=None,
categories=None,
products=None):
products=None,
validation_config=None):
"""Return a list of validations metadata which can be sorted by Groups, by
Categories or by Products.
@ -152,6 +168,10 @@ def parse_all_validations_on_disk(path,
:param products: Products of validations
:type products: `list`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: A list of validations metadata.
:rtype: `list`
@ -192,6 +212,9 @@ def parse_all_validations_on_disk(path,
results = []
validations_abspath = glob.glob("{path}/*.yaml".format(path=path))
if community_validations_on(validation_config):
validations_abspath.extend(glob.glob("{}/*.yaml".format(
constants.COMMUNITY_PLAYBOOKS_DIR)))
LOG.debug(
"Attempting to parse validations by:\n"
@ -200,7 +223,6 @@ def parse_all_validations_on_disk(path,
" - products: {}\n"
"from {}".format(groups, categories, products, validations_abspath)
)
for playbook in validations_abspath:
val = Validation(playbook)
@ -220,7 +242,8 @@ def get_validations_playbook(path,
validation_id=None,
groups=None,
categories=None,
products=None):
products=None,
validation_config=None):
"""Get a list of validations playbooks paths either by their names,
their groups, by their categories or by their products.
@ -239,6 +262,10 @@ def get_validations_playbook(path,
:param products: List of validation product
:type products: `list`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: A list of absolute validations playbooks path
:rtype: `list`
@ -281,12 +308,15 @@ def get_validations_playbook(path,
raise TypeError("The 'products' argument must be a List")
pl = []
for f in os.listdir(path):
pl_path = join(path, f)
validations_abspath = glob.glob("{path}/*.yaml".format(path=path))
if community_validations_on(validation_config):
validations_abspath.extend(glob.glob("{}/*.yaml".format(
constants.COMMUNITY_PLAYBOOKS_DIR)))
for pl_path in validations_abspath:
if os.path.isfile(pl_path):
if validation_id:
if os.path.splitext(f)[0] in validation_id or \
os.path.basename(f) in validation_id:
if os.path.splitext(os.path.basename(pl_path))[0] in validation_id or \
os.path.basename(pl_path) in validation_id:
pl.append(pl_path)
val = Validation(pl_path)
@ -377,7 +407,10 @@ def get_validations_details(validation):
return {}
def get_validations_data(validation, path=constants.ANSIBLE_VALIDATION_DIR):
def get_validations_data(
validation,
path=constants.ANSIBLE_VALIDATION_DIR,
validation_config=None):
"""Return validation data with format:
ID, Name, Description, Groups, Parameters
@ -387,6 +420,9 @@ def get_validations_data(validation, path=constants.ANSIBLE_VALIDATION_DIR):
:type validation: `string`
:param path: The path to the validations directory
:type path: `string`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: The validation data with the format
(ID, Name, Description, Groups, Parameters)
:rtype: `dict`
@ -408,6 +444,9 @@ def get_validations_data(validation, path=constants.ANSIBLE_VALIDATION_DIR):
data = {}
val_path = "{}/{}.yaml".format(path, validation)
comm_path = ""
if community_validations_on(validation_config):
comm_path = "{}/{}.yaml".format(constants.COMMUNITY_PLAYBOOKS_DIR, validation)
LOG.debug(
"Obtaining information about validation {} from {}".format(
@ -419,6 +458,11 @@ def get_validations_data(validation, path=constants.ANSIBLE_VALIDATION_DIR):
val = Validation(val_path)
data.update(val.get_formated_data)
data.update({'Parameters': val.get_vars})
if not data and comm_path:
if os.path.exists(comm_path):
val = Validation(comm_path)
data.update(val.get_formated_data)
data.update({'Parameters': val.get_vars})
return data

View File

@ -89,6 +89,7 @@ class Validation(object):
def __init__(self, validation_path):
self.dict = self._get_content(validation_path)
self.id = os.path.splitext(os.path.basename(validation_path))[0]
self.path = os.path.dirname(validation_path)
def _get_content(self, val_path):
try:
@ -176,10 +177,11 @@ class Validation(object):
'categories': ['category1', 'category2'],
'products': ['product1', 'product2'],
'id': 'val1',
'name': 'The validation val1\'s name'}
'name': 'The validation val1\'s name',
'path': '/tmp/foo/'}
"""
if self.has_metadata_dict:
self.metadata = {'id': self.id}
self.metadata = {'id': self.id, 'path': self.path}
self.metadata.update(self.dict['vars'].get('metadata'))
return self.metadata
else:
@ -353,7 +355,8 @@ class Validation(object):
'Description': 'description of val',
'Groups': ['group1', 'group2'],
'ID': 'val',
'Name': 'validation one'}
'Name': 'validation one',
'path': '/tmp/foo/'}
"""
data = {}
metadata = self.get_metadata

View File

@ -50,7 +50,8 @@ class ValidationActions(object):
def list_validations(self,
groups=None,
categories=None,
products=None):
products=None,
validation_config=None):
"""Get a list of the validations selected by group membership or by
category. With their names, group membership information, categories and
products.
@ -66,6 +67,10 @@ class ValidationActions(object):
:param products: List of validation products.
:type products: `list`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: Column names and a list of the selected validations
:rtype: `tuple`
@ -106,7 +111,8 @@ class ValidationActions(object):
path=self.validation_path,
groups=groups,
categories=categories,
products=products
products=products,
validation_config=validation_config
)
self.log.debug(
@ -124,13 +130,18 @@ class ValidationActions(object):
return (column_names, return_values)
def show_validations(self, validation,
log_path=constants.VALIDATIONS_LOG_BASEDIR):
log_path=constants.VALIDATIONS_LOG_BASEDIR,
validation_config=None):
"""Display detailed information about a Validation
:param validation: The name of the validation
:type validation: `string`
:param log_path: The absolute path of the validations logs
:type log_path: `string`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: The detailed information for a validation
:rtype: `dict`
@ -156,11 +167,18 @@ class ValidationActions(object):
self.log = logging.getLogger(__name__ + ".show_validations")
# Get validation data:
vlog = ValidationLogs(log_path)
data = v_utils.get_validations_data(validation, self.validation_path)
if not data:
msg = "Validation {} not found in the path: {}".format(
data = v_utils.get_validations_data(
validation,
self.validation_path)
self.validation_path,
validation_config=validation_config)
if not data:
extra_msg = ""
if v_utils.community_validations_on(validation_config):
extra_msg = " or {}".format(constants.COMMUNITY_LIBRARY_DIR)
msg = "Validation {} not found in the path: {}{}".format(
validation,
self.validation_path,
extra_msg)
raise RuntimeError(msg)
logfiles = vlog.get_logfile_content_by_validation(validation)
data_format = vlog.get_validations_stats(logfiles)
@ -331,15 +349,15 @@ class ValidationActions(object):
(Defaults to 'None')
:type skip_list: ``dict``
:return: A list of dictionary containing the informations of the
validations executions (Validations, Duration, Host_Group,
Status, Status_by_Host, UUID and Unreachable_Hosts)
:rtype: ``list``
:param ssh_user: Ssh user for Ansible remote connection
:type ssh_user: ``string``
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: A list of dictionary containing the informations of the
validations executions (Validations, Duration, Host_Group,
Status, Status_by_Host, UUID and Unreachable_Hosts)
:rtype: ``list``
:Example:
@ -378,13 +396,16 @@ class ValidationActions(object):
)
validations = v_utils.parse_all_validations_on_disk(
path=validations_dir, groups=group,
categories=category, products=product
categories=category, products=product,
validation_config=validation_config
)
for val in validations:
playbooks.append(val.get('id') + '.yaml')
playbooks.append("{path}/{id}.yaml".format(**val))
elif validation_name:
playbooks = v_utils.get_validations_playbook(validations_dir,
validation_name)
playbooks = v_utils.get_validations_playbook(
validations_dir,
validation_name,
validation_config=validation_config)
if not playbooks or len(validation_name) != len(playbooks):
p = []
@ -419,7 +440,7 @@ class ValidationActions(object):
workdir=artifacts_dir,
playbook=playbook,
base_dir=base_dir,
playbook_dir=validations_dir,
playbook_dir=os.path.dirname(playbook),
parallel_run=True,
inventory=inventory,
output_callback=output_callback,
@ -441,7 +462,7 @@ class ValidationActions(object):
workdir=artifacts_dir,
playbook=playbook,
base_dir=base_dir,
playbook_dir=validations_dir,
playbook_dir=os.path.dirname(playbook),
parallel_run=True,
inventory=inventory,
output_callback=output_callback,
@ -483,7 +504,7 @@ class ValidationActions(object):
vlog = ValidationLogs(log_path)
return vlog.get_results(uuid)
def group_information(self, groups):
def group_information(self, groups, validation_config=None):
"""Get Information about Validation Groups
This is used to print table from python ``Tuple`` with ``PrettyTable``.
@ -500,6 +521,9 @@ class ValidationActions(object):
:param groups: The absolute path of the groups.yaml file
:type groups: ``string``
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: The list of the available groups with their description and
the numbers of validation belonging to them.
@ -523,7 +547,8 @@ class ValidationActions(object):
validations = v_utils.parse_all_validations_on_disk(
path=self.validation_path,
groups=[group[0] for group in group_definitions])
groups=[group[0] for group in group_definitions],
validation_config=validation_config)
# Get validations number by group
for group in group_definitions:
@ -543,7 +568,8 @@ class ValidationActions(object):
categories=None,
products=None,
output_format='json',
download_file=None):
download_file=None,
validation_config=None):
"""
Return Validations Parameters for one or several validations by their
names, their groups, by their categories or by their products.
@ -566,6 +592,9 @@ class ValidationActions(object):
:param download_file: Path of a file in which the parameters will be
stored
:type download_file: `string`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
:type validation_config: ``dict``
:return: A JSON or a YAML dump (By default, JSON).
if `download_file` is used, a file containing only the
@ -622,7 +651,8 @@ class ValidationActions(object):
validation_id=validations,
groups=groups,
categories=categories,
products=products
products=products,
validation_config=validation_config
)
params = v_utils.get_validations_parameters(