diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f8797039..55595e7717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Ansible Changes By Release * better changed=True/False detection in user module on older Linux distros * when using paramiko and SFTP is not accessible, do not traceback, but return a nice human readable msg * nicer errors from modules when arguments are not key=value +* use -vvv for extreme debug levels. -v gives more playbook output as before, -vv not really used yet 0.6 "Cabo" -- August 6, 2012 diff --git a/bin/ansible b/bin/ansible index a7e77ee174..1e53aba064 100755 --- a/bin/ansible +++ b/bin/ansible @@ -96,7 +96,7 @@ class Cli(object): pattern=pattern, callbacks=self.callbacks, sudo=options.sudo, sudo_pass=sudopass,sudo_user=options.sudo_user, - transport=options.connection, verbose=options.verbose + transport=options.connection ) if options.seconds: diff --git a/bin/ansible-playbook b/bin/ansible-playbook index 10a9952510..61fe1cd0bc 100755 --- a/bin/ansible-playbook +++ b/bin/ansible-playbook @@ -81,15 +81,14 @@ def main(args): for playbook in args: stats = callbacks.AggregateStats() - playbook_cb = callbacks.PlaybookCallbacks(verbose=options.verbose) - runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=options.verbose) + playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) + runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) pb = ansible.playbook.PlayBook( playbook=playbook, module_path=options.module_path, host_list=options.inventory, forks=options.forks, - verbose=options.verbose, remote_user=options.remote_user, remote_pass=sshpass, callbacks=playbook_cb, diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index b0e25ecc3b..36e989e940 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -28,6 +28,13 @@ if os.path.exists("/usr/bin/cowsay"): elif os.path.exists("/usr/games/cowsay"): cowsay = "/usr/games/cowsay" +def vvv(msg, host=None): + if utils.VERBOSITY > 2: + if host is None: + print stringc(msg, 'blue') + else: + print stringc("<%s> %s" % (host, msg), 'blue') + class AggregateStats(object): ''' holds stats about per-host activity during playbook runs ''' @@ -242,11 +249,11 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks): class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): ''' callbacks used for Runner() from /usr/bin/ansible-playbook ''' - def __init__(self, stats, verbose=False): + def __init__(self, stats, verbose=utils.VERBOSITY): + self.verbose = verbose self.stats = stats self._async_notified = {} - self.verbose = verbose def on_unreachable(self, host, msg): diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index dbf42e77bf..9555d7eda1 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -51,7 +51,6 @@ class PlayBook(object): remote_port = C.DEFAULT_REMOTE_PORT, transport = C.DEFAULT_TRANSPORT, private_key_file = C.DEFAULT_PRIVATE_KEY_FILE, - verbose = False, callbacks = None, runner_callbacks = None, stats = None, @@ -94,7 +93,6 @@ class PlayBook(object): self.remote_pass = remote_pass self.remote_port = remote_port self.transport = transport - self.verbose = verbose self.callbacks = callbacks self.runner_callbacks = runner_callbacks self.stats = stats @@ -191,7 +189,7 @@ class PlayBook(object): private_key_file=self.private_key_file, setup_cache=self.SETUP_CACHE, basedir=self.basedir, conditional=task.only_if, callbacks=self.runner_callbacks, - verbose=self.verbose, sudo=task.play.sudo, sudo_user=task.play.sudo_user, + sudo=task.play.sudo, sudo_user=task.play.sudo_user, transport=task.play.transport, sudo_pass=self.sudo_pass, is_playbook=True ) @@ -272,7 +270,7 @@ class PlayBook(object): forks=self.forks, module_path=self.module_path, timeout=self.timeout, remote_user=play.remote_user, remote_pass=self.remote_pass, remote_port=play.remote_port, private_key_file=self.private_key_file, setup_cache=self.SETUP_CACHE, callbacks=self.runner_callbacks, sudo=play.sudo, sudo_user=play.sudo_user, - verbose=self.verbose, transport=play.transport, sudo_pass=self.sudo_pass, is_playbook=True + transport=play.transport, sudo_pass=self.sudo_pass, is_playbook=True ).run() self.stats.compute(setup_results, setup=True) diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 51989af1e2..4925efc21a 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -122,7 +122,6 @@ class Runner(object): transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local' conditional='True', # run only if this fact expression evals to true callbacks=None, # used for output - verbose=False, # whether to show more or less sudo=False, # whether to run sudo or not sudo_user=C.DEFAULT_SUDO_USER, # ex: 'root' module_vars=None, # a playbooks internals thing @@ -147,7 +146,6 @@ class Runner(object): self.pattern = pattern self.module_args = module_args self.timeout = timeout - self.verbose = verbose self.remote_user = remote_user self.remote_pass = remote_pass self.remote_port = remote_port diff --git a/lib/ansible/runner/connection/local.py b/lib/ansible/runner/connection/local.py index c1aeb78c52..130cdfd33f 100644 --- a/lib/ansible/runner/connection/local.py +++ b/lib/ansible/runner/connection/local.py @@ -20,6 +20,7 @@ import os import shutil import subprocess from ansible import errors +from ansible.callbacks import vvv class LocalConnection(object): ''' Local based connections ''' @@ -44,6 +45,7 @@ class LocalConnection(object): raise errors.AnsibleError("sudo with password is presently only supported on the paramiko (SSH) connection type") cmd = "sudo -u {0} -s {1}".format(sudo_user, cmd) + vvv("EXEC %s" % cmd, host=self.host) p = subprocess.Popen(cmd, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() @@ -52,6 +54,7 @@ class LocalConnection(object): def put_file(self, in_path, out_path): ''' transfer a file from local to local ''' + vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) try: @@ -64,6 +67,7 @@ class LocalConnection(object): raise errors.AnsibleError("failed to transfer file to %s" % out_path) def fetch_file(self, in_path, out_path): + vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) ''' fetch a file from local to local -- for copatibility ''' self.put_file(in_path, out_path) diff --git a/lib/ansible/runner/connection/paramiko_ssh.py b/lib/ansible/runner/connection/paramiko_ssh.py index 27e6ef4bd0..4a00a76d11 100644 --- a/lib/ansible/runner/connection/paramiko_ssh.py +++ b/lib/ansible/runner/connection/paramiko_ssh.py @@ -24,6 +24,8 @@ import subprocess import pipes import socket import random +from ansible import utils +from ansible.callbacks import vvv from ansible import errors # prevent paramiko warning noise -- see http://stackoverflow.com/questions/3920502/ @@ -84,6 +86,7 @@ class ParamikoConnection(object): if not self.runner.sudo or not sudoable: quoted_command = '"$SHELL" -c ' + pipes.quote(cmd) + vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # Rather than detect if sudo wants a password this time, -k makes @@ -98,6 +101,7 @@ class ParamikoConnection(object): prompt = '[sudo via ansible, key=%s] password: ' % randbits sudocmd = 'sudo -k && sudo -p "%s" -u %s -- "$SHELL" -c %s' % ( prompt, sudo_user, pipes.quote(cmd)) + vvv("EXEC %s" % sudo_cmd, host=self.host) sudo_output = '' try: chan.exec_command(sudocmd) @@ -120,6 +124,7 @@ class ParamikoConnection(object): def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' + vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) try: @@ -134,6 +139,7 @@ class ParamikoConnection(object): def fetch_file(self, in_path, out_path): ''' save a remote file to the specified path ''' + vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) try: sftp = self.ssh.open_sftp() except: diff --git a/lib/ansible/runner/connection/ssh.py b/lib/ansible/runner/connection/ssh.py index 3f20a54e3b..aa0b36e077 100644 --- a/lib/ansible/runner/connection/ssh.py +++ b/lib/ansible/runner/connection/ssh.py @@ -23,6 +23,7 @@ import pipes import random import select import fcntl +from ansible.callbacks import vvv from ansible import errors class SSHConnection(object): @@ -72,6 +73,7 @@ class SSHConnection(object): prompt, sudo_user, pipes.quote(cmd)) sudo_output = '' ssh_cmd.append(sudocmd) + vvv("EXEC %s" % ssh_cmd, host=self.host) p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if self.runner.sudo_pass: @@ -92,6 +94,7 @@ class SSHConnection(object): fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) else: ssh_cmd.append(cmd) + vvv("EXEC %s" % ssh_cmd, host=self.host) p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -114,6 +117,7 @@ class SSHConnection(object): def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' + vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) sftp_cmd = ["sftp"] + self.common_args + [self.host] @@ -125,6 +129,7 @@ class SSHConnection(object): def fetch_file(self, in_path, out_path): ''' fetch a file from remote to local ''' + vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) sftp_cmd = ["sftp"] + self.common_args + [self.host] p = subprocess.Popen(sftp_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/lib/ansible/utils.py b/lib/ansible/utils.py index bd3556c767..80156a461d 100644 --- a/lib/ansible/utils.py +++ b/lib/ansible/utils.py @@ -29,6 +29,8 @@ from ansible import color from ansible import __version__ import ansible.constants as C +VERBOSITY=0 + try: import json except ImportError: @@ -318,12 +320,17 @@ class SortedOptParser(optparse.OptionParser): self.option_list.sort(key=operator.methodcaller('get_opt_string')) return optparse.OptionParser.format_help(self, formatter=None) +def increment_debug(option, opt, value, parser): + global VERBOSITY + VERBOSITY += 1 + def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, async_opts=False, connect_opts=False): ''' create an options parser for any ansible script ''' parser = SortedOptParser(usage, version=version("%prog")) - parser.add_option('-v','--verbose', default=False, action="store_true", - help='verbose mode') + parser.add_option('-v','--verbose', default=False, action="callback", + callback=increment_debug, help="verbose mode (-vvv for more)") + parser.add_option('-f','--forks', dest='forks', default=constants.DEFAULT_FORKS, type='int', help="specify number of parallel processes to use (default=%s)" % constants.DEFAULT_FORKS) parser.add_option('-i', '--inventory-file', dest='inventory',