From cd0602889d9495376d296c0e8f4aaffa863178a7 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 18 Apr 2013 22:36:06 -0400 Subject: [PATCH 1/4] Showcase the new-style lookup plugin access in the authorized_key docs rather than the old-style $FILE --- docsite/latest/rst/index.rst | 10 +++++----- library/authorized_key | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docsite/latest/rst/index.rst b/docsite/latest/rst/index.rst index 383374f73c..1d2ec82b64 100644 --- a/docsite/latest/rst/index.rst +++ b/docsite/latest/rst/index.rst @@ -7,15 +7,15 @@ Ansible Documentation :alt: ansiblefest 2013 :target: http://ansibleworks.com/fest -This page contains documentation about how to use `Ansible `_ and covers the latest version. For the current -released version, see `Ansible 1.1 Docs `_. You may also be interested in taking a class: +NOTE: This page contains documentation about how to use `Ansible `_ and covers the latest DEVELOPMENT version (1.2). For the current +released version, see `Ansible 1.1 Docs `_ instead. You might be one of them. We hope you find what you are looking for in the documentation, and would like to point out that you may also be interested in taking a class: .. image:: http://www.ansibleworks.com.s3-website-us-east-1.amazonaws.com/img/banners/training.png :alt: ansibleworks training :target: http://www.ansibleworks.com/training/ Before we dive into playbooks, configuration management, deployment, and orchestration, we'll learn how to get Ansible installed and some -basic information. We'll go over how to execute ad-hoc commands in parallel across your nodes using /usr/bin/ansible. We'll also see +basic information. We'll go over how to execute ad-hoc commands in parallel across your nodes using /usr/bin/ansible. We'll also see what sort of modules are available in Ansible's core (though you can also write your own, which we'll also show later). @@ -31,7 +31,7 @@ Overview ```````` .. image:: http://ansible.cc/img/ansible_arch.png - :alt: ansible architecture diagram + :alt: ansible architecture diagram :width: 566px :height: 439px @@ -41,7 +41,7 @@ Playbooks Playbooks are Ansible's orchestration language. At a basic level, playbooks can be used to manage configurations and deployments of remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions -to other hosts, interacting with monitoring servers and load balancers along the way. You can start small and pick up more features +to other hosts, interacting with monitoring servers and load balancers along the way. You can start small and pick up more features over time as you need them. Playbooks are designed to be human-readable and are developed in a basic text language. There are multiple ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible. diff --git a/library/authorized_key b/library/authorized_key index b22afe19a2..fe88003823 100644 --- a/library/authorized_key +++ b/library/authorized_key @@ -65,10 +65,10 @@ author: Brad Olson EXAMPLES = ''' # Example using key data from a local file on the management machine -authorized_key: user=charlie key='$FILE(/home/charlie/.ssh/id_rsa.pub) +authorized_key: user=charlie key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}" # Using alternate directory locations: -authorized_key: user=charlie key='$FILE(/home/charlie/.ssh/id_rsa.pub)' sshdir='/etc/ssh/authorized_keys/charlie' manage_dir=no +authorized_key: user=charlie key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}" sshdir='/etc/ssh/authorized_keys/charlie' manage_dir=no ''' # Makes sure the public key line is present or absent in the user's .ssh/authorized_keys. From 56568177765f8de30f5515da5552701b78d76527 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 18 Apr 2013 22:43:14 -0400 Subject: [PATCH 2/4] Fixup authorized key documentation formatting --- library/authorized_key | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/authorized_key b/library/authorized_key index fe88003823..e56912727e 100644 --- a/library/authorized_key +++ b/library/authorized_key @@ -44,7 +44,7 @@ options: description: - Alternate path to the authorized_keys file required: false - default: "/home/user/.ssh/authorized_keys" + default: "(homedir)+/.ssh/authorized_keys" version_added: "1.2" manage_dir: description: @@ -59,7 +59,8 @@ options: required: false choices: [ "present", "absent" ] default: "present" -description: "Advanced usage with an alternate AuthorizedKeysFile configuration" +description: + - "adds or removes authorized keys for particular user accounts" author: Brad Olson ''' From df93d7dd977afff12199314f89a61a0de733074b Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 19 Apr 2013 19:01:19 -0400 Subject: [PATCH 3/4] Ignore inventory config files when using an inventory directory. --- lib/ansible/inventory/dir.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ansible/inventory/dir.py b/lib/ansible/inventory/dir.py index 89b290d4a5..0db6ba2dff 100644 --- a/lib/ansible/inventory/dir.py +++ b/lib/ansible/inventory/dir.py @@ -36,9 +36,14 @@ class InventoryDirectory(object): self.parsers = [] self.hosts = {} self.groups = {} + for i in self.names: + if i.endswith("~") or i.endswith(".orig") or i.endswith(".bak"): continue + if i.endswith(".ini"): + # configuration file for an inventory script + continue if i.endswith(".retry"): # this file is generated on a failed playbook and should only be # used when run specifically From 53ac0bbec2d45a5f71ae89176f33521347066688 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 20 Apr 2013 09:09:35 -0400 Subject: [PATCH 4/4] Instantiate callback plugins only once so we can set play/task objects on them and they'll stick. --- lib/ansible/callbacks.py | 17 +++++++- lib/ansible/playbook/__init__.py | 15 +++---- lib/ansible/utils/plugins.py | 66 +++++++++++++++++++++++++++---- plugins/callbacks/context_demo.py | 31 +++++++++++++++ 4 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 plugins/callbacks/context_demo.py diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index cf204bbdb2..75299e7284 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -45,10 +45,25 @@ if cowsay and noncow == 'random': cows = out.split() cows.append(False) noncow = random.choice(cows) + +callback_plugins = [x for x in utils.plugins.callback_loader.all()] + +def set_play(callback, play): + ''' used to notify callback plugins of context ''' + callback.play = play + for callback_plugin in callback_plugins: + callback_plugin.play = play + +def set_task(callback, task): + ''' used to notify callback plugins of context ''' + callback.task = task + for callback_plugin in callback_plugins: + callback_plugin.task = task + def call_callback_module(method_name, *args, **kwargs): - for callback_plugin in utils.plugins.callback_loader.all(): + for callback_plugin in callback_plugins: methods = [ getattr(callback_plugin, method_name, None), getattr(callback_plugin, 'on_any', None) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 2eb98fa05d..856ec63207 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -16,8 +16,8 @@ # along with Ansible. If not, see . import ansible.inventory -import ansible.runner import ansible.constants as C +import ansible.runner from ansible.utils import template from ansible import utils from ansible import errors @@ -214,8 +214,9 @@ class PlayBook(object): for (play_ds, play_basedir) in zip(self.playbook, self.play_basedirs): play = Play(self, play_ds, play_basedir) - self.callbacks.play = play - self.runner_callbacks.play = play + assert play is not None + ansible.callbacks.set_play(self.callbacks, play) + ansible.callbacks.set_play(self.runner_callbacks, play) matched_tags, unmatched_tags = play.compare_tags(self.only_tags) matched_tags_all = matched_tags_all | matched_tags @@ -317,8 +318,8 @@ class PlayBook(object): def _run_task(self, play, task, is_handler): ''' run a single task in the playbook and recursively run any subtasks. ''' - self.callbacks.task = task - self.runner_callbacks.task = task + ansible.callbacks.set_task(self.callbacks, task) + ansible.callbacks.set_task(self.runner_callbacks, task) self.callbacks.on_task_start(template.template(play.basedir, task.name, task.module_vars, lookup_fatal=False), is_handler) if hasattr(self.callbacks, 'skip_task') and self.callbacks.skip_task: @@ -402,8 +403,8 @@ class PlayBook(object): self.callbacks.on_setup() self.inventory.restrict_to(host_list) - self.callbacks.task = None - self.runner_callbacks.task = None + ansible.callbacks.set_task(self.callbacks, None) + ansible.callbacks.set_task(self.runner_callbacks, None) # push any variables down to the system setup_results = ansible.runner.Runner( diff --git a/lib/ansible/utils/plugins.py b/lib/ansible/utils/plugins.py index 5a2867fd83..33ed750b79 100644 --- a/lib/ansible/utils/plugins.py +++ b/lib/ansible/utils/plugins.py @@ -22,6 +22,7 @@ import imp import ansible.constants as C from ansible import errors +MODULE_CACHE = {} _basedirs = [] def push_basedir(basedir): @@ -41,7 +42,11 @@ class PluginLoader(object): self.config = config self.subdir = subdir self.aliases = aliases - self._module_cache = {} + + if not class_name in MODULE_CACHE: + MODULE_CACHE[class_name] = {} + + self._module_cache = MODULE_CACHE[class_name] self._extra_dirs = [] def _get_package_path(self): @@ -111,6 +116,7 @@ class PluginLoader(object): return getattr(self._module_cache[path], self.class_name)(*args, **kwargs) def all(self, *args, **kwargs): + for i in self._get_paths(): for path in glob.glob(os.path.join(i, "*.py")): name, ext = os.path.splitext(os.path.basename(path)) @@ -120,10 +126,54 @@ class PluginLoader(object): self._module_cache[path] = imp.load_source('.'.join([self.package, name]), path) yield getattr(self._module_cache[path], self.class_name)(*args, **kwargs) -action_loader = PluginLoader('ActionModule', 'ansible.runner.action_plugins', C.DEFAULT_ACTION_PLUGIN_PATH, 'action_plugins') -callback_loader = PluginLoader('CallbackModule', 'ansible.callback_plugins', C.DEFAULT_CALLBACK_PLUGIN_PATH, 'callback_plugins') -connection_loader = PluginLoader('Connection', 'ansible.runner.connection_plugins', C.DEFAULT_CONNECTION_PLUGIN_PATH, 'connection_plugins', aliases={'paramiko': 'paramiko_ssh'}) -module_finder = PluginLoader('', '', C.DEFAULT_MODULE_PATH, 'library') -lookup_loader = PluginLoader('LookupModule', 'ansible.runner.lookup_plugins', C.DEFAULT_LOOKUP_PLUGIN_PATH, 'lookup_plugins') -vars_loader = PluginLoader('VarsModule', 'ansible.inventory.vars_plugins', C.DEFAULT_VARS_PLUGIN_PATH, 'vars_plugins') -filter_loader = PluginLoader('FilterModule', 'ansible.runner.filter_plugins', C.DEFAULT_FILTER_PLUGIN_PATH, 'filter_plugins') +action_loader = PluginLoader( + 'ActionModule', + 'ansible.runner.action_plugins', + C.DEFAULT_ACTION_PLUGIN_PATH, + 'action_plugins' +) + +callback_loader = PluginLoader( + 'CallbackModule', + 'ansible.callback_plugins', + C.DEFAULT_CALLBACK_PLUGIN_PATH, + 'callback_plugins' +) + +connection_loader = PluginLoader( + 'Connection', + 'ansible.runner.connection_plugins', + C.DEFAULT_CONNECTION_PLUGIN_PATH, + 'connection_plugins', + aliases={'paramiko': 'paramiko_ssh'} +) + +module_finder = PluginLoader( + '', + '', + C.DEFAULT_MODULE_PATH, + 'library' +) + +lookup_loader = PluginLoader( + 'LookupModule', + 'ansible.runner.lookup_plugins', + C.DEFAULT_LOOKUP_PLUGIN_PATH, + 'lookup_plugins' +) + +vars_loader = PluginLoader( + 'VarsModule', + 'ansible.inventory.vars_plugins', + C.DEFAULT_VARS_PLUGIN_PATH, + 'vars_plugins' +) + +filter_loader = PluginLoader( + 'FilterModule', + 'ansible.runner.filter_plugins', + C.DEFAULT_FILTER_PLUGIN_PATH, + 'filter_plugins' +) + + diff --git a/plugins/callbacks/context_demo.py b/plugins/callbacks/context_demo.py new file mode 100644 index 0000000000..5c3015d85f --- /dev/null +++ b/plugins/callbacks/context_demo.py @@ -0,0 +1,31 @@ +# (C) 2012, Michael DeHaan, + +# 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 . + +import os +import time +import json + +class CallbackModule(object): + """ + This is a very trivial example of how any callback function can get at play and task objects. + play will be 'None' for runner invocations, and task will be None for 'setup' invocations. + """ + + def on_any(self, *args, **kwargs): + play = getattr(self, 'play', None) + task = getattr(self, 'task', None) + print "play = %s, task = %s, args = %s, kwargs = %s" % (play,task,args,kwargs)