summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cammarata <jimi@sngx.net>2015-09-03 18:08:34 -0400
committerJames Cammarata <jimi@sngx.net>2015-09-03 18:08:34 -0400
commitd4926382816e67731174c23f0467cb2792fa2f00 (patch)
tree4bbe1be654789fa6647c1b474cba9c5a0ac2134e
parentb2cb3153f140350c9f908df7fc14e05a8b773899 (diff)
downloadansible-issue_12191.tar.gz
Use proxy class to mark variables unsafe for templatingissue_12191
Fixes #12191
-rw-r--r--lib/ansible/template/__init__.py7
-rw-r--r--lib/ansible/vars/__init__.py6
-rw-r--r--lib/ansible/vars/unsafe_proxy.py132
3 files changed, 142 insertions, 3 deletions
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
index b4259bba9f..913837afb6 100644
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -213,6 +213,9 @@ class Templar:
before being sent through the template engine.
'''
+ if hasattr(variable, '__UNSAFE__'):
+ return variable
+
try:
if convert_bare:
variable = self._convert_bare_variable(variable)
@@ -250,13 +253,13 @@ class Templar:
return result
elif isinstance(variable, (list, tuple)):
- return [self.template(v, convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable]
+ return [self.template(v, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable]
elif isinstance(variable, dict):
d = {}
# we don't use iteritems() here to avoid problems if the underlying dict
# changes sizes due to the templating, which can happen with hostvars
for k in variable.keys():
- d[k] = self.template(variable[k], convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides)
+ d[k] = self.template(variable[k], preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides)
return d
else:
return variable
diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py
index 30b3d5d84b..ebb045e425 100644
--- a/lib/ansible/vars/__init__.py
+++ b/lib/ansible/vars/__init__.py
@@ -40,6 +40,7 @@ from ansible.template import Templar
from ansible.utils.debug import debug
from ansible.utils.vars import combine_vars
from ansible.vars.hostvars import HostVars
+from ansible.vars.unsafe_proxy import UnsafeProxy
CACHED_VARS = dict()
@@ -175,7 +176,10 @@ class VariableManager:
# next comes the facts cache and the vars cache, respectively
try:
- all_vars = combine_vars(all_vars, self._fact_cache.get(host.name, dict()))
+ host_facts = self._fact_cache.get(host.name, dict())
+ for k in host_facts.keys():
+ host_facts[k] = UnsafeProxy(host_facts[k])
+ all_vars = combine_vars(all_vars, host_facts)
except KeyError:
pass
diff --git a/lib/ansible/vars/unsafe_proxy.py b/lib/ansible/vars/unsafe_proxy.py
new file mode 100644
index 0000000000..69163802e0
--- /dev/null
+++ b/lib/ansible/vars/unsafe_proxy.py
@@ -0,0 +1,132 @@
+# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+# --------------------------------------------
+#
+# 1. This LICENSE AGREEMENT is between the Python Software Foundation
+# ("PSF"), and the Individual or Organization ("Licensee") accessing and
+# otherwise using this software ("Python") in source or binary form and
+# its associated documentation.
+#
+# 2. Subject to the terms and conditions of this License Agreement, PSF hereby
+# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+# analyze, test, perform and/or display publicly, prepare derivative works,
+# distribute, and otherwise use Python alone or in any derivative version,
+# provided, however, that PSF's License Agreement and PSF's notice of copyright,
+# i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+# 2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are
+# retained in Python alone or in any derivative version prepared by Licensee.
+#
+# 3. In the event Licensee prepares a derivative work that is based on
+# or incorporates Python or any part thereof, and wants to make
+# the derivative work available to others as provided herein, then
+# Licensee hereby agrees to include in any such work a brief summary of
+# the changes made to Python.
+#
+# 4. PSF is making Python available to Licensee on an "AS IS"
+# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+# INFRINGE ANY THIRD PARTY RIGHTS.
+#
+# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+#
+# 6. This License Agreement will automatically terminate upon a material
+# breach of its terms and conditions.
+#
+# 7. Nothing in this License Agreement shall be deemed to create any
+# relationship of agency, partnership, or joint venture between PSF and
+# Licensee. This License Agreement does not grant permission to use PSF
+# trademarks or trade name in a trademark sense to endorse or promote
+# products or services of Licensee, or any third party.
+#
+# 8. By copying, installing or otherwise using Python, Licensee
+# agrees to be bound by the terms and conditions of this License
+# Agreement.
+#
+# Original Python Recipe for Proxy:
+# http://code.activestate.com/recipes/496741-object-proxying/
+# Author: Tomer Filiba
+
+class UnsafeProxy(object):
+ __slots__ = ["_obj", "__weakref__"]
+ def __init__(self, obj):
+ object.__setattr__(self, "_obj", obj)
+
+ #
+ # proxying (special cases)
+ #
+ def __getattribute__(self, name):
+ if name == '__UNSAFE__':
+ return True
+ else:
+ return getattr(object.__getattribute__(self, "_obj"), name)
+ def __delattr__(self, name):
+ delattr(object.__getattribute__(self, "_obj"), name)
+ def __setattr__(self, name, value):
+ setattr(object.__getattribute__(self, "_obj"), name, value)
+
+ def __nonzero__(self):
+ return bool(object.__getattribute__(self, "_obj"))
+ def __str__(self):
+ return str(object.__getattribute__(self, "_obj"))
+ def __repr__(self):
+ return repr(object.__getattribute__(self, "_obj"))
+
+ #
+ # factories
+ #
+ _special_names = [
+ '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__',
+ '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__',
+ '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__',
+ '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__',
+ '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__',
+ '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__',
+ '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__',
+ '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__',
+ '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__',
+ '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__',
+ '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__',
+ '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
+ '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__',
+ '__truediv__', '__xor__', 'next',
+ ]
+
+ @classmethod
+ def _create_class_proxy(cls, theclass):
+ """creates a proxy for the given class"""
+
+ def make_method(name):
+ def method(self, *args, **kw):
+ return getattr(object.__getattribute__(self, "_obj"), name)(*args, **kw)
+ return method
+
+ namespace = {}
+ for name in cls._special_names:
+ if hasattr(theclass, name):
+ namespace[name] = make_method(name)
+ return type("%s(%s)" % (cls.__name__, theclass.__name__), (cls,), namespace)
+
+ def __new__(cls, obj, *args, **kwargs):
+ """
+ creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are
+ passed to this class' __init__, so deriving classes can define an
+ __init__ method of their own.
+ note: _class_proxy_cache is unique per deriving class (each deriving
+ class must hold its own cache)
+ """
+ try:
+ cache = cls.__dict__["_class_proxy_cache"]
+ except KeyError:
+ cls._class_proxy_cache = cache = {}
+ try:
+ theclass = cache[obj.__class__]
+ except KeyError:
+ cache[obj.__class__] = theclass = cls._create_class_proxy(obj.__class__)
+ ins = object.__new__(theclass)
+ theclass.__init__(ins, obj, *args, **kwargs)
+ return ins
+