Adds a logfile for ansible playbooks that can be set by the environment or configuration file.

pull/2479/merge
Michael DeHaan 12 years ago
parent 515fbd5a17
commit aa55268514

@ -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):
@ -476,7 +490,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
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…
Cancel
Save