I have a file with last modified time shown as "1563886751.38" (with stat and mtime). I wanted to check if the time is same when I run the playbook.
I have defined a variable as file_time: 1563872351.
I wanted to check if this value is present in the mtime.
vars:
file_date: 1563872351
- name: test
stat:
path: /tmp/logrotate/testx1
register: sym
- debug:
msg: "{{same}}"
when: sym.stat.mtime.find(file_date) != -1
But I get the below error:
'dict object' has no attribute 'find'
I see there is not attribute find but is there some way to do this? I need to check if it contains the value, not equal to.
This can be achieved by converting both values to strings and using the in operator:
- debug:
msg: same
when: file_date|string in sym.stat.mtime|string
I don't know exactly what you would use that for, but perhaps this would be a better approach for you:
- debug:
msg: same
when: sym.stat.mtime|int == file_date
Related
Here is the problem I have. I am running the following playbook
- name: Check for RSA-Key existence
stat:
path: /opt/cert/{{item.username}}.key
with_items: "{{roles}}"
register: rsa
- name: debug
debug:
var: item.stat.exists
loop: "{{rsa.results}}"
- name: Generate RSA-Key
community.crypto.openssl_privatekey:
path: /opt/cert/{{item.username}}.key
size: 2048
when: item.stat.exists == False
with_items:
- "{{roles}}"
- "{{rsa.results}}"
This is the error I receive:
The error was: error while evaluating conditional (item.stat.exists == False): 'dict object' has no attribute 'stat'
The debug task is not firing any error
"item.stat.exists": true
What am I doing wrong and how can I fix my playbook to make it work?
TL;DR
Replace all your tasks with a single one:
- name: Generate RSA-Key or check they exist
community.crypto.openssl_privatekey:
path: /opt/cert/{{ item.username }}.key
size: 2048
state: present
with_items: "{{ roles }}"
Problem with your last loop in original example
I don't know what you are trying to do exactly when writing the following in your last task:
with_items:
- "{{roles}}"
- "{{rsa.results}}"
What I know is the actual result: you are looping over a single list made of roles elements at the beginning followed by rsa.results elements. Since I am pretty sure no elements in your roles list has a stat.exists entry, the error you get is quite expected.
Once you have looped over an original list (e.g. roles) and registered the result of the tasks (in e.g. rsa), you actually have all the information you need inside that registered var. rsa.results is a list of individual results. In each elements, you will find all the keys returned by the module you ran (e.g. stat) and an item key holding the original element that was used in the loop (i.e. an entry of your original roles list).
I strongly suggest you study this by yourself with most attention by looking at the entire variable to see how it is globally structured:
- name: Show my entire registered var
debug:
var: rsa
Once you have looked at your incoming data, it will become obvious that you should modify your last task as the following (note the item.item referencing the original element from previous loop):
- name: Generate RSA-Key
community.crypto.openssl_privatekey:
path: /opt/cert/{{ item.item.username }}.key
size: 2048
when: not item.stat.exists # Comparing to explicit False is bad use this instead
with_items: "{{ rsa.results }}"
Using stat here is an overkill
To go further, if all the above actually answers your direct question, it does not make much sense in Ansible world. You are doing a bunch of work that Ansible is already doing behind the scene for you.
The community.crypto.openssl_privatekey module create keys idempotently, i.e. it will create the key only if it doesn't exist and report changed or do nothing if the key already exists and report ok. So you can basically reduce all of your 3 tasks example to a single task
- name: Generate RSA-Key or check they exist
community.crypto.openssl_privatekey:
path: /opt/cert/{{ item.username }}.key
size: 2048
state: present # This is not necessary (default) but good practice
with_items: "{{ roles }}"
Consider changing your var name
Last, I'd like to mention that roles is actually a reserved name in Ansible. So defining a var with that name should issue a warning in current ansible version, and will probably be deprecated in some time.
Refs:
registering variables
registering variables with a loop
Based on this document I am trying to check the /etc/hosts file exists. but Ansible giving
The error was: template error while templating string: unexpected '/'
error message
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-paths
- debug:
msg: "host file already exists"
when: /etc/hosts is exists
What is the right way of using this testing condition?
Thanks
The section of the documentation to which you've linked is probably
not what you want -- that is only for testing paths on the
controller (that is, the host on which Ansible is running), rather
than the target host(s) of your play.
If that is what you want, you need to fix the syntax of your when
expression. The examples to which you've linked are all of the form
<variable> is <test>, so you would write something like:
- debug:
msg: "host file already exists"
when: etchosts is exists
vars:
etchosts: "/etc/hosts"
Here we define a string variable named etchosts containing our path.
In the above example we've defined it at the task level, but this
could also be defined at the play level, as a group_ or host_var, via
set_fact, etc.
To test for a file on a target host, use the stat module:
- name: check for /etc/hosts
stat:
path: /etc/hosts
register: etchosts
We register the result in a variable named etchosts so that we can use it in a subsequent task. For example:
- debug:
msg: "/etc/hosts exists"
when: etchosts.stat.exists
- debug:
msg: "/etc/hosts does not exist"
when: not etchosts.stat.exists
I'am trying to automate the creation of the smart_[diskdevice] links to
/usr/share/munin/plugins/smart_
during the installation of the munin node via ansible.
The code here works partially, except there is no diskdevice to link on the target machine. Then I got a fatal failure with
{"msg": "with_dict expects a dict"}
I've review the ansible documentation and tried to search the problem in the web. For my understanding, the whole "file" directive should not be executed if the "when"-statement fails.
---
- name: Install Munin Node
any_errors_fatal: true
block:
...
# drives config
- file:
src: /usr/share/munin/plugins/smart_
dest: /etc/munin/plugins/smart_{{ item.key }}
state: link
with_dict: "{{ ansible_devices }}"
when: "item.value.host.startswith('SATA')"
notify:
- restart munin-node
On targets with a SATA-Drive, the code works. Drives like "sda" are found and the links are created. Loop- and other soft-Devices are ignored (as intended)
Only on a Raspberry with no SATA-Drive at all i got the fatal failure.
You are using the with_dict option to set the loop. This sets the value of the item variable for each iteration as a dictionary with two keys:
key: The name of the current key in the dict.
value: The value of the existing key in the dict.
You are then running the when option that checks the item variable on each iteration. So check if that is the behavior you want.
Regarding your error, it is being thrown because for some reason, ansible_devices is not a dict as the error says. And Ansible checks for the validity of the with_dict type before resolving the when condition.
Check the following example:
---
- name: Diff test
hosts: local
connection: local
gather_facts: no
vars:
dict:
value: True
name: "dict"
tasks:
- debug: var=item
when: dict.value == False
with_dict: '{{ dict }}'
- debug: var=item
when: dict.value == True
with_dict: '{{ dict }}'
- debug: var=item
when: dict.value == False
with_dict: "Not a dict"
The first two task will succeed because they have a valid dict on the with_dict option and a correct condition on the when option. The last one will fail because the with_dict value has the wrong type, even though the when condition resolves correctly and should guarantee to skip the task.
I hope it helps.
I have such a scenario, I need to use two files A.bin, B.bin, first look in the files directory, if not found, download directly from a server
- name: Send A.bin and B.bin
copy: src={{item}}.bin dest=/opt/
register: is_exist
failed_when: False
with_items:
- A
- B
- name: Download from a server A.bin, B.bin
shell: wget -P /opt/{{item.item}} {{base_url}}{{item.item}}.bin
when: item.item.exception is defined
with_items:
- is_exist.results
But this will give an error:
The conditional check 'item.item.exception is defined' failed. The error was: error while evaluating conditional (item.item.exception is defined): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'item'
what should I do?
I'd suggest visually reviewing the structure of is_exist variable.
- debug: msg="{{ is_exist }}"
Then, if item.item.exception is the expected variable name, always check that item.item is defined to avoid has no attribute 'item' before testing its exception key:
when: item.item is defined and item.item.exception is defined
I am trying to list the files in a directory and copy to some other directory.
This is my playbook:
---
- hosts: testserver
become: true
tasks:
- name: list files
command: " ls /root/"
register: r
- debug: var=r
- debug: msg="item.item={{item.item}}, item.stdout={{item.stdout}}, item.changed={{item.changed}}"
with_items: "{{r.results}}"
This is the error I am getting:
FAILED! => {"msg": "'dict object' has no attribute 'results'"}
I am trying to list the files in a directory and copy to some other directory.
Don't parse ls output! Neither in Ansible, nor anywhere else.
Don't use command module for what Ansible offers a native one.
Ansible has a find module which returns a list of files. In your case:
- name: list files
find:
paths: /root
register: my_find
- debug:
var: item.path
with_items: "{{ my_find.files }}"
You have a debug task there that shows you the contents of the variable r. Do you see a key named results?
You only see the results key (and need to use item.item) when are have registered values in a loop, as described here. You're not doing that, so the structure of r will be much simpler.
If you're trying to iterate over the lines of the ls output, you probably want:
- debug:
msg: "filename={{item}}"
with_items: "{{r.stdout_lines}}"