diff --git a/v2/ansible/executor/playbook_executor.py b/v2/ansible/executor/playbook_executor.py index 324e6b01af9..64f3f676210 100644 --- a/v2/ansible/executor/playbook_executor.py +++ b/v2/ansible/executor/playbook_executor.py @@ -43,7 +43,10 @@ class PlaybookExecutor: self._loader = loader self._options = options - self._tqm = TaskQueueManager(inventory=inventory, callback='default', variable_manager=variable_manager, loader=loader, options=options) + if options.listhosts or options.listtasks or options.listtags: + self._tqm = None + else: + self._tqm = TaskQueueManager(inventory=inventory, callback='default', variable_manager=variable_manager, loader=loader, options=options) def run(self): @@ -58,7 +61,7 @@ class PlaybookExecutor: try: for playbook_path in self._playbooks: pb = Playbook.load(playbook_path, variable_manager=self._variable_manager, loader=self._loader) - + # FIXME: playbook entries are just plays, so we should rename them for play in pb.get_entries(): self._inventory.remove_restriction() @@ -83,43 +86,40 @@ class PlaybookExecutor: break if result != 0: - # FIXME: do something here, to signify the playbook execution failed - self._cleanup() - return result - except: + raise AnsibleError("Play failed!: %d" % result) + finally: self._cleanup() - raise - self._cleanup() + if result == 0: + #TODO: move to callback + # FIXME: this stat summary stuff should be cleaned up and moved + # to a new method, if it even belongs here... + self._tqm._display.banner("PLAY RECAP") - # FIXME: this stat summary stuff should be cleaned up and moved - # to a new method, if it even belongs here... - self._tqm._display.banner("PLAY RECAP") + hosts = sorted(self._tqm._stats.processed.keys()) + for h in hosts: + t = self._tqm._stats.summarize(h) - hosts = sorted(self._tqm._stats.processed.keys()) - for h in hosts: - t = self._tqm._stats.summarize(h) + self._tqm._display.display("%s : %s %s %s %s" % ( + hostcolor(h, t), + colorize('ok', t['ok'], 'green'), + colorize('changed', t['changed'], 'yellow'), + colorize('unreachable', t['unreachable'], 'red'), + colorize('failed', t['failures'], 'red')), + screen_only=True + ) - self._tqm._display.display("%s : %s %s %s %s" % ( - hostcolor(h, t), - colorize('ok', t['ok'], 'green'), - colorize('changed', t['changed'], 'yellow'), - colorize('unreachable', t['unreachable'], 'red'), - colorize('failed', t['failures'], 'red')), - screen_only=True - ) + self._tqm._display.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), + colorize('failed', t['failures'], None)), + log_only=True + ) - self._tqm._display.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), - colorize('failed', t['failures'], None)), - log_only=True - ) - - self._tqm._display.display("", screen_only=True) - # END STATS STUFF + self._tqm._display.display("", screen_only=True) + # END STATS STUFF return result @@ -160,3 +160,24 @@ class PlaybookExecutor: serialized_batches.append(play_hosts) return serialized_batches + + def listhosts(self): + + playlist = [] + try: + for playbook_path in self._playbooks: + pb = Playbook.load(playbook_path, variable_manager=self._variable_manager, loader=self._loader) + for play in pb.get_entries(): + + # Use templated copies in case hosts: depends on variables + all_vars = self._variable_manager.get_vars(loader=self._loader, play=play) + new_play = play.copy() + new_play.post_validate(all_vars, fail_on_undefined=False) + + playlist.append(set(self._inventory.get_hosts(new_play.hosts))) + except AnsibleError: + raise + except Exception, e: + raise AnsibleParserError("Failed to process plays: %s" % str(e)) + + return playlist diff --git a/v2/ansible/playbook/helpers.py b/v2/ansible/playbook/helpers.py index cc262b4fb51..dd346c636f0 100644 --- a/v2/ansible/playbook/helpers.py +++ b/v2/ansible/playbook/helpers.py @@ -21,7 +21,7 @@ import os from types import NoneType from ansible.errors import AnsibleParserError -from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject +from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleSequence def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): @@ -34,7 +34,7 @@ def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block - assert type(ds) in (list, NoneType) + assert ds is None or isinstance(ds, AnsibleSequence), 'block has bad type: %s' % type(ds) block_list = [] if ds: @@ -64,7 +64,7 @@ def load_list_of_tasks(ds, block=None, role=None, task_include=None, use_handler from ansible.playbook.handler import Handler from ansible.playbook.task import Task - assert type(ds) == list + assert isinstance(ds, list), 'task has bad type: %s' % type(ds) task_list = [] for task in ds: @@ -101,7 +101,7 @@ def load_list_of_roles(ds, current_role_path=None, variable_manager=None, loader # we import here to prevent a circular dependency with imports from ansible.playbook.role.include import RoleInclude - assert isinstance(ds, list) + assert isinstance(ds, list), 'roles has bad type: %s' % type(ds) roles = [] for role_def in ds: diff --git a/v2/bin/ansible-playbook b/v2/bin/ansible-playbook index 3a3793affc6..57380590c47 100755 --- a/v2/bin/ansible-playbook +++ b/v2/bin/ansible-playbook @@ -133,7 +133,12 @@ def main(args): pbex = PlaybookExecutor(playbooks=args, inventory=inventory, variable_manager=variable_manager, loader=loader, options=options) if options.listhosts: - print('TODO: implement') + i = 1 + for play in pbex.listhosts(): + print("\nplay #%d" % i) + for host in sorted(play): + print(" %s" % host) + i = i + 1 sys.exit(0) elif options.listtasks: print('TODO: implement')