Add ansible-pull

A first stab at a pull-based model for ansible.  This does two things:
    1. Invoke the git module via Runner to set up a git repository on the
       localhost.  It sets up Runner to use transport='local' and forces
       the inventory to just 'localhost'.
    2. Run any playbooks provided.  By default, this wants to run the
       playbook local.yml.  This also sets transport='local' and sets
       the host_list to a list: localhost, fqdn, and hostname.
The reason for setting the host_list and not using override_hosts is
because there may be plays in the playbook that are not meant for a
specific host.  That is, if the git repository is for the entire site
and not host-specific, you don't want to override hosts and apply all
plays to any given host.  This has the downside of potentially running a
play three times if the play is defined for 'hosts: all'.
This commit is contained in:
Stephen Fromm 2012-04-30 14:27:57 -07:00
parent 3803b27f6c
commit 672794f586

147
bin/ansible-pull Executable file
View file

@ -0,0 +1,147 @@
#!/usr/bin/env python
# (c) 2012, Stephen Fromm <sfromm@gmail.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import socket
import logging
import ansible.playbook
import ansible.runner
import ansible.constants as C
from ansible import errors
from ansible import callbacks
from ansible import utils
from ansible import inventory
DEFAULT_PLAYBOOK = 'local.yml'
def main(args):
""" Set up and run a local playbook """
usage = "%prog [options]"
parser = utils.base_parser(constants=C, usage=usage,
connect_opts=False, runas_opts=False)
parser.set_defaults(module_name='git', transport='local',
one_line=False, tree=None)
parser.add_option('-d', '--directory', dest='dest', default=None,
help='Directory to checkout git repository')
parser.add_option('-U', '--url', dest='url', default=None,
help='URL of git repository')
parser.add_option('-C', '--checkout', dest='checkout', default="HEAD",
help='Branch/Tag/Commit to checkout. Defaults to HEAD.')
parser.remove_option('-k') # Remove ssh password option
parser.remove_option('-K') # Remove sudo password option
parser.remove_option('-T') # Remove ssh timeout option
options, args = parser.parse_args(args)
clirunner_cb = callbacks.CliRunnerCallbacks()
clirunner_cb.options = options
# ----------------------------------------------
# First git clone/pull
git_opts = "repo=%s dest=%s version=%s" % (options.url, options.dest, options.checkout)
pattern = "localhost"
inventory_manager = inventory.Inventory([pattern])
"""
Ideally, changes should be reported via logging and not to STDOUT
"""
runner = ansible.runner.Runner(
module_name=options.module_name,
module_args=git_opts,
module_path=options.module_path,
inventory=inventory_manager,
forks=options.forks,
pattern=pattern,
callbacks=clirunner_cb,
transport=options.transport,
debug=options.debug
)
try:
runner.run()
except errors.AnsibleError, e:
print >>sys.stderr, "ERROR: %s" % e
return 1
# ----------------------------------------------
# Second, run the playbook
"""
Change to the directory where the git checkout is located.
Insert 'local.yml' as the first playbook to be run. This
supports multiple playbooks being supplied on the CLI, similar
to ansible-playbook. This then loops on all the playbooks,
instantiates and runs a playbook. A couple things of note:
* The transport uses the default set above, local
* The host_list argument to Playbook is set to a list of
names. These are localhost, the fqdn, and hostname.
This last point is problematic because it will run a playbook
3 times if the playbook is for 'all' hosts. We do not necessarily
want to override 'hosts' in the playbook because they may be generic
across the entire infrastructure -- not host specific.
Finally, this should use the logging module in some manner and
not print data to STDOUT.
"""
if os.path.exists("%s/%s" % (options.dest, DEFAULT_PLAYBOOK)):
args.insert(0, DEFAULT_PLAYBOOK)
os.chdir(options.dest)
hostname = socket.getfqdn()
stats = callbacks.AggregateStats()
playbook_cb = callbacks.PlaybookCallbacks()
pbrunner_cb = callbacks.PlaybookRunnerCallbacks(stats)
local_host = [pattern, hostname, hostname.split('.')[0]]
for playbook in args:
pb = ansible.playbook.PlayBook(
playbook=playbook,
host_list=local_host,
module_path=options.module_path,
debug=options.debug,
runner_callbacks=pbrunner_cb,
callbacks=playbook_cb,
transport=options.transport,
stats=stats
)
"""
This just takes the reporting from ansible-playbook.
Ideally, this should use logging to report success/failure/changes.
"""
try:
pb.run()
hosts = sorted(pb.stats.processed.keys())
print "RECAP\n\n"
for h in hosts:
t = pb.stats.summarize(h)
print "%-30s : ok=%4s changed=%4s unreachable=%4s failed=%4s " % (h,
t['ok'], t['changed'], t['unreachable'], t['failures']
)
print "\n"
except errors.AnsibleError, e:
print >>sys.stderr, "ERROR: %s" % e
return 1
return 0
if __name__ == '__main__':
try:
sys.exit(main(sys.argv[1:]))
except errors.AnsibleError, e:
print >>sys.stderr, "ERROR: %s" % e
sys.exit(1)