We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
/tmp directory being used
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
template and potentially more

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
$ ansible --version
ansible 2.2.1.0
  config file = /opt/****/ansible.cfg
  configured module search path = [ '/home/****/ansible']
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
```
$ cat ansible.cfg | grep tmp
local_tmp = /opt/***/.ansible/tmp
remote_tmp = /opt/***/.ansible/tmp
$

```
##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
```
$ cat /etc/os-release
NAME="Red Hat Enterprise Linux Server"
VERSION="7.3 (Maipo)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="7.3"
PRETTY_NAME="Red Hat Enterprise Linux"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:7.3:GA:server"
HOME_URL="https://www.redhat.com/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"

REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7"
REDHAT_BUGZILLA_PRODUCT_VERSION=7.3
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="7.3"

```
##### SUMMARY
<!--- Explain the problem briefly -->
Files are writing to /tmp when remote_tmp and local_tmp is defined

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->


```yaml

    - name: "{{ site }} - GENERATE JSON REPORT PER SITE STATS ONLY"
      template:
        src: "{{ role_path }}/templates/reports/sites/partial_table.j2"
        dest: '{{ output_dir }}/reports/html/partial/{{ site }}.partial'
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
```
localhost                  : ok=360  changed=29   unreachable=0    failed=0
```
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
TASK [reports : *** - GENERATE JSON REPORT PER SITE STATS ONLY] ****************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "[Errno 13] Permission denied: '/tmp/tmpuv61yn'"}

PLAY RECAP *********************************************************************
localhost                  : ok=360  changed=29   unreachable=0    failed=1
```



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/plugins/action/template.py`
Content:
```
1 # (c) 2015, Michael DeHaan <michael.dehaan@gmail.com>
2 #
3 # This file is part of Ansible
4 #
5 # Ansible is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Ansible is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17 from __future__ import (absolute_import, division, print_function)
18 __metaclass__ = type
19 
20 import os
21 import shutil
22 import tempfile
23 
24 from ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleAction, AnsibleActionFail
25 from ansible.module_utils._text import to_bytes, to_text
26 from ansible.module_utils.parsing.convert_bool import boolean
27 from ansible.plugins.action import ActionBase
28 from ansible.template import generate_ansible_template_vars
29 
30 
31 class ActionModule(ActionBase):
32 
33     TRANSFERS_FILES = True
34     DEFAULT_NEWLINE_SEQUENCE = "\n"
35 
36     def run(self, tmp=None, task_vars=None):
37         ''' handler for template operations '''
38 
39         if task_vars is None:
40             task_vars = dict()
41 
42         result = super(ActionModule, self).run(tmp, task_vars)
43 
44         source = self._task.args.get('src', None)
45         dest = self._task.args.get('dest', None)
46         force = boolean(self._task.args.get('force', True), strict=False)
47         follow = boolean(self._task.args.get('follow', False), strict=False)
48         state = self._task.args.get('state', None)
49         newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE)
50         variable_start_string = self._task.args.get('variable_start_string', None)
51         variable_end_string = self._task.args.get('variable_end_string', None)
52         block_start_string = self._task.args.get('block_start_string', None)
53         block_end_string = self._task.args.get('block_end_string', None)
54         trim_blocks = self._task.args.get('trim_blocks', None)
55 
56         wrong_sequences = ["\\n", "\\r", "\\r\\n"]
57         allowed_sequences = ["\n", "\r", "\r\n"]
58 
59         # We need to convert unescaped sequences to proper escaped sequences for Jinja2
60         if newline_sequence in wrong_sequences:
61             newline_sequence = allowed_sequences[wrong_sequences.index(newline_sequence)]
62 
63         try:
64             if state is not None:
65                 raise AnsibleActionFail("'state' cannot be specified on a template")
66             elif source is None or dest is None:
67                 raise AnsibleActionFail("src and dest are required")
68             elif newline_sequence not in allowed_sequences:
69                 raise AnsibleActionFail("newline_sequence needs to be one of: \n, \r or \r\n")
70             else:
71                 try:
72                     source = self._find_needle('templates', source)
73                 except AnsibleError as e:
74                     raise AnsibleActionFail(to_text(e))
75 
76             # Get vault decrypted tmp file
77             try:
78                 tmp_source = self._loader.get_real_file(source)
79             except AnsibleFileNotFound as e:
80                 raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e)))
81 
82             # template the source data locally & get ready to transfer
83             try:
84                 with open(tmp_source, 'r') as f:
85                     template_data = to_text(f.read())
86 
87                 # set jinja2 internal search path for includes
88                 searchpath = task_vars.get('ansible_search_path', [])
89                 searchpath.extend([self._loader._basedir, os.path.dirname(source)])
90 
91                 # We want to search into the 'templates' subdir of each search path in
92                 # addition to our original search paths.
93                 newsearchpath = []
94                 for p in searchpath:
95                     newsearchpath.append(os.path.join(p, 'templates'))
96                     newsearchpath.append(p)
97                 searchpath = newsearchpath
98 
99                 self._templar.environment.loader.searchpath = searchpath
100                 self._templar.environment.newline_sequence = newline_sequence
101                 if block_start_string is not None:
102                     self._templar.environment.block_start_string = block_start_string
103                 if block_end_string is not None:
104                     self._templar.environment.block_end_string = block_end_string
105                 if variable_start_string is not None:
106                     self._templar.environment.variable_start_string = variable_start_string
107                 if variable_end_string is not None:
108                     self._templar.environment.variable_end_string = variable_end_string
109                 if trim_blocks is not None:
110                     self._templar.environment.trim_blocks = bool(trim_blocks)
111 
112                 # add ansible 'template' vars
113                 temp_vars = task_vars.copy()
114                 temp_vars.update(generate_ansible_template_vars(source))
115 
116                 old_vars = self._templar._available_variables
117                 self._templar.set_available_variables(temp_vars)
118                 resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)
119                 self._templar.set_available_variables(old_vars)
120             except AnsibleAction:
121                 raise
122             except Exception as e:
123                 raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e)))
124             finally:
125                 self._loader.cleanup_tmp_file(tmp_source)
126 
127             new_task = self._task.copy()
128             new_task.args.pop('newline_sequence', None)
129             new_task.args.pop('block_start_string', None)
130             new_task.args.pop('block_end_string', None)
131             new_task.args.pop('variable_start_string', None)
132             new_task.args.pop('variable_end_string', None)
133             new_task.args.pop('trim_blocks', None)
134             try:
135                 tempdir = tempfile.mkdtemp()
136                 result_file = os.path.join(tempdir, os.path.basename(source))
137                 with open(result_file, 'wb') as f:
138                     f.write(to_bytes(resultant, errors='surrogate_or_strict'))
139 
140                 new_task.args.update(
141                     dict(
142                         src=result_file,
143                         dest=dest,
144                         follow=follow,
145                     ),
146                 )
147                 copy_action = self._shared_loader_obj.action_loader.get('copy',
148                                                                         task=new_task,
149                                                                         connection=self._connection,
150                                                                         play_context=self._play_context,
151                                                                         loader=self._loader,
152                                                                         templar=self._templar,
153                                                                         shared_loader_obj=self._shared_loader_obj)
154                 result.update(copy_action.run(task_vars=task_vars))
155             finally:
156                 shutil.rmtree(tempdir)
157         except AnsibleAction as e:
158             result.update(e.result)
159         finally:
160             self._remove_tmp_path(self._connection._shell.tempdir)
161 
162         return result
163
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/plugins/action/template.py b/lib/ansible/plugins/action/template.py
--- a/lib/ansible/plugins/action/template.py
+++ b/lib/ansible/plugins/action/template.py
@@ -21,6 +21,7 @@
 import shutil
 import tempfile
 
+from ansible import constants as C
 from ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleAction, AnsibleActionFail
 from ansible.module_utils._text import to_bytes, to_text
 from ansible.module_utils.parsing.convert_bool import boolean
@@ -132,7 +133,7 @@
             new_task.args.pop('variable_end_string', None)
             new_task.args.pop('trim_blocks', None)
             try:
-                tempdir = tempfile.mkdtemp()
+                tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)
                 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'))

{"golden_diff": "diff --git a/lib/ansible/plugins/action/template.py b/lib/ansible/plugins/action/template.py\n--- a/lib/ansible/plugins/action/template.py\n+++ b/lib/ansible/plugins/action/template.py\n@@ -21,6 +21,7 @@\n import shutil\n import tempfile\n \n+from ansible import constants as C\n from ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleAction, AnsibleActionFail\n from ansible.module_utils._text import to_bytes, to_text\n from ansible.module_utils.parsing.convert_bool import boolean\n@@ -132,7 +133,7 @@\n             new_task.args.pop('variable_end_string', None)\n             new_task.args.pop('trim_blocks', None)\n             try:\n-                tempdir = tempfile.mkdtemp()\n+                tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)\n                 result_file = os.path.join(tempdir, os.path.basename(source))\n                 with open(result_file, 'wb') as f:\n                     f.write(to_bytes(resultant, errors='surrogate_or_strict'))\n", "issue": "/tmp directory being used\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nAlso test if the latest release, and master branch are affected too.\r\n-->\r\n\r\n##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest -->\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\n<!---\r\nName of the module, plugin, task or feature\r\nDo not include extra details here, e.g. \"vyos_command\" not \"the network module vyos_command\" or the full path\r\n-->\r\ntemplate and potentially more\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \"ansible --version\" between quotes below -->\r\n```\r\n$ ansible --version\r\nansible 2.2.1.0\r\n  config file = /opt/****/ansible.cfg\r\n  configured module search path = [ '/home/****/ansible']\r\n```\r\n\r\n##### CONFIGURATION\r\n<!---\r\nIf using Ansible 2.4 or above, paste the results of \"ansible-config dump --only-changed\"\r\nOtherwise, mention any settings you have changed/added/removed in ansible.cfg\r\n(or using the ANSIBLE_* environment variables).\r\n-->\r\n```\r\n$ cat ansible.cfg | grep tmp\r\nlocal_tmp = /opt/***/.ansible/tmp\r\nremote_tmp = /opt/***/.ansible/tmp\r\n$\r\n\r\n```\r\n##### OS / ENVIRONMENT\r\n<!---\r\nMention the OS you are running Ansible from, and the OS you are\r\nmanaging, or say \"N/A\" for anything that is not platform-specific.\r\nAlso mention the specific version of what you are trying to control,\r\ne.g. if this is a network bug the version of firmware on the network device.\r\n-->\r\n```\r\n$ cat /etc/os-release\r\nNAME=\"Red Hat Enterprise Linux Server\"\r\nVERSION=\"7.3 (Maipo)\"\r\nID=\"rhel\"\r\nID_LIKE=\"fedora\"\r\nVERSION_ID=\"7.3\"\r\nPRETTY_NAME=\"Red Hat Enterprise Linux\"\r\nANSI_COLOR=\"0;31\"\r\nCPE_NAME=\"cpe:/o:redhat:enterprise_linux:7.3:GA:server\"\r\nHOME_URL=\"https://www.redhat.com/\"\r\nBUG_REPORT_URL=\"https://bugzilla.redhat.com/\"\r\n\r\nREDHAT_BUGZILLA_PRODUCT=\"Red Hat Enterprise Linux 7\"\r\nREDHAT_BUGZILLA_PRODUCT_VERSION=7.3\r\nREDHAT_SUPPORT_PRODUCT=\"Red Hat Enterprise Linux\"\r\nREDHAT_SUPPORT_PRODUCT_VERSION=\"7.3\"\r\n\r\n```\r\n##### SUMMARY\r\n<!--- Explain the problem briefly -->\r\nFiles are writing to /tmp when remote_tmp and local_tmp is defined\r\n\r\n##### STEPS TO REPRODUCE\r\n<!---\r\nFor bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used.\r\n-->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n\r\n\r\n```yaml\r\n\r\n    - name: \"{{ site }} - GENERATE JSON REPORT PER SITE STATS ONLY\"\r\n      template:\r\n        src: \"{{ role_path }}/templates/reports/sites/partial_table.j2\"\r\n        dest: '{{ output_dir }}/reports/html/partial/{{ site }}.partial'\r\n```\r\n\r\n<!--- You can also paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- What did you expect to happen when running the steps above? -->\r\n```\r\nlocalhost                  : ok=360  changed=29   unreachable=0    failed=0\r\n```\r\n##### ACTUAL RESULTS\r\n<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->\r\n\r\n<!--- Paste verbatim command output between quotes below -->\r\n```\r\nTASK [reports : *** - GENERATE JSON REPORT PER SITE STATS ONLY] ****************\r\nfatal: [localhost]: FAILED! => {\"changed\": false, \"failed\": true, \"msg\": \"[Errno 13] Permission denied: '/tmp/tmpuv61yn'\"}\r\n\r\nPLAY RECAP *********************************************************************\r\nlocalhost                  : ok=360  changed=29   unreachable=0    failed=1\r\n```\r\n\r\n\n", "before_files": [{"content": "# (c) 2015, Michael DeHaan <michael.dehaan@gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport os\nimport shutil\nimport tempfile\n\nfrom ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleAction, AnsibleActionFail\nfrom ansible.module_utils._text import to_bytes, to_text\nfrom ansible.module_utils.parsing.convert_bool import boolean\nfrom ansible.plugins.action import ActionBase\nfrom ansible.template import generate_ansible_template_vars\n\n\nclass ActionModule(ActionBase):\n\n    TRANSFERS_FILES = True\n    DEFAULT_NEWLINE_SEQUENCE = \"\\n\"\n\n    def run(self, tmp=None, task_vars=None):\n        ''' handler for template operations '''\n\n        if task_vars is None:\n            task_vars = dict()\n\n        result = super(ActionModule, self).run(tmp, task_vars)\n\n        source = self._task.args.get('src', None)\n        dest = self._task.args.get('dest', None)\n        force = boolean(self._task.args.get('force', True), strict=False)\n        follow = boolean(self._task.args.get('follow', False), strict=False)\n        state = self._task.args.get('state', None)\n        newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE)\n        variable_start_string = self._task.args.get('variable_start_string', None)\n        variable_end_string = self._task.args.get('variable_end_string', None)\n        block_start_string = self._task.args.get('block_start_string', None)\n        block_end_string = self._task.args.get('block_end_string', None)\n        trim_blocks = self._task.args.get('trim_blocks', None)\n\n        wrong_sequences = [\"\\\\n\", \"\\\\r\", \"\\\\r\\\\n\"]\n        allowed_sequences = [\"\\n\", \"\\r\", \"\\r\\n\"]\n\n        # We need to convert unescaped sequences to proper escaped sequences for Jinja2\n        if newline_sequence in wrong_sequences:\n            newline_sequence = allowed_sequences[wrong_sequences.index(newline_sequence)]\n\n        try:\n            if state is not None:\n                raise AnsibleActionFail(\"'state' cannot be specified on a template\")\n            elif source is None or dest is None:\n                raise AnsibleActionFail(\"src and dest are required\")\n            elif newline_sequence not in allowed_sequences:\n                raise AnsibleActionFail(\"newline_sequence needs to be one of: \\n, \\r or \\r\\n\")\n            else:\n                try:\n                    source = self._find_needle('templates', source)\n                except AnsibleError as e:\n                    raise AnsibleActionFail(to_text(e))\n\n            # Get vault decrypted tmp file\n            try:\n                tmp_source = self._loader.get_real_file(source)\n            except AnsibleFileNotFound as e:\n                raise AnsibleActionFail(\"could not find src=%s, %s\" % (source, to_text(e)))\n\n            # template the source data locally & get ready to transfer\n            try:\n                with open(tmp_source, 'r') as f:\n                    template_data = to_text(f.read())\n\n                # set jinja2 internal search path for includes\n                searchpath = task_vars.get('ansible_search_path', [])\n                searchpath.extend([self._loader._basedir, os.path.dirname(source)])\n\n                # We want to search into the 'templates' subdir of each search path in\n                # addition to our original search paths.\n                newsearchpath = []\n                for p in searchpath:\n                    newsearchpath.append(os.path.join(p, 'templates'))\n                    newsearchpath.append(p)\n                searchpath = newsearchpath\n\n                self._templar.environment.loader.searchpath = searchpath\n                self._templar.environment.newline_sequence = newline_sequence\n                if block_start_string is not None:\n                    self._templar.environment.block_start_string = block_start_string\n                if block_end_string is not None:\n                    self._templar.environment.block_end_string = block_end_string\n                if variable_start_string is not None:\n                    self._templar.environment.variable_start_string = variable_start_string\n                if variable_end_string is not None:\n                    self._templar.environment.variable_end_string = variable_end_string\n                if trim_blocks is not None:\n                    self._templar.environment.trim_blocks = bool(trim_blocks)\n\n                # add ansible 'template' vars\n                temp_vars = task_vars.copy()\n                temp_vars.update(generate_ansible_template_vars(source))\n\n                old_vars = self._templar._available_variables\n                self._templar.set_available_variables(temp_vars)\n                resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)\n                self._templar.set_available_variables(old_vars)\n            except AnsibleAction:\n                raise\n            except Exception as e:\n                raise AnsibleActionFail(\"%s: %s\" % (type(e).__name__, to_text(e)))\n            finally:\n                self._loader.cleanup_tmp_file(tmp_source)\n\n            new_task = self._task.copy()\n            new_task.args.pop('newline_sequence', None)\n            new_task.args.pop('block_start_string', None)\n            new_task.args.pop('block_end_string', None)\n            new_task.args.pop('variable_start_string', None)\n            new_task.args.pop('variable_end_string', None)\n            new_task.args.pop('trim_blocks', None)\n            try:\n                tempdir = tempfile.mkdtemp()\n                result_file = os.path.join(tempdir, os.path.basename(source))\n                with open(result_file, 'wb') as f:\n                    f.write(to_bytes(resultant, errors='surrogate_or_strict'))\n\n                new_task.args.update(\n                    dict(\n                        src=result_file,\n                        dest=dest,\n                        follow=follow,\n                    ),\n                )\n                copy_action = self._shared_loader_obj.action_loader.get('copy',\n                                                                        task=new_task,\n                                                                        connection=self._connection,\n                                                                        play_context=self._play_context,\n                                                                        loader=self._loader,\n                                                                        templar=self._templar,\n                                                                        shared_loader_obj=self._shared_loader_obj)\n                result.update(copy_action.run(task_vars=task_vars))\n            finally:\n                shutil.rmtree(tempdir)\n        except AnsibleAction as e:\n            result.update(e.result)\n        finally:\n            self._remove_tmp_path(self._connection._shell.tempdir)\n\n        return result\n", "path": "lib/ansible/plugins/action/template.py"}], "after_files": [{"content": "# (c) 2015, Michael DeHaan <michael.dehaan@gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport os\nimport shutil\nimport tempfile\n\nfrom ansible import constants as C\nfrom ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleAction, AnsibleActionFail\nfrom ansible.module_utils._text import to_bytes, to_text\nfrom ansible.module_utils.parsing.convert_bool import boolean\nfrom ansible.plugins.action import ActionBase\nfrom ansible.template import generate_ansible_template_vars\n\n\nclass ActionModule(ActionBase):\n\n    TRANSFERS_FILES = True\n    DEFAULT_NEWLINE_SEQUENCE = \"\\n\"\n\n    def run(self, tmp=None, task_vars=None):\n        ''' handler for template operations '''\n\n        if task_vars is None:\n            task_vars = dict()\n\n        result = super(ActionModule, self).run(tmp, task_vars)\n\n        source = self._task.args.get('src', None)\n        dest = self._task.args.get('dest', None)\n        force = boolean(self._task.args.get('force', True), strict=False)\n        follow = boolean(self._task.args.get('follow', False), strict=False)\n        state = self._task.args.get('state', None)\n        newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE)\n        variable_start_string = self._task.args.get('variable_start_string', None)\n        variable_end_string = self._task.args.get('variable_end_string', None)\n        block_start_string = self._task.args.get('block_start_string', None)\n        block_end_string = self._task.args.get('block_end_string', None)\n        trim_blocks = self._task.args.get('trim_blocks', None)\n\n        wrong_sequences = [\"\\\\n\", \"\\\\r\", \"\\\\r\\\\n\"]\n        allowed_sequences = [\"\\n\", \"\\r\", \"\\r\\n\"]\n\n        # We need to convert unescaped sequences to proper escaped sequences for Jinja2\n        if newline_sequence in wrong_sequences:\n            newline_sequence = allowed_sequences[wrong_sequences.index(newline_sequence)]\n\n        try:\n            if state is not None:\n                raise AnsibleActionFail(\"'state' cannot be specified on a template\")\n            elif source is None or dest is None:\n                raise AnsibleActionFail(\"src and dest are required\")\n            elif newline_sequence not in allowed_sequences:\n                raise AnsibleActionFail(\"newline_sequence needs to be one of: \\n, \\r or \\r\\n\")\n            else:\n                try:\n                    source = self._find_needle('templates', source)\n                except AnsibleError as e:\n                    raise AnsibleActionFail(to_text(e))\n\n            # Get vault decrypted tmp file\n            try:\n                tmp_source = self._loader.get_real_file(source)\n            except AnsibleFileNotFound as e:\n                raise AnsibleActionFail(\"could not find src=%s, %s\" % (source, to_text(e)))\n\n            # template the source data locally & get ready to transfer\n            try:\n                with open(tmp_source, 'r') as f:\n                    template_data = to_text(f.read())\n\n                # set jinja2 internal search path for includes\n                searchpath = task_vars.get('ansible_search_path', [])\n                searchpath.extend([self._loader._basedir, os.path.dirname(source)])\n\n                # We want to search into the 'templates' subdir of each search path in\n                # addition to our original search paths.\n                newsearchpath = []\n                for p in searchpath:\n                    newsearchpath.append(os.path.join(p, 'templates'))\n                    newsearchpath.append(p)\n                searchpath = newsearchpath\n\n                self._templar.environment.loader.searchpath = searchpath\n                self._templar.environment.newline_sequence = newline_sequence\n                if block_start_string is not None:\n                    self._templar.environment.block_start_string = block_start_string\n                if block_end_string is not None:\n                    self._templar.environment.block_end_string = block_end_string\n                if variable_start_string is not None:\n                    self._templar.environment.variable_start_string = variable_start_string\n                if variable_end_string is not None:\n                    self._templar.environment.variable_end_string = variable_end_string\n                if trim_blocks is not None:\n                    self._templar.environment.trim_blocks = bool(trim_blocks)\n\n                # add ansible 'template' vars\n                temp_vars = task_vars.copy()\n                temp_vars.update(generate_ansible_template_vars(source))\n\n                old_vars = self._templar._available_variables\n                self._templar.set_available_variables(temp_vars)\n                resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)\n                self._templar.set_available_variables(old_vars)\n            except AnsibleAction:\n                raise\n            except Exception as e:\n                raise AnsibleActionFail(\"%s: %s\" % (type(e).__name__, to_text(e)))\n            finally:\n                self._loader.cleanup_tmp_file(tmp_source)\n\n            new_task = self._task.copy()\n            new_task.args.pop('newline_sequence', None)\n            new_task.args.pop('block_start_string', None)\n            new_task.args.pop('block_end_string', None)\n            new_task.args.pop('variable_start_string', None)\n            new_task.args.pop('variable_end_string', None)\n            new_task.args.pop('trim_blocks', None)\n            try:\n                tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)\n                result_file = os.path.join(tempdir, os.path.basename(source))\n                with open(result_file, 'wb') as f:\n                    f.write(to_bytes(resultant, errors='surrogate_or_strict'))\n\n                new_task.args.update(\n                    dict(\n                        src=result_file,\n                        dest=dest,\n                        follow=follow,\n                    ),\n                )\n                copy_action = self._shared_loader_obj.action_loader.get('copy',\n                                                                        task=new_task,\n                                                                        connection=self._connection,\n                                                                        play_context=self._play_context,\n                                                                        loader=self._loader,\n                                                                        templar=self._templar,\n                                                                        shared_loader_obj=self._shared_loader_obj)\n                result.update(copy_action.run(task_vars=task_vars))\n            finally:\n                shutil.rmtree(tempdir)\n        except AnsibleAction as e:\n            result.update(e.result)\n        finally:\n            self._remove_tmp_path(self._connection._shell.tempdir)\n\n        return result\n", "path": "lib/ansible/plugins/action/template.py"}]}