mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Adds a logfile for ansible playbooks that can be set by the environment or configuration file.
This commit is contained in:
parent
515fbd5a17
commit
aa55268514
5 changed files with 68 additions and 27 deletions
15
bin/ansible
15
bin/ansible
|
@ -27,7 +27,6 @@ from ansible import utils
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible import callbacks
|
from ansible import callbacks
|
||||||
from ansible import inventory
|
from ansible import inventory
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
class Cli(object):
|
class Cli(object):
|
||||||
|
@ -84,17 +83,17 @@ class Cli(object):
|
||||||
inventory_manager.subset(options.subset)
|
inventory_manager.subset(options.subset)
|
||||||
hosts = inventory_manager.list_hosts(pattern)
|
hosts = inventory_manager.list_hosts(pattern)
|
||||||
if len(hosts) == 0:
|
if len(hosts) == 0:
|
||||||
print >>sys.stderr, "No hosts matched"
|
callbacks.display("No hosts matched", stderr=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if options.listhosts:
|
if options.listhosts:
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
print ' %s' % host
|
callbacks.display(' %s' % host)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if ((options.module_name == 'command' or options.module_name == 'shell')
|
if ((options.module_name == 'command' or options.module_name == 'shell')
|
||||||
and not options.module_args):
|
and not options.module_args):
|
||||||
print >>sys.stderr, "No argument passed to %s module" % options.module_name
|
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sshpass = None
|
sshpass = None
|
||||||
|
@ -124,7 +123,7 @@ class Cli(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
if options.seconds:
|
if options.seconds:
|
||||||
print "background launch...\n\n"
|
callbacks.display("background launch...\n\n", color='cyan')
|
||||||
results, poller = runner.run_async(options.seconds)
|
results, poller = runner.run_async(options.seconds)
|
||||||
results = self.poll_while_needed(poller, options)
|
results = self.poll_while_needed(poller, options)
|
||||||
else:
|
else:
|
||||||
|
@ -147,6 +146,10 @@ class Cli(object):
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
callbacks.display("", log_only=True)
|
||||||
|
callbacks.display(" ".join(sys.argv), log_only=True)
|
||||||
|
callbacks.display("", log_only=True)
|
||||||
|
|
||||||
cli = Cli()
|
cli = Cli()
|
||||||
(options, args) = cli.parse()
|
(options, args) = cli.parse()
|
||||||
try:
|
try:
|
||||||
|
@ -158,6 +161,6 @@ if __name__ == '__main__':
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
except errors.AnsibleError, e:
|
except errors.AnsibleError, e:
|
||||||
# Generic handler for ansible specific errors
|
# Generic handler for ansible specific errors
|
||||||
print "ERROR: %s" % str(e)
|
callbacks.display("ERROR: %s" % str(e), stderr=True, color='red')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
|
@ -28,16 +28,17 @@ from ansible import errors
|
||||||
from ansible import callbacks
|
from ansible import callbacks
|
||||||
from ansible import utils
|
from ansible import utils
|
||||||
from ansible.color import ANSIBLE_COLOR, stringc
|
from ansible.color import ANSIBLE_COLOR, stringc
|
||||||
|
from ansible.callbacks import display
|
||||||
|
|
||||||
def colorize(lead, num, color):
|
def colorize(lead, num, color):
|
||||||
""" Print 'lead' = 'num' in 'color' """
|
""" Print 'lead' = 'num' in 'color' """
|
||||||
if num != 0 and ANSIBLE_COLOR:
|
if num != 0 and ANSIBLE_COLOR and color is not None:
|
||||||
return "%s%s%-15s" % (stringc(lead, color), stringc("=", color), stringc(str(num), color))
|
return "%s%s%-15s" % (stringc(lead, color), stringc("=", color), stringc(str(num), color))
|
||||||
else:
|
else:
|
||||||
return "%s=%-4s" % (lead, str(num))
|
return "%s=%-4s" % (lead, str(num))
|
||||||
|
|
||||||
def hostcolor(host, stats):
|
def hostcolor(host, stats, color=True):
|
||||||
if ANSIBLE_COLOR:
|
if ANSIBLE_COLOR and color:
|
||||||
if stats['failures'] != 0 or stats['unreachable'] != 0:
|
if stats['failures'] != 0 or stats['unreachable'] != 0:
|
||||||
return "%-37s" % stringc(host, 'red')
|
return "%-37s" % stringc(host, 'red')
|
||||||
elif stats['changed'] != 0:
|
elif stats['changed'] != 0:
|
||||||
|
@ -180,7 +181,7 @@ def main(args):
|
||||||
pb.run()
|
pb.run()
|
||||||
|
|
||||||
hosts = sorted(pb.stats.processed.keys())
|
hosts = sorted(pb.stats.processed.keys())
|
||||||
print callbacks.banner("PLAY RECAP")
|
display(callbacks.banner("PLAY RECAP"))
|
||||||
playbook_cb.on_stats(pb.stats)
|
playbook_cb.on_stats(pb.stats)
|
||||||
|
|
||||||
for h in hosts:
|
for h in hosts:
|
||||||
|
@ -191,16 +192,28 @@ def main(args):
|
||||||
if len(failed_hosts) > 0:
|
if len(failed_hosts) > 0:
|
||||||
filename = pb.generate_retry_inventory(failed_hosts)
|
filename = pb.generate_retry_inventory(failed_hosts)
|
||||||
if filename:
|
if filename:
|
||||||
print " to retry, use: --limit @%s\n" % filename
|
display(" to retry, use: --limit @%s\n" % filename)
|
||||||
|
|
||||||
for h in hosts:
|
for h in hosts:
|
||||||
t = pb.stats.summarize(h)
|
t = pb.stats.summarize(h)
|
||||||
print "%s : %s %s %s %s" % (
|
|
||||||
|
display("%s : %s %s %s %s" % (
|
||||||
hostcolor(h, t),
|
hostcolor(h, t),
|
||||||
colorize('ok', t['ok'], 'green'),
|
colorize('ok', t['ok'], 'green'),
|
||||||
colorize('changed', t['changed'], 'yellow'),
|
colorize('changed', t['changed'], 'yellow'),
|
||||||
colorize('unreachable', t['unreachable'], 'red'),
|
colorize('unreachable', t['unreachable'], 'red'),
|
||||||
colorize('failed', t['failures'], 'red'))
|
colorize('failed', t['failures'], 'red')),
|
||||||
|
screen_only=True
|
||||||
|
)
|
||||||
|
|
||||||
|
display("%s : %s %s %s %s" % (
|
||||||
|
hostcolor(h, t, False),
|
||||||
|
colorize('ok', t['ok'], None),
|
||||||
|
colorize('changed', t['changed'], None),
|
||||||
|
colorize('unreachable', t['unreachable'], None),
|
||||||
|
colorize('failed', t['failures'], None)),
|
||||||
|
log_only=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
print ""
|
print ""
|
||||||
|
@ -208,16 +221,19 @@ def main(args):
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
except errors.AnsibleError, e:
|
except errors.AnsibleError, e:
|
||||||
print >>sys.stderr, "ERROR: %s" % e
|
display("ERROR: %s" % e, color='red')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
display(" ", log_only=True)
|
||||||
|
display(" ".join(sys.argv), log_only=True)
|
||||||
|
display(" ", log_only=True)
|
||||||
try:
|
try:
|
||||||
sys.exit(main(sys.argv[1:]))
|
sys.exit(main(sys.argv[1:]))
|
||||||
except errors.AnsibleError, e:
|
except errors.AnsibleError, e:
|
||||||
print >>sys.stderr, "ERROR: %s" % e
|
display("ERROR: %s" % e, color='red', stderr=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ library = /usr/share/ansible
|
||||||
|
|
||||||
module_name = command
|
module_name = command
|
||||||
|
|
||||||
|
# location for ansible log file. If set, will store output from ansible
|
||||||
|
# and ansible-playbook. If enabling, you may wish to configure
|
||||||
|
# logrotate.
|
||||||
|
|
||||||
|
#log_path = /var/log/ansible.log
|
||||||
|
|
||||||
# home directory where temp files are stored on remote systems. Should
|
# home directory where temp files are stored on remote systems. Should
|
||||||
# almost always contain $HOME or be a directory writeable by all users
|
# almost always contain $HOME or be a directory writeable by all users
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,13 @@ import random
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import tempfile
|
import tempfile
|
||||||
import fcntl
|
import fcntl
|
||||||
|
import constants
|
||||||
from ansible.color import stringc
|
from ansible.color import stringc
|
||||||
|
|
||||||
|
import logging
|
||||||
|
if constants.DEFAULT_LOG_PATH != '':
|
||||||
|
logging.basicConfig(filename=constants.DEFAULT_LOG_PATH, level=logging.DEBUG, format='%(asctime)s %(message)s')
|
||||||
|
|
||||||
callback_plugins = [x for x in utils.plugins.callback_loader.all()]
|
callback_plugins = [x for x in utils.plugins.callback_loader.all()]
|
||||||
|
|
||||||
def get_cowsay_info():
|
def get_cowsay_info():
|
||||||
|
@ -83,16 +88,25 @@ def set_task(callback, task):
|
||||||
for callback_plugin in callback_plugins:
|
for callback_plugin in callback_plugins:
|
||||||
callback_plugin.task = task
|
callback_plugin.task = task
|
||||||
|
|
||||||
def display(msg, color=None, stderr=False):
|
def display(msg, color=None, stderr=False, screen_only=False, log_only=False):
|
||||||
# prevent a very rare case of interlaced multiprocess I/O
|
# prevent a very rare case of interlaced multiprocess I/O
|
||||||
log_flock()
|
log_flock()
|
||||||
msg2 = msg
|
msg2 = msg
|
||||||
if color:
|
if color:
|
||||||
msg2 = stringc(msg, color)
|
msg2 = stringc(msg, color)
|
||||||
|
if not log_only:
|
||||||
if not stderr:
|
if not stderr:
|
||||||
print msg2
|
print msg2
|
||||||
else:
|
else:
|
||||||
print >>sys.stderr, msg2
|
print >>sys.stderr, msg2
|
||||||
|
if constants.DEFAULT_LOG_PATH != '':
|
||||||
|
while msg.startswith("\n"):
|
||||||
|
msg = msg.replace("\n","")
|
||||||
|
if not screen_only:
|
||||||
|
if color == 'red':
|
||||||
|
logging.error(msg)
|
||||||
|
else:
|
||||||
|
logging.info(msg)
|
||||||
log_unflock()
|
log_unflock()
|
||||||
|
|
||||||
def call_callback_module(method_name, *args, **kwargs):
|
def call_callback_module(method_name, *args, **kwargs):
|
||||||
|
@ -301,7 +315,7 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
def on_unreachable(self, host, res):
|
def on_unreachable(self, host, res):
|
||||||
if type(res) == dict:
|
if type(res) == dict:
|
||||||
res = res.get('msg','')
|
res = res.get('msg','')
|
||||||
display("%s | FAILED => %s" % (host, res))
|
display("%s | FAILED => %s" % (host, res), stderr=True, color='red')
|
||||||
if self.options.tree:
|
if self.options.tree:
|
||||||
utils.write_tree_file(
|
utils.write_tree_file(
|
||||||
self.options.tree, host,
|
self.options.tree, host,
|
||||||
|
@ -334,7 +348,7 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
super(CliRunnerCallbacks, self).on_async_ok(host, res, jid)
|
super(CliRunnerCallbacks, self).on_async_ok(host, res, jid)
|
||||||
|
|
||||||
def on_async_failed(self, host, res, jid):
|
def on_async_failed(self, host, res, jid):
|
||||||
display("<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True)))
|
display("<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True)), color='red', stderr=True)
|
||||||
super(CliRunnerCallbacks, self).on_async_failed(host,res,jid)
|
super(CliRunnerCallbacks, self).on_async_failed(host,res,jid)
|
||||||
|
|
||||||
def _on_any(self, host, result):
|
def _on_any(self, host, result):
|
||||||
|
@ -475,8 +489,8 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
super(PlaybookRunnerCallbacks, self).on_async_ok(host, res, jid)
|
super(PlaybookRunnerCallbacks, self).on_async_ok(host, res, jid)
|
||||||
|
|
||||||
def on_async_failed(self, host, res, jid):
|
def on_async_failed(self, host, res, jid):
|
||||||
msg = "<job %s> FAILED on %s"%(jid, host)
|
msg = "<job %s> FAILED on %s" % (jid, host)
|
||||||
display(msg, color='red')
|
display(msg, color='red', stderr=True)
|
||||||
super(PlaybookRunnerCallbacks, self).on_async_failed(host,res,jid)
|
super(PlaybookRunnerCallbacks, self).on_async_failed(host,res,jid)
|
||||||
|
|
||||||
def on_file_diff(self, host, diff):
|
def on_file_diff(self, host, diff):
|
||||||
|
|
|
@ -103,11 +103,13 @@ DEFAULT_CONNECTION_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'conn
|
||||||
DEFAULT_LOOKUP_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '/usr/share/ansible_plugins/lookup_plugins'))
|
DEFAULT_LOOKUP_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '/usr/share/ansible_plugins/lookup_plugins'))
|
||||||
DEFAULT_VARS_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '/usr/share/ansible_plugins/vars_plugins'))
|
DEFAULT_VARS_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '/usr/share/ansible_plugins/vars_plugins'))
|
||||||
DEFAULT_FILTER_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '/usr/share/ansible_plugins/filter_plugins'))
|
DEFAULT_FILTER_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '/usr/share/ansible_plugins/filter_plugins'))
|
||||||
|
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
||||||
|
|
||||||
|
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)
|
||||||
|
ZEROMQ_PORT = int(get_config(p, 'fireball', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099))
|
||||||
|
|
||||||
# non-configurable things
|
# non-configurable things
|
||||||
DEFAULT_SUDO_PASS = None
|
DEFAULT_SUDO_PASS = None
|
||||||
DEFAULT_REMOTE_PASS = None
|
DEFAULT_REMOTE_PASS = None
|
||||||
DEFAULT_SUBSET = None
|
DEFAULT_SUBSET = None
|
||||||
|
|
||||||
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)
|
|
||||||
ZEROMQ_PORT = int(get_config(p, 'fireball', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099))
|
|
||||||
|
|
Loading…
Reference in a new issue