'How to copy files that match a glob pattern with Ansible
I have the following local set of files:
$ find . -maxdepth 2
.
./.DS_Store
./radio
./radio/server.properties
./radio/recordings/
./radio/requirements.txt
./radio/__pycache__
./radio/radio.iml
./radio/radio.py
./radio/audio.py
./ansibleplaybook.yml
./inventory
I would like to copy only the .py
files such that my remote server looks like this:
$ find ~/radio/
/home/pi/radio/
/home/pi/radio/radio.py
/home/pi/radio/audio.py
I have used examples found here and in the Ansible documentation to come up with this playbook:
---
- hosts: all
tasks:
- name: Copy radio source
copy:
src: "radio"
dest: "~/radio"
with_fileglob:
- "*.py"
However, run I run this no directory is created and nothing is copied.
$ ansible-playbook -i inventory ansibleplaybook.yml -u pi
PLAY [pi] **************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************
ok: [pi]
TASK [Copy radio source] **********************************************************************************************************************************************
PLAY RECAP ************************************************************************************************************************************************************
pi : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
If I specify the .py files individually then the radio directory is created and the .py file is copied. I know I only have two .py files now, but having to update my playbook every time I add a new python module will obviously start to get very tedious. How can I get the above playbook to work so that all the files that match the pattern are copied?
Solution 1:[1]
I think part of your confusion is that with_fileglob
doesn't know anything about the parameters to the copy
module, so it doesn't know you want files relative to the radio
directory. Consider, for example, what happens if you run this shell command from your home directory:
ls *.py
It won't find those *.py
files, either, because they're inside the
radio
directory.
Because with_fileglob
returns no results, the task never runs.
There are a number of ways of tackling this problem. Note that in the following examples I'm using a destination of /tmp/target
to avoid cluttering up my home directory with test files.
Recursive copy
If you remove the with_fileglob
loop, then Ansible will recursively copy the contents of the local radio
directory to ~/radio
on the target host. This will copy all the files, not just the Python sources.
Use find
module to generate a list of files
If you really want only the Python sources, you can use the find
module to generate a list of matching files, and then use that as input to the copy
module. In this case, you will need to take care of creating target directories yourself, because copy
won't do that for you.
Something like this:
- hosts: localhost
gather_facts: false
tasks:
- find:
paths: radio
patterns: "*.py"
register: python_files
- name: Create target directories
file:
path: "/tmp/target/{{ item.path|dirname }}"
state: directory
loop: "{{ python_files.files }}"
loop_control:
label: "{{ item.path }}"
- name: Copy files
copy:
src: "{{ item.path }}"
dest: "/tmp/target/{{ item.path }}"
loop: "{{ python_files.files }}"
loop_control:
label: "{{ item.path }}"
Use the synchronize
module
You could use the synchronize
module to copy the files instead. synchronize
is a wrapper around rsync
so in many ways it's more appropriate to the task at hand. That might look like this:
- hosts: localhost
gather_facts: false
tasks:
- synchronize:
src: radio/
dest: /tmp/target/
rsync_opts:
- '--include=*.py'
- '--include=*/'
- '--exclude=*'
See the rsync
man page for more information about the --include
and --exclude
options.
Solution 2:[2]
What you need is actually this:
---
- hosts: all
tasks:
- name: Copy radio source
copy:
src: "{{ item }}"
dest: "~/radio/"
with_fileglob:
- "radio/*.py"
Note the trailing slash "/" on the dest to ensure writing to a directory.
And as larsks says the "with" part do not know anything about the parameters to Copy. Actually the with_fileglob is used to create the parameters to copy, thus the "{{ item }}"
as src that reffers to the items provided by the "with" part, you can almost look at it as a loop over the task.
Ref: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fileglob_lookup.html
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | larsks |
Solution 2 | Samuel Åslund |