diff --git a/README.md b/README.md index 75f9d6a3c8..63fe15b9de 100644 --- a/README.md +++ b/README.md @@ -66,22 +66,22 @@ Example: 192.168.10.52 When running ansible commands, specific hosts are addressed by wildcard or group name. -The default pattern is '*', meaning all ansible hosts. +This is required for all ansible commands. - -p '*.example.com' - -p 'atlanta;raleigh' - -p 'database*;appserver*' - -p '192.168.10.50;192.168.10.52' + '*.example.com' + 'atlanta;raleigh' + 'database*;appserver*' + '192.168.10.50;192.168.10.52' Example: Massive Parallelism and Running Shell Commands ======================================================= Reboot all web servers in Atlanta, 10 at a time: - ssh-agent bash - ssh-add ~/.ssh/id_rsa.pub + > ssh-agent bash + > ssh-add ~/.ssh/id_rsa.pub - ansible -p "atlanta-web*" -f 10 -n command -a "/sbin/reboot" + > ansible atlanta -a "/sbin/reboot" -f 10 The -f 10 specifies the usage of 10 simultaneous processes. @@ -98,15 +98,15 @@ them as template sources. To just transfer a file directly to many different servers: - ansible -n copy -a "/etc/hosts /tmp/hosts" + > ansible atlanta copy -a "/etc/hosts /tmp/hosts" To use templating, first run the setup module to put the template variables you would like to use on the remote host. Then use the template module to write the files using the templates. Templates are written in Jinja2 format. - ansible -p webservers -n setup -a "favcolor=red ntp_server=192.168.1.1" - ansible -p webservers -n template -a "src=/srv/motd.j2 dest=/etc/motd" - ansible -p webservers -n template -a "src=/srv/ntp.j2 dest=/etc/ntp.conf" + > ansible webservers -m setup -a "favcolor=red ntp_server=192.168.1.1" + > ansible webservers -m template -a "src=/srv/motd.j2 dest=/etc/motd" + > ansible webservers -m template -a "src=/srv/ntp.j2 dest=/etc/ntp.conf" Need something like the fqdn in a template? If facter or ohai are installed, data from these projects will also be made available to the template engine, using 'facter_' and 'ohai_' prefixes for each. @@ -116,7 +116,7 @@ Example: Software Deployment From Source Control Deploy your webapp straight from git - ansible -p webservers -n git -a "repo=git://foo dest=/srv/myapp version=HEAD" + > ansible webservers -m git -a "repo=git://foo dest=/srv/myapp version=HEAD" Other Modules ============= diff --git a/bin/ansible b/bin/ansible index b4b561f13d..bf72f440fb 100755 --- a/bin/ansible +++ b/bin/ansible @@ -38,37 +38,33 @@ class Cli(object): pass def runner(self): - parser = OptionParser() + parser = OptionParser(usage = 'ansible [options]') parser.add_option("-a", "--args", dest="module_args", help="module arguments", default=C.DEFAULT_MODULE_ARGS) parser.add_option('-f','--forks', dest='forks', default=C.DEFAULT_FORKS, type='int', - help='set the number of forks to start up') - parser.add_option("-p", "--host-pattern", dest="hosts", - help="hostname glob or group name", default=C.DEFAULT_PATTERN) - parser.add_option("-i", "--inventory", dest="inventory", - help="inventory host list", default=C.DEFAULT_HOST_LIST) + help='number of parallel processes to use') + parser.add_option("-i", "--inventory-file", dest="inventory", + help="inventory host file", default=C.DEFAULT_HOST_LIST) parser.add_option("-k", "--ask-pass", default=False, action="store_true", - help="ask the user to input the ssh password for connecting") - parser.add_option("-m", "--module-path", dest="module_path", + help="ask for SSH password") + parser.add_option("-M", "--module-path", dest="module_path", help="path to module library", default=C.DEFAULT_MODULE_PATH) - parser.add_option("-n", "--name", dest="module_name", - help="module name to execute", default=None) + parser.add_option("-m", "--module-name", dest="module_name", + help="module name to execute", default=C.DEFAULT_MODULE_NAME) parser.add_option('-o', '--one-line', dest='one_line', action='store_true', - help="try to print output on one line") + help="condense output") parser.add_option('-t', '--tree', dest='tree', default=None, - help="if specified, a directory name to save output to, one file per host") + help="log output to this directory") parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', - dest='timeout', help="set the timeout in seconds for ssh") + dest='timeout', help="set the SSH timeout in seconds") parser.add_option('-u', '--user', default=C.DEFAULT_REMOTE_USER, - dest='remote_user', help='set the default username') + dest='remote_user', help='connect as this user') options, args = parser.parse_args() - if options.module_name is None: - print >> sys.stderr, "-n is required" + if len(args) == 0 or len(args) > 1: + parser.print_help() sys.exit(1) - - # TODO: more shell like splitting on module_args would - # be a good idea + pattern = args[0] sshpass = None if options.ask_pass: @@ -85,7 +81,7 @@ class Cli(object): host_list=options.inventory, timeout=options.timeout, forks=options.forks, - pattern=options.hosts, + pattern=pattern, verbose=True, ) return runner @@ -95,6 +91,10 @@ class Cli(object): # if specifying output destination (aka tree output saves), create the # directory to output to + if results is None: + print >> sys.stderr, "No hosts matched" + sys.exit(1) + options = self.options # TODO: split into function @@ -117,64 +117,64 @@ class Cli(object): for hostname in sorted(results['contacted']): result = results['contacted'][hostname] + + # TODO: refactor rc = 0 + msg = '' failed = False stdout = None stderr = None traceback = None - error = None if type(result) == dict: failed = result.get('failed', 0) + msg = result.get('msg', '') if module_name == 'command': + # TODO: refactor rc = result.get('rc',0) stdout = result.get('stdout', '') stderr = result.get('stderr', '') traceback = result.get('traceback', '') - error = result.get('error', '') - - # detect and show failures, if any - if rc != 0 or failed: - msg = "Error: %s: \n" % hostname - if stdout: - msg += stdout - if stderr: - msg += stderr - if traceback: - msg += traceback - if error: - msg += error - print >> sys.stderr, msg - continue if options.one_line: # try to print everything on one line, but don't strip newlines # if the command output happend to be too long if module_name == 'command': - msg = "(stdout) %s" % stdout - if stderr.rstrip() != '': - msg = "(stdout) %s (stderr) %s" % (stdout,stderr) - print "%s | rc=%s | %s" % ( - hostname, rc, msg - ) + if not failed: + buf = "(stdout) %s" % stdout + if stderr.rstrip() != '': + buf = "(stdout) %s (stderr) %s" % (stdout,stderr) + print "%s | rc=%s | %s" % ( + hostname, rc, buf + ) + else: + print "%s | (error) %s" % (hostname, msg) else: print "%s | %s" % (hostname, result) else: # summarize response from command in multiple lines + buf = '' if module_name == 'command': - buf = '' - buf += "%s | rc=%s >>\n" % (hostname, rc) + if not failed: + buf += "%s | rc=%s >>\n" % (hostname, rc) + else: + buf += "%s | rc=%s | FAILED >>\n" % (hostname, rc) buf += stdout if stderr: buf += stderr + if msg: + buf += msg print buf - if options.tree: - path = os.path.join(options.tree, hostname) - fd = open(path, "w+") - fd.write(buf) - fd.close() else: - print "%s >>" % hostname - print json.dumps(result, indent=4, sort_keys=True) + if not failed: + buf += "%s >>" % hostname + else: + buf += "%s | FAILED >>" % hostname + buf += json.dumps(result, indent=4, sort_keys=True) + if options.tree: + path = os.path.join(options.tree, hostname) + fd = open(path, "w+") + fd.write(buf) + fd.close() if len(results['dark'].keys()) > 0: print >> sys.stderr, "*** Hosts which could not be contacted or did not respond: ***" diff --git a/docs/man/man1/ansible.1.asciidoc b/docs/man/man1/ansible.1.asciidoc index fa278d6d49..27ca985b08 100644 --- a/docs/man/man1/ansible.1.asciidoc +++ b/docs/man/man1/ansible.1.asciidoc @@ -12,7 +12,7 @@ ansible - run a command somewhere else SYNOPSIS -------- -ansible [-f forks] [-p pattern ] [-n module_name] [-a args] +ansible [-f forks] [-m module_name] [-a args] DESCRIPTION @@ -22,6 +22,15 @@ DESCRIPTION SSH. +ARGUMENTS +--------- + +*hostspec* + +A name of a group in the inventory file, a shell-like glob selecting hosts in inventory +file, or any combination of the two seperated by semicolons. + + OPTIONS ------- @@ -36,16 +45,11 @@ Path to the inventory hosts file, which defaults to /etc/ansible/hosts. Level of parallelism. Specify as an integer, the default is 5. -*-n*, *--name*:: +*-m*, *--module-name*:: Module name to execute. -*-a*, *--args*:: - -Arguments to module, as a single string. - - *-p*, *--pattern*:: Hostname pattern. Accepts shell-like globs which can be seperated with ";" @@ -89,8 +93,9 @@ Ansible is released under the terms of the GPLv3 License. SEE ALSO -------- +Ansible home page: + *ansible-modules*(5), *ansible-playbook*(5), -Ansible home page: diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 2e55f0104b..eab035f6e5 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -19,8 +19,8 @@ # control side (aka 'overlord') DEFAULT_HOST_LIST = '/etc/ansible/hosts' DEFAULT_MODULE_PATH = '/usr/share/ansible' -DEFAULT_MODULE_NAME = 'ping' -DEFAULT_PATTERN = '*' +DEFAULT_MODULE_NAME = 'command' +DEFAULT_PATTERN = None DEFAULT_FORKS = 3 DEFAULT_MODULE_ARGS = '' DEFAULT_TIMEOUT = 10 diff --git a/lib/ansible/playbook.py b/lib/ansible/playbook.py index a2e3998cac..9242bfd2da 100755 --- a/lib/ansible/playbook.py +++ b/lib/ansible/playbook.py @@ -151,6 +151,11 @@ class PlayBook(object): remote_user=remote_user ) results = runner.run() + + # if no hosts are matched, carry on, unlike /bin/ansible + # which would warn you about this + if results is None: + results = {} # walk through the results and build up # summary information about successes and diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index d71e53d276..574ab941f9 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -326,7 +326,9 @@ class Runner(object): # find hosts that match the pattern hosts = self.match_hosts(self.pattern) - + if len(hosts) == 0: + return None + # attack pool of hosts in N forks # _executor_hook does all of the work hosts = [ (self,x) for x in hosts ] diff --git a/library/command b/library/command index 7d3c2f32bd..a9af113f24 100755 --- a/library/command +++ b/library/command @@ -45,13 +45,13 @@ try: except (OSError, IOError), e: print json.dumps({ "failed": 1, - "error": str(e), + "msg": str(e), }) sys.exit(1) except: print json.dumps({ "failed" : 1, - "traceback" : traceback.format_exc() + "msg" : traceback.format_exc() }) sys.exit(1)