def run(self, terms, variables, **kwargs): convert_data_p = kwargs.get('convert_data', True) lookup_template_vars = kwargs.get('template_vars', {}) ret = [] variable_start_string = kwargs.get('variable_start_string', None) variable_end_string = kwargs.get('variable_end_string', None) old_vars = self._templar.available_variables for term in terms: display.debug("File lookup term: %s" % term) lookupfile = self.find_file_in_search_path(variables, 'templates', term) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: b_template_data, show_data = self._loader._get_file_contents(lookupfile) template_data = to_text(b_template_data, errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = variables.get('ansible_search_path', []) if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath searchpath.insert(0, os.path.dirname(lookupfile)) self._templar.environment.loader.searchpath = searchpath if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string # The template will have access to all existing variables, # plus some added by ansible (e.g., template_{path,mtime}), # plus anything passed to the lookup with the template_vars= # argument. vars = deepcopy(variables) vars.update(generate_ansible_template_vars(lookupfile)) vars.update(lookup_template_vars) self._templar.available_variables = vars # do the templating res = self._templar.template(template_data, preserve_trailing_newlines=True, convert_data=convert_data_p, escape_backslashes=False) ret.append(res) else: raise AnsibleError("the template file %s could not be found for the lookup" % term) # restore old variables self._templar.available_variables = old_vars return ret
def from_template(self, template, searchpath, variables): template_file = template['file'] template_vars = template.get('vars', {}) lookupfile = self._loader.path_dwim_relative_stack( searchpath, 'templates', template_file) b_template_data, show_data = self._loader._get_file_contents( lookupfile) template_data = to_text(b_template_data, errors='surrogate_or_strict') # set jinja2 internal search path for includes template_searchpath = [ os.path.join(item, 'templates') for item in searchpath ] template_searchpath.insert(0, os.path.dirname(lookupfile)) self._templar.environment.loader.searchpath = searchpath vars = deepcopy(variables) vars.update(generate_ansible_template_vars(lookupfile)) vars.update(template_vars) self._templar.available_variables = vars res = self._templar.template(template_data, preserve_trailing_newlines=True, convert_data=True, escape_backslashes=False) ret = [] for yaml_document in yaml.safe_load_all(res): ret.extend(self.from_definition(yaml_document)) return ret
def _render_template(self, task_vars=None, extra_vars=None, srcfile=None, parser='json'): """ Renders the template, interprets the output using the specified parser and returns a dictionary with the result. :param task_vars: the global Ansible task_vars dictionary :type task_vars: dict :param extra_vars: extra variables to inject into template context :type extra_vars: dict :param srcfile: the absolute path on disk to read for the template from :type srcfile: str :param parser: the parser to interpret the template result with (default json) :type parser: str :return: the interpreted app definition :rtype: dict """ # make a copy of task_vars so we can manipulate them freely temp_vars = task_vars.copy() # save template engine's vars to restore later old_vars = self._templar._available_variables # generate template-specific vars (ansible_managed, etc.) temp_vars.update(generate_ansible_template_vars(srcfile)) # add our own custom variables to the template context if extra_vars: temp_vars.update(extra_vars) # swap temp_vars into template context self._templar.set_available_variables(temp_vars) # open the template file try: with open(srcfile, 'r') as f: template_data = to_text(f.read()) except IOError: raise ValueError('unable to load src file {}'.format(srcfile)) # render the template # convert_data=False disables the interpretation of the resulting json blob # we do this explicitly below, since we'll need to support both json and yaml (later). result = self._templar.template(template_data, preserve_trailing_newlines=True, escape_backslashes=False, convert_data=False) # parse the result according to the value in 'parser' if parser == 'json': out = json.loads(result) elif parser == 'yml' or parser == 'yaml': out = yaml.load(result) if not out: raise ValueError("'{}' does not contain a valid YAML document".format(srcfile)) else: raise NotImplementedError("unknown parser {}".format(parser)) # restore old task_vars back into template engine context self._templar.set_available_variables(old_vars) return out
def run(self, terms, variables, **kwargs): convert_data_p = kwargs.get('convert_data', True) lookup_template_vars = kwargs.get('template_vars', {}) jinja2_native = kwargs.get('jinja2_native', False) ret = [] variable_start_string = kwargs.get('variable_start_string', None) variable_end_string = kwargs.get('variable_end_string', None) if USE_JINJA2_NATIVE and not jinja2_native: templar = self._templar.copy_with_new_env(environment_class=AnsibleEnvironment) else: templar = self._templar for term in terms: display.debug("File lookup term: %s" % term) lookupfile = self.find_file_in_search_path(variables, 'templates', term) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: b_template_data, show_data = self._loader._get_file_contents(lookupfile) template_data = to_text(b_template_data, errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = variables.get('ansible_search_path', []) if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath searchpath.insert(0, os.path.dirname(lookupfile)) # The template will have access to all existing variables, # plus some added by ansible (e.g., template_{path,mtime}), # plus anything passed to the lookup with the template_vars= # argument. vars = deepcopy(variables) vars.update(generate_ansible_template_vars(term, lookupfile)) vars.update(lookup_template_vars) with templar.set_temporary_context(variable_start_string=variable_start_string, variable_end_string=variable_end_string, available_variables=vars, searchpath=searchpath): res = templar.template(template_data, preserve_trailing_newlines=True, convert_data=convert_data_p, escape_backslashes=False) if USE_JINJA2_NATIVE and not jinja2_native: # jinja2_native is true globally but off for the lookup, we need this text # not to be processed by literal_eval anywhere in Ansible res = NativeJinjaText(res) ret.append(res) else: raise AnsibleError("the template file %s could not be found for the lookup" % term) return ret
def lookup(plugin, ansible_vars, file, params): display.debug("File lookup term: %s" % file) lookupfile = plugin.find_file_in_search_path(ansible_vars, 'templates', file) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: b_template_data, _ = plugin._loader._get_file_contents(lookupfile) template_data = to_text(b_template_data, errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = ansible_vars.get('ansible_search_path', []) if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for path in searchpath: newsearchpath.append(os.path.join(path, 'templates')) newsearchpath.append(path) searchpath = newsearchpath searchpath.insert(0, os.path.dirname(lookupfile)) # The template will have access to all existing variables, # plus some added by ansible (e.g., template_{path,mtime}), # plus anything passed to the lookup with the template_vars= # argument. new_vars = deepcopy(ansible_vars) new_vars.update(generate_ansible_template_vars(lookupfile)) new_vars.update(params) display.vv("params keys: %s" % params.keys()) templar = plugin._templar with templar.set_temporary_context( variable_start_string=None, variable_end_string=None, available_variables=new_vars, searchpath=searchpath ): res = templar.template( template_data, preserve_trailing_newlines=True, escape_backslashes=False ) return res else: raise AnsibleError( "the template file %s could not be found for the lookup" % file )
def run(self, terms, variables, **kwargs): convert_data_p = kwargs.get('convert_data', True) ret = [] for term in terms: display.debug("File lookup term: %s" % term) lookupfile = self.find_file_in_search_path(variables, 'templates', term) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: with open(to_bytes(lookupfile, errors='surrogate_or_strict'), 'rb') as f: template_data = to_text(f.read(), errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = variables.get('ansible_search_path') if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath else: searchpath = [ self._loader._basedir, os.path.dirname(lookupfile) ] self._templar.environment.loader.searchpath = searchpath # add ansible 'template' vars temp_vars = variables.copy() temp_vars.update( generate_ansible_template_vars(lookupfile)) self._templar.set_available_variables(temp_vars) # do the templating res = self._templar.template( template_data, preserve_trailing_newlines=True, convert_data=convert_data_p, escape_backslashes=False) ret.append(res) else: raise AnsibleError( "the template file %s could not be found for the lookup" % term) return ret
def run(self, terms, variables, **kwargs): convert_data_p = kwargs.get('convert_data', True) lookup_template_vars = kwargs.get('template_vars', {}) ret = [] for term in terms: display.debug("File lookup term: %s" % term) lookupfile = self.find_file_in_search_path(variables, 'templates', term) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: with open(to_bytes(lookupfile, errors='surrogate_or_strict'), 'rb') as f: template_data = to_text(f.read(), errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = variables.get('ansible_search_path') if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath else: searchpath = [self._loader._basedir, os.path.dirname(lookupfile)] self._templar.environment.loader.searchpath = searchpath # The template will have access to all existing variables, # plus some added by ansible (e.g., template_{path,mtime}), # plus anything passed to the lookup with the template_vars= # argument. vars = variables.copy() vars.update(generate_ansible_template_vars(lookupfile)) vars.update(lookup_template_vars) self._templar.set_available_variables(vars) # do the templating res = self._templar.template(template_data, preserve_trailing_newlines=True, convert_data=convert_data_p, escape_backslashes=False) ret.append(res) else: raise AnsibleError("the template file %s could not be found for the lookup" % term) return ret
def from_template(self, template, variables): template_file = template['file'] template_vars = template.get('vars', {}) lookupfile = self.find_file_in_search_path(variables, 'templates', template_file) if not lookupfile: raise AnsibleError('Unable to find file: {}'.format(template_file)) b_template_data, show_data = self._loader._get_file_contents( lookupfile) template_data = to_text(b_template_data, errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = variables.get('ansible_search_path', []) if searchpath: # our search paths aren't actually the proper ones for jinja includes. # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath searchpath.insert(0, os.path.dirname(lookupfile)) self._templar.environment.loader.searchpath = searchpath vars = deepcopy(variables) vars.update(generate_ansible_template_vars(lookupfile)) vars.update(template_vars) self._templar.available_variables = vars res = self._templar.template(template_data, preserve_trailing_newlines=True, convert_data=True, escape_backslashes=False) ret = [] for yaml_document in yaml.safe_load_all(res): ret.extend(self.from_definition(yaml_document)) return ret
def run(self, terms, variables, **kwargs): if not HAS_JSONNET: raise AnsibleError( "Requires the _jsonnet Python package. Try `pip install jsonnet`" ) ret = [] for term in terms: display.debug("File lookup term: %s" % term) lookupfile = self.find_file_in_search_path(variables, 'templates', term) display.vvvv("File lookup using %s as file" % lookupfile) if lookupfile: vars = variables.copy() vars.update(generate_ansible_template_vars(lookupfile)) self._templar.set_available_variables(vars) def ansible_expr(expr): return self._templar.template(expr, convert_bare=True) native_callbacks = { 'ansible_expr': (('expr', ), ansible_expr), } res = _jsonnet.evaluate_file(lookupfile, native_callbacks=native_callbacks, **kwargs) ret.append(res) else: raise AnsibleError( "the Jsonnet document file %s could not be found for the lookup" % term) return ret
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True), strict=False) follow = boolean(self._task.args.get('follow', False), strict=False) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) trim_blocks = self._task.args.get('trim_blocks', None) wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index( newline_sequence)] try: if state is not None: raise AnsibleActionFail( "'state' cannot be specified on a template") elif source is None or dest is None: raise AnsibleActionFail("src and dest are required") elif newline_sequence not in allowed_sequences: raise AnsibleActionFail( "newline_sequence needs to be one of: \n, \r or \r\n") else: try: source = self._find_needle('templates', source) except AnsibleError as e: raise AnsibleActionFail(to_text(e)) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e))) # template the source data locally & get ready to transfer try: with open(tmp_source, 'r') as f: template_data = to_text(f.read()) # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend( [self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string if trim_blocks is not None: self._templar.environment.trim_blocks = bool(trim_blocks) # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source)) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.do_template( template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.set_available_variables(old_vars) except AnsibleAction: raise except Exception as e: raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) finally: self._loader.cleanup_tmp_file(tmp_source) new_task = self._task.copy() new_task.args.pop('newline_sequence', None) new_task.args.pop('block_start_string', None) new_task.args.pop('block_end_string', None) new_task.args.pop('variable_start_string', None) new_task.args.pop('variable_end_string', None) new_task.args.pop('trim_blocks', None) local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) try: result_file = os.path.join(local_tempdir, os.path.basename(source)) with open(result_file, 'wb') as f: f.write(to_bytes(resultant, errors='surrogate_or_strict')) new_task.args.update( dict( src=result_file, dest=dest, follow=follow, ), ) copy_action = self._shared_loader_obj.action_loader.get( 'copy', task=new_task, connection=self._connection, play_context=self._play_context, loader=self._loader, templar=self._templar, shared_loader_obj=self._shared_loader_obj) result.update(copy_action.run(task_vars=task_vars)) finally: shutil.rmtree(local_tempdir) except AnsibleAction as e: result.update(e.result) finally: self._remove_tmp_path(self._connection._shell.tempdir) return result
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect # Options type validation # stings for s_type in ('src', 'dest', 'state', 'newline_sequence', 'variable_start_string', 'variable_end_string', 'block_start_string', 'block_end_string'): if s_type in self._task.args: value = ensure_type(self._task.args[s_type], 'string') if value is not None and not isinstance(value, string_types): raise AnsibleActionFail( "%s is expected to be a string, but got %s instead" % (s_type, type(value))) self._task.args[s_type] = value # booleans try: follow = boolean(self._task.args.get('follow', False), strict=False) trim_blocks = boolean(self._task.args.get('trim_blocks', True), strict=False) lstrip_blocks = boolean(self._task.args.get( 'lstrip_blocks', False), strict=False) except TypeError as e: raise AnsibleActionFail(to_native(e)) # assign to local vars for ease of use source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) output_encoding = self._task.args.get('output_encoding', 'utf-8') or 'utf-8' # Option `lstrip_blocks' was added in Jinja2 version 2.7. if lstrip_blocks: try: import jinja2.defaults except ImportError: raise AnsibleError( 'Unable to import Jinja2 defaults for determining Jinja2 features.' ) try: jinja2.defaults.LSTRIP_BLOCKS except AttributeError: raise AnsibleError( "Option `lstrip_blocks' is only available in Jinja2 versions >=2.7" ) wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index( newline_sequence)] try: # logical validation if state is not None: raise AnsibleActionFail( "'state' cannot be specified on a template") elif source is None or dest is None: raise AnsibleActionFail("src and dest are required") elif newline_sequence not in allowed_sequences: raise AnsibleActionFail( "newline_sequence needs to be one of: \n, \r or \r\n") else: try: source = self._find_needle('templates', source) except AnsibleError as e: raise AnsibleActionFail(to_text(e)) mode = self._task.args.get('mode', None) if mode == 'preserve': mode = '0%03o' % stat.S_IMODE(os.stat(source).st_mode) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e))) b_tmp_source = to_bytes(tmp_source, errors='surrogate_or_strict') # template the source data locally & get ready to transfer try: with open(b_tmp_source, 'rb') as f: try: template_data = to_text(f.read(), errors='surrogate_or_strict') except UnicodeError: raise AnsibleActionFail( "Template source files must be utf-8 encoded") # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend( [self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string self._templar.environment.trim_blocks = trim_blocks self._templar.environment.lstrip_blocks = lstrip_blocks # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source, dest)) old_vars = self._templar.available_variables self._templar.available_variables = temp_vars resultant = self._templar.do_template( template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.available_variables = old_vars except AnsibleAction: raise except Exception as e: raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) finally: self._loader.cleanup_tmp_file(b_tmp_source) new_task = self._task.copy() # mode is either the mode from task.args or the mode of the source file if the task.args # mode == 'preserve' new_task.args['mode'] = mode # remove 'template only' options: for remove in ('newline_sequence', 'block_start_string', 'block_end_string', 'variable_start_string', 'variable_end_string', 'trim_blocks', 'lstrip_blocks', 'output_encoding'): new_task.args.pop(remove, None) local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) try: result_file = os.path.join(local_tempdir, os.path.basename(source)) with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f: f.write( to_bytes(resultant, encoding=output_encoding, errors='surrogate_or_strict')) new_task.args.update( dict( src=result_file, dest=dest, follow=follow, ), ) copy_action = self._shared_loader_obj.action_loader.get( 'copy', task=new_task, connection=self._connection, play_context=self._play_context, loader=self._loader, templar=self._templar, shared_loader_obj=self._shared_loader_obj) result.update(copy_action.run(task_vars=task_vars)) finally: shutil.rmtree( to_bytes(local_tempdir, errors='surrogate_or_strict')) except AnsibleAction as e: result.update(e.result) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True)) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) trim_blocks = self._task.args.get('trim_blocks', None) wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index( newline_sequence)] if state is not None: result['failed'] = True result['msg'] = "'state' cannot be specified on a template" elif source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" elif newline_sequence not in allowed_sequences: result['failed'] = True result[ 'msg'] = "newline_sequence needs to be one of: \n, \r or \r\n" else: try: source = self._find_needle('templates', source) except AnsibleError as e: result['failed'] = True result['msg'] = to_native(e) if 'failed' in result: return result # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): # Optimization. trailing slash means we know it's a directory directory_prepended = True dest = self._connection._shell.join_path(dest, os.path.basename(source)) else: # Find out if it's a directory dest_stat = self._execute_remote_stat(dest, task_vars, True, tmp=tmp) if dest_stat['exists'] and dest_stat['isdir']: dest = self._connection._shell.join_path( dest, os.path.basename(source)) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: result['failed'] = True result['msg'] = "could not find src=%s, %s" % (source, e) self._remove_tmp_path(tmp) return result # template the source data locally & get ready to transfer try: with open(tmp_source, 'r') as f: template_data = to_text(f.read()) # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend([self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string if trim_blocks is not None: self._templar.environment.trim_blocks = bool(trim_blocks) # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source)) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.do_template( template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.set_available_variables(old_vars) except Exception as e: result['failed'] = True result['msg'] = type(e).__name__ + ": " + str(e) return result finally: self._loader.cleanup_tmp_file(tmp_source) if not tmp: tmp = self._make_tmp_path() local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source, tmp=tmp) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str result.update(remote_checksum) return result diff = {} new_module_args = self._task.args.copy() # remove newline_sequence from standard arguments new_module_args.pop('newline_sequence', None) new_module_args.pop('block_start_string', None) new_module_args.pop('block_end_string', None) new_module_args.pop('variable_start_string', None) new_module_args.pop('variable_end_string', None) new_module_args.pop('trim_blocks', None) if (remote_checksum == '1') or (force and local_checksum != remote_checksum): result['changed'] = True # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work through copy xfered = self._transfer_data( self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user self._fixup_perms2((tmp, xfered)) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result.update( self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) result.update( self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True), strict=False) follow = boolean(self._task.args.get('follow', False), strict=False) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) trim_blocks = self._task.args.get('trim_blocks', None) wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index(newline_sequence)] if state is not None: result['failed'] = True result['msg'] = "'state' cannot be specified on a template" elif source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" elif newline_sequence not in allowed_sequences: result['failed'] = True result['msg'] = "newline_sequence needs to be one of: \n, \r or \r\n" else: try: source = self._find_needle('templates', source) except AnsibleError as e: result['failed'] = True result['msg'] = to_text(e) if 'failed' in result: return result # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: result['failed'] = True result['msg'] = "could not find src=%s, %s" % (source, e) self._remove_tmp_path(tmp) return result # template the source data locally & get ready to transfer try: with open(tmp_source, 'r') as f: template_data = to_text(f.read()) # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend([self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string if trim_blocks is not None: self._templar.environment.trim_blocks = bool(trim_blocks) # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source)) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.set_available_variables(old_vars) except Exception as e: result['failed'] = True result['msg'] = "%s: %s" % (type(e).__name__, to_text(e)) return result finally: self._loader.cleanup_tmp_file(tmp_source) new_task = self._task.copy() new_task.args.pop('newline_sequence', None) new_task.args.pop('block_start_string', None) new_task.args.pop('block_end_string', None) new_task.args.pop('variable_start_string', None) new_task.args.pop('variable_end_string', None) new_task.args.pop('trim_blocks', None) try: tempdir = tempfile.mkdtemp() result_file = os.path.join(tempdir, os.path.basename(source)) with open(result_file, 'wb') as f: f.write(to_bytes(resultant, errors='surrogate_or_strict')) new_task.args.update( dict( src=result_file, dest=dest, follow=follow, ), ) copy_action = self._shared_loader_obj.action_loader.get('copy', task=new_task, connection=self._connection, play_context=self._play_context, loader=self._loader, templar=self._templar, shared_loader_obj=self._shared_loader_obj) result.update(copy_action.run(task_vars=task_vars)) finally: shutil.rmtree(tempdir) return result
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True), strict=False) follow = boolean(self._task.args.get('follow', False), strict=False) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) trim_blocks = boolean(self._task.args.get('trim_blocks', True), strict=False) lstrip_blocks = boolean(self._task.args.get('lstrip_blocks', False), strict=False) # Option `lstrip_blocks' was added in Jinja2 version 2.7. if lstrip_blocks: try: import jinja2.defaults except ImportError: raise AnsibleError('Unable to import Jinja2 defaults for determing Jinja2 features.') try: jinja2.defaults.LSTRIP_BLOCKS except AttributeError: raise AnsibleError("Option `lstrip_blocks' is only available in Jinja2 versions >=2.7") wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index(newline_sequence)] try: for s_type in ('source', 'dest', 'state', 'newline_sequence', 'variable_start_string', 'variable_end_string', 'block_start_string', 'block_end_string'): value = locals()[s_type] value = ensure_type(value, 'string') if value is not None and not isinstance(value, string_types): raise AnsibleActionFail("%s is expected to be a string, but got %s instead" % (s_type, type(value))) locals()[s_type] = value for b_type in ('force', 'follow', 'trim_blocks'): value = locals()[b_type] value = ensure_type(value, 'string') if value is not None and not isinstance(value, bool): raise AnsibleActionFail("%s is expected to be a boolean, but got %s instead" % (b_type, type(value))) locals()[b_type] = value if state is not None: raise AnsibleActionFail("'state' cannot be specified on a template") elif source is None or dest is None: raise AnsibleActionFail("src and dest are required") elif newline_sequence not in allowed_sequences: raise AnsibleActionFail("newline_sequence needs to be one of: \n, \r or \r\n") else: try: source = self._find_needle('templates', source) except AnsibleError as e: raise AnsibleActionFail(to_text(e)) mode = self._task.args.get('mode', None) if mode == 'preserve': mode = '0%03o' % stat.S_IMODE(os.stat(source).st_mode) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e))) b_tmp_source = to_bytes(tmp_source, errors='surrogate_or_strict') # template the source data locally & get ready to transfer try: with open(b_tmp_source, 'rb') as f: template_data = to_text(f.read(), errors='surrogate_or_strict') # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend([self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string self._templar.environment.trim_blocks = trim_blocks self._templar.environment.lstrip_blocks = lstrip_blocks # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source)) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.set_available_variables(old_vars) except AnsibleAction: raise except Exception as e: raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) finally: self._loader.cleanup_tmp_file(b_tmp_source) new_task = self._task.copy() # mode is either the mode from task.args or the mode of the source file if the task.args # mode == 'preserve' new_task.args['mode'] = mode new_task.args.pop('newline_sequence', None) new_task.args.pop('block_start_string', None) new_task.args.pop('block_end_string', None) new_task.args.pop('variable_start_string', None) new_task.args.pop('variable_end_string', None) new_task.args.pop('trim_blocks', None) new_task.args.pop('lstrip_blocks', None) local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) try: result_file = os.path.join(local_tempdir, os.path.basename(source)) with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f: f.write(to_bytes(resultant, errors='surrogate_or_strict')) new_task.args.update( dict( src=result_file, dest=dest, follow=follow, ), ) copy_action = self._shared_loader_obj.action_loader.get('copy', task=new_task, connection=self._connection, play_context=self._play_context, loader=self._loader, templar=self._templar, shared_loader_obj=self._shared_loader_obj) result.update(copy_action.run(task_vars=task_vars)) finally: shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict')) except AnsibleAction as e: result.update(e.result) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result