Python3 fixes to copy, file, and stat so that the connection integration tests can be run (#4632)
* Python3 fixes to copy, file, and stat so that the connection integration tests can be run * Forgot to audit the helper functions as well. * Fix dest to refledt b_dest (found by @mattclay)
This commit is contained in:
parent
baa2e930bb
commit
42c74a0ce2
3 changed files with 163 additions and 139 deletions
|
@ -18,9 +18,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: copy
|
module: copy
|
||||||
|
@ -183,13 +180,25 @@ state:
|
||||||
sample: "file"
|
sample: "file"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# import module snippets
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.pycompat24 import get_exception
|
||||||
|
from ansible.module_utils._text import to_bytes, to_native
|
||||||
|
|
||||||
|
|
||||||
def split_pre_existing_dir(dirname):
|
def split_pre_existing_dir(dirname):
|
||||||
'''
|
'''
|
||||||
Return the first pre-existing directory and a list of the new directories that will be created.
|
Return the first pre-existing directory and a list of the new directories that will be created.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
head, tail = os.path.split(dirname)
|
head, tail = os.path.split(dirname)
|
||||||
if not os.path.exists(head):
|
b_head = to_bytes(head, errors='surrogate_or_strict')
|
||||||
|
if not os.path.exists(b_head):
|
||||||
(pre_existing_dir, new_directory_list) = split_pre_existing_dir(head)
|
(pre_existing_dir, new_directory_list) = split_pre_existing_dir(head)
|
||||||
else:
|
else:
|
||||||
return (head, [tail])
|
return (head, [tail])
|
||||||
|
@ -215,10 +224,10 @@ def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
# not checking because of daisy chain to file module
|
# not checking because of daisy chain to file module
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
src = dict(required=False),
|
src = dict(required=False, type='path'),
|
||||||
original_basename = dict(required=False), # used to handle 'dest is a directory' via template, a slight hack
|
original_basename = dict(required=False), # used to handle 'dest is a directory' via template, a slight hack
|
||||||
content = dict(required=False, no_log=True),
|
content = dict(required=False, no_log=True),
|
||||||
dest = dict(required=True),
|
dest = dict(required=True, type='path'),
|
||||||
backup = dict(default=False, type='bool'),
|
backup = dict(default=False, type='bool'),
|
||||||
force = dict(default=True, aliases=['thirsty'], type='bool'),
|
force = dict(default=True, aliases=['thirsty'], type='bool'),
|
||||||
validate = dict(required=False, type='str'),
|
validate = dict(required=False, type='str'),
|
||||||
|
@ -229,8 +238,10 @@ def main():
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
src = os.path.expanduser(module.params['src'])
|
src = module.params['src']
|
||||||
dest = os.path.expanduser(module.params['dest'])
|
b_src = to_bytes(src, errors='surrogate_or_strict')
|
||||||
|
dest = module.params['dest']
|
||||||
|
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||||
backup = module.params['backup']
|
backup = module.params['backup']
|
||||||
force = module.params['force']
|
force = module.params['force']
|
||||||
original_basename = module.params.get('original_basename', None)
|
original_basename = module.params.get('original_basename', None)
|
||||||
|
@ -239,11 +250,11 @@ def main():
|
||||||
mode = module.params['mode']
|
mode = module.params['mode']
|
||||||
remote_src = module.params['remote_src']
|
remote_src = module.params['remote_src']
|
||||||
|
|
||||||
if not os.path.exists(src):
|
if not os.path.exists(b_src):
|
||||||
module.fail_json(msg="Source %s not found" % (src))
|
module.fail_json(msg="Source %s not found" % (src))
|
||||||
if not os.access(src, os.R_OK):
|
if not os.access(b_src, os.R_OK):
|
||||||
module.fail_json(msg="Source %s not readable" % (src))
|
module.fail_json(msg="Source %s not readable" % (src))
|
||||||
if os.path.isdir(src):
|
if os.path.isdir(b_src):
|
||||||
module.fail_json(msg="Remote copy does not support recursive copy of directory: %s" % (src))
|
module.fail_json(msg="Remote copy does not support recursive copy of directory: %s" % (src))
|
||||||
|
|
||||||
checksum_src = module.sha1(src)
|
checksum_src = module.sha1(src)
|
||||||
|
@ -259,10 +270,12 @@ def main():
|
||||||
# Special handling for recursive copy - create intermediate dirs
|
# Special handling for recursive copy - create intermediate dirs
|
||||||
if original_basename and dest.endswith(os.sep):
|
if original_basename and dest.endswith(os.sep):
|
||||||
dest = os.path.join(dest, original_basename)
|
dest = os.path.join(dest, original_basename)
|
||||||
|
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||||
dirname = os.path.dirname(dest)
|
dirname = os.path.dirname(dest)
|
||||||
if not os.path.exists(dirname) and os.path.isabs(dirname):
|
b_dirname = to_bytes(dirname, errors='surrogate_or_strict')
|
||||||
|
if not os.path.exists(b_dirname) and os.path.isabs(b_dirname):
|
||||||
(pre_existing_dir, new_directory_list) = split_pre_existing_dir(dirname)
|
(pre_existing_dir, new_directory_list) = split_pre_existing_dir(dirname)
|
||||||
os.makedirs(dirname)
|
os.makedirs(b_dirname)
|
||||||
directory_args = module.load_file_common_arguments(module.params)
|
directory_args = module.load_file_common_arguments(module.params)
|
||||||
directory_mode = module.params["directory_mode"]
|
directory_mode = module.params["directory_mode"]
|
||||||
if directory_mode is not None:
|
if directory_mode is not None:
|
||||||
|
@ -271,45 +284,47 @@ def main():
|
||||||
directory_args['mode'] = None
|
directory_args['mode'] = None
|
||||||
adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed)
|
adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed)
|
||||||
|
|
||||||
if os.path.exists(dest):
|
if os.path.exists(b_dest):
|
||||||
if os.path.islink(dest) and follow:
|
if os.path.islink(b_dest) and follow:
|
||||||
dest = os.path.realpath(dest)
|
b_dest = os.path.realpath(b_dest)
|
||||||
|
dest = to_native(b_dest, errors='surrogate_or_strict')
|
||||||
if not force:
|
if not force:
|
||||||
module.exit_json(msg="file already exists", src=src, dest=dest, changed=False)
|
module.exit_json(msg="file already exists", src=src, dest=dest, changed=False)
|
||||||
if (os.path.isdir(dest)):
|
if os.path.isdir(b_dest):
|
||||||
basename = os.path.basename(src)
|
basename = os.path.basename(src)
|
||||||
if original_basename:
|
if original_basename:
|
||||||
basename = original_basename
|
basename = original_basename
|
||||||
dest = os.path.join(dest, basename)
|
dest = os.path.join(dest, basename)
|
||||||
if os.access(dest, os.R_OK):
|
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||||
|
if os.access(b_dest, os.R_OK):
|
||||||
checksum_dest = module.sha1(dest)
|
checksum_dest = module.sha1(dest)
|
||||||
else:
|
else:
|
||||||
if not os.path.exists(os.path.dirname(dest)):
|
if not os.path.exists(os.path.dirname(b_dest)):
|
||||||
try:
|
try:
|
||||||
# os.path.exists() can return false in some
|
# os.path.exists() can return false in some
|
||||||
# circumstances where the directory does not have
|
# circumstances where the directory does not have
|
||||||
# the execute bit for the current user set, in
|
# the execute bit for the current user set, in
|
||||||
# which case the stat() call will raise an OSError
|
# which case the stat() call will raise an OSError
|
||||||
os.stat(os.path.dirname(dest))
|
os.stat(os.path.dirname(b_dest))
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
if "permission denied" in str(e).lower():
|
if "permission denied" in to_native(e).lower():
|
||||||
module.fail_json(msg="Destination directory %s is not accessible" % (os.path.dirname(dest)))
|
module.fail_json(msg="Destination directory %s is not accessible" % (os.path.dirname(dest)))
|
||||||
module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest)))
|
module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest)))
|
||||||
if not os.access(os.path.dirname(dest), os.W_OK):
|
if not os.access(os.path.dirname(b_dest), os.W_OK):
|
||||||
module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest)))
|
module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest)))
|
||||||
|
|
||||||
backup_file = None
|
backup_file = None
|
||||||
if checksum_src != checksum_dest or os.path.islink(dest):
|
if checksum_src != checksum_dest or os.path.islink(b_dest):
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
try:
|
try:
|
||||||
if backup:
|
if backup:
|
||||||
if os.path.exists(dest):
|
if os.path.exists(b_dest):
|
||||||
backup_file = module.backup_local(dest)
|
backup_file = module.backup_local(dest)
|
||||||
# allow for conversion from symlink.
|
# allow for conversion from symlink.
|
||||||
if os.path.islink(dest):
|
if os.path.islink(b_dest):
|
||||||
os.unlink(dest)
|
os.unlink(b_dest)
|
||||||
open(dest, 'w').close()
|
open(b_dest, 'w').close()
|
||||||
if validate:
|
if validate:
|
||||||
# if we have a mode, make sure we set it on the temporary
|
# if we have a mode, make sure we set it on the temporary
|
||||||
# file source as some validations may require it
|
# file source as some validations may require it
|
||||||
|
@ -321,11 +336,11 @@ def main():
|
||||||
(rc, out, err) = module.run_command(validate % src)
|
(rc, out, err) = module.run_command(validate % src)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err)
|
module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err)
|
||||||
mysrc = src
|
b_mysrc = b_src
|
||||||
if remote_src:
|
if remote_src:
|
||||||
_, mysrc = tempfile.mkstemp(dir=os.path.dirname(dest))
|
_, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest))
|
||||||
shutil.copy2(src, mysrc)
|
shutil.copy2(b_src, b_mysrc)
|
||||||
module.atomic_move(mysrc, dest, unsafe_writes=module.params['unsafe_writes'])
|
module.atomic_move(b_mysrc, dest, unsafe_writes=module.params['unsafe_writes'])
|
||||||
except IOError:
|
except IOError:
|
||||||
module.fail_json(msg="failed to copy: %s to %s" % (src, dest), traceback=traceback.format_exc())
|
module.fail_json(msg="failed to copy: %s to %s" % (src, dest), traceback=traceback.format_exc())
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -345,6 +360,5 @@ def main():
|
||||||
|
|
||||||
module.exit_json(**res_args)
|
module.exit_json(**res_args)
|
||||||
|
|
||||||
# import module snippets
|
if __name__ == '__main__':
|
||||||
from ansible.module_utils.basic import *
|
|
||||||
main()
|
main()
|
||||||
|
|
176
files/file.py
176
files/file.py
|
@ -18,12 +18,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import errno
|
|
||||||
import shutil
|
|
||||||
import stat
|
|
||||||
import grp
|
|
||||||
import pwd
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: file
|
module: file
|
||||||
|
@ -112,16 +106,27 @@ EXAMPLES = '''
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import time
|
||||||
|
|
||||||
def get_state(path):
|
# import module snippets
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.pycompat24 import get_exception
|
||||||
|
from ansible.module_utils.six import b
|
||||||
|
from ansible.module_utils._text import to_bytes, to_native
|
||||||
|
|
||||||
|
|
||||||
|
def get_state(b_path):
|
||||||
''' Find out current state '''
|
''' Find out current state '''
|
||||||
|
|
||||||
if os.path.lexists(path):
|
if os.path.lexists(b_path):
|
||||||
if os.path.islink(path):
|
if os.path.islink(b_path):
|
||||||
return 'link'
|
return 'link'
|
||||||
elif os.path.isdir(path):
|
elif os.path.isdir(b_path):
|
||||||
return 'directory'
|
return 'directory'
|
||||||
elif os.stat(path).st_nlink > 1:
|
elif os.stat(b_path).st_nlink > 1:
|
||||||
return 'hard'
|
return 'hard'
|
||||||
else:
|
else:
|
||||||
# could be many other things, but defaulting to file
|
# could be many other things, but defaulting to file
|
||||||
|
@ -129,40 +134,42 @@ def get_state(path):
|
||||||
|
|
||||||
return 'absent'
|
return 'absent'
|
||||||
|
|
||||||
def recursive_set_attributes(module, path, follow, file_args):
|
|
||||||
|
def recursive_set_attributes(module, b_path, follow, file_args):
|
||||||
changed = False
|
changed = False
|
||||||
for root, dirs, files in os.walk(path):
|
for b_root, b_dirs, b_files in os.walk(b_path):
|
||||||
for fsobj in dirs + files:
|
for b_fsobj in b_dirs + b_files:
|
||||||
fsname = os.path.join(root, fsobj)
|
b_fsname = os.path.join(b_root, b_fsobj)
|
||||||
if not os.path.islink(fsname):
|
if not os.path.islink(b_fsname):
|
||||||
tmp_file_args = file_args.copy()
|
tmp_file_args = file_args.copy()
|
||||||
tmp_file_args['path']=fsname
|
tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict')
|
||||||
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
||||||
else:
|
else:
|
||||||
tmp_file_args = file_args.copy()
|
tmp_file_args = file_args.copy()
|
||||||
tmp_file_args['path']=fsname
|
tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict')
|
||||||
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
||||||
if follow:
|
if follow:
|
||||||
fsname = os.path.join(root, os.readlink(fsname))
|
b_fsname = os.path.join(b_root, os.readlink(b_fsname))
|
||||||
if os.path.isdir(fsname):
|
if os.path.isdir(b_fsname):
|
||||||
changed |= recursive_set_attributes(module, fsname, follow, file_args)
|
changed |= recursive_set_attributes(module, b_fsname, follow, file_args)
|
||||||
tmp_file_args = file_args.copy()
|
tmp_file_args = file_args.copy()
|
||||||
tmp_file_args['path']=fsname
|
tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict')
|
||||||
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
changed |= module.set_fs_attributes_if_different(tmp_file_args, changed)
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
state=dict(choices=['file', 'directory', 'link', 'hard', 'touch', 'absent'], default=None),
|
state=dict(choices=['file', 'directory', 'link', 'hard', 'touch', 'absent'], default=None),
|
||||||
path = dict(aliases=['dest', 'name'], required=True),
|
path=dict(aliases=['dest', 'name'], required=True, type='path'),
|
||||||
original_basename=dict(required=False), # Internal use only, for recursive ops
|
original_basename=dict(required=False), # Internal use only, for recursive ops
|
||||||
recurse=dict(default=False, type='bool'),
|
recurse=dict(default=False, type='bool'),
|
||||||
force=dict(required=False, default=False, type='bool'),
|
force=dict(required=False, default=False, type='bool'),
|
||||||
diff_peek=dict(default=None), # Internal use only, for internal checks in the action plugins
|
diff_peek=dict(default=None), # Internal use only, for internal checks in the action plugins
|
||||||
validate=dict(required=False, default=None), # Internal use only, for template and copy
|
validate=dict(required=False, default=None), # Internal use only, for template and copy
|
||||||
src = dict(required=False, default=None),
|
src=dict(required=False, default=None, type='path'),
|
||||||
),
|
),
|
||||||
add_file_common_args=True,
|
add_file_common_args=True,
|
||||||
supports_check_mode=True
|
supports_check_mode=True
|
||||||
|
@ -173,26 +180,27 @@ def main():
|
||||||
force = params['force']
|
force = params['force']
|
||||||
diff_peek = params['diff_peek']
|
diff_peek = params['diff_peek']
|
||||||
src = params['src']
|
src = params['src']
|
||||||
|
b_src = to_bytes(src, errors='surrogate_or_strict')
|
||||||
follow = params['follow']
|
follow = params['follow']
|
||||||
|
|
||||||
# modify source as we later reload and pass, specially relevant when used by other modules.
|
# modify source as we later reload and pass, specially relevant when used by other modules.
|
||||||
params['path'] = path = os.path.expanduser(params['path'])
|
path = params['path']
|
||||||
|
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||||
|
|
||||||
# short-circuit for diff_peek
|
# short-circuit for diff_peek
|
||||||
if diff_peek is not None:
|
if diff_peek is not None:
|
||||||
appears_binary = False
|
appears_binary = False
|
||||||
try:
|
try:
|
||||||
f = open(path)
|
f = open(b_path, 'rb')
|
||||||
b = f.read(8192)
|
head = f.read(8192)
|
||||||
f.close()
|
f.close()
|
||||||
if "\x00" in b:
|
if b("\x00") in head:
|
||||||
appears_binary = True
|
appears_binary = True
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
module.exit_json(path=path, changed=False, appears_binary=appears_binary)
|
module.exit_json(path=path, changed=False, appears_binary=appears_binary)
|
||||||
|
|
||||||
prev_state = get_state(path)
|
prev_state = get_state(b_path)
|
||||||
|
|
||||||
|
|
||||||
# state should default to file, but since that creates many conflicts,
|
# state should default to file, but since that creates many conflicts,
|
||||||
# default to 'current' when it exists.
|
# default to 'current' when it exists.
|
||||||
|
@ -204,18 +212,16 @@ def main():
|
||||||
|
|
||||||
# source is both the source of a symlink or an informational passing of the src for a template module
|
# source is both the source of a symlink or an informational passing of the src for a template module
|
||||||
# or copy module, even if this module never uses it, it is needed to key off some things
|
# or copy module, even if this module never uses it, it is needed to key off some things
|
||||||
if src is not None:
|
if src is None:
|
||||||
src = os.path.expanduser(src)
|
if state in ('link', 'hard'):
|
||||||
else:
|
|
||||||
if state in ['link','hard']:
|
|
||||||
if follow and state == 'link':
|
if follow and state == 'link':
|
||||||
# use the current target of the link as the source
|
# use the current target of the link as the source
|
||||||
src = os.path.realpath(path)
|
src = to_native(os.path.realpath(b_path), errors='strict')
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='src and dest are required for creating links')
|
module.fail_json(msg='src and dest are required for creating links')
|
||||||
|
|
||||||
# original_basename is used by other modules that depend on file.
|
# original_basename is used by other modules that depend on file.
|
||||||
if os.path.isdir(path) and state not in ["link", "absent"]:
|
if os.path.isdir(b_path) and state not in ("link", "absent"):
|
||||||
basename = None
|
basename = None
|
||||||
if params['original_basename']:
|
if params['original_basename']:
|
||||||
basename = params['original_basename']
|
basename = params['original_basename']
|
||||||
|
@ -223,6 +229,7 @@ def main():
|
||||||
basename = os.path.basename(src)
|
basename = os.path.basename(src)
|
||||||
if basename:
|
if basename:
|
||||||
params['path'] = path = os.path.join(path, basename)
|
params['path'] = path = os.path.join(path, basename)
|
||||||
|
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||||
|
|
||||||
# make sure the target path is a directory when we're doing a recursive operation
|
# make sure the target path is a directory when we're doing a recursive operation
|
||||||
recurse = params['recurse']
|
recurse = params['recurse']
|
||||||
|
@ -232,11 +239,8 @@ def main():
|
||||||
file_args = module.load_file_common_arguments(params)
|
file_args = module.load_file_common_arguments(params)
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
diff = {'before':
|
diff = {'before': {'path': path},
|
||||||
{'path': path}
|
'after': {'path': path},
|
||||||
,
|
|
||||||
'after':
|
|
||||||
{'path': path}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state_change = False
|
state_change = False
|
||||||
|
@ -250,13 +254,13 @@ def main():
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
if prev_state == 'directory':
|
if prev_state == 'directory':
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(path, ignore_errors=False)
|
shutil.rmtree(b_path, ignore_errors=False)
|
||||||
except Exception:
|
except Exception:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
module.fail_json(msg="rmtree failed: %s" % str(e))
|
module.fail_json(msg="rmtree failed: %s" % str(e))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(b_path)
|
||||||
except Exception:
|
except Exception:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
module.fail_json(path=path, msg="unlinking failed: %s " % str(e))
|
module.fail_json(path=path, msg="unlinking failed: %s " % str(e))
|
||||||
|
@ -269,11 +273,12 @@ def main():
|
||||||
if state_change:
|
if state_change:
|
||||||
if follow and prev_state == 'link':
|
if follow and prev_state == 'link':
|
||||||
# follow symlink and operate on original
|
# follow symlink and operate on original
|
||||||
path = os.path.realpath(path)
|
b_path = os.path.realpath(b_path)
|
||||||
prev_state = get_state(path)
|
path = to_native(b_path, errors='strict')
|
||||||
|
prev_state = get_state(b_path)
|
||||||
file_args['path'] = path
|
file_args['path'] = path
|
||||||
|
|
||||||
if prev_state not in ['file','hard']:
|
if prev_state not in ('file', 'hard'):
|
||||||
# file is not absent and any other state is a conflict
|
# file is not absent and any other state is a conflict
|
||||||
module.fail_json(path=path, msg='file (%s) is %s, cannot continue' % (path, prev_state))
|
module.fail_json(path=path, msg='file (%s) is %s, cannot continue' % (path, prev_state))
|
||||||
|
|
||||||
|
@ -282,8 +287,9 @@ def main():
|
||||||
|
|
||||||
elif state == 'directory':
|
elif state == 'directory':
|
||||||
if follow and prev_state == 'link':
|
if follow and prev_state == 'link':
|
||||||
path = os.path.realpath(path)
|
b_path = os.path.realpath(b_path)
|
||||||
prev_state = get_state(path)
|
path = to_native(b_path, errors='strict')
|
||||||
|
prev_state = get_state(b_path)
|
||||||
|
|
||||||
if prev_state == 'absent':
|
if prev_state == 'absent':
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
|
@ -301,14 +307,15 @@ def main():
|
||||||
# Remove leading slash if we're creating a relative path
|
# Remove leading slash if we're creating a relative path
|
||||||
if not os.path.isabs(path):
|
if not os.path.isabs(path):
|
||||||
curpath = curpath.lstrip('/')
|
curpath = curpath.lstrip('/')
|
||||||
if not os.path.exists(curpath):
|
b_curpath = to_bytes(curpath, errors='surrogate_or_strict')
|
||||||
|
if not os.path.exists(b_curpath):
|
||||||
try:
|
try:
|
||||||
os.mkdir(curpath)
|
os.mkdir(b_curpath)
|
||||||
except OSError:
|
except OSError:
|
||||||
ex = get_exception()
|
ex = get_exception()
|
||||||
# Possibly something else created the dir since the os.path.exists
|
# Possibly something else created the dir since the os.path.exists
|
||||||
# check above. As long as it's a dir, we don't need to error out.
|
# check above. As long as it's a dir, we don't need to error out.
|
||||||
if not (ex.errno == errno.EEXIST and os.path.isdir(curpath)):
|
if not (ex.errno == errno.EEXIST and os.path.isdir(b_curpath)):
|
||||||
raise
|
raise
|
||||||
tmp_file_args = file_args.copy()
|
tmp_file_args = file_args.copy()
|
||||||
tmp_file_args['path'] = curpath
|
tmp_file_args['path'] = curpath
|
||||||
|
@ -324,45 +331,47 @@ def main():
|
||||||
changed = module.set_fs_attributes_if_different(file_args, changed, diff)
|
changed = module.set_fs_attributes_if_different(file_args, changed, diff)
|
||||||
|
|
||||||
if recurse:
|
if recurse:
|
||||||
changed |= recursive_set_attributes(module, file_args['path'], follow, file_args)
|
changed |= recursive_set_attributes(module, to_bytes(file_args['path'], errors='surrogate_or_strict'), follow, file_args)
|
||||||
|
|
||||||
module.exit_json(path=path, changed=changed, diff=diff)
|
module.exit_json(path=path, changed=changed, diff=diff)
|
||||||
|
|
||||||
elif state in ['link','hard']:
|
elif state in ('link', 'hard'):
|
||||||
|
|
||||||
if os.path.isdir(path) and not os.path.islink(path):
|
if os.path.isdir(b_path) and not os.path.islink(b_path):
|
||||||
relpath = path
|
relpath = path
|
||||||
else:
|
else:
|
||||||
relpath = os.path.dirname(path)
|
b_relpath = os.path.dirname(b_path)
|
||||||
|
relpath = to_native(b_relpath, errors='strict')
|
||||||
|
|
||||||
absrc = os.path.join(relpath, src)
|
absrc = os.path.join(relpath, src)
|
||||||
if not os.path.exists(absrc) and not force:
|
b_absrc = to_bytes(absrc, errors='surrogate_or_strict')
|
||||||
|
if not os.path.exists(b_absrc) and not force:
|
||||||
module.fail_json(path=path, src=src, msg='src file does not exist, use "force=yes" if you really want to create the link: %s' % absrc)
|
module.fail_json(path=path, src=src, msg='src file does not exist, use "force=yes" if you really want to create the link: %s' % absrc)
|
||||||
|
|
||||||
if state == 'hard':
|
if state == 'hard':
|
||||||
if not os.path.isabs(src):
|
if not os.path.isabs(b_src):
|
||||||
module.fail_json(msg="absolute paths are required")
|
module.fail_json(msg="absolute paths are required")
|
||||||
elif prev_state == 'directory':
|
elif prev_state == 'directory':
|
||||||
if not force:
|
if not force:
|
||||||
module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, path))
|
module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, path))
|
||||||
elif len(os.listdir(path)) > 0:
|
elif len(os.listdir(b_path)) > 0:
|
||||||
# refuse to replace a directory that has files in it
|
# refuse to replace a directory that has files in it
|
||||||
module.fail_json(path=path, msg='the directory %s is not empty, refusing to convert it' % path)
|
module.fail_json(path=path, msg='the directory %s is not empty, refusing to convert it' % path)
|
||||||
elif prev_state in ['file', 'hard'] and not force:
|
elif prev_state in ('file', 'hard') and not force:
|
||||||
module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, path))
|
module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, path))
|
||||||
|
|
||||||
if prev_state == 'absent':
|
if prev_state == 'absent':
|
||||||
changed = True
|
changed = True
|
||||||
elif prev_state == 'link':
|
elif prev_state == 'link':
|
||||||
old_src = os.readlink(path)
|
b_old_src = os.readlink(b_path)
|
||||||
if old_src != src:
|
if b_old_src != b_src:
|
||||||
changed = True
|
changed = True
|
||||||
elif prev_state == 'hard':
|
elif prev_state == 'hard':
|
||||||
if not (state == 'hard' and os.stat(path).st_ino == os.stat(src).st_ino):
|
if not (state == 'hard' and os.stat(b_path).st_ino == os.stat(b_src).st_ino):
|
||||||
changed = True
|
changed = True
|
||||||
if not force:
|
if not force:
|
||||||
module.fail_json(dest=path, src=src, msg='Cannot link, different hard link exists at destination')
|
module.fail_json(dest=path, src=src, msg='Cannot link, different hard link exists at destination')
|
||||||
elif prev_state in ['file', 'directory']:
|
elif prev_state in ('file', 'directory'):
|
||||||
changed = True
|
changed = True
|
||||||
if not force:
|
if not force:
|
||||||
module.fail_json(dest=path, src=src, msg='Cannot link, %s exists at destination' % prev_state)
|
module.fail_json(dest=path, src=src, msg='Cannot link, %s exists at destination' % prev_state)
|
||||||
|
@ -372,31 +381,33 @@ def main():
|
||||||
if changed and not module.check_mode:
|
if changed and not module.check_mode:
|
||||||
if prev_state != 'absent':
|
if prev_state != 'absent':
|
||||||
# try to replace atomically
|
# try to replace atomically
|
||||||
tmppath = '/'.join([os.path.dirname(path), ".%s.%s.tmp" % (os.getpid(),time.time())])
|
b_tmppath = to_bytes(os.path.sep).join(
|
||||||
|
[os.path.dirname(b_path), to_bytes(".%s.%s.tmp" % (os.getpid(), time.time()))]
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
if prev_state == 'directory' and (state == 'hard' or state == 'link'):
|
if prev_state == 'directory' and (state == 'hard' or state == 'link'):
|
||||||
os.rmdir(path)
|
os.rmdir(b_path)
|
||||||
if state == 'hard':
|
if state == 'hard':
|
||||||
os.link(src,tmppath)
|
os.link(b_src, b_tmppath)
|
||||||
else:
|
else:
|
||||||
os.symlink(src, tmppath)
|
os.symlink(b_src, b_tmppath)
|
||||||
os.rename(tmppath, path)
|
os.rename(b_tmppath, b_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
if os.path.exists(tmppath):
|
if os.path.exists(b_tmppath):
|
||||||
os.unlink(tmppath)
|
os.unlink(b_tmppath)
|
||||||
module.fail_json(path=path, msg='Error while replacing: %s' % str(e))
|
module.fail_json(path=path, msg='Error while replacing: %s' % to_native(e, nonstring='simplerepr'))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if state == 'hard':
|
if state == 'hard':
|
||||||
os.link(src,path)
|
os.link(b_src, b_path)
|
||||||
else:
|
else:
|
||||||
os.symlink(src, path)
|
os.symlink(b_src, b_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
module.fail_json(path=path, msg='Error while linking: %s' % str(e))
|
module.fail_json(path=path, msg='Error while linking: %s' % to_native(e, nonstring='simplerepr'))
|
||||||
|
|
||||||
if module.check_mode and not os.path.exists(path):
|
if module.check_mode and not os.path.exists(b_path):
|
||||||
module.exit_json(dest=path, src=src, changed=changed, diff=diff)
|
module.exit_json(dest=path, src=src, changed=changed, diff=diff)
|
||||||
|
|
||||||
changed = module.set_fs_attributes_if_different(file_args, changed, diff)
|
changed = module.set_fs_attributes_if_different(file_args, changed, diff)
|
||||||
|
@ -407,16 +418,16 @@ def main():
|
||||||
|
|
||||||
if prev_state == 'absent':
|
if prev_state == 'absent':
|
||||||
try:
|
try:
|
||||||
open(path, 'w').close()
|
open(b_path, 'wb').close()
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
module.fail_json(path=path, msg='Error, could not touch target: %s' % str(e))
|
module.fail_json(path=path, msg='Error, could not touch target: %s' % to_native(e, nonstring='simplerepr'))
|
||||||
elif prev_state in ['file', 'directory', 'hard']:
|
elif prev_state in ('file', 'directory', 'hard'):
|
||||||
try:
|
try:
|
||||||
os.utime(path, None)
|
os.utime(b_path, None)
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
module.fail_json(path=path, msg='Error while touching existing target: %s' % str(e))
|
module.fail_json(path=path, msg='Error while touching existing target: %s' % to_native(e, nonstring='simplerepr'))
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='Cannot touch other than files, directories, and hardlinks (%s is %s)' % (path, prev_state))
|
module.fail_json(msg='Cannot touch other than files, directories, and hardlinks (%s is %s)' % (path, prev_state))
|
||||||
try:
|
try:
|
||||||
|
@ -428,15 +439,12 @@ def main():
|
||||||
# somewhere in basic.py
|
# somewhere in basic.py
|
||||||
if prev_state == 'absent':
|
if prev_state == 'absent':
|
||||||
# If we just created the file we can safely remove it
|
# If we just created the file we can safely remove it
|
||||||
os.remove(path)
|
os.remove(b_path)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
module.exit_json(dest=path, changed=True, diff=diff)
|
module.exit_json(dest=path, changed=True, diff=diff)
|
||||||
|
|
||||||
module.fail_json(path=path, msg='unexpected position reached')
|
module.fail_json(path=path, msg='unexpected position reached')
|
||||||
|
|
||||||
# import module snippets
|
|
||||||
from ansible.module_utils.basic import *
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -328,6 +328,7 @@ import stat
|
||||||
# import module snippets
|
# import module snippets
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.pycompat24 import get_exception
|
from ansible.module_utils.pycompat24 import get_exception
|
||||||
|
from ansible.module_utils._text import to_bytes
|
||||||
|
|
||||||
|
|
||||||
def format_output(module, path, st, follow, get_md5, get_checksum,
|
def format_output(module, path, st, follow, get_md5, get_checksum,
|
||||||
|
@ -417,6 +418,7 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
path = module.params.get('path')
|
path = module.params.get('path')
|
||||||
|
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||||
follow = module.params.get('follow')
|
follow = module.params.get('follow')
|
||||||
get_mime = module.params.get('mime')
|
get_mime = module.params.get('mime')
|
||||||
get_md5 = module.params.get('get_md5')
|
get_md5 = module.params.get('get_md5')
|
||||||
|
@ -425,9 +427,9 @@ def main():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if follow:
|
if follow:
|
||||||
st = os.stat(path)
|
st = os.stat(b_path)
|
||||||
else:
|
else:
|
||||||
st = os.lstat(path)
|
st = os.lstat(b_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
|
|
Loading…
Reference in a new issue