def template_from_string(basedir, data, vars, fail_on_undefined=False): ''' run a string through the (Jinja2) templating engine ''' try: if type(data) == str: data = unicode(data, 'utf-8') def my_finalize(thing): return thing if thing is not None else '' environment = jinja2.Environment(trim_blocks=True, undefined=StrictUndefined, extensions=_get_extensions(), finalize=my_finalize) environment.filters.update(_get_filters()) environment.template_class = J2Template if '_original_file' in vars: basedir = os.path.dirname(vars['_original_file']) filesdir = os.path.abspath(os.path.join(basedir, '..', 'files')) if os.path.exists(filesdir): basedir = filesdir data = data.decode('utf-8') try: t = environment.from_string(data) except Exception, e: if 'recursion' in str(e): raise errors.AnsibleError( "recursive loop detected in template string: %s" % data) elif isinstance(e, TemplateSyntaxError): raise errors.AnsibleError( "there was an error in the template: %s" % data) else: return data def my_lookup(*args, **kwargs): kwargs['vars'] = vars return lookup(*args, basedir=basedir, **kwargs) t.globals['lookup'] = my_lookup t.globals['finalize'] = my_finalize jvars = _jinja2_vars(basedir, vars, t.globals, fail_on_undefined) new_context = t.new_context(jvars, shared=True) rf = t.root_render_func(new_context) try: res = jinja2.utils.concat(rf) except TypeError, te: if 'StrictUndefined' in str(te): raise errors.AnsibleUndefinedVariable( "unable to look up a name or access an attribute in template string" ) else: raise errors.AnsibleError( "an unexpected type error occured. Error was %s" % te)
def _lookup_variables(self, terms): results = [] for x in terms: try: intermediate = listify_lookup_plugin_terms( x, templar=self._templar, loader=self._loader) except UndefinedError as e: raise errors.AnsibleUndefinedVariable( "One of the nested variables was undefined. The error was: %s" % e) results.append(intermediate) return results
def lookup(name, *args, **kwargs): from ansible import utils instance = utils.plugins.lookup_loader.get(name.lower(), basedir=kwargs.get('basedir',None)) tvars = kwargs.get('vars', None) wantlist = kwargs.pop('wantlist', False) if instance is not None: try: ran = instance.run(*args, inject=tvars, **kwargs) except errors.AnsibleError: raise except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable("One or more undefined variables: %s" % str(e)) except Exception, e: raise errors.AnsibleError('Unexpected error in during lookup: %s' % e)
def template_from_string(basedir, data, vars, fail_on_undefined=False): ''' run a string through the (Jinja2) templating engine ''' try: if type(data) == str: data = unicode(data, 'utf-8') environment = jinja2.Environment(trim_blocks=True, undefined=StrictUndefined, extensions=_get_extensions()) environment.filters.update(_get_filters()) environment.template_class = J2Template if '_original_file' in vars: basedir = os.path.dirname(vars['_original_file']) filesdir = os.path.abspath(os.path.join(basedir, '..', 'files')) if os.path.exists(filesdir): basedir = filesdir # TODO: may need some way of using lookup plugins here seeing we aren't calling # the legacy engine, lookup() as a function, perhaps? data = data.decode('utf-8') try: t = environment.from_string(data) except Exception, e: if 'recursion' in str(e): raise errors.AnsibleError( "recursive loop detected in template string: %s" % data) else: return data def my_lookup(*args, **kwargs): kwargs['vars'] = vars return lookup(*args, basedir=basedir, **kwargs) t.globals['lookup'] = my_lookup jvars = _jinja2_vars(basedir, vars, t.globals, fail_on_undefined) new_context = t.new_context(jvars, shared=True) rf = t.root_render_func(new_context) try: res = jinja2.utils.concat(rf) except TypeError, te: if 'StrictUndefined' in str(te): raise errors.AnsibleUndefinedVariable( "unable to look up a name or access an attribute in template string" )
def run(self, terms, variables=None, **kwargs): if variables is not None: self._templar.set_available_variables(variables) myvars = getattr(self._templar, '_available_variables', {}) for term in terms: if not isinstance(term, string_types): raise errors.AnsibleError('Invalid setting identifier, "%s" ' 'is not a string, its a %s' % (term, type(term))) if term in myvars: value = myvars[term] elif 'hostvars' in myvars and term in myvars['hostvars']: # maybe it is a host var? value = myvars['hostvars'][term] else: continue result = self._templar.template(value, fail_on_undefined=True) return [result] raise errors.AnsibleUndefinedVariable('No variable found with name: ' '%s' % term)
host = vars['template_host'], uid = vars['template_uid'], file = to_bytes(vars['template_path']) ) vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(realpath)) ) # This line performs deep Jinja2 magic that uses the _jinja2_vars object for vars # Ideally, this could use some API where setting shared=True and the object won't get # passed through dict(o), but I have not found that yet. try: res = jinja2.utils.concat(t.root_render_func(t.new_context(_jinja2_vars(basedir, vars, t.globals, fail_on_undefined), shared=True))) except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable("One or more undefined variables: %s" % str(e)) except jinja2.exceptions.TemplateNotFound, e: # Throw an exception which includes a more user friendly error message # This likely will happen for included sub-template. Not that besides # pure "file not found" it may happen due to Jinja2's "security" # checks on path. values = {'name': realpath, 'subname': str(e)} msg = 'file: %(name)s, error: Cannot find/not allowed to load (include) template %(subname)s' % \ values error = errors.AnsibleError(msg) raise error # The low level calls above do not preserve the newline # characters at the end of the input data, so we use the # calculate the difference in newlines and append them # to the resulting output for parity
except errors.AnsibleConnectionFailed, e: result = dict(failed=True, msg="FAILED: %s" % str(e)) return ReturnData(host=host, comm_ok=False, result=result) tmp = '' # all modules get a tempdir, action plugins get one unless they have NEEDS_TMPPATH set to False if getattr(handler, 'NEEDS_TMPPATH', True): tmp = self._make_tmp_path(conn) # render module_args and complex_args templates try: module_args = template.template(self.basedir, module_args, inject, fail_on_undefined=self.error_on_undefined_vars) complex_args = template.template(self.basedir, complex_args, inject, fail_on_undefined=self.error_on_undefined_vars) except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable("One or more undefined variables: %s" % str(e)) result = handler.run(conn, tmp, module_name, module_args, inject, complex_args) # Code for do until feature until = self.module_vars.get('until', None) if until is not None and result.comm_ok: inject[self.module_vars.get('register')] = result.result cond = template.template(self.basedir, until, inject, expand_lists=False) if not utils.check_conditional(cond, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars): retries = self.module_vars.get('retries') delay = self.module_vars.get('delay') for x in range(1, retries + 1): time.sleep(delay) tmp = '' if getattr(handler, 'NEEDS_TMPPATH', True):
def template_from_file(basedir, path, vars): ''' run a file through the templating engine ''' fail_on_undefined = C.DEFAULT_UNDEFINED_VAR_BEHAVIOR from ansible import utils realpath = utils.path_dwim(basedir, path) loader = jinja2.FileSystemLoader([basedir, os.path.dirname(realpath)]) def my_lookup(*args, **kwargs): kwargs['vars'] = vars return lookup(*args, basedir=basedir, **kwargs) environment = jinja2.Environment(loader=loader, trim_blocks=True, extensions=_get_extensions()) environment.filters.update(_get_filters()) environment.globals['lookup'] = my_lookup if fail_on_undefined: environment.undefined = StrictUndefined try: data = codecs.open(realpath, encoding="utf8").read() except UnicodeDecodeError: raise errors.AnsibleError("unable to process as utf-8: %s" % realpath) except: raise errors.AnsibleError("unable to read %s" % realpath) # Get jinja env overrides from template if data.startswith(JINJA2_OVERRIDE): eol = data.find('\n') line = data[len(JINJA2_OVERRIDE):eol] data = data[eol + 1:] for pair in line.split(','): (key, val) = pair.split(':') setattr(environment, key.strip(), ast.literal_eval(val.strip())) environment.template_class = J2Template t = environment.from_string(data) vars = vars.copy() try: template_uid = pwd.getpwuid(os.stat(realpath).st_uid).pw_name except: template_uid = os.stat(realpath).st_uid vars['template_host'] = os.uname()[1] vars['template_path'] = realpath vars['template_mtime'] = datetime.datetime.fromtimestamp( os.path.getmtime(realpath)) vars['template_uid'] = template_uid vars['template_fullpath'] = os.path.abspath(realpath) vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format(host=vars['template_host'], uid=vars['template_uid'], file=vars['template_path']) vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(realpath))) # This line performs deep Jinja2 magic that uses the _jinja2_vars object for vars # Ideally, this could use some API where setting shared=True and the object won't get # passed through dict(o), but I have not found that yet. try: res = jinja2.utils.concat( t.root_render_func( t.new_context(_jinja2_vars(basedir, vars, t.globals, fail_on_undefined), shared=True))) except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable( "One or more undefined variables: %s" % str(e))
def _deprecated(new_var, old_var=None, old_var_name=None, new_var_name=None, removed_in=None, fatal=False): """Provide a deprecation warning on deprecated variables. This filter will return the old_var value if defined along with a deprecation warning that will inform the user that the old variable should no longer be used. In order to use this filter the old and new variable names must be provided to the filter as a string which is used to render the warning message. The removed_in option is used to give a date or release name where the old option will be removed. Optionally, if fatal is set to True, the filter will raise an exception if the old variable is used. USAGE: {{ new_var | deprecated(old_var, "old_var_name", "new_var_name", "removed_in", false) }} :param new_var: ``object`` :param old_var: ``object`` :param old_var_name: ``str`` :param new_var_name: ``str`` :param removed_in: ``str`` :param fatal: ``bol`` """ _usage = ( 'USAGE: ' '{{ new_var | deprecated(old_var=old_var, old_var_name="old_var_name",' ' new_var_name="new_var_name", removed_in="removed_in",' ' fatal=false) }}') if not old_var_name: raise errors.AnsibleUndefinedVariable( 'To use this filter you must provide the "old_var_name" option' ' with the string name of the old variable that will be' ' replaced. ' + _usage) if not new_var_name: raise errors.AnsibleUndefinedVariable( 'To use this filter you must provide the "new_var_name" option' ' with the string name of the new variable that will replace the' ' deprecated one. ' + _usage) if not removed_in: raise errors.AnsibleUndefinedVariable( 'To use this filter you must provide the "removed_in" option with' ' the string name of the release where the old_var will be' ' removed. ' + _usage) # If old_var is undefined or has a None value return the new_var value if isinstance(old_var, Undefined) or not old_var: return new_var name = 'Ansible-Warning| ' log = logging.getLogger(name) for handler in log.handlers: if name == handler.name: break else: stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.DEBUG) stream_handler.name = name stream_format = logging.Formatter( '%(asctime)s - %(name)s%(levelname)s => %(message)s') stream_handler.setFormatter(stream_format) log.setLevel(logging.DEBUG) log.addHandler(stream_handler) message = ( 'Deprecated Option provided: Deprecated variable: "%(old)s", Removal' ' timeframe: "%(removed_in)s", Future usage: "%(new)s"' % { 'old': old_var_name, 'new': new_var_name, 'removed_in': removed_in }) if str(fatal).lower() in ['yes', 'true']: message = 'Fatally %s' % message log.fatal(message) raise RuntimeError(message) else: log.warn(message) return old_var
def template_from_file(basedir, path, vars): ''' run a file through the templating engine ''' fail_on_undefined = C.DEFAULT_UNDEFINED_VAR_BEHAVIOR realpath = filesystem.path_dwim(basedir, path) loader = jinja2.FileSystemLoader([basedir, os.path.dirname(realpath)]) def my_lookup(*args, **kwargs): kwargs['vars'] = vars return lookup(*args, basedir=basedir, **kwargs) environment = jinja2.Environment(loader=loader, trim_blocks=True, extensions=_get_extensions()) environment.filters.update(_get_filters()) environment.globals['lookup'] = my_lookup if fail_on_undefined: environment.undefined = StrictUndefined try: data = codecs.open(realpath, encoding="utf8").read() except UnicodeDecodeError: raise errors.AnsibleError("unable to process as utf-8: %s" % realpath) except: raise errors.AnsibleError("unable to read %s" % realpath) # Get jinja env overrides from template if data.startswith(JINJA2_OVERRIDE): eol = data.find('\n') line = data[len(JINJA2_OVERRIDE):eol] data = data[eol + 1:] for pair in line.split(','): (key, val) = pair.split(':') setattr(environment, key.strip(), ast.literal_eval(val.strip())) vars = vars.copy() try: template_uid = pwd.getpwuid(os.stat(realpath).st_uid).pw_name except: template_uid = os.stat(realpath).st_uid vars.update( dict( template_host=os.uname()[1], template_path=realpath, template_mtime=datetime.datetime.fromtimestamp( os.path.getmtime(realpath)), template_uid=template_uid, template_fullpath=os.path.abspath(realpath), template_run_date=datetime.datetime.now(), )) managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format(host=vars['template_host'], uid=vars['template_uid'], file=vars['template_path']) vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(realpath))) # this double template pass is here to detect errors while we still have context # actual recursion is handled by the mainline template function further down try: t = environment.from_string(data) res = t.render(vars) except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable( "One or more undefined variables: %s" % str(e))