How can I take filename from a specific directory and use it as a variable?
Then I'll use this variable to copy a new file with variable filename
For example: In the directory /home/user1/test/ there is always only one file named test1 or some other name.
I need to extract the filename (test1) from this path to some variable
- hosts: linux
become: yes
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ files_matched.files }}"
- name: "Save find results to file"
copy:
content: "{{ files_matched.files }}"
dest: "/tmp/find_result.txt"
Then I've should get "test1" as variable and use it in the new filename in this code:
copy:
src: /home/myuser/myfile
dest: "{{ item.dest }}"
owner: root
group: root
mode: 0666
with_items:
- { dest: '/home/user2/test/{{ files_matched }}' }
As result of first script i've got:
: 0, "ischr": false, "wusr": true, "xoth": false, "islnk": false, "nlink": 1, "issock": false, "rgrp": true, "gr_name": "root", "path": "/home/user1/test/test1", "xusr":
false, "atime": 1564553299.6092095, "isdir": false, "ctime": 1564553304.7172158, "isblk": false, "xgrp": false, "dev": 2050, "wgrp": false, "isfifo": false, "mode": "0644
", "rusr": true}]
But i need only test1 part as result, not this all.
Thank you!
Q: "How can I take filename from a specific directory and use it as a variable?'
A: Given the tree
$ tree /home/user1/test/
/home/user1/test/
├── test1
├── test2
└── test3
the tasks below
- find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ '/home/user2/test/' ~ item|basename }}"
loop: "{{ files_matched.files|json_query('[*].path') }}
give
"msg": "/home/user2/test/test1"
"msg": "/home/user2/test/test3"
"msg": "/home/user2/test/test2"
I hope you are expecting something like this. this will save the list of files under the specified directory into the /tmp/find_result.txt. I hope from here you can proceed.
---
- name: find file
hosts: linux
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- name: "Save find results to file"
lineinfile:
line: "{{ item.path }}"
path: "/tmp/find_result.txt"
create: yes
loop: "{{ files_matched.files | flatten }}"
note: make sure that you delete the /tmp/find_result.txt once your task completed.
if you want to store only the filename not the entire path, then replace line: "{{ item.path }}" with line: "{{ item.path | basename }}"
Related
I'm trying to get an output to a file using a Jinja2 template. However the last task 'Write to the file' fails. The idea is to create a dynamic folder based on m-d-yyyy--Hr:Min as the task is running on the same set of devices. Therefore they will be saved to folders based on date and time. The folder gets created. But the file is not. Also if I go to a static folder and use it in a "dest" it also works. Can someone please provide a direction? Thank you in advance.
- name: Create directory
file:
path: ./config-backups/{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}
mode: 0755
recurse: yes
register: folder_name_with_date
- name: Write to the file
ansible.builtin.template:
src: ./templates/show_run.j2
dest: ./{{folder_name_with_date}}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt
newline_sequence: '\r\n'
Error
fatal: [10.6.66.66]: FAILED! => {"changed": false, "checksum": "699bd34165f85658300254daf40e5322cf7faaa", "msg": "Destination directory ./{'path': './config-backups/03-17-2021--09:58', 'changed': True, 'diff': {'before': {'path': './config-backups/03-17-2021--09:58', 'state': 'absent', 'mode': '0775'}, 'after': {'path': './config-backups/03-17-2021--09:58', 'state': 'directory', 'mode': '0755'}}, 'uid': 1000, 'gid': 1000, 'owner': 'xxxx', 'group': 'xxxx', 'mode': '0755', 'state': 'directory', 'size': 4096, 'failed': False} does not exist"}
Q: "Destination directory ./{'path': './config-backups/03-17-2021--09:58' ... does not exist"}"
A: If you want to create a directory it's necessary to set the attribute state=directory. The default state is file. Create the variable with the name of the directory first. Then use it in the playbook. Given the template
shell> cat templates/show_run.j2
This is show_run.j2
the playbook
- hosts: localhost
tasks:
- name: Set name of the directory
set_fact:
folder_name_with_date: "{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}"
- name: Create directory
file:
state: directory
path: ./config-backups/{{ folder_name_with_date }}
mode: 0755
- name: Write to the file
ansible.builtin.template:
src: ./templates/show_run.j2
dest: "./config-backups/{{ folder_name_with_date }}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt"
newline_sequence: '\r\n'
gives
shell> tree config-backups
config-backups
└── 03-17-2021--16:42
└── localhost-running-config-03-17-2021--16:42.txt
shell> cat config-backups/03-17-2021--16\:42/localhost-running-config-03-17-2021--16\:42.txt
This is show_run.j2
First, the variable you are registering as folder_name_with_date is a list of items, not just a string. You need to get just the path variable from your folder_name_with_date list. Call it via folder_name_with_date.path to get the path you are looking for.
Also, because you are trying to concatenate variables with a string, I would recommend encapsulating your entire statement with "'s, like so: dest: "./{{folder_name_with_date.path}}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt"
Not entirely sure whether adding the "'s or not is necessary, but be sure to get the folder_name_with_date's .path instead.
EDIT: For the future, I would recommend outputting what your variables look like after you create them, so you can see how to use them in future tasks. Technically, Ansible will output what the variable is when it is registered, but I also like to include a quick debug like this to view my variables:
name: what is my variables structure??
debug:
msg: "{{ folder_name_with_date }}"
Debug documentation: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html
Hej *,
I'm writing a playbook which should set some disk attributes in the Windows registry.
All disk attributes are stored under a specific vendor folder. There is a value under each disk folder that I want to change in the end.
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK
|- 00000
|- 00001
- Disk&Ven_VMware&Prod_Virtual_disk
|- 00000
I'm able to determine the vendor folder as well as the disk folders under the root of HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI:
- hosts: some_host
tasks:
- name: Get vendor folders
win_reg_stat:
path: HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI
register: vendors
- name: Get disk folders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item }}"
with_items:
- "{{ vendors.sub_keys }}"
register: disks
- debug:
var: disks.results
The output looks like this:
ok: [some_host] => {
"disks.results": [
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "CdRom&Ven_QEMU&Prod_QEMU_DVD-ROM",
"properties": {},
"sub_keys": [
"000001"
]
},
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "Disk&Ven_QEMU&Prod_QEMU_HARDDISK",
"properties": {},
"sub_keys": [
"000000",
"000001"
]
},
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "Disk&Ven_VMware&Prod_Virtual_disk",
"properties": {},
"sub_keys": [
"000000"
]
}
]
}
I'm a bit stuck right now. I'm trying to get a dictionary or list for iterating through all those disk folders. Something like that:
Disk&Ven_QEMU&Prod_QEMU_HARDDISK: 000000
Disk&Ven_QEMU&Prod_QEMU_HARDDISK: 000001
Disk&Ven_VMware&Prod_Virtual_disk: 000000
Is there a way to accomplish that?
Thanks and regards
Erik
For example
- set_fact:
my_list: "{{ my_list|default([]) + [{item.0.item:item.1}] }}"
with_subelements:
- "{{ disks.results }}"
- sub_keys
gives
my_list:
- CdRom&Ven_QEMU&Prod_QEMU_DVD-ROM: '000001'
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK: '000000'
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK: '000001'
- Disk&Ven_VMware&Prod_Virtual_disk: '000000'
It's not possible to create a dictionary because, in a dictionary, the keys must be unique.
I want to share my final solution with you. Thanks to Vladimir for the help.
- name: Get vendors
win_reg_stat:
path: HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI
register: os_vendors
- name: Get vendors subfolders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item }}"
loop: "{{ os_vendors.sub_keys }}"
register: os_subfolders
- name: Build registry pathes with vendors and disks
set_fact:
os_regpath_dict: "{{ os_regpath_dict | default({}) | combine ({item.item : item.sub_keys })}}"
loop: "{{ os_subfolders.results }}"
- name: Get disk folders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item.0.key }}\\{{ item.1 }}\\Device Parameters\\Partmgr"
name: "Attributes"
loop: "{{ os_regpath_dict | dict2items | subelements('value') }}"
when: "'Disk' in item.0.key"
register: os_disks
- name: Set value of entry Attributes to 0
win_regedit:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item.item.0.key }}\\{{ item.item.1 }}\\Device Parameters\\Partmgr"
name: Attributes
data: 0
type: dword
loop: "{{ os_disks.results }}"
when: item.value is defined
I would like to delete file in some sub directory which contains certain format. However, I am getting the error
'dict object' has no attribute 'files'.
Below is my code. The file pattern would be file_name.file_extension.processID.YYYY-MM-DD#HH:MM:SS~
My variables
fileToFindInAllSubDirecotry
- "home/usr/file1"
- "home/usr/file2"
- "home/usr/file3/file4"
- "home/usr/file5"
My playbook role
- name: Find file
find:
paths: "{{ item }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
loop: "{{ fileToFindInAllSubDirecotry }}"
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete.files }}"
This is the sample file and directory
home
|-------usr
|-------file1
|-------configFile.xml
|-------file2
|-------propertiesFile.txt.2012.2020-07-13#23:08:10~
|-------file3
|-------file4
|-------content.yml.2012.2020-04-04#23:08:10~
|-------file5
|-------configFile.xml.2012.2020-03-05#13:08:10~
This is happening because you are populating the find with a loop, so you end up with a result that would be a dictionary having a list or results.
Something like:
ok: [localhost] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
...
"files": [ ... ],
...
"item": "/home/usr/file1",
...
},
{
...
"files": [ ... ],
...
"item": "/home/usr/file2",
...
},
...
]
}
}
There is two ways to fix this:
The nicest one, because, as pointed by the documentation, the paths parameter of the module find can accept lists of paths, just pass it your whole fileToFindInAllSubDirecotry variables instead of using a loop, this way your deletion works as is:
- name: Find file
find:
paths: "{{ fileToFindInAllSubDirecotry }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete.files }}"
Use json_query to fetch the result[*].files then flatten the resulting list of list
- name: Find file
find:
paths: "{{ item }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
loop: "{{ fileToFindInAllSubDirecotry }}"
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete | json_query('results[*].files') | flatten }}"
Hello guys I have a Problem.
The Problem I am having at the moment, is that the role to copy the files will skip all the files no matter if the file with the filenames is empty or not.
In Role1 I want to save the output of cat for each file. In Role2 in the when conditional, I want the task to skip if the registered output is == "".
Role1:
---
- name: copy files
shell: "cat path{{ item }}files"
register: checkempty
loop:
- test1
- test2
- test3
- test4
Role2:
---
- name: Copy Files
copy:
src: "{{ var1 }}{{ var2 }}{{ var3 }}{{ var4 }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ lookup('file', 'pathtofile/file').split('\n')}}"
when: hostvars['localhost'].checkempty.results == ""
Playbook:
- name: check emptiness
hosts: localhost
become: yes
vars_files:
- ../variables/varsfile
roles:
- ../variables/role1
- name: Copy Files to prod/stag
hosts: "{{hosts_exec}}"
become: yes
vars_files:
- ../vars/recommendation-delta.yml
roles:
- ../roles/role2
How can I set a registered variable with with_items and compare the output of it to ""(nothing)?
Can somebody help me with this issue?
Problem of copying whole dir is occurring as {{item}} is Empty in case of your file holding Filename is empty. It is considering src as
src: "{{ git_dest }}{{ git_recoprop_files }}"
in spite of
src: "{{ git_dest }}{{ git_recoprop_files }}{{ item }}/"
because {{item}} is Empty. I am not sure if it is compulsory for you to use first role to check if file is empty or not. if is not compulsory then you can update your 2nd role to
when: item != ""
In addition to that -
checkempty.results == ""
is also wrong. no matter if file is empty or not, this will be having an array of dictionaries for result of each loop item. And dictionaries are having kay value pair of a lot of things like item, stdout etc.
Sample :-
{
"_ansible_ignore_errors": null,
"_ansible_item_label": "inventory1",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": "cat /home/rohit/ansible/inventory1",
"delta": "0:00:00.004505",
"end": "2019-04-21 21:13:55.042776",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "cat /home/rohit/ansible/inventory1",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "inventory1",
"rc": 0,
"start": "2019-04-21 21:13:55.038271",
"stderr": "",
"stderr_lines": [],
"stdout": "inventory\nplaybook.yml",
"stdout_lines": [
"inventory",
"playbook.yml"
]
}
]
I think the main problem is you need to use stdout instead of results on your checkempty variable like this:
when: hostvars['localhost'].checkempty.stdout != ""
It seems like you have your logic backwards too, I think you want to use != instead of ==
I'd suggest that you refactor a bit and simplify your code by using stdout_lines in your loop like this:
- name: Copy sys Files to prod/stag
copy:
src: "{{ git_dest }}{{ git_sys_files }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ hostvars['localhost'].checkempty.stdout_lines }}"
when: hostvars['localhost'].checkempty.stdout != ""
stdout_lines will give you an array of the lines in the file that you cat'ed and you don't have to use your lookup and split code.
I am trying to use the mv module on Ansible but I am not having luck.
In my initial attempt I did the following:
- name: changing the name of the file
shell: mv /tmp/bundle /opt/Rocket.Chat
And I get the following error:
FAILED! => {"changed": true, "cmd": "mv /tmp/bundle /opt/Rocket.Chat", "delta": "0:00:00.033553", "end": "2019-02-11 06:06:43.273787", "msg": "non-zero return code", "rc": 1, "start": "2019-02-11 06:06:43.240234", "stderr": "mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists", "stderr_lines": ["mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists"], "stdout": "", "stdout_lines": []}
So, I changed it to:
- name: create directory
file:
state: directory
path: "/opt/Rocket.Chat"
- name: copy the files
copy:
src: "/tmp/bundle"
dest: "/opt/Rocket.Chat"
remote_src: yes
- name: delete the other files
file: path=/tmp/bundle state=absent
My new error is:
FAILED! => {"changed": false, "msg": "Remote copy does not support recursive copy of directory: /tmp/bundle"}
Seems that the "copy module to work with recursive and remote_src" does not work yet, but will be supported from May 2019
Here is a workaround, edit the folder names to your setup.
# Copy all files and directories from /usr/share/easy-rsa to /etc/easy-rsa
- name: List files in /usr/share/easy-rsa
find:
path: /usr/share/easy-rsa
recurse: yes
file_type: any
register: find_result
- name: Create the directories
file:
path: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
state: directory
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir
- name: Copy the files
copy:
src: "{{ item.path }}"
dest: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
remote_src: yes
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir == False