2014-11-14 22:14:08 +00:00
|
|
|
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
|
|
|
#
|
|
|
|
# This file is part of Ansible
|
|
|
|
#
|
|
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
# Make coding more python3-ish
|
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
import json
|
|
|
|
import os
|
2016-10-25 03:57:50 +00:00
|
|
|
import stat
|
2014-11-14 22:14:08 +00:00
|
|
|
import tempfile
|
|
|
|
|
2017-02-25 02:10:09 +00:00
|
|
|
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
2016-09-07 05:54:17 +00:00
|
|
|
from ansible.module_utils._text import to_bytes, to_native, to_text
|
2017-07-14 23:44:58 +00:00
|
|
|
from ansible.module_utils.parsing.convert_bool import boolean
|
2014-11-14 22:14:08 +00:00
|
|
|
from ansible.plugins.action import ActionBase
|
|
|
|
from ansible.utils.hashing import checksum
|
|
|
|
|
2015-10-22 23:07:26 +00:00
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
class ActionModule(ActionBase):
|
|
|
|
|
2015-10-22 23:07:26 +00:00
|
|
|
def run(self, tmp=None, task_vars=None):
|
2014-11-14 22:14:08 +00:00
|
|
|
''' handler for file transfer operations '''
|
2015-10-22 23:07:26 +00:00
|
|
|
if task_vars is None:
|
|
|
|
task_vars = dict()
|
|
|
|
|
|
|
|
result = super(ActionModule, self).run(tmp, task_vars)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2017-06-02 11:14:11 +00:00
|
|
|
source = self._task.args.get('src', None)
|
2014-11-14 22:14:08 +00:00
|
|
|
content = self._task.args.get('content', None)
|
2017-06-02 11:14:11 +00:00
|
|
|
dest = self._task.args.get('dest', None)
|
2017-07-14 23:44:58 +00:00
|
|
|
raw = boolean(self._task.args.get('raw', 'no'), strict=False)
|
|
|
|
force = boolean(self._task.args.get('force', 'yes'), strict=False)
|
|
|
|
remote_src = boolean(self._task.args.get('remote_src', False), strict=False)
|
|
|
|
follow = boolean(self._task.args.get('follow', False), strict=False)
|
|
|
|
decrypt = boolean(self._task.args.get('decrypt', True), strict=False)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-12-14 17:52:18 +00:00
|
|
|
result['failed'] = True
|
2016-09-27 16:31:46 +00:00
|
|
|
if (source is None and content is None) or dest is None:
|
2015-10-22 23:07:26 +00:00
|
|
|
result['msg'] = "src (or content) and dest are required"
|
2016-09-27 16:31:46 +00:00
|
|
|
elif source is not None and content is not None:
|
2015-10-22 23:07:26 +00:00
|
|
|
result['msg'] = "src and content are mutually exclusive"
|
2014-09-30 06:56:57 +00:00
|
|
|
elif content is not None and dest is not None and dest.endswith("/"):
|
2015-10-22 23:07:26 +00:00
|
|
|
result['msg'] = "dest must be a file if content is defined"
|
2016-12-14 17:52:18 +00:00
|
|
|
else:
|
|
|
|
del result['failed']
|
|
|
|
|
|
|
|
if result.get('failed'):
|
2015-10-22 23:07:26 +00:00
|
|
|
return result
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# Check if the source ends with a "/"
|
|
|
|
source_trailing_slash = False
|
|
|
|
if source:
|
2015-07-24 16:39:54 +00:00
|
|
|
source_trailing_slash = self._connection._shell.path_has_trailing_slash(source)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# Define content_tempfile in case we set it after finding content populated.
|
|
|
|
content_tempfile = None
|
|
|
|
|
|
|
|
# If content is defined make a temp file and write the content into it.
|
|
|
|
if content is not None:
|
|
|
|
try:
|
|
|
|
# If content comes to us as a dict it should be decoded json.
|
|
|
|
# We need to encode it back into a string to write it out.
|
2015-06-13 03:43:36 +00:00
|
|
|
if isinstance(content, dict) or isinstance(content, list):
|
2014-11-14 22:14:08 +00:00
|
|
|
content_tempfile = self._create_content_tempfile(json.dumps(content))
|
|
|
|
else:
|
|
|
|
content_tempfile = self._create_content_tempfile(content)
|
|
|
|
source = content_tempfile
|
2015-04-13 16:35:20 +00:00
|
|
|
except Exception as err:
|
2015-10-22 23:07:26 +00:00
|
|
|
result['failed'] = True
|
2016-09-07 05:54:17 +00:00
|
|
|
result['msg'] = "could not write content temp file: %s" % to_native(err)
|
2015-10-22 23:07:26 +00:00
|
|
|
return result
|
2015-01-02 13:51:15 +00:00
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
# if we have first_available_file in our vars
|
|
|
|
# look up the files and use the first one we find as src
|
2015-08-16 05:08:40 +00:00
|
|
|
elif remote_src:
|
2016-12-14 17:52:18 +00:00
|
|
|
result.update(self._execute_module(task_vars=task_vars))
|
2015-10-22 23:07:26 +00:00
|
|
|
return result
|
2016-09-07 05:54:17 +00:00
|
|
|
else: # find in expected paths
|
2016-06-28 21:23:30 +00:00
|
|
|
try:
|
|
|
|
source = self._find_needle('files', source)
|
|
|
|
except AnsibleError as e:
|
|
|
|
result['failed'] = True
|
2016-09-07 05:54:17 +00:00
|
|
|
result['msg'] = to_text(e)
|
2016-06-28 21:23:30 +00:00
|
|
|
return result
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# A list of source file tuples (full_path, relative_path) which will try to copy to the destination
|
|
|
|
source_files = []
|
|
|
|
|
|
|
|
# If source is a directory populate our list else source is a file and translate it to a tuple.
|
2016-09-07 05:54:17 +00:00
|
|
|
if os.path.isdir(to_bytes(source, errors='surrogate_or_strict')):
|
2014-11-14 22:14:08 +00:00
|
|
|
# Get the amount of spaces to remove to get the relative path.
|
|
|
|
if source_trailing_slash:
|
2015-08-25 01:06:42 +00:00
|
|
|
sz = len(source)
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
|
|
|
sz = len(source.rsplit('/', 1)[0]) + 1
|
|
|
|
|
|
|
|
# Walk the directory and append the file tuples to source_files.
|
2017-04-06 00:26:11 +00:00
|
|
|
for base_path, sub_folders, files in os.walk(to_bytes(source), followlinks=True):
|
2014-11-14 22:14:08 +00:00
|
|
|
for file in files:
|
2016-09-07 05:54:17 +00:00
|
|
|
full_path = to_text(os.path.join(base_path, file), errors='surrogate_or_strict')
|
2014-11-14 22:14:08 +00:00
|
|
|
rel_path = full_path[sz:]
|
2015-09-22 05:29:32 +00:00
|
|
|
if rel_path.startswith('/'):
|
|
|
|
rel_path = rel_path[1:]
|
2014-11-14 22:14:08 +00:00
|
|
|
source_files.append((full_path, rel_path))
|
|
|
|
|
|
|
|
# If it's recursive copy, destination is always a dir,
|
|
|
|
# explicitly mark it so (note - copy module relies on this).
|
2015-07-01 15:15:40 +00:00
|
|
|
if not self._connection._shell.path_has_trailing_slash(dest):
|
|
|
|
dest = self._connection._shell.join_path(dest, '')
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
|
|
|
source_files.append((source, os.path.basename(source)))
|
|
|
|
|
|
|
|
changed = False
|
2015-10-22 23:07:26 +00:00
|
|
|
module_return = dict(changed=False)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# A register for if we executed a module.
|
|
|
|
# Used to cut down on command calls when not recursive.
|
|
|
|
module_executed = False
|
|
|
|
|
|
|
|
# Tell _execute_module to delete the file if there is one file.
|
|
|
|
delete_remote_tmp = (len(source_files) == 1)
|
|
|
|
|
|
|
|
# If this is a recursive action create a tmp path that we can share as the _exec_module create is too late.
|
|
|
|
if not delete_remote_tmp:
|
|
|
|
if tmp is None or "-tmp-" not in tmp:
|
2016-12-14 17:52:18 +00:00
|
|
|
tmp = self._make_tmp_path()
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# expand any user home dir specifier
|
2015-09-24 20:29:36 +00:00
|
|
|
dest = self._remote_expand_user(dest)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-10-25 03:57:50 +00:00
|
|
|
# Keep original value for mode parameter
|
|
|
|
mode_value = self._task.args.get('mode', None)
|
|
|
|
|
2015-08-16 05:08:40 +00:00
|
|
|
diffs = []
|
2014-11-14 22:14:08 +00:00
|
|
|
for source_full, source_rel in source_files:
|
2015-04-03 13:20:19 +00:00
|
|
|
|
2017-02-25 02:10:09 +00:00
|
|
|
# If the local file does not exist, get_real_file() raises AnsibleFileNotFound
|
|
|
|
try:
|
2017-03-24 19:39:25 +00:00
|
|
|
source_full = self._loader.get_real_file(source_full, decrypt=decrypt)
|
2017-02-25 02:10:09 +00:00
|
|
|
except AnsibleFileNotFound as e:
|
|
|
|
result['failed'] = True
|
|
|
|
result['msg'] = "could not find src=%s, %s" % (source_full, e)
|
|
|
|
self._remove_tmp_path(tmp)
|
|
|
|
return result
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-10-25 03:57:50 +00:00
|
|
|
# Get the local mode and set if user wanted it preserved
|
|
|
|
# https://github.com/ansible/ansible-modules-core/issues/1124
|
|
|
|
if self._task.args.get('mode', None) == 'preserve':
|
|
|
|
lmode = '0%03o' % stat.S_IMODE(os.stat(source_full).st_mode)
|
|
|
|
self._task.args['mode'] = lmode
|
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
# This is kind of optimization - if user told us destination is
|
|
|
|
# dir, do path manipulation right away, otherwise we still check
|
|
|
|
# for dest being a dir via remote call below.
|
2015-07-01 15:15:40 +00:00
|
|
|
if self._connection._shell.path_has_trailing_slash(dest):
|
|
|
|
dest_file = self._connection._shell.join_path(dest, source_rel)
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
2015-07-01 15:15:40 +00:00
|
|
|
dest_file = self._connection._shell.join_path(dest)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-02-15 22:11:49 +00:00
|
|
|
# Attempt to get remote file info
|
2017-02-25 02:10:09 +00:00
|
|
|
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp, checksum=force)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-02-15 22:11:49 +00:00
|
|
|
if dest_status['exists'] and dest_status['isdir']:
|
|
|
|
# The dest is a directory.
|
2014-11-14 22:14:08 +00:00
|
|
|
if content is not None:
|
|
|
|
# If source was defined as content remove the temporary file and fail out.
|
|
|
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
2016-04-20 17:39:12 +00:00
|
|
|
self._remove_tmp_path(tmp)
|
2015-10-22 23:07:26 +00:00
|
|
|
result['failed'] = True
|
|
|
|
result['msg'] = "can not use content with a dir as dest"
|
|
|
|
return result
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
2016-02-15 22:11:49 +00:00
|
|
|
# Append the relative source location to the destination and get remote stats again
|
2015-07-01 15:15:40 +00:00
|
|
|
dest_file = self._connection._shell.join_path(dest, source_rel)
|
2017-02-25 02:10:09 +00:00
|
|
|
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp, checksum=force)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-02-23 21:29:57 +00:00
|
|
|
if dest_status['exists'] and not force:
|
2017-02-25 02:10:09 +00:00
|
|
|
# remote_file exists so continue to next iteration.
|
2014-11-14 22:14:08 +00:00
|
|
|
continue
|
|
|
|
|
2017-02-25 02:10:09 +00:00
|
|
|
# Generate a hash of the local file.
|
|
|
|
local_checksum = checksum(source_full)
|
|
|
|
|
2016-02-15 22:11:49 +00:00
|
|
|
if local_checksum != dest_status['checksum']:
|
2014-11-14 22:14:08 +00:00
|
|
|
# The checksums don't match and we will change or error out.
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
# Create a tmp path if missing only if this is not recursive.
|
|
|
|
# If this is recursive we already have a tmp path.
|
|
|
|
if delete_remote_tmp:
|
|
|
|
if tmp is None or "-tmp-" not in tmp:
|
2016-12-14 17:52:18 +00:00
|
|
|
tmp = self._make_tmp_path()
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2015-07-26 16:21:38 +00:00
|
|
|
if self._play_context.diff and not raw:
|
2015-09-24 20:29:36 +00:00
|
|
|
diffs.append(self._get_diff_data(dest_file, source_full, task_vars))
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2015-07-21 16:12:22 +00:00
|
|
|
if self._play_context.check_mode:
|
2015-07-01 19:10:25 +00:00
|
|
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
|
|
|
changed = True
|
|
|
|
module_return = dict(changed=True)
|
|
|
|
continue
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# Define a remote directory that we will copy the file to.
|
2015-07-24 16:39:54 +00:00
|
|
|
tmp_src = self._connection._shell.join_path(tmp, 'source')
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-08-06 01:40:28 +00:00
|
|
|
remote_path = None
|
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
if not raw:
|
2016-08-06 01:40:28 +00:00
|
|
|
remote_path = self._transfer_file(source_full, tmp_src)
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
2016-03-21 21:17:53 +00:00
|
|
|
self._transfer_file(source_full, dest_file)
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
# We have copied the file remotely and no longer require our content_tempfile
|
|
|
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
2016-04-14 14:31:39 +00:00
|
|
|
self._loader.cleanup_tmp_file(source_full)
|
2016-01-22 16:23:10 +00:00
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
# fix file permissions when the copy is done as a different user
|
2016-08-06 01:40:28 +00:00
|
|
|
if remote_path:
|
2016-12-14 17:52:18 +00:00
|
|
|
self._fixup_perms2((tmp, remote_path))
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
if raw:
|
|
|
|
# Continue to next iteration if raw is defined.
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Run the copy module
|
|
|
|
|
|
|
|
# src and dest here come after original and override them
|
|
|
|
# we pass dest only to make sure it includes trailing slash in case of recursive copy
|
|
|
|
new_module_args = self._task.args.copy()
|
|
|
|
new_module_args.update(
|
|
|
|
dict(
|
|
|
|
src=tmp_src,
|
|
|
|
dest=dest,
|
|
|
|
original_basename=source_rel,
|
|
|
|
)
|
|
|
|
)
|
2017-03-24 19:39:25 +00:00
|
|
|
|
|
|
|
# remove action plugin only keys
|
|
|
|
for key in ('content', 'decrypt'):
|
|
|
|
if key in new_module_args:
|
|
|
|
del new_module_args[key]
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2016-09-07 05:54:17 +00:00
|
|
|
module_return = self._execute_module(module_name='copy',
|
2017-06-02 11:14:11 +00:00
|
|
|
module_args=new_module_args, task_vars=task_vars,
|
|
|
|
tmp=tmp, delete_remote_tmp=delete_remote_tmp)
|
2014-11-14 22:14:08 +00:00
|
|
|
module_executed = True
|
|
|
|
|
|
|
|
else:
|
|
|
|
# no need to transfer the file, already correct hash, but still need to call
|
|
|
|
# the file module in case we want to change attributes
|
|
|
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
2016-04-14 14:31:39 +00:00
|
|
|
self._loader.cleanup_tmp_file(source_full)
|
2016-01-22 16:23:10 +00:00
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
if raw:
|
|
|
|
# Continue to next iteration if raw is defined.
|
2015-07-26 16:21:38 +00:00
|
|
|
self._remove_tmp_path(tmp)
|
2014-11-14 22:14:08 +00:00
|
|
|
continue
|
|
|
|
|
Fix for ansible/ansible-modules-core#1568
When checksums of local and remote files match, and when follow = True,
determine if remote destination is a symlink. If so, de-reference it and
pass the link target to the file module as 'dest'.
This change fixes an edge case in file copy behavior when:
- 'dest' is a symlink to some other file ('realdest')
- follow = True
- the checksums of the source file, 'src', and the symlink target, 'realdest',
match.
Because the checksums match, the copy module is skipped and the file module
is invoked directly with 'dest' = the symlink, and 'src' = the source of the
copy module, whether that source is present on the target machine or not.
When 'src' doesn't exist on the target machine, this leads to an error that
looks like this because it can't change the target of the symlink:
TASK [copy] ********************************************************************
fatal: [192.168.56.101]: FAILED! => {"changed": false, "checksum": "f572d396fae9206628714fb2ce00f72e94f2258f", "failed": true, "gid": 1000, "group": "ajdecon", "mode": "0777", "msg": "src file does not exist, use \"force=yes\" if you really want to create the link: /tmp/issue1568/dest_dir/source", "owner": "ajdecon", "path": "/tmp/issue1568/dest_dir/dest", "size": 8, "src": "source", "state": "link", "uid": 1000}
When the path 'src' *does* exist on the target machine, the file module makes
this the symlink "dest -> src" instead of "dest -> realdest"... even if the
checksum of 'src' on the target machine is different from the checksum of 'src'
on the machine where Ansible is running.
2016-02-23 20:47:51 +00:00
|
|
|
# Fix for https://github.com/ansible/ansible-modules-core/issues/1568.
|
|
|
|
# If checksums match, and follow = True, find out if 'dest' is a link. If so,
|
|
|
|
# change it to point to the source of the link.
|
|
|
|
if follow:
|
|
|
|
dest_status_nofollow = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=False)
|
|
|
|
if dest_status_nofollow['islnk'] and 'lnk_source' in dest_status_nofollow.keys():
|
|
|
|
dest = dest_status_nofollow['lnk_source']
|
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
# Build temporary module_args.
|
|
|
|
new_module_args = self._task.args.copy()
|
|
|
|
new_module_args.update(
|
|
|
|
dict(
|
|
|
|
src=source_rel,
|
|
|
|
dest=dest,
|
|
|
|
original_basename=source_rel
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Execute the file module.
|
2016-09-07 05:54:17 +00:00
|
|
|
module_return = self._execute_module(module_name='file',
|
2017-06-02 11:14:11 +00:00
|
|
|
module_args=new_module_args, task_vars=task_vars,
|
|
|
|
tmp=tmp, delete_remote_tmp=delete_remote_tmp)
|
2014-11-14 22:14:08 +00:00
|
|
|
module_executed = True
|
|
|
|
|
|
|
|
if not module_return.get('checksum'):
|
|
|
|
module_return['checksum'] = local_checksum
|
2015-10-22 23:07:26 +00:00
|
|
|
if module_return.get('failed'):
|
|
|
|
result.update(module_return)
|
2016-04-20 17:39:12 +00:00
|
|
|
if not delete_remote_tmp:
|
|
|
|
self._remove_tmp_path(tmp)
|
2015-10-22 23:07:26 +00:00
|
|
|
return result
|
|
|
|
if module_return.get('changed'):
|
2014-11-14 22:14:08 +00:00
|
|
|
changed = True
|
|
|
|
|
2015-04-13 16:35:20 +00:00
|
|
|
# the file module returns the file path as 'path', but
|
2015-02-09 22:54:44 +00:00
|
|
|
# the copy module uses 'dest', so add it if it's not there
|
|
|
|
if 'path' in module_return and 'dest' not in module_return:
|
|
|
|
module_return['dest'] = module_return['path']
|
|
|
|
|
2016-10-25 03:57:50 +00:00
|
|
|
# reset the mode
|
|
|
|
self._task.args['mode'] = mode_value
|
|
|
|
|
2014-11-14 22:14:08 +00:00
|
|
|
# Delete tmp path if we were recursive or if we did not execute a module.
|
2016-04-20 17:39:12 +00:00
|
|
|
if not delete_remote_tmp or (delete_remote_tmp and not module_executed):
|
2014-11-14 22:14:08 +00:00
|
|
|
self._remove_tmp_path(tmp)
|
|
|
|
|
2015-02-09 22:54:44 +00:00
|
|
|
if module_executed and len(source_files) == 1:
|
2015-10-22 23:07:26 +00:00
|
|
|
result.update(module_return)
|
2014-11-14 22:14:08 +00:00
|
|
|
else:
|
2015-10-22 23:07:26 +00:00
|
|
|
result.update(dict(dest=dest, src=source, changed=changed))
|
2014-11-14 22:14:08 +00:00
|
|
|
|
2015-08-16 06:18:32 +00:00
|
|
|
if diffs:
|
|
|
|
result['diff'] = diffs
|
2014-11-14 22:14:08 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def _create_content_tempfile(self, content):
|
|
|
|
''' Create a tempfile containing defined content '''
|
|
|
|
fd, content_tempfile = tempfile.mkstemp()
|
2015-03-31 02:19:34 +00:00
|
|
|
f = os.fdopen(fd, 'wb')
|
|
|
|
content = to_bytes(content)
|
2014-11-14 22:14:08 +00:00
|
|
|
try:
|
|
|
|
f.write(content)
|
2015-04-13 16:35:20 +00:00
|
|
|
except Exception as err:
|
2014-11-14 22:14:08 +00:00
|
|
|
os.remove(content_tempfile)
|
|
|
|
raise Exception(err)
|
|
|
|
finally:
|
|
|
|
f.close()
|
|
|
|
return content_tempfile
|
|
|
|
|
|
|
|
def _remove_tempfile_if_content_defined(self, content, content_tempfile):
|
|
|
|
if content is not None:
|
|
|
|
os.remove(content_tempfile)
|