From 8ffed6df756bc19bffa25c89919204c2b2031927 Mon Sep 17 00:00:00 2001 From: Jeroen Hoekx Date: Mon, 5 Nov 2012 15:09:34 +0100 Subject: [PATCH 1/2] Support custom jinja2 filters. This uses the plugin framework to add filter plugins. The previously hardcoded core filters are defined using the plugin framework now. --- examples/playbooks/custom_filters.yml | 6 ++++ .../filter_plugins/custom_plugins.py | 29 +++++++++++++++++ .../playbooks/templates/custom-filters.j2 | 1 + lib/ansible/constants.py | 1 + lib/ansible/runner/filter_plugins/__init__.py | 0 lib/ansible/runner/filter_plugins/core.py | 31 +++++++++++++++++++ lib/ansible/utils/plugins.py | 1 + lib/ansible/utils/template.py | 9 +++--- 8 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 examples/playbooks/custom_filters.yml create mode 100644 examples/playbooks/filter_plugins/custom_plugins.py create mode 100644 examples/playbooks/templates/custom-filters.j2 create mode 100644 lib/ansible/runner/filter_plugins/__init__.py create mode 100644 lib/ansible/runner/filter_plugins/core.py diff --git a/examples/playbooks/custom_filters.yml b/examples/playbooks/custom_filters.yml new file mode 100644 index 00000000000..2bc8feffa0c --- /dev/null +++ b/examples/playbooks/custom_filters.yml @@ -0,0 +1,6 @@ +--- + +- name: Demonstrate custom jinja2 filters + hosts: all + tasks: + - action: template src=templates/custom-filters.j2 dest=/tmp/custom-filters.txt diff --git a/examples/playbooks/filter_plugins/custom_plugins.py b/examples/playbooks/filter_plugins/custom_plugins.py new file mode 100644 index 00000000000..d00e6c3fccf --- /dev/null +++ b/examples/playbooks/filter_plugins/custom_plugins.py @@ -0,0 +1,29 @@ +# (c) 2012, Jeroen Hoekx +# +# 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 . + +class FilterModule(object): + ''' Custom filters are loaded by FilterModule objects ''' + + def filters(self): + ''' FilterModule objects return a dict mapping filter names to + filter functions. ''' + return { + 'generate_answer': self.generate_answer, + } + + def generate_answer(self, value): + return '42' diff --git a/examples/playbooks/templates/custom-filters.j2 b/examples/playbooks/templates/custom-filters.j2 new file mode 100644 index 00000000000..08126f958a4 --- /dev/null +++ b/examples/playbooks/templates/custom-filters.j2 @@ -0,0 +1 @@ +1 + 1 = {{ '1+1' | generate_answer }} diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index b97cfab85ce..5f3f35aff7e 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -93,6 +93,7 @@ DEFAULT_CALLBACK_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'call DEFAULT_CONNECTION_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'connection_plugins', None, '/usr/share/ansible_plugins/connection_plugins')) DEFAULT_LOOKUP_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'lookup_plugins', None, '/usr/share/ansible_plugins/lookup_plugins')) DEFAULT_VARS_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'vars_plugins', None, '/usr/share/ansible_plugins/vars_plugins')) +DEFAULT_FILTER_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'filter_plugins', None, '/usr/share/ansible_plugins/filter_plugins')) # non-configurable things DEFAULT_SUDO_PASS = None diff --git a/lib/ansible/runner/filter_plugins/__init__.py b/lib/ansible/runner/filter_plugins/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py new file mode 100644 index 00000000000..63eec29678b --- /dev/null +++ b/lib/ansible/runner/filter_plugins/core.py @@ -0,0 +1,31 @@ +# (c) 2012, Jeroen Hoekx +# +# 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 json +import yaml + +class FilterModule(object): + ''' Ansible core jinja2 filters ''' + + def filters(self): + return { + 'to_json': json.dumps, + 'from_json': json.loads, + 'to_yaml': yaml.dump, + 'from_yaml': yaml.load, + } + diff --git a/lib/ansible/utils/plugins.py b/lib/ansible/utils/plugins.py index 6ff7241b2ab..5bc91407eb7 100644 --- a/lib/ansible/utils/plugins.py +++ b/lib/ansible/utils/plugins.py @@ -95,3 +95,4 @@ callback_loader = PluginLoader('CallbackModule', 'ansible.callback_plugins', connection_loader = PluginLoader('Connection', 'ansible.runner.connection_plugins', C.DEFAULT_CONNECTION_PLUGIN_PATH, 'connection_plugins', aliases={'paramiko': 'paramiko_ssh'}) 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/lib/ansible/utils/template.py b/lib/ansible/utils/template.py index 60cdd4f1246..4f59e31165c 100644 --- a/lib/ansible/utils/template.py +++ b/lib/ansible/utils/template.py @@ -221,10 +221,11 @@ def template_from_file(basedir, path, vars): realpath = utils.path_dwim(basedir, path) loader=jinja2.FileSystemLoader([basedir,os.path.dirname(realpath)]) environment = jinja2.Environment(loader=loader, trim_blocks=True) - environment.filters['to_json'] = json.dumps - environment.filters['from_json'] = json.loads - environment.filters['to_yaml'] = yaml.dump - environment.filters['from_yaml'] = yaml.load + for filter_plugin in utils.plugins.filter_loader.all(): + filters = filter_plugin.filters() + if not isinstance(filters, dict): + raise errors.AnsibleError("FilterModule.filters should return a dict.") + environment.filters.update(filters) try: data = codecs.open(realpath, encoding="utf8").read() except UnicodeDecodeError: From 67321a60122c464dd438b6c92bee22adf4beaa6e Mon Sep 17 00:00:00 2001 From: Jeroen Hoekx Date: Mon, 5 Nov 2012 15:23:04 +0100 Subject: [PATCH 2/2] Add ansible.runner.filter_plugins package to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index d6baeb1f7bf..dd2d085633b 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ setup(name='ansible', 'ansible.runner.lookup_plugins', 'ansible.runner.connection_plugins', 'ansible.runner.action_plugins', + 'ansible.runner.filter_plugins', 'ansible.callback_plugins', ], scripts=[