Skip to content

Commit 02b92e2

Browse files
committed
Copy-pasted playbook CLI, passing host vars as result
1 parent 9303607 commit 02b92e2

File tree

2 files changed

+202
-7
lines changed

2 files changed

+202
-7
lines changed

app/playbook.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# (c) 2012, Michael DeHaan <[email protected]>
2+
# Copyright: (c) 2018, Ansible Project
3+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4+
5+
from __future__ import (absolute_import, division, print_function)
6+
__metaclass__ = type
7+
8+
import os
9+
import stat
10+
11+
from ansible import context
12+
from ansible.cli import CLI
13+
from ansible.cli.arguments import optparse_helpers as opt_help
14+
from ansible.errors import AnsibleError, AnsibleOptionsError
15+
from ansible.executor.playbook_executor import PlaybookExecutor
16+
from ansible.module_utils._text import to_bytes
17+
from ansible.playbook.block import Block
18+
from ansible.utils.display import Display
19+
from ansible.utils.collection_loader import set_collection_playbook_paths
20+
from ansible.plugins.loader import add_all_plugin_dirs
21+
22+
23+
display = Display()
24+
25+
26+
class PlaybookCLI(CLI):
27+
''' the tool to run *Ansible playbooks*, which are a configuration and multinode deployment system.
28+
See the project home page (https://docs.ansible.com) for more information. '''
29+
30+
def init_parser(self):
31+
32+
# create parser for CLI options
33+
super(PlaybookCLI, self).init_parser(
34+
usage="%prog [options] playbook.yml [playbook2 ...]",
35+
desc="Runs Ansible playbooks, executing the defined tasks on the targeted hosts.")
36+
37+
opt_help.add_connect_options(self.parser)
38+
opt_help.add_meta_options(self.parser)
39+
opt_help.add_runas_options(self.parser)
40+
opt_help.add_subset_options(self.parser)
41+
opt_help.add_check_options(self.parser)
42+
opt_help.add_inventory_options(self.parser)
43+
opt_help.add_runtask_options(self.parser)
44+
opt_help.add_vault_options(self.parser)
45+
opt_help.add_fork_options(self.parser)
46+
opt_help.add_module_options(self.parser)
47+
48+
# ansible playbook specific opts
49+
self.parser.add_option('--list-tasks', dest='listtasks', action='store_true',
50+
help="list all tasks that would be executed")
51+
self.parser.add_option('--list-tags', dest='listtags', action='store_true',
52+
help="list all available tags")
53+
self.parser.add_option('--step', dest='step', action='store_true',
54+
help="one-step-at-a-time: confirm each task before running")
55+
self.parser.add_option('--start-at-task', dest='start_at_task',
56+
help="start the playbook at the task matching this name")
57+
58+
def post_process_args(self, options, args):
59+
options, args = super(PlaybookCLI, self).post_process_args(options, args)
60+
61+
if len(args) == 0:
62+
raise AnsibleOptionsError("You must specify a playbook file to run")
63+
64+
display.verbosity = options.verbosity
65+
self.validate_conflicts(options, runas_opts=True, vault_opts=True, fork_opts=True)
66+
67+
return options, args
68+
69+
def run(self):
70+
71+
super(PlaybookCLI, self).run()
72+
73+
# Note: slightly wrong, this is written so that implicit localhost
74+
# manages passwords
75+
sshpass = None
76+
becomepass = None
77+
passwords = {}
78+
79+
# initial error check, to make sure all specified playbooks are accessible
80+
# before we start running anything through the playbook executor
81+
82+
b_playbook_dirs = []
83+
for playbook in context.CLIARGS['args']:
84+
if not os.path.exists(playbook):
85+
raise AnsibleError("the playbook: %s could not be found" % playbook)
86+
if not (os.path.isfile(playbook) or stat.S_ISFIFO(os.stat(playbook).st_mode)):
87+
raise AnsibleError("the playbook: %s does not appear to be a file" % playbook)
88+
89+
b_playbook_dir = os.path.dirname(os.path.abspath(to_bytes(playbook, errors='surrogate_or_strict')))
90+
# load plugins from all playbooks in case they add callbacks/inventory/etc
91+
add_all_plugin_dirs(b_playbook_dir)
92+
93+
b_playbook_dirs.append(b_playbook_dir)
94+
95+
set_collection_playbook_paths(b_playbook_dirs)
96+
97+
# don't deal with privilege escalation or passwords when we don't need to
98+
if not (context.CLIARGS['listhosts'] or context.CLIARGS['listtasks'] or
99+
context.CLIARGS['listtags'] or context.CLIARGS['syntax']):
100+
(sshpass, becomepass) = self.ask_passwords()
101+
passwords = {'conn_pass': sshpass, 'become_pass': becomepass}
102+
103+
# create base objects
104+
loader, inventory, variable_manager = self._play_prereqs()
105+
106+
# (which is not returned in list_hosts()) is taken into account for
107+
# warning if inventory is empty. But it can't be taken into account for
108+
# checking if limit doesn't match any hosts. Instead we don't worry about
109+
# limit if only implicit localhost was in inventory to start with.
110+
#
111+
# Fix this when we rewrite inventory by making localhost a real host (and thus show up in list_hosts())
112+
CLI.get_host_list(inventory, context.CLIARGS['subset'])
113+
114+
# flush fact cache if requested
115+
if context.CLIARGS['flush_cache']:
116+
self._flush_cache(inventory, variable_manager)
117+
118+
# create the playbook executor, which manages running the plays via a task queue manager
119+
pbex = PlaybookExecutor(playbooks=context.CLIARGS['args'], inventory=inventory,
120+
variable_manager=variable_manager, loader=loader,
121+
passwords=passwords)
122+
123+
results = pbex.run()
124+
125+
if isinstance(results, list):
126+
for p in results:
127+
128+
display.display('\nplaybook: %s' % p['playbook'])
129+
for idx, play in enumerate(p['plays']):
130+
if play._included_path is not None:
131+
loader.set_basedir(play._included_path)
132+
else:
133+
pb_dir = os.path.realpath(os.path.dirname(p['playbook']))
134+
loader.set_basedir(pb_dir)
135+
136+
msg = "\n play #%d (%s): %s" % (idx + 1, ','.join(play.hosts), play.name)
137+
mytags = set(play.tags)
138+
msg += '\tTAGS: [%s]' % (','.join(mytags))
139+
140+
if context.CLIARGS['listhosts']:
141+
playhosts = set(inventory.get_hosts(play.hosts))
142+
msg += "\n pattern: %s\n hosts (%d):" % (play.hosts, len(playhosts))
143+
for host in playhosts:
144+
msg += "\n %s" % host
145+
146+
display.display(msg)
147+
148+
all_tags = set()
149+
if context.CLIARGS['listtags'] or context.CLIARGS['listtasks']:
150+
taskmsg = ''
151+
if context.CLIARGS['listtasks']:
152+
taskmsg = ' tasks:\n'
153+
154+
def _process_block(b):
155+
taskmsg = ''
156+
for task in b.block:
157+
if isinstance(task, Block):
158+
taskmsg += _process_block(task)
159+
else:
160+
if task.action == 'meta':
161+
continue
162+
163+
all_tags.update(task.tags)
164+
if context.CLIARGS['listtasks']:
165+
cur_tags = list(mytags.union(set(task.tags)))
166+
cur_tags.sort()
167+
if task.name:
168+
taskmsg += " %s" % task.get_name()
169+
else:
170+
taskmsg += " %s" % task.action
171+
taskmsg += "\tTAGS: [%s]\n" % ', '.join(cur_tags)
172+
173+
return taskmsg
174+
175+
all_vars = variable_manager.get_vars(play=play)
176+
for block in play.compile():
177+
block = block.filter_tagged_tasks(all_vars)
178+
if not block.has_tasks():
179+
continue
180+
taskmsg += _process_block(block)
181+
182+
if context.CLIARGS['listtags']:
183+
cur_tags = list(mytags.union(all_tags))
184+
cur_tags.sort()
185+
taskmsg += " TASK TAGS: [%s]\n" % ', '.join(cur_tags)
186+
187+
display.display(taskmsg)
188+
189+
return inventory.groups['vpn-host'].hosts[0].vars
190+
191+
@staticmethod
192+
def _flush_cache(inventory, variable_manager):
193+
for host in inventory.list_hosts():
194+
hostname = host.get_name()
195+
variable_manager.clear_facts(hostname)

app/server.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
import yaml
66
from os.path import join, dirname
77
from aiohttp import web
8-
from ansible.cli.playbook import PlaybookCLI
9-
from time import sleep
108
import concurrent.futures
9+
from playbook import PlaybookCLI
1110

1211

1312
routes = web.RouteTableDef()
@@ -16,13 +15,14 @@
1615
task_future = None
1716
task_program = ''
1817

19-
def run_playbook(data={}):
18+
19+
def run_playbook(data):
2020
global task_program
2121
extra_vars = ' '.join(['{0}={1}'.format(key, data[key]) for key in data.keys()])
2222
task_program = ['ansible-playbook', 'main.yml', '--extra-vars', extra_vars]
23-
cli = PlaybookCLI(task_program).run()
24-
return cli
25-
23+
vars = PlaybookCLI(task_program).run()
24+
# TODO: filter only necessary vars
25+
return vars
2626

2727
@routes.get('/static/{path}')
2828
async def handle_static(request):
@@ -116,4 +116,4 @@ async def get_do_regions(request):
116116

117117
app = web.Application()
118118
app.router.add_routes(routes)
119-
web.run_app(app)
119+
web.run_app(app, port=9000)

0 commit comments

Comments
 (0)