01e51b12 |
#!/usr/bin/env python |
7de661dd |
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
# This file is part of Ansible
#
# 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/>. |
c57df622 |
####################################################### |
7de661dd |
import sys |
7256c5af |
import os |
7de661dd |
|
a4b8cdf8 |
import ansible.playbook
import ansible.constants as C |
04349ec3 |
import ansible.utils.template |
a4b8cdf8 |
from ansible import errors |
7ed734df |
from ansible import callbacks |
7e50d170 |
from ansible import utils |
12ff9b5b |
from ansible.color import ANSIBLE_COLOR, stringc |
aa552685 |
from ansible.callbacks import display |
bc17553d |
def colorize(lead, num, color): |
12ff9b5b |
""" Print 'lead' = 'num' in 'color' """ |
aa552685 |
if num != 0 and ANSIBLE_COLOR and color is not None: |
bc17553d |
return "%s%s%-15s" % (stringc(lead, color), stringc("=", color), stringc(str(num), color))
else:
return "%s=%-4s" % (lead, str(num))
|
aa552685 |
def hostcolor(host, stats, color=True):
if ANSIBLE_COLOR and color: |
12ff9b5b |
if stats['failures'] != 0 or stats['unreachable'] != 0: |
bc0be456 |
return "%-37s" % stringc(host, 'red') |
12ff9b5b |
elif stats['changed'] != 0: |
bc0be456 |
return "%-37s" % stringc(host, 'yellow') |
12ff9b5b |
else: |
bc0be456 |
return "%-37s" % stringc(host, 'green')
return "%-26s" % host |
bc17553d |
|
7de661dd |
def main(args): |
c57df622 |
''' run ansible-playbook operations '''
# create parser for CLI options |
a9162a86 |
parser = utils.base_parser( |
04349ec3 |
constants=C, |
dc984d94 |
usage = "%prog playbook.yml", |
04349ec3 |
connect_opts=True,
runas_opts=True,
subset_opts=True, |
a9162a86 |
check_opts=True,
diff_opts=True
) |
21d2069a |
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append", |
8c9f84f6 |
help="set additional variables as key=value or YAML/JSON", default=[]) |
83f23ef8 |
parser.add_option('-t', '--tags', dest='tags', default='all',
help="only run plays and tasks tagged with these values") |
04349ec3 |
parser.add_option('--skip-tags', dest='skip_tags',
help="only run plays and tasks whose tags do not match these values") |
41195d0d |
parser.add_option('--syntax-check', dest='syntax', action='store_true', |
dc984d94 |
help="perform a syntax check on the playbook, but do not execute it") |
1205bbe1 |
parser.add_option('--list-tasks', dest='listtasks', action='store_true', |
dc984d94 |
help="list all tasks that would be executed") |
7687c2ca |
parser.add_option('--step', dest='step', action='store_true',
help="one-step-at-a-time: confirm each task before running") |
04349ec3 |
parser.add_option('--start-at-task', dest='start_at', |
dc984d94 |
help="start the playbook at the task matching this name") |
8ae71cc7 |
|
7de661dd |
options, args = parser.parse_args(args)
if len(args) == 0: |
5a4d4bc0 |
parser.print_help(file=sys.stderr) |
cbfabcd0 |
return 1
|
a259b955 |
inventory = ansible.inventory.Inventory(options.inventory)
inventory.subset(options.subset)
if len(inventory.list_hosts()) == 0:
raise errors.AnsibleError("provided hosts list is empty")
|
cbfabcd0 |
sshpass = None |
f2465e05 |
sudopass = None |
1205bbe1 |
if not options.listhosts and not options.syntax and not options.listtasks: |
f436a8c8 |
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS |
5a47953e |
# Never ask for an SSH password when we run with local connection
if options.connection == "local": |
0988a866 |
options.ask_pass = False |
604bf9f5 |
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS |
0988a866 |
(sshpass, sudopass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass) |
8e039a63 |
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER |
c19c2c72 |
extra_vars = {} |
21d2069a |
for extra_vars_opt in options.extra_vars:
if extra_vars_opt.startswith("@"): |
8c9f84f6 |
# Argument is a YAML file (JSON is a subset of YAML)
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml_from_file(extra_vars_opt[1:])) |
ebd8e262 |
elif extra_vars_opt and extra_vars_opt[0] in '[{': |
8c9f84f6 |
# Arguments as YAML
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml(extra_vars_opt)) |
c19c2c72 |
else:
# Arguments as Key-value |
21d2069a |
extra_vars = utils.combine_vars(extra_vars, utils.parse_kv(extra_vars_opt)) |
c19c2c72 |
|
83f23ef8 |
only_tags = options.tags.split(",") |
04349ec3 |
skip_tags = options.skip_tags
if options.skip_tags is not None:
skip_tags = options.skip_tags.split(",") |
7de661dd |
|
7256c5af |
for playbook in args:
if not os.path.exists(playbook):
raise errors.AnsibleError("the playbook: %s could not be found" % playbook)
if not os.path.isfile(playbook):
raise errors.AnsibleError("the playbook: %s does not appear to be a file" % playbook)
|
c57df622 |
# run all playbooks specified on the command line |
7de661dd |
for playbook in args: |
6dda6f12 |
|
6cd3ba5b |
# let inventory know which playbooks are using so it can know the basedirs
inventory.set_playbook_basedir(os.path.dirname(playbook))
|
6dda6f12 |
stats = callbacks.AggregateStats() |
846186e2 |
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) |
7687c2ca |
if options.step:
playbook_cb.step = options.step |
690738ea |
if options.start_at:
playbook_cb.start_at = options.start_at |
846186e2 |
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) |
6dda6f12 |
|
7de661dd |
pb = ansible.playbook.PlayBook( |
35fdf663 |
playbook=playbook,
module_path=options.module_path, |
900790af |
inventory=inventory, |
faed4b5a |
forks=options.forks, |
35fdf663 |
remote_user=options.remote_user, |
faed4b5a |
remote_pass=sshpass,
callbacks=playbook_cb,
runner_callbacks=runner_cb, |
35fdf663 |
stats=stats, |
faed4b5a |
timeout=options.timeout, |
35fdf663 |
transport=options.connection,
sudo=options.sudo, |
710d085d |
sudo_user=options.sudo_user, |
b9982fc1 |
sudo_pass=sudopass, |
b42628d8 |
extra_vars=extra_vars, |
83f23ef8 |
private_key_file=options.private_key_file,
only_tags=only_tags, |
04349ec3 |
skip_tags=skip_tags, |
a9162a86 |
check=options.check,
diff=options.diff |
7de661dd |
) |
1205bbe1 |
if options.listhosts or options.listtasks: |
e564de39 |
print '' |
9ea26c75 |
print 'playbook: %s' % playbook |
e564de39 |
print '' |
8e039a63 |
playnum = 0 |
9a34c20c |
for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs): |
8e039a63 |
playnum += 1 |
9a34c20c |
play = ansible.playbook.Play(pb, play_ds, play_basedir)
label = play.name |
1205bbe1 |
if options.listhosts:
hosts = pb.inventory.list_hosts(play.hosts) |
e564de39 |
print ' play #%d (%s): host count=%d' % (playnum, label, len(hosts)) |
1205bbe1 |
for host in hosts:
print ' %s' % host
if options.listtasks:
matched_tags, unmatched_tags = play.compare_tags(pb.only_tags) |
04349ec3 |
# Remove skipped tasks
matched_tags = matched_tags - set(pb.skip_tags)
|
1205bbe1 |
unmatched_tags.discard('all') |
04349ec3 |
unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
(matched_tags | unmatched_tags))
|
1205bbe1 |
if unknown_tags: |
89ab3a0b |
continue |
d8e5fc9d |
print ' play #%d (%s):' % (playnum, label)
|
1205bbe1 |
for task in play.tasks(): |
04349ec3 |
if (set(task.tags).intersection(pb.only_tags) and not
set(task.tags).intersection(pb.skip_tags)): |
89ab3a0b |
if getattr(task, 'name', None) is not None:
# meta tasks have no names
print ' %s' % task.name |
e564de39 |
print '' |
9ea26c75 |
continue |
41195d0d |
if options.syntax:
# if we've not exited by now then we are fine.
print 'Playbook Syntax is fine'
return 0 |
04349ec3 |
|
c695aa2d |
failed_hosts = [] |
2b96c347 |
unreachable_hosts = [] |
a2f76c1c |
|
dfd2c6dc |
try: |
6dda6f12 |
|
f074f1c4 |
pb.run() |
a2f76c1c |
|
6dda6f12 |
hosts = sorted(pb.stats.processed.keys()) |
aa552685 |
display(callbacks.banner("PLAY RECAP")) |
3017dc92 |
playbook_cb.on_stats(pb.stats) |
c695aa2d |
for h in hosts:
t = pb.stats.summarize(h) |
2b96c347 |
if t['failures'] > 0: |
c695aa2d |
failed_hosts.append(h) |
2b96c347 |
if t['unreachable'] > 0:
unreachable_hosts.append(h) |
c695aa2d |
|
2b96c347 |
retries = failed_hosts + unreachable_hosts
if len(retries) > 0:
filename = pb.generate_retry_inventory(retries) |
c695aa2d |
if filename: |
aa552685 |
display(" to retry, use: --limit @%s\n" % filename) |
c695aa2d |
|
6dda6f12 |
for h in hosts:
t = pb.stats.summarize(h) |
aa552685 |
display("%s : %s %s %s %s" % ( |
8e039a63 |
hostcolor(h, t),
colorize('ok', t['ok'], 'green'),
colorize('changed', t['changed'], 'yellow'),
colorize('unreachable', t['unreachable'], 'red'), |
04349ec3 |
colorize('failed', t['failures'], 'red')), |
aa552685 |
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), |
04349ec3 |
colorize('failed', t['failures'], None)), |
aa552685 |
log_only=True
) |
b09ef21e |
|
04349ec3 |
|
c695aa2d |
print ""
if len(failed_hosts) > 0:
return 2 |
2b96c347 |
if len(unreachable_hosts) > 0:
return 3 |
6dda6f12 |
|
a5f4ca50 |
except errors.AnsibleError, e: |
aa552685 |
display("ERROR: %s" % e, color='red') |
dfd2c6dc |
return 1 |
7de661dd |
return 0
if __name__ == "__main__": |
aa552685 |
display(" ", log_only=True)
display(" ".join(sys.argv), log_only=True)
display(" ", log_only=True) |
a5f4ca50 |
try:
sys.exit(main(sys.argv[1:]))
except errors.AnsibleError, e: |
aa552685 |
display("ERROR: %s" % e, color='red', stderr=True) |
a5f4ca50 |
sys.exit(1)
|