Exemple #1
0
    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
Exemple #4
0
    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
Exemple #5
0
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
    )
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #9
0
    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
Exemple #10
0
    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
Exemple #12
0
    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
Exemple #13
0
    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
Exemple #14
0
    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