Open
Description
Summary
When i read code in lib/ansible/plugins/action/copy.py, i find a bug in it:
class ActionModule(ActionBase):
TRANSFERS_FILES = True
# ...
def run(self, tmp=None, task_vars=None):
''' handler for file transfer operations '''
# ...
implicit_directories = set()
for source_full, source_rel in source_files['files']:
# copy files over. This happens first as directories that have
# a file do not need to be created later
# We only follow symlinks for files in the non-recursive case
if source_files['directories']:
follow = False
else:
follow = boolean(self._task.args.get('follow', False), strict=False)
module_return = self._copy_file(source_full, source_rel, content, content_tempfile, dest, task_vars, follow)
if module_return is None:
continue
if module_return.get('failed'):
result.update(module_return)
return self._ensure_invocation(result)
###################bug here !!!!###################
paths = os.path.split(source_rel)
dir_path = ''
for dir_component in paths:
os.path.join(dir_path, dir_component)
implicit_directories.add(dir_path)
##############################################
if 'diff' in result and not result['diff']:
del result['diff']
module_executed = True
changed = changed or module_return.get('changed', False)
# ...
look at example code as follow:
>>> import os
>>> dir_path = ''
>>> os.path.join(dir_path, 'xxxxx')
'xxxxx'
>>> dir_path
''
>>>
dir_path always be empty string and thus implicit_directories always be a set with one empty data!!!!
Issue Type
Bug Report
Component Name
copy
Ansible Version
$ ansible --version
ansible [core 2.17.6]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /opt/python3.12/lib/python3.12/site-packages/ansible_core-2.17.6-py3.12.egg/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /opt/python3.12/bin/ansible
python version = 3.12.4 (main, Nov 13 2024, 10:46:13) [GCC 10.2.1 20200825 (Alibaba 10.2.1-3.8 2.32)] (/opt/python3.12/bin/python3)
jinja version = 3.1.4
libyaml = True
Configuration
# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
CONFIG_FILE() = /etc/ansible/ansible.cfg
OS / Environment
Alibaba Cloud Linux release 3 (Soaring Falcon
Steps to Reproduce
- create directories:
[store@spyinx ~]$ cat create_dirs.sh
#! /bin/bash
for i in `seq 1 100`
do
mkdir test-create-$i
cd test-create-$i
done
touch test.txt
[store@spyinx ~]$ sh create_dirs.sh
- run ansible
[store@spyinx ~]$ rm -rf dest
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest"
spyinx | CHANGED => {
"changed": true,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m34.394s
user 0m2.923s
sys 0m1.169s
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest" # 再次执行
spyinx | SUCCESS => {
"changed": false,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m36.739s
user 0m3.141s
sys 0m1.301s
because it will use file module to create all the directories, then it is slow !!!
Expected Results
i change the code:
[root@spyinx ansible]# git diff lib/ansible/plugins/action/copy.py
diff --git a/lib/ansible/plugins/action/copy.py b/lib/ansible/plugins/action/copy.py
index 2047671b47..f3abf22985 100644
--- a/lib/ansible/plugins/action/copy.py
+++ b/lib/ansible/plugins/action/copy.py
@@ -525,11 +525,13 @@ class ActionModule(ActionBase):
result.update(module_return)
return self._ensure_invocation(result)
- paths = os.path.split(source_rel)
+ paths = source_rel.split(os.path.sep)
dir_path = ''
- for dir_component in paths:
- os.path.join(dir_path, dir_component)
+ # skip last file name
+ for dir_component in paths[:-1]:
+ dir_path = os.path.join(dir_path, dir_component)
implicit_directories.add(dir_path)
+
if 'diff' in result and not result['diff']:
del result['diff']
module_executed = True
then run it again:
[store@spyinx ~]$ rm -rf dest
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest"
spyinx | CHANGED => {
"changed": true,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m4.098s
user 0m1.167s
sys 0m0.224s
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest" # 再次执行
spyinx | SUCCESS => {
"changed": false,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m3.710s
user 0m0.993s
sys 0m0.216s
only leaf directories will be created by file module, the others have already been created when using copy module to transfer file
Actual Results
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest"
spyinx | CHANGED => {
"changed": true,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m34.394s
user 0m2.923s
sys 0m1.169s
[store@spyinx ~]$ time ansible spyinx -i hosts -m copy -a "src=/home/store/test-create-1 dest=dest" # 再次执行
spyinx | SUCCESS => {
"changed": false,
"dest": "dest/",
"src": "/home/store/test-create-1"
}
real 0m36.739s
user 0m3.141s
sys 0m1.301s
Code of Conduct
- I agree to follow the Ansible Code of Conduct