From 8ffed6df756bc19bffa25c89919204c2b2031927 Mon Sep 17 00:00:00 2001 From: Jeroen Hoekx Date: Mon, 5 Nov 2012 15:09:34 +0100 Subject: [PATCH] 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 0000000000..2bc8feffa0 --- /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 0000000000..d00e6c3fcc --- /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 0000000000..08126f958a --- /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 b97cfab85c..5f3f35aff7 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 0000000000..e69de29bb2 diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py new file mode 100644 index 0000000000..63eec29678 --- /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 6ff7241b2a..5bc91407eb 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 60cdd4f124..4f59e31165 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: