From 8edd1cf18220bbb2d8743035a3843ff4e4064c24 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 18 Jan 2018 05:54:10 +1000 Subject: [PATCH] changed to enable windows temp path override (#34967) --- lib/ansible/plugins/action/win_copy.py | 56 +++++++++++++++---------- lib/ansible/plugins/shell/powershell.py | 54 +++++++++++++++++++++--- 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/lib/ansible/plugins/action/win_copy.py b/lib/ansible/plugins/action/win_copy.py index cdb4f2999d6..b0be7ff507d 100644 --- a/lib/ansible/plugins/action/win_copy.py +++ b/lib/ansible/plugins/action/win_copy.py @@ -232,7 +232,7 @@ class ActionModule(ActionBase): return content_tempfile def _create_zip_tempfile(self, files, directories): - tmpdir = tempfile.mkdtemp() + tmpdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) zip_file_path = os.path.join(tmpdir, "win_copy.zip") zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_STORED, True) @@ -259,14 +259,13 @@ class ActionModule(ActionBase): if content is not None: os.remove(content_tempfile) - def _copy_single_file(self, local_file, dest, source_rel, task_vars): + def _copy_single_file(self, local_file, dest, source_rel, task_vars, tmp): if self._play_context.check_mode: module_return = dict(changed=True) return module_return # copy the file across to the server - tmp_path = self._make_tmp_path() - tmp_src = self._connection._shell.join_path(tmp_path, 'source') + tmp_src = self._connection._shell.join_path(tmp, 'source') self._transfer_file(local_file, tmp_src) copy_args = self._task.args.copy() @@ -280,14 +279,20 @@ class ActionModule(ActionBase): ) copy_args.pop('content', None) - copy_result = self._execute_module(module_name="copy", module_args=copy_args, task_vars=task_vars) - self._remove_tmp_path(tmp_path) + copy_result = self._execute_module(module_name="copy", + module_args=copy_args, + task_vars=task_vars, + tmp=tmp) return copy_result - def _copy_zip_file(self, dest, files, directories, task_vars): + def _copy_zip_file(self, dest, files, directories, task_vars, tmp): # create local zip file containing all the files and directories that # need to be copied to the server + if self._play_context.check_mode: + module_return = dict(changed=True) + return module_return + try: zip_file = self._create_zip_tempfile(files, directories) except Exception as e: @@ -301,15 +306,9 @@ class ActionModule(ActionBase): zip_path = self._loader.get_real_file(zip_file) - if self._play_context.check_mode: - module_return = dict(changed=True) - os.remove(zip_path) - os.removedirs(os.path.dirname(zip_path)) - return module_return - - # send zip file to remote, file must end in .zip so Com Shell.Application works - tmp_path = self._make_tmp_path() - tmp_src = self._connection._shell.join_path(tmp_path, 'source.zip') + # send zip file to remote, file must end in .zip so + # Com Shell.Application works + tmp_src = self._connection._shell.join_path(tmp, 'source.zip') self._transfer_file(zip_path, tmp_src) # run the explode operation of win_copy on remote @@ -323,10 +322,12 @@ class ActionModule(ActionBase): ) copy_args.pop('content', None) os.remove(zip_path) - os.removedirs(os.path.dirname(zip_path)) - module_return = self._execute_module(module_args=copy_args, task_vars=task_vars) - self._remove_tmp_path(tmp_path) + module_return = self._execute_module(module_name='copy', + module_args=copy_args, + task_vars=task_vars, + tmp=tmp) + os.removedirs(os.path.dirname(zip_path)) return module_return def run(self, tmp=None, task_vars=None): @@ -480,17 +481,23 @@ class ActionModule(ActionBase): query_args.pop('src', None) query_args.pop('content', None) - query_return = self._execute_module(module_args=query_args, task_vars=task_vars) + query_return = self._execute_module(module_args=query_args, + task_vars=task_vars, + tmp=tmp) if query_return.get('failed') is True: result.update(query_return) return result + if len(query_return['files']) > 0 or len(query_return['directories']) > 0 and tmp is None: + tmp = self._make_tmp_path() + if len(query_return['files']) == 1 and len(query_return['directories']) == 0: # we only need to copy 1 file, don't mess around with zips file_src = query_return['files'][0]['src'] file_dest = query_return['files'][0]['dest'] - copy_result = self._copy_single_file(file_src, dest, file_dest, task_vars) + copy_result = self._copy_single_file(file_src, dest, file_dest, + task_vars, tmp) result['changed'] = True if copy_result.get('failed') is True: @@ -500,13 +507,16 @@ class ActionModule(ActionBase): # either multiple files or directories need to be copied, compress # to a zip and 'explode' the zip on the server # TODO: handle symlinks - result.update(self._copy_zip_file(dest, source_files['files'], source_files['directories'], task_vars)) + result.update(self._copy_zip_file(dest, source_files['files'], + source_files['directories'], + task_vars, tmp)) result['changed'] = True else: # no operations need to occur result['failed'] = False result['changed'] = False - # remove the content temp file if it was created + # remove the content temp file and remote tmp file if it was created self._remove_tempfile_if_content_defined(content, content_tempfile) + self._remove_tmp_path(tmp) return result diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py index dd0de946954..4323f125b9f 100644 --- a/lib/ansible/plugins/shell/powershell.py +++ b/lib/ansible/plugins/shell/powershell.py @@ -10,8 +10,41 @@ DOCUMENTATION = ''' version_added: "" short_description: Windows Powershell description: - - The only option whne using 'winrm' as a connection plugin + - The only option when using 'winrm' as a connection plugin + options: + remote_temp: + description: + - Temporary directory to use on targets when copying files to the host. + default: '%TEMP%' + ini: + - section: powershell + key: remote_tmp + vars: + - name: ansible_remote_tmp + admin_users: + description: + - List of users to be expected to have admin privileges, this is unused + in the PowerShell plugin + type: list + default: [] + set_module_language: + description: + - Controls if we set the locale for moduels when executing on the + target. + - Windows only supports C(no) as an option. + type: bool + default: 'no' + choices: + - 'no' + environment: + description: + - Dictionary of environment variables and their values to use when + executing commands. + type: dict + default: {} ''' +# FIXME: admin_users and set_module_language don't belong here but must be set +# so they don't failk when someone get_option('admin_users') on this plugin import base64 import os @@ -19,7 +52,8 @@ import re import shlex from ansible.errors import AnsibleError -from ansible.module_utils._text import to_bytes, to_text +from ansible.module_utils._text import to_text +from ansible.plugins.shell import ShellBase _common_args = ['PowerShell', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Unrestricted'] @@ -1692,7 +1726,7 @@ Function Run($payload) { from ansible.plugins import AnsiblePlugin -class ShellModule(AnsiblePlugin): +class ShellModule(ShellBase): # Common shell filenames that this plugin handles # Powershell is handled differently. It's selected when winrm is the @@ -1766,10 +1800,18 @@ class ShellModule(AnsiblePlugin): else: return self._encode_script('''Remove-Item "%s" -Force;''' % path) - def mkdtemp(self, basefile, system=False, mode=None, tmpdir=None): + def mkdtemp(self, basefile=None, system=False, mode=None, tmpdir=None): + # Windows does not have an equivalent for the system temp files, so + # the param is ignored basefile = self._escape(self._unquote(basefile)) - # FIXME: Support system temp path and passed in tmpdir! - return self._encode_script('''(New-Item -Type Directory -Path $env:temp -Name "%s").FullName | Write-Host -Separator '';''' % basefile) + basetmpdir = tmpdir if tmpdir else self.get_option('remote_temp') + + script = ''' + $tmp_path = [System.Environment]::ExpandEnvironmentVariables('%s') + $tmp = New-Item -Type Directory -Path $tmp_path -Name '%s' + $tmp.FullName | Write-Host -Separator '' + ''' % (basetmpdir, basefile) + return self._encode_script(script.strip()) def expand_user(self, user_home_path, username=''): # PowerShell only supports "~" (not "~username"). Resolve-Path ~ does