Improved log path handling
Validations logging now has a documented fallback, in order to improve readability and provide reference. Tests are included. Complementary patch for validations_commons callbacks is optional, as the behavior should stay largely the same. Depends-On: https://review.opendev.org/c/openstack/validations-libs/+/794638 Signed-off-by: Jiri Podivin <jpodivin@redhat.com> Change-Id: I02df49532974cd0bcb2df8176b19ffc1cd5a65d1
This commit is contained in:
parent
6a0293b11e
commit
b0a4bd7140
|
@ -20,9 +20,7 @@ ANSIBLE_VALIDATION_DIR = '/usr/share/ansible/validation-playbooks'
|
||||||
|
|
||||||
VALIDATION_GROUPS_INFO = '%s/groups.yaml' % DEFAULT_VALIDATIONS_BASEDIR
|
VALIDATION_GROUPS_INFO = '%s/groups.yaml' % DEFAULT_VALIDATIONS_BASEDIR
|
||||||
|
|
||||||
VALIDATIONS_LOG_BASEDIR = ('/var/log/validations'
|
VALIDATIONS_LOG_BASEDIR = os.path.join(os.environ.get('HOME'), 'validations')
|
||||||
if os.path.exists('/var/log/validations') else
|
|
||||||
os.getcwd())
|
|
||||||
|
|
||||||
VALIDATION_ANSIBLE_ARTIFACT_PATH = '{}/artifacts/'.format(
|
VALIDATION_ANSIBLE_ARTIFACT_PATH = '{}/artifacts/'.format(
|
||||||
VALIDATIONS_LOG_BASEDIR)
|
VALIDATIONS_LOG_BASEDIR)
|
||||||
|
|
|
@ -47,17 +47,64 @@ class TestUtils(TestCase):
|
||||||
utils.get_validations_data,
|
utils.get_validations_data,
|
||||||
validation)
|
validation)
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.LOG.error')
|
||||||
|
@mock.patch('validations_libs.utils.LOG.warning')
|
||||||
|
@mock.patch('validations_libs.utils.os.path.exists',
|
||||||
|
return_value=True)
|
||||||
@mock.patch('validations_libs.utils.current_time',
|
@mock.patch('validations_libs.utils.current_time',
|
||||||
return_value='2020-04-02T06:58:20.352272Z')
|
return_value='2020-04-02T06:58:20.352272Z')
|
||||||
@mock.patch('os.makedirs')
|
@mock.patch('os.makedirs')
|
||||||
@mock.patch('uuid.uuid4', return_value='1234')
|
@mock.patch('uuid.uuid4', return_value='1234')
|
||||||
def test_create_artifacts_dir(self, mock_uuid, mock_makedirs,
|
def test_create_artifacts_subdir(self, mock_uuid, mock_makedirs,
|
||||||
mock_datetime):
|
mock_datetime, mock_path_exists,
|
||||||
uuid, dir_path = utils.create_artifacts_dir(dir_path='/tmp/foo',
|
mock_warning, mock_error):
|
||||||
prefix='ntp')
|
|
||||||
|
uuid, artifacts_dir = utils.get_artifacts_subdir(
|
||||||
|
log_dir='/tmp/foo/artifacts',
|
||||||
|
prefix='ntp')
|
||||||
|
|
||||||
self.assertEqual(uuid, '1234')
|
self.assertEqual(uuid, '1234')
|
||||||
self.assertEqual(dir_path,
|
self.assertEqual(artifacts_dir,
|
||||||
'/tmp/foo/1234_ntp_2020-04-02T06:58:20.352272Z')
|
'/tmp/foo/artifacts/1234_ntp_2020-04-02T06:58:20.352272Z')
|
||||||
|
mock_error.assert_not_called()
|
||||||
|
mock_warning.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.LOG.exception')
|
||||||
|
@mock.patch('validations_libs.utils.current_time',
|
||||||
|
return_value='2020-04-02T06:58:20.352272Z')
|
||||||
|
@mock.patch('validations_libs.utils.os.path.exists', return_value=False)
|
||||||
|
@mock.patch('os.makedirs', side_effect=[OSError, None])
|
||||||
|
@mock.patch('uuid.uuid4', return_value='1234')
|
||||||
|
def test_create_artifacts_subdir_OSError(self, mock_uuid, mock_makedirs,
|
||||||
|
mock_exists, mock_datetime,
|
||||||
|
mock_exception):
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
RuntimeError,
|
||||||
|
utils.get_artifacts_subdir,
|
||||||
|
log_dir='/tmp/foo/artifacts',
|
||||||
|
prefix='ntp')
|
||||||
|
|
||||||
|
mock_exception.assert_called()
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.LOG.exception')
|
||||||
|
@mock.patch('validations_libs.utils.current_time',
|
||||||
|
return_value='2020-04-02T06:58:20.352272Z')
|
||||||
|
@mock.patch('validations_libs.utils.os.path.exists', return_value=False)
|
||||||
|
@mock.patch('os.makedirs', side_effect=[PermissionError, None])
|
||||||
|
@mock.patch('uuid.uuid4', return_value='1234')
|
||||||
|
def test_create_artifacts_subdir_PermissionError(self, mock_uuid,
|
||||||
|
mock_makedirs,
|
||||||
|
mock_exists, mock_datetime,
|
||||||
|
mock_exception):
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
RuntimeError,
|
||||||
|
utils.get_artifacts_subdir,
|
||||||
|
log_dir='/tmp/foo/artifacts',
|
||||||
|
prefix='ntp')
|
||||||
|
|
||||||
|
mock_exception.assert_called()
|
||||||
|
|
||||||
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
|
@mock.patch('yaml.safe_load', return_value=fakes.FAKE_PLAYBOOK)
|
||||||
@mock.patch('six.moves.builtins.open')
|
@mock.patch('six.moves.builtins.open')
|
||||||
|
|
|
@ -45,11 +45,12 @@ class TestValidationActions(TestCase):
|
||||||
'My Validation Two Name',
|
'My Validation Two Name',
|
||||||
['prep', 'pre-introspection'])]))
|
['prep', 'pre-introspection'])]))
|
||||||
|
|
||||||
@mock.patch('validations_libs.utils.create_artifacts_dir',
|
@mock.patch('validations_libs.utils.get_artifacts_subdir',
|
||||||
return_value=('1234', '/tmp/'))
|
return_value=('123', '/var/log/validations'))
|
||||||
@mock.patch('validations_libs.utils.get_validations_playbook',
|
@mock.patch('validations_libs.utils.get_validations_playbook',
|
||||||
return_value=['/tmp/foo/fake.yaml'])
|
return_value=['/tmp/foo/fake.yaml'])
|
||||||
def test_validation_skip_validation(self, mock_validation_play, mock_tmp):
|
def test_validation_skip_validation(self, mock_validation_play,
|
||||||
|
mock_artsubdir):
|
||||||
|
|
||||||
playbook = ['fake.yaml']
|
playbook = ['fake.yaml']
|
||||||
inventory = 'tmp/inventory.yaml'
|
inventory = 'tmp/inventory.yaml'
|
||||||
|
@ -66,16 +67,23 @@ class TestValidationActions(TestCase):
|
||||||
limit_hosts=None)
|
limit_hosts=None)
|
||||||
self.assertEqual(run_return, [])
|
self.assertEqual(run_return, [])
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.os.path.exists',
|
||||||
|
return_value=True)
|
||||||
|
@mock.patch('validations_libs.utils.get_artifacts_subdir',
|
||||||
|
return_value=(
|
||||||
|
'123',
|
||||||
|
'/var/log/validations/artifacts/123_foo_time'))
|
||||||
@mock.patch('validations_libs.utils.get_validations_playbook',
|
@mock.patch('validations_libs.utils.get_validations_playbook',
|
||||||
return_value=['/tmp/foo/fake.yaml'])
|
return_value=['/tmp/foo/fake.yaml'])
|
||||||
@mock.patch('validations_libs.ansible.Ansible.run')
|
@mock.patch('validations_libs.ansible.Ansible.run')
|
||||||
@mock.patch('validations_libs.utils.create_artifacts_dir',
|
def test_validation_skip_on_specific_host(self, mock_ansible_run,
|
||||||
return_value=('1234', '/tmp/'))
|
mock_validation_play,
|
||||||
def test_validation_skip_on_specific_host(self, mock_tmp, mock_ansible_run,
|
mock_artsubdir,
|
||||||
mock_validation_play):
|
mock_exists):
|
||||||
|
|
||||||
mock_ansible_run.return_value = ('fake.yaml', 0, 'successful')
|
mock_ansible_run.return_value = ('fake.yaml', 0, 'successful')
|
||||||
run_called_args = {
|
run_called_args = {
|
||||||
'workdir': '/tmp/',
|
'workdir': '/var/log/validations/artifacts/123_foo_time',
|
||||||
'playbook': '/tmp/foo/fake.yaml',
|
'playbook': '/tmp/foo/fake.yaml',
|
||||||
'base_dir': '/usr/share/ansible/',
|
'base_dir': '/usr/share/ansible/',
|
||||||
'playbook_dir': '/tmp/foo',
|
'playbook_dir': '/tmp/foo',
|
||||||
|
@ -86,11 +94,11 @@ class TestValidationActions(TestCase):
|
||||||
'quiet': True,
|
'quiet': True,
|
||||||
'extra_vars': None,
|
'extra_vars': None,
|
||||||
'limit_hosts': '!cloud1',
|
'limit_hosts': '!cloud1',
|
||||||
'ansible_artifact_path': '/tmp/',
|
|
||||||
'extra_env_variables': None,
|
'extra_env_variables': None,
|
||||||
'ansible_cfg': None,
|
'ansible_cfg': None,
|
||||||
'gathering_policy': 'explicit',
|
'gathering_policy': 'explicit',
|
||||||
'log_path': None,
|
'ansible_artifact_path': '/var/log/validations/artifacts/123_foo_time',
|
||||||
|
'log_path': '/var/log/validations',
|
||||||
'run_async': False,
|
'run_async': False,
|
||||||
'python_interpreter': None,
|
'python_interpreter': None,
|
||||||
'ssh_user': None
|
'ssh_user': None
|
||||||
|
@ -106,22 +114,30 @@ class TestValidationActions(TestCase):
|
||||||
|
|
||||||
run = ValidationActions()
|
run = ValidationActions()
|
||||||
run_return = run.run_validations(playbook, inventory,
|
run_return = run.run_validations(playbook, inventory,
|
||||||
|
log_path='/var/log/validations',
|
||||||
validations_dir='/tmp/foo',
|
validations_dir='/tmp/foo',
|
||||||
skip_list=skip_list,
|
skip_list=skip_list,
|
||||||
limit_hosts='!cloud1',
|
limit_hosts='!cloud1',
|
||||||
)
|
)
|
||||||
mock_ansible_run.assert_called_with(**run_called_args)
|
mock_ansible_run.assert_called_with(**run_called_args)
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.os.path.exists',
|
||||||
|
return_value=True)
|
||||||
|
@mock.patch('validations_libs.utils.get_artifacts_subdir',
|
||||||
|
return_value=(
|
||||||
|
'123',
|
||||||
|
'/var/log/validations/artifacts/123_foo_time'))
|
||||||
@mock.patch('validations_libs.utils.get_validations_playbook',
|
@mock.patch('validations_libs.utils.get_validations_playbook',
|
||||||
return_value=['/tmp/foo/fake.yaml'])
|
return_value=['/tmp/foo/fake.yaml'])
|
||||||
@mock.patch('validations_libs.ansible.Ansible.run')
|
@mock.patch('validations_libs.ansible.Ansible.run')
|
||||||
@mock.patch('validations_libs.utils.create_artifacts_dir',
|
def test_validation_skip_with_limit_host(self, mock_ansible_run,
|
||||||
return_value=('1234', '/tmp/'))
|
mock_validation_play,
|
||||||
def test_validation_skip_with_limit_host(self, mock_tmp, mock_ansible_run,
|
mock_artsubdir,
|
||||||
mock_validation_play):
|
mock_exists):
|
||||||
|
|
||||||
mock_ansible_run.return_value = ('fake.yaml', 0, 'successful')
|
mock_ansible_run.return_value = ('fake.yaml', 0, 'successful')
|
||||||
run_called_args = {
|
run_called_args = {
|
||||||
'workdir': '/tmp/',
|
'workdir': '/var/log/validations/artifacts/123_foo_time',
|
||||||
'playbook': '/tmp/foo/fake.yaml',
|
'playbook': '/tmp/foo/fake.yaml',
|
||||||
'base_dir': '/usr/share/ansible/',
|
'base_dir': '/usr/share/ansible/',
|
||||||
'playbook_dir': '/tmp/foo',
|
'playbook_dir': '/tmp/foo',
|
||||||
|
@ -135,8 +151,8 @@ class TestValidationActions(TestCase):
|
||||||
'extra_env_variables': None,
|
'extra_env_variables': None,
|
||||||
'ansible_cfg': None,
|
'ansible_cfg': None,
|
||||||
'gathering_policy': 'explicit',
|
'gathering_policy': 'explicit',
|
||||||
'ansible_artifact_path': '/tmp/',
|
'ansible_artifact_path': '/var/log/validations/artifacts/123_foo_time',
|
||||||
'log_path': None,
|
'log_path': '/var/log/validations',
|
||||||
'run_async': False,
|
'run_async': False,
|
||||||
'python_interpreter': None,
|
'python_interpreter': None,
|
||||||
'ssh_user': None
|
'ssh_user': None
|
||||||
|
@ -152,40 +168,36 @@ class TestValidationActions(TestCase):
|
||||||
|
|
||||||
run = ValidationActions()
|
run = ValidationActions()
|
||||||
run_return = run.run_validations(playbook, inventory,
|
run_return = run.run_validations(playbook, inventory,
|
||||||
|
log_path='/var/log/validations',
|
||||||
validations_dir='/tmp/foo',
|
validations_dir='/tmp/foo',
|
||||||
skip_list=skip_list,
|
skip_list=skip_list,
|
||||||
limit_hosts='cloud,cloud1,!cloud2')
|
limit_hosts='cloud,cloud1,!cloud2')
|
||||||
|
|
||||||
mock_ansible_run.assert_called_with(**run_called_args)
|
mock_ansible_run.assert_called_with(**run_called_args)
|
||||||
|
|
||||||
@mock.patch('validations_libs.validation_logs.ValidationLogs.get_results')
|
@mock.patch('validations_libs.utils.get_artifacts_subdir',
|
||||||
|
return_value=(
|
||||||
|
'123',
|
||||||
|
'/var/log/validations/artifacts/123_foo_time'))
|
||||||
|
@mock.patch('validations_libs.validation_actions.ValidationLogs.get_results',
|
||||||
|
side_effect=fakes.FAKE_SUCCESS_RUN)
|
||||||
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
||||||
@mock.patch('validations_libs.ansible.Ansible.run')
|
@mock.patch('validations_libs.ansible.Ansible.run')
|
||||||
@mock.patch('validations_libs.utils.create_artifacts_dir',
|
def test_validation_run_success(self, mock_ansible_run,
|
||||||
return_value=('1234', '/tmp/'))
|
mock_validation_dir,
|
||||||
def test_validation_run_success(self, mock_tmp, mock_ansible_run,
|
mock_results,
|
||||||
mock_validation_dir, mock_results):
|
mock_artsubdir):
|
||||||
|
|
||||||
mock_validation_dir.return_value = [{
|
mock_validation_dir.return_value = [{
|
||||||
'description': 'My Validation One Description',
|
'description': 'My Validation One Description',
|
||||||
'groups': ['prep', 'pre-deployment'],
|
'groups': ['prep', 'pre-deployment'],
|
||||||
'id': 'foo',
|
'id': 'foo',
|
||||||
'name': 'My Validition One Name',
|
'name': 'My Validition One Name',
|
||||||
'parameters': {}}]
|
'parameters': {}}]
|
||||||
|
|
||||||
mock_ansible_run.return_value = ('foo.yaml', 0, 'successful')
|
mock_ansible_run.return_value = ('foo.yaml', 0, 'successful')
|
||||||
|
|
||||||
mock_results.return_value = [{'Duration': '0:00:01.761',
|
expected_run_return = fakes.FAKE_SUCCESS_RUN[0]
|
||||||
'Host_Group': 'overcloud',
|
|
||||||
'Status': 'PASSED',
|
|
||||||
'Status_by_Host': 'subnode-1,PASSED',
|
|
||||||
'UUID': 'foo',
|
|
||||||
'Unreachable_Hosts': '',
|
|
||||||
'Validations': 'ntp'}]
|
|
||||||
expected_run_return = [{'Duration': '0:00:01.761',
|
|
||||||
'Host_Group': 'overcloud',
|
|
||||||
'Status': 'PASSED',
|
|
||||||
'Status_by_Host': 'subnode-1,PASSED',
|
|
||||||
'UUID': 'foo',
|
|
||||||
'Unreachable_Hosts': '',
|
|
||||||
'Validations': 'ntp'}]
|
|
||||||
|
|
||||||
playbook = ['fake.yaml']
|
playbook = ['fake.yaml']
|
||||||
inventory = 'tmp/inventory.yaml'
|
inventory = 'tmp/inventory.yaml'
|
||||||
|
@ -206,20 +218,26 @@ class TestValidationActions(TestCase):
|
||||||
validations_dir='/tmp/foo'
|
validations_dir='/tmp/foo'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock.patch('validations_libs.utils.get_artifacts_subdir',
|
||||||
|
return_value=(
|
||||||
|
'123',
|
||||||
|
'/var/log/validations/artifacts/123_foo_time'))
|
||||||
@mock.patch('validations_libs.validation_logs.ValidationLogs.get_results')
|
@mock.patch('validations_libs.validation_logs.ValidationLogs.get_results')
|
||||||
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
||||||
@mock.patch('validations_libs.ansible.Ansible.run')
|
@mock.patch('validations_libs.ansible.Ansible.run')
|
||||||
@mock.patch('validations_libs.utils.create_artifacts_dir',
|
def test_validation_run_failed(self, mock_ansible_run,
|
||||||
return_value=('1234', '/tmp/'))
|
mock_validation_dir, mock_results,
|
||||||
def test_validation_run_failed(self, mock_tmp, mock_ansible_run,
|
mock_artsubdir):
|
||||||
mock_validation_dir, mock_results):
|
|
||||||
mock_validation_dir.return_value = [{
|
mock_validation_dir.return_value = [{
|
||||||
'description': 'My Validation One Description',
|
'description': 'My Validation One Description',
|
||||||
'groups': ['prep', 'pre-deployment'],
|
'groups': ['prep', 'pre-deployment'],
|
||||||
'id': 'foo',
|
'id': 'foo',
|
||||||
'name': 'My Validition One Name',
|
'name': 'My Validition One Name',
|
||||||
'parameters': {}}]
|
'parameters': {}}]
|
||||||
|
|
||||||
mock_ansible_run.return_value = ('foo.yaml', 0, 'failed')
|
mock_ansible_run.return_value = ('foo.yaml', 0, 'failed')
|
||||||
|
|
||||||
mock_results.return_value = [{'Duration': '0:00:01.761',
|
mock_results.return_value = [{'Duration': '0:00:01.761',
|
||||||
'Host_Group': 'overcloud',
|
'Host_Group': 'overcloud',
|
||||||
'Status': 'PASSED',
|
'Status': 'PASSED',
|
||||||
|
@ -227,6 +245,7 @@ class TestValidationActions(TestCase):
|
||||||
'UUID': 'foo',
|
'UUID': 'foo',
|
||||||
'Unreachable_Hosts': '',
|
'Unreachable_Hosts': '',
|
||||||
'Validations': 'ntp'}]
|
'Validations': 'ntp'}]
|
||||||
|
|
||||||
expected_run_return = [{'Duration': '0:00:01.761',
|
expected_run_return = [{'Duration': '0:00:01.761',
|
||||||
'Host_Group': 'overcloud',
|
'Host_Group': 'overcloud',
|
||||||
'Status': 'PASSED',
|
'Status': 'PASSED',
|
||||||
|
|
|
@ -32,32 +32,137 @@ def current_time():
|
||||||
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
||||||
|
|
||||||
|
|
||||||
def create_artifacts_dir(dir_path=None, prefix=None):
|
def _create_directory(path):
|
||||||
"""Create Ansible artifacts directory
|
"""Create directory and all intemediate directories in the path.
|
||||||
|
|
||||||
:param dir_path: Directory asbolute path
|
:param log_path: selected dir absolute path
|
||||||
:type dir_path: `string`
|
:log_path type: `string`
|
||||||
:param prefix: Playbook name
|
:return: verified path
|
||||||
:type prefix: `string`
|
:rtype: `string`
|
||||||
:return: The UUID of the validation and the absolute Path of the log file
|
"""
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _create_default_log_dir():
|
||||||
|
"""Create default validations log dir.
|
||||||
|
Log the failure if encountering OSError or PermissionError.
|
||||||
|
:raises: RuntimeError
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log_path = "{}".format(constants.VALIDATIONS_LOG_BASEDIR)
|
||||||
|
os.makedirs(log_path)
|
||||||
|
except (OSError, PermissionError):
|
||||||
|
LOG.error(
|
||||||
|
(
|
||||||
|
"Error while creating the log directory. "
|
||||||
|
"Please check the access rights for: '{}'"
|
||||||
|
).format(log_path)
|
||||||
|
)
|
||||||
|
raise RuntimeError()
|
||||||
|
|
||||||
|
|
||||||
|
def get_results_dirs(log_path):
|
||||||
|
"""Retrieve log directory and the Ansible artifacts directory.
|
||||||
|
|
||||||
|
This function contains additional checks for the log directory
|
||||||
|
access as well as possible fallback.
|
||||||
|
In order, following locations are considered for artifacts directory:
|
||||||
|
|
||||||
|
`log_path` parameter
|
||||||
|
`validations_libs.constants.VALIDATIONS_LOG_BASEDIR`
|
||||||
|
|
||||||
|
Error and warning is logged, to notify the user about remediation,
|
||||||
|
and possible complexities it may lead to.
|
||||||
|
|
||||||
|
Attempt to create default log directory if it's missing.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The reason why we are not using the os.path.access function
|
||||||
|
to greatly simplify this process is because we would break with
|
||||||
|
recommended practices described in the Python docs.
|
||||||
|
|
||||||
|
:param log_path: Absolute log directory path
|
||||||
|
:type log_path: `string`
|
||||||
|
:return: log path and artifacts path
|
||||||
:rtype: `string`, `string`
|
:rtype: `string`, `string`
|
||||||
"""
|
"""
|
||||||
dir_path = (dir_path if dir_path else
|
|
||||||
constants.VALIDATION_ANSIBLE_ARTIFACT_PATH)
|
|
||||||
validation_uuid = str(uuid.uuid4())
|
|
||||||
log_dir = "{}/{}_{}_{}".format(dir_path, validation_uuid,
|
|
||||||
(prefix if prefix else ''), current_time())
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(log_dir)
|
artifact_dir = os.path.join(log_path, 'artifacts')
|
||||||
return validation_uuid, log_dir
|
artifacts_dir = _create_directory(artifact_dir)
|
||||||
except OSError:
|
except (OSError, PermissionError):
|
||||||
|
LOG.error(
|
||||||
|
(
|
||||||
|
"Error while creating Ansible artifacts directory. "
|
||||||
|
"Please check the access rights for: '{}'"
|
||||||
|
).format(log_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
log_path = constants.VALIDATIONS_LOG_BASEDIR
|
||||||
|
|
||||||
|
LOG.warning(
|
||||||
|
(
|
||||||
|
"Requested log folder unavailable, defaulting to: '{}'"
|
||||||
|
).format(log_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(log_path):
|
||||||
|
LOG.warning(
|
||||||
|
(
|
||||||
|
"Default log folder unavailable, creating: '{}'"
|
||||||
|
).format(log_path)
|
||||||
|
)
|
||||||
|
_create_default_log_dir()
|
||||||
|
|
||||||
|
artifact_dir = os.path.join(log_path, 'artifacts')
|
||||||
|
artifacts_dir = _create_directory(log_path)
|
||||||
|
|
||||||
|
LOG.warning(
|
||||||
|
(
|
||||||
|
"Selected artifact folder unavailable, defaulting to: '{}'"
|
||||||
|
).format(artifacts_dir)
|
||||||
|
)
|
||||||
|
|
||||||
|
return log_path, artifacts_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_artifacts_subdir(log_dir, prefix=''):
|
||||||
|
"""Retrieve Ansible artifacts directory
|
||||||
|
|
||||||
|
:param log_dir: Directory absolute path
|
||||||
|
:type log_dir: `string`
|
||||||
|
:param prefix: Playbook name
|
||||||
|
:type prefix: `string`
|
||||||
|
:return: The UUID of the validation and the absolute path of the log file
|
||||||
|
:rtype: `string`, `string`
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
validation_uuid = str(uuid.uuid4())
|
||||||
|
|
||||||
|
artifact_sub_dir = "{}_{}_{}".format(
|
||||||
|
validation_uuid,
|
||||||
|
prefix,
|
||||||
|
current_time())
|
||||||
|
|
||||||
|
artifact_sub_dir = os.path.join(log_dir, artifact_sub_dir)
|
||||||
|
|
||||||
|
os.makedirs(artifact_sub_dir)
|
||||||
|
except (OSError, PermissionError):
|
||||||
LOG.exception(
|
LOG.exception(
|
||||||
(
|
(
|
||||||
"Error while creating Ansible artifacts log file."
|
"Error while creating Ansible artifacts directory. "
|
||||||
"Please check the access rights for {}"
|
"Please check the access rights for: '{}'"
|
||||||
).format(log_dir)
|
).format(log_dir)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
raise RuntimeError()
|
||||||
|
|
||||||
|
return validation_uuid, artifact_sub_dir
|
||||||
|
|
||||||
|
|
||||||
def parse_all_validations_on_disk(path, groups=None):
|
def parse_all_validations_on_disk(path, groups=None):
|
||||||
"""Return a list of validations metadata which can be sorted by Groups
|
"""Return a list of validations metadata which can be sorted by Groups
|
||||||
|
|
|
@ -228,7 +228,8 @@ class ValidationActions(object):
|
||||||
extra_env_vars=None, ansible_cfg=None, quiet=True,
|
extra_env_vars=None, ansible_cfg=None, quiet=True,
|
||||||
workdir=None, limit_hosts=None, run_async=False,
|
workdir=None, limit_hosts=None, run_async=False,
|
||||||
base_dir=constants.DEFAULT_VALIDATIONS_BASEDIR,
|
base_dir=constants.DEFAULT_VALIDATIONS_BASEDIR,
|
||||||
log_path=None, python_interpreter=None,
|
log_path=constants.VALIDATIONS_LOG_BASEDIR,
|
||||||
|
python_interpreter=None,
|
||||||
skip_list=None,
|
skip_list=None,
|
||||||
callback_whitelist=None,
|
callback_whitelist=None,
|
||||||
output_callback='validation_stdout',
|
output_callback='validation_stdout',
|
||||||
|
@ -268,6 +269,8 @@ class ValidationActions(object):
|
||||||
``constants.DEFAULT_VALIDATIONS_BASEDIR``)
|
``constants.DEFAULT_VALIDATIONS_BASEDIR``)
|
||||||
:type base_dir: ``string``
|
:type base_dir: ``string``
|
||||||
:param log_path: The absolute path of the validations logs directory
|
:param log_path: The absolute path of the validations logs directory
|
||||||
|
(Defaults to
|
||||||
|
``constants.VALIDATIONS_LOG_BASEDIR``)
|
||||||
:type log_path: ``string``
|
:type log_path: ``string``
|
||||||
:param python_interpreter: Path to the Python interpreter to be
|
:param python_interpreter: Path to the Python interpreter to be
|
||||||
used for module execution on remote targets,
|
used for module execution on remote targets,
|
||||||
|
@ -353,6 +356,7 @@ class ValidationActions(object):
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("No validations found")
|
raise RuntimeError("No validations found")
|
||||||
|
|
||||||
|
log_path, artifacts_path = v_utils.get_results_dirs(log_path)
|
||||||
self.log.debug('Running the validations with Ansible')
|
self.log.debug('Running the validations with Ansible')
|
||||||
results = []
|
results = []
|
||||||
for playbook in playbooks:
|
for playbook in playbooks:
|
||||||
|
@ -362,11 +366,12 @@ class ValidationActions(object):
|
||||||
play_name,
|
play_name,
|
||||||
limit_hosts)
|
limit_hosts)
|
||||||
if _play:
|
if _play:
|
||||||
validation_uuid, artifacts_dir = v_utils.create_artifacts_dir(
|
val_uuid, art_dir = v_utils.get_artifacts_subdir(
|
||||||
dir_path=log_path, prefix=os.path.basename(playbook))
|
log_dir=artifacts_path,
|
||||||
run_ansible = v_ansible(validation_uuid)
|
prefix=os.path.basename(playbook))
|
||||||
|
run_ansible = v_ansible(val_uuid)
|
||||||
_playbook, _rc, _status = run_ansible.run(
|
_playbook, _rc, _status = run_ansible.run(
|
||||||
workdir=artifacts_dir,
|
workdir=art_dir,
|
||||||
playbook=playbook,
|
playbook=playbook,
|
||||||
base_dir=base_dir,
|
base_dir=base_dir,
|
||||||
playbook_dir=validations_dir,
|
playbook_dir=validations_dir,
|
||||||
|
@ -380,7 +385,7 @@ class ValidationActions(object):
|
||||||
extra_env_variables=extra_env_vars,
|
extra_env_variables=extra_env_vars,
|
||||||
ansible_cfg=ansible_cfg,
|
ansible_cfg=ansible_cfg,
|
||||||
gathering_policy='explicit',
|
gathering_policy='explicit',
|
||||||
ansible_artifact_path=artifacts_dir,
|
ansible_artifact_path=art_dir,
|
||||||
log_path=log_path,
|
log_path=log_path,
|
||||||
run_async=run_async,
|
run_async=run_async,
|
||||||
python_interpreter=python_interpreter,
|
python_interpreter=python_interpreter,
|
||||||
|
@ -389,7 +394,7 @@ class ValidationActions(object):
|
||||||
'rc_code': _rc,
|
'rc_code': _rc,
|
||||||
'status': _status,
|
'status': _status,
|
||||||
'validations': _playbook.split('.')[0],
|
'validations': _playbook.split('.')[0],
|
||||||
'UUID': validation_uuid,
|
'UUID': val_uuid,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.log.debug('Skipping Validations: {}'.format(playbook))
|
self.log.debug('Skipping Validations: {}'.format(playbook))
|
||||||
|
@ -398,7 +403,7 @@ class ValidationActions(object):
|
||||||
return results
|
return results
|
||||||
# Return log results
|
# Return log results
|
||||||
uuid = [id['UUID'] for id in results]
|
uuid = [id['UUID'] for id in results]
|
||||||
vlog = ValidationLogs()
|
vlog = ValidationLogs(log_path)
|
||||||
return vlog.get_results(uuid)
|
return vlog.get_results(uuid)
|
||||||
|
|
||||||
def group_information(self, groups):
|
def group_information(self, groups):
|
||||||
|
|
Loading…
Reference in New Issue