Implement validations-libs framework for ALL validator commands
Implement All command with validations libs framework Change-Id: I0443da140020540917396cdfe00da8ac29d20297
This commit is contained in:
parent
f66b42757b
commit
6dee93a9e7
@ -149,6 +149,7 @@ traceback2==1.4.0
|
|||||||
tripleo-common==12.4.0
|
tripleo-common==12.4.0
|
||||||
ujson==1.35
|
ujson==1.35
|
||||||
unittest2==1.1.0
|
unittest2==1.1.0
|
||||||
|
validations-libs==1.0.0
|
||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
voluptuous==0.8.9
|
voluptuous==0.8.9
|
||||||
waitress==1.1.0
|
waitress==1.1.0
|
||||||
|
@ -18,3 +18,4 @@ websocket-client>=0.44.0 # LGPLv2+
|
|||||||
tripleo-common>=12.4.0 # Apache-2.0
|
tripleo-common>=12.4.0 # Apache-2.0
|
||||||
cryptography>=2.1 # BSD/Apache-2.0
|
cryptography>=2.1 # BSD/Apache-2.0
|
||||||
ansible-runner>=1.4.4 # Apache 2.0
|
ansible-runner>=1.4.4 # Apache 2.0
|
||||||
|
validations-libs>=1.0.0
|
||||||
|
@ -1590,51 +1590,6 @@ class TestGetLocalTimezone(TestCase):
|
|||||||
self.assertEqual('UTC', utils.get_local_timezone())
|
self.assertEqual('UTC', utils.get_local_timezone())
|
||||||
|
|
||||||
|
|
||||||
class TestGetParamFieldName(TestCase):
|
|
||||||
def test_with_empty_val_data(self):
|
|
||||||
input_parameter = {}
|
|
||||||
expected = "parameters"
|
|
||||||
|
|
||||||
result = utils.get_param_field_name(input_parameter)
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
def test_with_val_data_and_returns_parameters(self):
|
|
||||||
input_parameter = {'validations': [
|
|
||||||
{'description': 'validation number one',
|
|
||||||
'groups': ['prep', 'pre-deployment'],
|
|
||||||
'id': 'Validation_Number_One',
|
|
||||||
'name': 'Validation Number One',
|
|
||||||
'parameters': {}},
|
|
||||||
{'description': 'validation number two',
|
|
||||||
'groups': ['post-deployment'],
|
|
||||||
'id': 'Validation_Number_Two',
|
|
||||||
'name': 'Validation Number Two',
|
|
||||||
'parameters': {'config_file': "/etc/config.conf"}},
|
|
||||||
]}
|
|
||||||
expected = "parameters"
|
|
||||||
|
|
||||||
result = utils.get_param_field_name(input_parameter)
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
def test_with_val_data_and_returns_metadata(self):
|
|
||||||
input_parameter = {'validations': [
|
|
||||||
{'description': 'validation number one',
|
|
||||||
'groups': ['prep', 'pre-deployment'],
|
|
||||||
'id': 'Validation_Number_One',
|
|
||||||
'name': 'Validation Number One',
|
|
||||||
'metadata': {}},
|
|
||||||
{'description': 'validation number two',
|
|
||||||
'groups': ['post-deployment'],
|
|
||||||
'id': 'Validation_Number_Two',
|
|
||||||
'name': 'Validation Number Two',
|
|
||||||
'metadata': {'config_file': "/etc/config.conf"}},
|
|
||||||
]}
|
|
||||||
expected = "metadata"
|
|
||||||
|
|
||||||
result = utils.get_param_field_name(input_parameter)
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
|
|
||||||
class TestParseExtraVars(TestCase):
|
class TestParseExtraVars(TestCase):
|
||||||
def test_simple_case_text_format(self):
|
def test_simple_case_text_format(self):
|
||||||
input_parameter = ['key1=val1', 'key2=val2 key3=val3']
|
input_parameter = ['key1=val1', 'key2=val2 key3=val3']
|
||||||
|
@ -151,8 +151,8 @@ class TestValidatorGroupInfo(utils.TestCommand):
|
|||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = tripleo_validator.TripleOValidatorGroupInfo(self.app, None)
|
self.cmd = tripleo_validator.TripleOValidatorGroupInfo(self.app, None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.prepare_validation_groups_for_display',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
return_value=GROUPS_LIST)
|
'group_information', return_value=GROUPS_LIST)
|
||||||
def test_show_group_info(self, mock_validations):
|
def test_show_group_info(self, mock_validations):
|
||||||
arglist = []
|
arglist = []
|
||||||
verifylist = []
|
verifylist = []
|
||||||
@ -170,7 +170,8 @@ class TestValidatorList(utils.TestCommand):
|
|||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = tripleo_validator.TripleOValidatorList(self.app, None)
|
self.cmd = tripleo_validator.TripleOValidatorList(self.app, None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
|
'list_validations',
|
||||||
return_value=VALIDATIONS_LIST)
|
return_value=VALIDATIONS_LIST)
|
||||||
def test_validation_list_noargs(self, mock_validations):
|
def test_validation_list_noargs(self, mock_validations):
|
||||||
arglist = []
|
arglist = []
|
||||||
@ -189,8 +190,9 @@ class TestValidatorShow(utils.TestCommand):
|
|||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = tripleo_validator.TripleOValidatorShow(self.app, None)
|
self.cmd = tripleo_validator.TripleOValidatorShow(self.app, None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
return_value=VALIDATIONS_LIST)
|
'show_validations',
|
||||||
|
return_value=VALIDATIONS_LIST[0])
|
||||||
def test_validation_show(self, mock_validations):
|
def test_validation_show(self, mock_validations):
|
||||||
arglist = ['my_val1']
|
arglist = ['my_val1']
|
||||||
verifylist = [('validation_id', 'my_val1')]
|
verifylist = [('validation_id', 'my_val1')]
|
||||||
@ -209,8 +211,9 @@ class TestValidatorShowParameter(utils.TestCommand):
|
|||||||
self.cmd = tripleo_validator.TripleOValidatorShowParameter(self.app,
|
self.cmd = tripleo_validator.TripleOValidatorShowParameter(self.app,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
return_value=VALIDATIONS_LIST)
|
'show_validations_parameters',
|
||||||
|
return_value=VALIDATIONS_LIST[1])
|
||||||
def test_validation_show_parameter(self, mock_validations):
|
def test_validation_show_parameter(self, mock_validations):
|
||||||
arglist = ['--validation', 'my_val2']
|
arglist = ['--validation', 'my_val2']
|
||||||
verifylist = [('validation_name', ['my_val2'])]
|
verifylist = [('validation_name', ['my_val2'])]
|
||||||
@ -229,7 +232,8 @@ class TestValidatorShowRun(utils.TestCommand):
|
|||||||
self.cmd = tripleo_validator.TripleOValidatorShowRun(self.app,
|
self.cmd = tripleo_validator.TripleOValidatorShowRun(self.app,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_logs_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationLogs.'
|
||||||
|
'get_logfile_content_by_uuid',
|
||||||
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
||||||
def test_validation_show_run(self, mock_validations):
|
def test_validation_show_run(self, mock_validations):
|
||||||
arglist = ['008886df-d297-1eaa-2a74-000000000008']
|
arglist = ['008886df-d297-1eaa-2a74-000000000008']
|
||||||
@ -249,7 +253,8 @@ class TestValidatorShowHistory(utils.TestCommand):
|
|||||||
self.cmd = tripleo_validator.TripleOValidatorShowHistory(self.app,
|
self.cmd = tripleo_validator.TripleOValidatorShowHistory(self.app,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_logs_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
|
'show_history',
|
||||||
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
||||||
def test_validation_show_history(self, mock_validations):
|
def test_validation_show_history(self, mock_validations):
|
||||||
arglist = []
|
arglist = []
|
||||||
@ -259,7 +264,8 @@ class TestValidatorShowHistory(utils.TestCommand):
|
|||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.parse_all_validations_logs_on_disk',
|
@mock.patch('validations_libs.validation_actions.ValidationActions.'
|
||||||
|
'show_history',
|
||||||
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
return_value=VALIDATIONS_LOGS_CONTENTS_LIST)
|
||||||
def test_validation_show_history_for_a_validation(self, mock_validations):
|
def test_validation_show_history_for_a_validation(self, mock_validations):
|
||||||
arglist = [
|
arglist = [
|
||||||
|
@ -30,7 +30,6 @@ import hashlib
|
|||||||
import logging
|
import logging
|
||||||
from six.moves.configparser import ConfigParser
|
from six.moves.configparser import ConfigParser
|
||||||
|
|
||||||
import json
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import netaddr
|
import netaddr
|
||||||
import os
|
import os
|
||||||
@ -2191,200 +2190,6 @@ def _get_from_cfg(cfg, accessor, param, section):
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def get_validation_metadata(validation, key):
|
|
||||||
default_metadata = {
|
|
||||||
'name': 'Unnamed',
|
|
||||||
'description': 'No description',
|
|
||||||
'stage': 'No stage',
|
|
||||||
'groups': [],
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
return validation[0]['vars']['metadata'].get(key,
|
|
||||||
default_metadata[key])
|
|
||||||
except KeyError:
|
|
||||||
LOG.exception(_("Key '{key}' not even found in "
|
|
||||||
"default metadata").format(key=key))
|
|
||||||
except TypeError:
|
|
||||||
LOG.exception(_("Failed to get validation metadata."))
|
|
||||||
|
|
||||||
|
|
||||||
def get_validation_parameters(validation):
|
|
||||||
try:
|
|
||||||
return {
|
|
||||||
k: v
|
|
||||||
for k, v in validation[0]['vars'].items()
|
|
||||||
if k != 'metadata'
|
|
||||||
}
|
|
||||||
except KeyError:
|
|
||||||
LOG.debug(_("No parameters found for this validation"))
|
|
||||||
return dict()
|
|
||||||
|
|
||||||
|
|
||||||
def read_validation_groups_file(groups_file_path=None):
|
|
||||||
"""Load groups.yaml file and return a dictionary with its contents"""
|
|
||||||
if not groups_file_path:
|
|
||||||
groups_file_path = constants.VALIDATION_GROUPS_INFO
|
|
||||||
|
|
||||||
if not os.path.exists(groups_file_path):
|
|
||||||
return []
|
|
||||||
|
|
||||||
with open(groups_file_path, 'r') as grps:
|
|
||||||
contents = yaml.safe_load(grps)
|
|
||||||
|
|
||||||
return contents
|
|
||||||
|
|
||||||
|
|
||||||
def get_validation_group_name_list():
|
|
||||||
"""Get the validation group name list only"""
|
|
||||||
results = []
|
|
||||||
|
|
||||||
groups = read_validation_groups_file()
|
|
||||||
|
|
||||||
if groups and isinstance(groups, dict):
|
|
||||||
for grp_name in six.viewkeys(groups):
|
|
||||||
results.append(grp_name)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_validation_groups_for_display():
|
|
||||||
results = []
|
|
||||||
groups = read_validation_groups_file()
|
|
||||||
|
|
||||||
for grp_name, grp_desc in sorted(groups.items()):
|
|
||||||
results.append((grp_name, grp_desc[0].get('description')))
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def parse_all_validations_on_disk(path, groups=None):
|
|
||||||
results = []
|
|
||||||
validations_abspath = glob.glob("{path}/*.yaml".format(path=path))
|
|
||||||
|
|
||||||
if isinstance(groups, six.string_types):
|
|
||||||
group_list = []
|
|
||||||
group_list.append(groups)
|
|
||||||
groups = group_list
|
|
||||||
|
|
||||||
for pl in validations_abspath:
|
|
||||||
validation_id, _ext = os.path.splitext(os.path.basename(pl))
|
|
||||||
|
|
||||||
with open(pl, 'r') as val_playbook:
|
|
||||||
contents = yaml.safe_load(val_playbook)
|
|
||||||
|
|
||||||
validation_groups = get_validation_metadata(contents, 'groups') or []
|
|
||||||
if not groups or set.intersection(set(groups), set(validation_groups)):
|
|
||||||
results.append({
|
|
||||||
'id': validation_id,
|
|
||||||
'name': get_validation_metadata(contents, 'name'),
|
|
||||||
'groups': get_validation_metadata(contents, 'groups'),
|
|
||||||
'description': get_validation_metadata(contents,
|
|
||||||
'description'),
|
|
||||||
'parameters': get_validation_parameters(contents)
|
|
||||||
})
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def get_param_field_name(validations_data=None):
|
|
||||||
"""Get the current parameters field name in a Dict
|
|
||||||
|
|
||||||
Returns either 'parameters' or 'metadata'.
|
|
||||||
By Default, it returns 'parameters'.
|
|
||||||
"""
|
|
||||||
# TODO(gchamoul): Added for backwards compatibility and will be
|
|
||||||
# removed for Train release.
|
|
||||||
if validations_data is None:
|
|
||||||
validations_data = {}
|
|
||||||
|
|
||||||
if 'metadata' in validations_data.get('validations', [[]])[0]:
|
|
||||||
return 'metadata'
|
|
||||||
return 'parameters'
|
|
||||||
|
|
||||||
|
|
||||||
def get_validations_parameters(validations_data,
|
|
||||||
validation_name=None,
|
|
||||||
groups=None):
|
|
||||||
if validation_name is None:
|
|
||||||
validation_name = []
|
|
||||||
|
|
||||||
if groups is None:
|
|
||||||
groups = []
|
|
||||||
|
|
||||||
params = {}
|
|
||||||
param_field_name = get_param_field_name(validations_data)
|
|
||||||
|
|
||||||
for val in validations_data['validations']:
|
|
||||||
wanted_validation = False
|
|
||||||
wanted_group = False
|
|
||||||
if val.get('id') in validation_name:
|
|
||||||
wanted_validation = True
|
|
||||||
|
|
||||||
for grp in groups:
|
|
||||||
if grp in val.get('groups'):
|
|
||||||
wanted_group = True
|
|
||||||
|
|
||||||
if wanted_validation or wanted_group:
|
|
||||||
params[val.get('id')] = {
|
|
||||||
'parameters': val.get(param_field_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def get_validations_json(validations_data):
|
|
||||||
"""Return the validations information as a pretty printed json """
|
|
||||||
return json.dumps(validations_data, indent=4, sort_keys=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_validations_yaml(validations_data):
|
|
||||||
"""Return the validations information as a pretty printed yaml """
|
|
||||||
return yaml.safe_dump(validations_data,
|
|
||||||
allow_unicode=True,
|
|
||||||
default_flow_style=False,
|
|
||||||
indent=2)
|
|
||||||
|
|
||||||
|
|
||||||
def get_new_validations_logs_on_disk():
|
|
||||||
"""Return a list of new log execution filenames """
|
|
||||||
files = []
|
|
||||||
|
|
||||||
for root, dirs, filenames in os.walk(constants.VALIDATIONS_LOG_BASEDIR):
|
|
||||||
files = [
|
|
||||||
f for f in filenames if not f.startswith('processed')
|
|
||||||
and os.path.splitext(f)[1] == '.json'
|
|
||||||
]
|
|
||||||
|
|
||||||
return files
|
|
||||||
|
|
||||||
|
|
||||||
def parse_all_validations_logs_on_disk(uuid_run=None, validation_id=None):
|
|
||||||
results = []
|
|
||||||
path = constants.VALIDATIONS_LOG_BASEDIR
|
|
||||||
logfile = "{}/*.json".format(path)
|
|
||||||
|
|
||||||
if validation_id:
|
|
||||||
logfile = "{}/*_{}_*.json".format(path, validation_id)
|
|
||||||
|
|
||||||
if uuid_run:
|
|
||||||
logfile = "{}/*_{}_*.json".format(path, uuid_run)
|
|
||||||
|
|
||||||
logfiles_path = glob.glob(logfile)
|
|
||||||
|
|
||||||
for logfile_path in logfiles_path:
|
|
||||||
with open(logfile_path, 'r') as log:
|
|
||||||
contents = json.load(log)
|
|
||||||
results.append(contents)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def indent(text):
|
|
||||||
'''Indent the given text by four spaces.'''
|
|
||||||
return ''.join(' {}\n'.format(line) for line in text.splitlines())
|
|
||||||
|
|
||||||
|
|
||||||
def get_local_timezone():
|
def get_local_timezone():
|
||||||
info = run_command(['timedatectl'], name='timedatectl')
|
info = run_command(['timedatectl'], name='timedatectl')
|
||||||
timezoneline = [tz for tz in info.split('\n') if 'Time zone:' in tz]
|
timezoneline = [tz for tz in info.split('\n') if 'Time zone:' in tz]
|
||||||
|
@ -16,12 +16,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import six
|
|
||||||
import textwrap
|
|
||||||
import time
|
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib.i18n import _
|
from osc_lib.i18n import _
|
||||||
from prettytable import PrettyTable
|
from prettytable import PrettyTable
|
||||||
@ -30,10 +25,16 @@ from tripleoclient import command
|
|||||||
from tripleoclient import constants
|
from tripleoclient import constants
|
||||||
from tripleoclient import utils as oooutils
|
from tripleoclient import utils as oooutils
|
||||||
|
|
||||||
|
from validations_libs import constants as v_consts
|
||||||
|
from validations_libs import utils as v_utils
|
||||||
|
from validations_libs.validation_actions import ValidationActions
|
||||||
|
from validations_libs.validation_logs import ValidationLogs
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__ + ".TripleoValidator")
|
LOG = logging.getLogger(__name__ + ".TripleoValidator")
|
||||||
|
|
||||||
RED = "\033[1;31m"
|
RED = "\033[1;31m"
|
||||||
GREEN = "\033[0;32m"
|
GREEN = "\033[0;32m"
|
||||||
|
CYAN = "\033[36m"
|
||||||
RESET = "\033[0;0m"
|
RESET = "\033[0;0m"
|
||||||
|
|
||||||
FAILED_VALIDATION = "{}FAILED{}".format(RED, RESET)
|
FAILED_VALIDATION = "{}FAILED{}".format(RED, RESET)
|
||||||
@ -44,7 +45,7 @@ GROUP_FILE = constants.VALIDATION_GROUPS_INFO
|
|||||||
|
|
||||||
class _CommaListGroupAction(argparse.Action):
|
class _CommaListGroupAction(argparse.Action):
|
||||||
def __call__(self, parser, namespace, values, option_string=None):
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
opts = oooutils.get_validation_group_name_list()
|
opts = v_utils.get_validation_group_name_list(GROUP_FILE)
|
||||||
for value in values.split(','):
|
for value in values.split(','):
|
||||||
if value not in opts:
|
if value not in opts:
|
||||||
message = ("Invalid choice: {value} (choose from {choice})"
|
message = ("Invalid choice: {value} (choose from {choice})"
|
||||||
@ -67,20 +68,8 @@ class TripleOValidatorGroupInfo(command.Lister):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
group = oooutils.prepare_validation_groups_for_display()
|
actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
|
||||||
|
return actions.group_information(GROUP_FILE)
|
||||||
if not group:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
"Could not find groups information file %s" % GROUP_FILE)
|
|
||||||
|
|
||||||
group_info = []
|
|
||||||
for gp in group:
|
|
||||||
validations = oooutils.parse_all_validations_on_disk(
|
|
||||||
constants.ANSIBLE_VALIDATION_DIR, gp[0])
|
|
||||||
group_info.append((gp[0], gp[1], len(validations)))
|
|
||||||
|
|
||||||
column_name = ("Groups", "Description", "Number of Validations")
|
|
||||||
return (column_name, group_info)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOValidatorShow(command.ShowOne):
|
class TripleOValidatorShow(command.ShowOne):
|
||||||
@ -97,83 +86,14 @@ class TripleOValidatorShow(command.ShowOne):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
validation = self.get_validations_details(parsed_args.validation_id)
|
LOG.debug(_('Show validation result'))
|
||||||
logfile_contents = oooutils.parse_all_validations_logs_on_disk(
|
try:
|
||||||
validation_id=parsed_args.validation_id)
|
actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
|
||||||
|
data = actions.show_validations(parsed_args.validation_id)
|
||||||
if not validation:
|
return data.keys(), data.values()
|
||||||
raise exceptions.CommandError(
|
except Exception as e:
|
||||||
"Could not find validation %s" % parsed_args.validation_id)
|
raise RuntimeError(_("Validations listing finished with errors\n"
|
||||||
|
"Output: {}").format(e))
|
||||||
return self.format_validation(validation, logfile_contents)
|
|
||||||
|
|
||||||
def get_validations_details(self, validation):
|
|
||||||
results = oooutils.parse_all_validations_on_disk(
|
|
||||||
constants.ANSIBLE_VALIDATION_DIR)
|
|
||||||
|
|
||||||
for r in results:
|
|
||||||
if r['id'] == validation:
|
|
||||||
return r
|
|
||||||
return []
|
|
||||||
|
|
||||||
def format_validation(self, validation, logfile):
|
|
||||||
column_names = ["ID"]
|
|
||||||
data = [validation.pop('id')]
|
|
||||||
|
|
||||||
if 'name' in validation:
|
|
||||||
column_names.append("Name")
|
|
||||||
data.append(validation.pop('name'))
|
|
||||||
|
|
||||||
if 'description' in validation:
|
|
||||||
column_names.append("Description")
|
|
||||||
data.append(textwrap.fill(validation.pop('description')))
|
|
||||||
|
|
||||||
if 'groups' in validation:
|
|
||||||
column_names.append("Groups")
|
|
||||||
data.append(", ".join(validation.pop('groups')))
|
|
||||||
|
|
||||||
other_fields = list(validation.keys())
|
|
||||||
other_fields.sort()
|
|
||||||
for field in other_fields:
|
|
||||||
column_names.append(field.capitalize())
|
|
||||||
data.append(validation[field])
|
|
||||||
|
|
||||||
# history, stats ...
|
|
||||||
total_number = 0
|
|
||||||
failed_number = 0
|
|
||||||
passed_number = 0
|
|
||||||
last_execution = None
|
|
||||||
dates = []
|
|
||||||
|
|
||||||
if logfile:
|
|
||||||
total_number = len(logfile)
|
|
||||||
|
|
||||||
for run in logfile:
|
|
||||||
if 'validation_output' in run and run.get('validation_output'):
|
|
||||||
failed_number += 1
|
|
||||||
else:
|
|
||||||
passed_number += 1
|
|
||||||
|
|
||||||
date_time = \
|
|
||||||
run['plays'][0]['play']['duration'].get('start').split('T')
|
|
||||||
date_start = date_time[0]
|
|
||||||
time_start = date_time[1].split('Z')[0]
|
|
||||||
newdate = \
|
|
||||||
time.strptime(date_start + time_start, '%Y-%m-%d%H:%M:%S.%f')
|
|
||||||
dates.append(newdate)
|
|
||||||
|
|
||||||
if dates:
|
|
||||||
last_execution = time.strftime('%Y-%m-%d %H:%M:%S', max(dates))
|
|
||||||
|
|
||||||
column_names.append("Number of execution")
|
|
||||||
data.append("Total: {}, Passed: {}, Failed: {}".format(total_number,
|
|
||||||
passed_number,
|
|
||||||
failed_number))
|
|
||||||
|
|
||||||
column_names.append("Last execution date")
|
|
||||||
data.append(last_execution)
|
|
||||||
|
|
||||||
return column_names, data
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOValidatorShowParameter(command.Command):
|
class TripleOValidatorShowParameter(command.Command):
|
||||||
@ -216,14 +136,12 @@ class TripleOValidatorShowParameter(command.Command):
|
|||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--download',
|
'--download',
|
||||||
metavar=('[json|yaml]', '/tmp/myvars'),
|
|
||||||
action='store',
|
action='store',
|
||||||
default=[],
|
default=None,
|
||||||
nargs=2,
|
|
||||||
help=_("Create a json or a yaml file "
|
help=_("Create a json or a yaml file "
|
||||||
"containing all the variables "
|
"containing all the variables "
|
||||||
"available for the validations: "
|
"available for the validations: "
|
||||||
"[yaml|json] /tmp/myvars")
|
"/tmp/myvars")
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -238,65 +156,18 @@ class TripleOValidatorShowParameter(command.Command):
|
|||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _create_variables_file(self, data, varsfile):
|
|
||||||
msg = (_("The file %s already exists on the filesystem, "
|
|
||||||
"do you still want to continue [y/N] "))
|
|
||||||
|
|
||||||
if varsfile[0] not in ['json', 'yaml']:
|
|
||||||
raise RuntimeError(_('Wrong file type: %s') % varsfile[0])
|
|
||||||
else:
|
|
||||||
LOG.debug(_('Launch variables file creation'))
|
|
||||||
try:
|
|
||||||
if os.path.exists(varsfile[-1]):
|
|
||||||
confirm = oooutils.prompt_user_for_confirmation(
|
|
||||||
message=msg % varsfile[-1], logger=LOG)
|
|
||||||
if not confirm:
|
|
||||||
raise RuntimeError(_("Action not confirmed, exiting"))
|
|
||||||
|
|
||||||
with open(varsfile[-1], 'w') as f:
|
|
||||||
params = {}
|
|
||||||
for val_name in list(data.keys()):
|
|
||||||
for k, v in data[val_name].get('parameters').items():
|
|
||||||
params[k] = v
|
|
||||||
|
|
||||||
if varsfile[0] == 'json':
|
|
||||||
f.write(oooutils.get_validations_json(params))
|
|
||||||
elif varsfile[0] == 'yaml':
|
|
||||||
f.write(oooutils.get_validations_yaml(params))
|
|
||||||
print(
|
|
||||||
_('The file %s has been created successfully') %
|
|
||||||
varsfile[-1])
|
|
||||||
except Exception as e:
|
|
||||||
print(_("Creating variables file finished with errors"))
|
|
||||||
print('Output: {}'.format(e))
|
|
||||||
|
|
||||||
def _run_validator_show_parameter(self, parsed_args):
|
|
||||||
LOG.debug(_('Launch showing parameters for the validations'))
|
|
||||||
try:
|
|
||||||
validations = oooutils.parse_all_validations_on_disk(
|
|
||||||
constants.ANSIBLE_VALIDATION_DIR)
|
|
||||||
|
|
||||||
out = oooutils.get_validations_parameters(
|
|
||||||
{'validations': validations},
|
|
||||||
parsed_args.validation_name,
|
|
||||||
parsed_args.group
|
|
||||||
)
|
|
||||||
|
|
||||||
if parsed_args.download:
|
|
||||||
self._create_variables_file(out,
|
|
||||||
parsed_args.download)
|
|
||||||
else:
|
|
||||||
if parsed_args.format == 'yaml':
|
|
||||||
print(oooutils.get_validations_yaml(out))
|
|
||||||
else:
|
|
||||||
print(oooutils.get_validations_json(out))
|
|
||||||
except Exception as e:
|
|
||||||
raise RuntimeError(_("Validations Show Parameters "
|
|
||||||
"finished with errors\n"
|
|
||||||
"Output: {}").format(e))
|
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self._run_validator_show_parameter(parsed_args)
|
actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
|
||||||
|
params = actions.show_validations_parameters(
|
||||||
|
parsed_args.validation_name,
|
||||||
|
parsed_args.group,
|
||||||
|
parsed_args.format,
|
||||||
|
parsed_args.download)
|
||||||
|
if parsed_args.download:
|
||||||
|
print("The file {} has been created successfully").format(
|
||||||
|
parsed_args.download)
|
||||||
|
else:
|
||||||
|
print(params)
|
||||||
|
|
||||||
|
|
||||||
class TripleOValidatorList(command.Lister):
|
class TripleOValidatorList(command.Lister):
|
||||||
@ -322,16 +193,11 @@ class TripleOValidatorList(command.Lister):
|
|||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
LOG.debug(_('Launch listing the validations'))
|
LOG.debug(_('Launch listing the validations'))
|
||||||
try:
|
try:
|
||||||
validations = oooutils.parse_all_validations_on_disk(
|
v_consts.DEFAULT_VALIDATIONS_BASEDIR = constants.\
|
||||||
constants.ANSIBLE_VALIDATION_DIR, parsed_args.group)
|
DEFAULT_VALIDATIONS_BASEDIR
|
||||||
|
actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR,
|
||||||
return_values = []
|
parsed_args.group)
|
||||||
column_name = ('ID', 'Name', 'Groups')
|
return actions.list_validations()
|
||||||
|
|
||||||
for val in validations:
|
|
||||||
return_values.append((val.get('id'), val.get('name'),
|
|
||||||
", ".join(val.get('groups'))))
|
|
||||||
return (column_name, return_values)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(_("Validations listing finished with errors\n"
|
raise RuntimeError(_("Validations listing finished with errors\n"
|
||||||
"Output: {}").format(e))
|
"Output: {}").format(e))
|
||||||
@ -356,15 +222,29 @@ class TripleOValidatorRun(command.Command):
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--workers', '-w',
|
'--quiet',
|
||||||
metavar='N',
|
action='store',
|
||||||
dest='workers',
|
default=False,
|
||||||
default=1,
|
help=_(
|
||||||
type=int,
|
"Run Ansible in silent mode."
|
||||||
help=_("The maximum number of threads that can "
|
)
|
||||||
"be used to execute the given validations")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit', action='store', required=False, help=_(
|
||||||
|
"A string that identifies a single node or comma-separated"
|
||||||
|
"list of nodes to be upgraded in parallel in this upgrade"
|
||||||
|
" run invocation. For example: --limit \"compute-0,"
|
||||||
|
" compute-1, compute-5\".")
|
||||||
|
)
|
||||||
|
parser.add_argument('--playbook',
|
||||||
|
nargs="*",
|
||||||
|
default=None,
|
||||||
|
help=_("List of Ansible playbook to use for "
|
||||||
|
"validations. It could be a playbook path "
|
||||||
|
"or a list of playbook.")
|
||||||
|
)
|
||||||
|
|
||||||
extra_vars_group = parser.add_mutually_exclusive_group(required=False)
|
extra_vars_group = parser.add_mutually_exclusive_group(required=False)
|
||||||
|
|
||||||
extra_vars_group.add_argument(
|
extra_vars_group.add_argument(
|
||||||
@ -390,6 +270,17 @@ class TripleOValidatorRun(command.Command):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extra_vars_group.add_argument(
|
||||||
|
'--extra-env-vars',
|
||||||
|
action='store',
|
||||||
|
default={},
|
||||||
|
type=json.loads,
|
||||||
|
help=_(
|
||||||
|
"A dictionary as extra environment variables you may need "
|
||||||
|
"to provide to your Ansible execution example:"
|
||||||
|
"ANSIBLE_STDOUT_CALLBACK=default")
|
||||||
|
)
|
||||||
|
|
||||||
ex_group = parser.add_mutually_exclusive_group(required=True)
|
ex_group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
|
||||||
ex_group.add_argument(
|
ex_group.add_argument(
|
||||||
@ -421,37 +312,8 @@ class TripleOValidatorRun(command.Command):
|
|||||||
|
|
||||||
def _run_validator_run(self, parsed_args):
|
def _run_validator_run(self, parsed_args):
|
||||||
LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible")
|
LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible")
|
||||||
playbooks = []
|
limit = parsed_args.limit
|
||||||
extra_vars_input = {}
|
playbook = parsed_args.playbook
|
||||||
|
|
||||||
if parsed_args.extra_vars:
|
|
||||||
extra_vars_input = parsed_args.extra_vars
|
|
||||||
|
|
||||||
if parsed_args.extra_vars_file:
|
|
||||||
extra_vars_input = parsed_args.extra_vars_file
|
|
||||||
|
|
||||||
if parsed_args.group:
|
|
||||||
LOG.debug(_('Getting the validations list by group'))
|
|
||||||
try:
|
|
||||||
output = oooutils.parse_all_validations_on_disk(
|
|
||||||
constants.ANSIBLE_VALIDATION_DIR, parsed_args.group)
|
|
||||||
for val in output:
|
|
||||||
playbooks.append(val.get('id') + '.yaml')
|
|
||||||
except Exception as e:
|
|
||||||
print(
|
|
||||||
_("Getting Validations list by group name"
|
|
||||||
"finished with errors"))
|
|
||||||
print('Output: {}'.format(e))
|
|
||||||
|
|
||||||
else:
|
|
||||||
for pb in parsed_args.validation_name:
|
|
||||||
if pb not in oooutils.get_validation_group_name_list():
|
|
||||||
playbooks.append(pb + '.yaml')
|
|
||||||
else:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
"Please, use '--group' argument instead of "
|
|
||||||
"'--validation' to run validation(s) by their name(s)."
|
|
||||||
)
|
|
||||||
|
|
||||||
static_inventory = oooutils.get_tripleo_ansible_inventory(
|
static_inventory = oooutils.get_tripleo_ansible_inventory(
|
||||||
ssh_user='heat-admin',
|
ssh_user='heat-admin',
|
||||||
@ -459,131 +321,45 @@ class TripleOValidatorRun(command.Command):
|
|||||||
undercloud_connection='local',
|
undercloud_connection='local',
|
||||||
return_inventory_file_path=True)
|
return_inventory_file_path=True)
|
||||||
|
|
||||||
failed_val = False
|
v_consts.DEFAULT_VALIDATIONS_BASEDIR = constants.\
|
||||||
|
DEFAULT_VALIDATIONS_BASEDIR
|
||||||
|
actions = ValidationActions()
|
||||||
|
results = actions.run_validations(
|
||||||
|
playbook=(playbook if playbook else []),
|
||||||
|
inventory=static_inventory,
|
||||||
|
limit_hosts=limit,
|
||||||
|
group=parsed_args.group,
|
||||||
|
extra_vars=parsed_args.extra_vars,
|
||||||
|
validations_dir=constants.ANSIBLE_VALIDATION_DIR,
|
||||||
|
validation_name=parsed_args.validation_name,
|
||||||
|
extra_env_vars=parsed_args.extra_env_vars,
|
||||||
|
quiet=parsed_args.quiet)
|
||||||
|
|
||||||
with oooutils.TempDirs() as tmp:
|
# Build output
|
||||||
with ThreadPoolExecutor(max_workers=parsed_args.workers) as exe:
|
t = PrettyTable(border=True, header=True, padding_width=1)
|
||||||
LOG.debug(_('Running the validations with Ansible'))
|
# Set Field name by getting the result dict keys
|
||||||
tasks_exec = {
|
t.field_names = results[0].keys()
|
||||||
exe.submit(
|
for r in results:
|
||||||
oooutils.run_ansible_playbook,
|
if r.get('Status_by_Host'):
|
||||||
plan=parsed_args.plan,
|
h = []
|
||||||
workdir=tmp,
|
for host in r['Status_by_Host'].split(', '):
|
||||||
playbook=playbook,
|
_name, _status = host.split(',')
|
||||||
playbook_dir=constants.ANSIBLE_VALIDATION_DIR,
|
color = (GREEN if _status == 'PASSED' else RED)
|
||||||
parallel_run=True,
|
_name = '{}{}{}'.format(color, _name, RESET)
|
||||||
inventory=static_inventory,
|
h.append(_name)
|
||||||
output_callback='validation_json',
|
r['Status_by_Host'] = ', '.join(h)
|
||||||
quiet=True,
|
if r.get('status'):
|
||||||
extra_vars=extra_vars_input,
|
status = r.get('status')
|
||||||
gathering_policy='explicit'): playbook
|
color = (CYAN if status in ['starting', 'running']
|
||||||
for playbook in playbooks
|
else GREEN if status == 'PASSED' else RED)
|
||||||
}
|
r['status'] = '{}{}{}'.format(color, status, RESET)
|
||||||
|
t.add_row(r.values())
|
||||||
results = []
|
print(t)
|
||||||
|
|
||||||
for tk, pl in six.iteritems(tasks_exec):
|
|
||||||
try:
|
|
||||||
_rc, output = tk.result()
|
|
||||||
results.append({
|
|
||||||
'validation': {
|
|
||||||
'validation_id': pl,
|
|
||||||
'logfile': None,
|
|
||||||
'status': 'PASSED',
|
|
||||||
'output': output
|
|
||||||
}})
|
|
||||||
except Exception as e:
|
|
||||||
failed_val = True
|
|
||||||
results.append({
|
|
||||||
'validation': {
|
|
||||||
'validation_id': pl,
|
|
||||||
'logfile': None,
|
|
||||||
'status': 'FAILED',
|
|
||||||
'output': str(e)
|
|
||||||
}})
|
|
||||||
|
|
||||||
if results:
|
|
||||||
new_log_files = oooutils.get_new_validations_logs_on_disk()
|
|
||||||
|
|
||||||
for i in new_log_files:
|
|
||||||
val_id = "{}.yaml".format(i.split('_')[1])
|
|
||||||
for res in results:
|
|
||||||
if res['validation'].get('validation_id') == val_id:
|
|
||||||
res['validation']['logfile'] = \
|
|
||||||
os.path.join(constants.VALIDATIONS_LOG_BASEDIR, i)
|
|
||||||
|
|
||||||
t = PrettyTable(border=True, header=True, padding_width=1)
|
|
||||||
t.field_names = [
|
|
||||||
"UUID", "Validations", "Status", "Host Group(s)",
|
|
||||||
"Status by Host", "Unreachable Host(s)", "Duration"]
|
|
||||||
|
|
||||||
for validation in results:
|
|
||||||
r = []
|
|
||||||
logfile = validation['validation'].get('logfile', None)
|
|
||||||
if logfile and os.path.exists(logfile):
|
|
||||||
with open(logfile, 'r') as val:
|
|
||||||
contents = json.load(val)
|
|
||||||
|
|
||||||
for i in contents['plays']:
|
|
||||||
host = [x for x in i['play'].get('host').split(', ')]
|
|
||||||
val_id = i['play'].get('validation_id')
|
|
||||||
time_elapsed = \
|
|
||||||
i['play']['duration'].get('time_elapsed', None)
|
|
||||||
|
|
||||||
r.append(contents['plays'][0]['play'].get('id'))
|
|
||||||
r.append(val_id)
|
|
||||||
if validation['validation'].get('status') == "PASSED":
|
|
||||||
r.append(PASSED_VALIDATION)
|
|
||||||
else:
|
|
||||||
r.append(FAILED_VALIDATION)
|
|
||||||
|
|
||||||
unreachable_hosts = []
|
|
||||||
hosts_result = []
|
|
||||||
for ht in list(contents['stats'].keys()):
|
|
||||||
if contents['stats'][ht]['unreachable'] != 0:
|
|
||||||
unreachable_hosts.append(ht)
|
|
||||||
elif contents['stats'][ht]['failures'] != 0:
|
|
||||||
hosts_result.append("{}{}{}".format(
|
|
||||||
RED, ht, RESET))
|
|
||||||
else:
|
|
||||||
hosts_result.append("{}{}{}".format(
|
|
||||||
GREEN, ht, RESET))
|
|
||||||
|
|
||||||
r.append(", ".join(host))
|
|
||||||
r.append(", ".join(hosts_result))
|
|
||||||
r.append("{}{}{}".format(RED,
|
|
||||||
", ".join(unreachable_hosts),
|
|
||||||
RESET))
|
|
||||||
r.append(time_elapsed)
|
|
||||||
t.add_row(r)
|
|
||||||
|
|
||||||
t.sortby = "UUID"
|
|
||||||
for field in t.field_names:
|
|
||||||
if field == "Status":
|
|
||||||
t.align['Status'] = "l"
|
|
||||||
else:
|
|
||||||
t.align[field] = "l"
|
|
||||||
|
|
||||||
print(t)
|
|
||||||
|
|
||||||
if len(new_log_files) > len(results):
|
|
||||||
LOG.warn(_('Looks like we have more log files than '
|
|
||||||
'executed validations'))
|
|
||||||
|
|
||||||
for i in new_log_files:
|
|
||||||
os.rename(
|
|
||||||
"{}/{}".format(constants.VALIDATIONS_LOG_BASEDIR,
|
|
||||||
i), "{}/processed_{}".format(
|
|
||||||
constants.VALIDATIONS_LOG_BASEDIR, i))
|
|
||||||
|
|
||||||
LOG.debug(_('Removing static tripleo ansible inventory file'))
|
LOG.debug(_('Removing static tripleo ansible inventory file'))
|
||||||
oooutils.cleanup_tripleo_ansible_inventory_file(
|
oooutils.cleanup_tripleo_ansible_inventory_file(
|
||||||
static_inventory)
|
static_inventory)
|
||||||
|
|
||||||
if failed_val:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
_('One or more validations have failed!'))
|
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self._run_validator_run(parsed_args)
|
self._run_validator_run(parsed_args)
|
||||||
|
|
||||||
@ -606,25 +382,22 @@ class TripleOValidatorShowRun(command.Command):
|
|||||||
|
|
||||||
parser.add_argument('--full',
|
parser.add_argument('--full',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
default=True,
|
||||||
help='Show Full Details for the run')
|
help='Show Full Details for the run')
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
logfile_contents = oooutils.parse_all_validations_logs_on_disk(
|
vlogs = ValidationLogs()
|
||||||
uuid_run=parsed_args.uuid)
|
data = vlogs.get_logfile_content_by_uuid(parsed_args.uuid)
|
||||||
|
if data:
|
||||||
if len(logfile_contents) > 1:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
"Multiple log files found for UUID: %s" % parsed_args.uuid)
|
|
||||||
|
|
||||||
if logfile_contents:
|
|
||||||
if parsed_args.full:
|
if parsed_args.full:
|
||||||
print(oooutils.get_validations_json(logfile_contents[0]))
|
for d in data:
|
||||||
|
print(json.dumps(d, indent=4, sort_keys=True))
|
||||||
else:
|
else:
|
||||||
for data in logfile_contents:
|
for d in data:
|
||||||
for tasks in data['validation_output']:
|
for p in d['plays']:
|
||||||
print(oooutils.get_validations_json(tasks))
|
print(json.dumps(p['tasks'], indent=4, sort_keys=True))
|
||||||
else:
|
else:
|
||||||
raise exceptions.CommandError(
|
raise exceptions.CommandError(
|
||||||
"Could not find the log file linked to this UUID: %s" %
|
"Could not find the log file linked to this UUID: %s" %
|
||||||
@ -645,41 +418,5 @@ class TripleOValidatorShowHistory(command.Lister):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
logfile_contents = oooutils.parse_all_validations_logs_on_disk(
|
actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
|
||||||
validation_id=parsed_args.validation)
|
return actions.show_history(parsed_args.validation)
|
||||||
|
|
||||||
if not logfile_contents:
|
|
||||||
msg = "No History Found"
|
|
||||||
if parsed_args.validation:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
"{} for {}.".format(
|
|
||||||
msg, parsed_args.validation))
|
|
||||||
else:
|
|
||||||
raise exceptions.CommandError(
|
|
||||||
"{}.".format(msg, parsed_args.validation))
|
|
||||||
|
|
||||||
return_values = []
|
|
||||||
column_name = ('UUID', 'Validations',
|
|
||||||
'Status', 'Execution at',
|
|
||||||
'Duration')
|
|
||||||
|
|
||||||
for run in logfile_contents:
|
|
||||||
status = PASSED_VALIDATION
|
|
||||||
if 'plays' in run and run.get('plays'):
|
|
||||||
date_time = \
|
|
||||||
run['plays'][0]['play']['duration'].get('start').split('T')
|
|
||||||
time_elapsed = \
|
|
||||||
run['plays'][0]['play']['duration'].get('time_elapsed')
|
|
||||||
date_start = date_time[0]
|
|
||||||
time_start = date_time[1].split('Z')[0]
|
|
||||||
|
|
||||||
for k, v in six.iteritems(run['stats']):
|
|
||||||
if v.get('failures') != 0:
|
|
||||||
status = FAILED_VALIDATION
|
|
||||||
|
|
||||||
return_values.append(
|
|
||||||
(run['plays'][0]['play'].get('id'),
|
|
||||||
run['plays'][0]['play'].get('validation_id'), status,
|
|
||||||
"{} {}".format(date_start, time_start), time_elapsed))
|
|
||||||
|
|
||||||
return (column_name, return_values)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user