add spinner to highlight progress
Implementing a Spinner class and utilizing it around a run action. This will add a moving icon that animates in place until output is reached, letting the operator know that things are happening. The interactive TTY test is included to ensure that during some unknown usage such as logging to disk this feature doesn't spew unnecessary clutter. Change-Id: Ieef256a5c12b238008e9250c0ee182d80a2b6dfb
This commit is contained in:
parent
85947ee8e0
commit
e11b458946
@ -18,6 +18,9 @@ import json
|
||||
import logging
|
||||
from prettytable import PrettyTable
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import yaml
|
||||
|
||||
try:
|
||||
@ -110,3 +113,38 @@ def read_extra_vars_file(extra_vars_file):
|
||||
"The extra_vars file must be properly formatted YAML/JSON."
|
||||
"Details: {}.").format(error)
|
||||
raise RuntimeError(error_msg)
|
||||
|
||||
|
||||
class Spinner(object):
|
||||
"""Animated spinner to indicate activity during processing"""
|
||||
busy = False
|
||||
delay = 0.1
|
||||
|
||||
@staticmethod
|
||||
def spinning_cursor():
|
||||
while 1:
|
||||
for cursor in '|/-\\':
|
||||
yield cursor
|
||||
|
||||
def __init__(self, delay=None):
|
||||
self.spinner_generator = self.spinning_cursor()
|
||||
if delay and float(delay):
|
||||
self.delay = delay
|
||||
|
||||
def spinner_task(self):
|
||||
while self.busy:
|
||||
sys.stdout.write(next(self.spinner_generator))
|
||||
sys.stdout.flush()
|
||||
time.sleep(self.delay)
|
||||
sys.stdout.write('\b')
|
||||
sys.stdout.flush()
|
||||
|
||||
def __enter__(self):
|
||||
self.busy = True
|
||||
threading.Thread(target=self.spinner_task).start()
|
||||
|
||||
def __exit__(self, exception, value, tb):
|
||||
self.busy = False
|
||||
time.sleep(self.delay)
|
||||
if exception is not None:
|
||||
return False
|
||||
|
@ -273,6 +273,58 @@ class TestValidationActions(TestCase):
|
||||
validations_dir='/tmp/foo')
|
||||
self.assertEqual(run_return, expected_run_return)
|
||||
|
||||
@mock.patch('validations_libs.ansible.Ansible._playbook_check',
|
||||
side_effect=RuntimeError)
|
||||
@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)
|
||||
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
||||
def test_spinner_exception_failure_condition(self, mock_validation_dir,
|
||||
mock_exists, mock_access,
|
||||
mock_makedirs,
|
||||
mock_playbook_check):
|
||||
|
||||
mock_validation_dir.return_value = [{
|
||||
'description': 'My Validation One Description',
|
||||
'groups': ['prep', 'pre-deployment'],
|
||||
'id': 'foo',
|
||||
'name': 'My Validition One Name',
|
||||
'parameters': {}}]
|
||||
playbook = ['fake.yaml']
|
||||
inventory = 'tmp/inventory.yaml'
|
||||
|
||||
run = ValidationActions()
|
||||
|
||||
self.assertRaises(RuntimeError, run.run_validations, playbook,
|
||||
inventory, group=fakes.GROUPS_LIST,
|
||||
validations_dir='/tmp/foo')
|
||||
|
||||
@mock.patch('validations_libs.ansible.Ansible._playbook_check',
|
||||
side_effect=RuntimeError)
|
||||
@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)
|
||||
@mock.patch('validations_libs.utils.parse_all_validations_on_disk')
|
||||
@mock.patch('sys.__stdin__.isatty', return_value=True)
|
||||
def test_spinner_forced_run(self, mock_stdin_isatty, mock_validation_dir,
|
||||
mock_exists, mock_access, mock_makedirs,
|
||||
mock_playbook_check):
|
||||
|
||||
mock_validation_dir.return_value = [{
|
||||
'description': 'My Validation One Description',
|
||||
'groups': ['prep', 'pre-deployment'],
|
||||
'id': 'foo',
|
||||
'name': 'My Validition One Name',
|
||||
'parameters': {}}]
|
||||
playbook = ['fake.yaml']
|
||||
inventory = 'tmp/inventory.yaml'
|
||||
|
||||
run = ValidationActions()
|
||||
|
||||
self.assertRaises(RuntimeError, run.run_validations, playbook,
|
||||
inventory, group=fakes.GROUPS_LIST,
|
||||
validations_dir='/tmp/foo')
|
||||
|
||||
@mock.patch('validations_libs.utils.get_validations_playbook',
|
||||
return_value=[])
|
||||
def test_validation_run_no_validation(self, mock_get_val):
|
||||
|
@ -14,11 +14,13 @@
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from validations_libs.ansible import Ansible as v_ansible
|
||||
from validations_libs.group import Group
|
||||
from validations_libs.cli.common import Spinner
|
||||
from validations_libs.validation_logs import ValidationLogs, ValidationLog
|
||||
from validations_libs import constants
|
||||
from validations_libs import utils as v_utils
|
||||
@ -374,26 +376,49 @@ class ValidationActions(object):
|
||||
validation_uuid, artifacts_dir = v_utils.create_artifacts_dir(
|
||||
log_path=log_path, prefix=os.path.basename(playbook))
|
||||
run_ansible = v_ansible(validation_uuid)
|
||||
_playbook, _rc, _status = run_ansible.run(
|
||||
workdir=artifacts_dir,
|
||||
playbook=playbook,
|
||||
base_dir=base_dir,
|
||||
playbook_dir=validations_dir,
|
||||
parallel_run=True,
|
||||
inventory=inventory,
|
||||
output_callback=output_callback,
|
||||
callback_whitelist=callback_whitelist,
|
||||
quiet=quiet,
|
||||
extra_vars=extra_vars,
|
||||
limit_hosts=_hosts,
|
||||
extra_env_variables=extra_env_vars,
|
||||
ansible_cfg=ansible_cfg,
|
||||
gathering_policy='explicit',
|
||||
ansible_artifact_path=artifacts_dir,
|
||||
log_path=log_path,
|
||||
run_async=run_async,
|
||||
python_interpreter=python_interpreter,
|
||||
ssh_user=ssh_user)
|
||||
if sys.__stdin__.isatty():
|
||||
with Spinner():
|
||||
_playbook, _rc, _status = run_ansible.run(
|
||||
workdir=artifacts_dir,
|
||||
playbook=playbook,
|
||||
base_dir=base_dir,
|
||||
playbook_dir=validations_dir,
|
||||
parallel_run=True,
|
||||
inventory=inventory,
|
||||
output_callback=output_callback,
|
||||
callback_whitelist=callback_whitelist,
|
||||
quiet=quiet,
|
||||
extra_vars=extra_vars,
|
||||
limit_hosts=_hosts,
|
||||
extra_env_variables=extra_env_vars,
|
||||
ansible_cfg=ansible_cfg,
|
||||
gathering_policy='explicit',
|
||||
ansible_artifact_path=artifacts_dir,
|
||||
log_path=log_path,
|
||||
run_async=run_async,
|
||||
python_interpreter=python_interpreter,
|
||||
ssh_user=ssh_user)
|
||||
else:
|
||||
_playbook, _rc, _status = run_ansible.run(
|
||||
workdir=artifacts_dir,
|
||||
playbook=playbook,
|
||||
base_dir=base_dir,
|
||||
playbook_dir=validations_dir,
|
||||
parallel_run=True,
|
||||
inventory=inventory,
|
||||
output_callback=output_callback,
|
||||
callback_whitelist=callback_whitelist,
|
||||
quiet=quiet,
|
||||
extra_vars=extra_vars,
|
||||
limit_hosts=_hosts,
|
||||
extra_env_variables=extra_env_vars,
|
||||
ansible_cfg=ansible_cfg,
|
||||
gathering_policy='explicit',
|
||||
ansible_artifact_path=artifacts_dir,
|
||||
log_path=log_path,
|
||||
run_async=run_async,
|
||||
python_interpreter=python_interpreter,
|
||||
ssh_user=ssh_user)
|
||||
results.append({'playbook': _playbook,
|
||||
'rc_code': _rc,
|
||||
'status': _status,
|
||||
|
Loading…
x
Reference in New Issue
Block a user