I have a role and I want to restart a service when file changed for an item, so I try to make a variable for the handlers, but on ansible deployment, I get file
ac
- name: Create jinja templating
template:
src: "/var/opt/config.json.j2"
dest: "/var/opt/{{ item }}/config.json"
with_list: "{{ variable }}"
register: template_out
notify:
- my_handler
main/handlers.yml
- name: "Restart {{ item }}"
shell: "service restart {{ item }}"
with_items: "{{ template_out.results | selectattr('changed', 'equalto', true) | list }}"
(item={u'md5sum': u'e48695da5017f1a5558b66eafc1cdccf', u'uid': 0, u'dest': u'config.yml', u'owner': u'root', 'diff': [], u'size': 4272, u'src': u'/root/.ansible/tmp/ansible_mitogen_action_1073ea002b288ef0/source', 'ansible_loop_var': u'item', u'group': u'root', 'item': u'elcos', u'checksum': u'918eb1bda64b3c9cfb14fd9f6b526cb0492fbff4', u'changed': True, 'failed': False, u'state': u'file', u'gid': 0, u'mode': u'0644', u'invocation': {u'module_args': {u'directory_mode': None, u'force': True, u'remote_src': None, u'dest': u'config.yml', u'selevel': None, u'_original_basename': u'vector_conf_elcos/elcos.toml', u'delimiter': None, u'regexp': None, u'owner': None, u'follow': False, u'validate': None, u'local_follow': None, u'src': u'/root/.ansible/tmp/ansible_mitogen_action_1073ea002b288ef0/source', u'group': None, u'unsafe_writes': None, u'checksum': u'918eb1bda64b3c9cfb14fd9f6b526cb0492fbff4', u'seuser': None, u'serole': None, u'content': None, u'setype': None, u'mode': None, u'attributes': None, u'backup': False}}})
so how I can fetch only the name value and not whole output
---
- hosts: localhost
gather_facts: false
vars:
variable:
- memcached
- apache
tasks:
- name: Create jinja templating
template:
src: "config.json.j2"
dest: "{{ item }}_config.json"
with_list: "{{ variable }}"
register: template_out
notify: "Restart Service"
handlers:
- name: Restart Service
shell: "service restart {{ item }}"
with_items: "{{ template_out.results
| selectattr('changed', 'equalto', true)
| map(attribute='item')
| list }}"
I recommend using this handler to restart services:
- name: Restart service
service:
name: "{{ item }}"
state: restarted
loop: "{{ service_restart.results
| selectattr('changed', 'equalto', true)
| map(attribute='item')
| list }}"
Related
I'm trying to setup an role for rolling out users. I have a list of users with their variables and I would like to only roll out authorized_key when the variable pubkey is set. Here is my code:
provisioning_user:
- name: ansible
state: present
pubkeys:
- 'ssh-rsa peter-key-1 peter#key1'
- 'ssh-rsa peter-key-3 peter#key3'
root: true
# removes directorys associated with the user
remove: true
create_home: true
comment: Deploy user for ansible
# If set to true when used with home: , attempt to move the user’s old home
# directory to the specified directory if it isn’t there already and the
# old home exists.
#non_unique: false
#uid: 11
#group:
groups: admin, developer
# If false, user will only be added to the groups specified in groups,
# removing them from all other groups.
append: yes
password: '!'
#ssh_public_keyfiles: ['ansible.pub', 'patrick.pub']
#key: ssh-ed25519 AAAAC3NzetfqeafaC1lZDI1NTE5AAAAIHu28wqv0r4aqoK1obosoLCBP0vqZj8MIlkvpAbXv0LL
#key_options:
#key_comment:
key_exclusive: true
key_manage_dir: true
- name: testuser2
state: present
# removes directorys associated with the user
#remove: false
create_home: yes
comment: Deploy user for ansible
As you see, the second user has no attribute pubkeys. Here is my Ansible code:
- name: test key
authorized_key:
user: "{{ item.name }}"
key: "{{ '\n'.join(provisioning_user|map(attribute='pubkeys')|flatten) }}"
comment: "{{ item.key_comment | default('managed by ansible') }}"
state: "{{ item.state | default('true') }}"
exclusive: "{{ item.key_exclusive | default('true') }}"
key_options: "{{ item.key_options | default(omit) }}"
manage_dir: "{{ item.manage_dir | default('true') }}"
loop: "{{ provisioning_user }}"
when: item.pubkeys is defined
Thats what Ansible says:
fatal: [cloud.xxx.xxx]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'pubkeys'
The error appears to be in '/home/xxx/gitlab.com/xxx/ansible/roles/provisioning/tasks/keys.yaml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- name: test key
^ here
Can you please help me getting the when condition work? I only want this task to run if a user has pubkeys defined, when not just skipping it.
This doesn't make any sense:
key: "{{ '\n'.join(provisioning_user|map(attribute='pubkeys')|flatten) }}"
You're looping over the contents of provisioning_user in this task; provisioning_user doesn't have a key pubkeys, rather, each individual item in that list may have a pubkeys value. So you'd want something like:
key: "{{ '\n'.join(item.pubkeys) }}"
Making the complete task look like:
- name: test key
authorized_key:
user: "{{ item.name }}"
key: "{{ '\n'.join(item.pubkeys) }}"
comment: "{{ item.key_comment | default('managed by ansible') }}"
state: "{{ item.state | default('true') }}"
exclusive: "{{ item.key_exclusive | default('true') }}"
key_options: "{{ item.key_options | default(omit) }}"
manage_dir: "{{ item.manage_dir | default('true') }}"
loop: "{{ provisioning_user }}"
when: item.pubkeys is defined
Running the above in a test environment produces:
TASK [test key] *****************************************************************************************
changed: [node0] => (item={'name': 'ansible', 'state': 'present', 'pubkeys': ['ssh-rsa peter-key-1 peter#key1', 'ssh-rsa peter-key-3 peter#key3'], 'root': True, 'remove': True, 'create_home': True, 'comment': 'Deploy user for ansible', 'groups': 'admin, developer', 'append': True, 'password': '!', 'key_exclusive': True, 'key_manage_dir': True})
skipping: [node0] => (item={'name': 'testuser2', 'state': 'present', 'create_home': True, 'comment': 'Deploy user for ansible'})
...which is I think what you want.
I try to restart a docker for specific item on inventory (aa,bb) when the file changed with copy module. If the copy changed the file so it trigger a handler to restart the specific item. The copy works but the handler is never trigger.
instances.yml
instances:
aa:
name: "aa"
bb:
name: "bb"
playbook.yml
- name: Copy config
copy:
src: "roles/{{ item.value.name }}.yaml"
dest: "etc/{{ item.value.name }}.yaml"
with_dict: "{{ instances1 }}"
register: template
notify:
- my_handler
handlers/main.yml
- name: my_handler
shell: "notify {{ item.key }}"
with_items: "{{ template.results | selectattr('changed', 'equalto', true) | map(attribute='item')| list }}"
A functional sample:
/my_project
|playbook.yml
|--/roles
| |--/test
| |--/tasks
| | main.yml
| |
| |--/handlers
| | main.yml
| |
| |--/files
| | aa.yaml
| | bb.yam
playbook.yml
- hosts: localhost
gather_facts: false
roles:
- test
In folder my_project/roles/test/tasks: main.yml
- name: Copy config
copy:
src: "{{ item.value.name }}.yaml"
dest: "zzz{{ item.value.name }}.txt"
with_dict: "{{ instances }}"
register: template
vars:
instances:
aa:
name: "aa"
bb:
name: "bb"
notify:
- my_handler
- name: Force all notified handlers to run at this point, not waiting for normal sync points
meta: flush_handlers
In folder my_project/roles/test/handlers: main.yml
- name: my_handler
debug:
msg: notify called {{ item }}
with_items: "{{ template.results | selectattr('changed', 'equalto', true) | map(attribute='item')| list }}"
Result:
RUNNING HANDLER [test : my_handler]
ok: [localhost] => (item={'key': 'aa', 'value': {'name': 'aa'}}) => {
"msg": "notify called {'key': 'aa', 'value': {'name': 'aa'}}"
}
ok: [localhost] => (item={'key': 'bb', 'value': {'name': 'bb'}}) => {
"msg": "notify called {'key': 'bb', 'value': {'name': 'bb'}}"
}
to execute Handlers at once add task meta: flush_handlers
I'm attempting to audit my systems via files copied to a single host. The default output is very verbose. I would like to see just the pertinent fields of Ansible log output; that way, over 1000 hosts, I can zero into my problems more quickly. . When my play is successful, I'd like to just see:
ok: u'/mnt/inventory/hostname999'
I have a playbook that looks like this:
- hosts: 'localhost'
name: Playbook for the Audit for our infrastructure.
gather_facts: False
become: no
connection: local
roles:
- { role: network, tags: network }
My network role main.xml file looks like this:
---
- name: find matching pattern files
find:
paths: "/mnt/inventory"
patterns: "hostname*"
file_type: directory
register: subdirs
- name: check files for net.ipv4.ip_forward = 0
no_log: False
lineinfile:
name: "{{ item.path }}/sysctl.conf"
line: "net.ipv4.ip_forward = 0"
state: present
with_items: "{{ subdirs.files }}"
register: conf
check_mode: yes
failed_when: (conf is changed) or (conf is failed)
- debug:
msg: "CONF OUTPUT: {{ conf }}"
But I get log output like this:
ok: [localhost] => (item={u'uid': 0, u'woth': False, u'mtime': 1546922126.0,
u'inode': 773404, u'isgid': False, u'size': 4096, u'roth': True, u'isuid':
False, u'isreg': False, u'pw_name': u'root', u'gid': 0, u'ischr': False,
u'wusr': False, u'xoth': True, u'rusr': True, u'nlink': 12, u'issock':
False, u'rgrp': True, u'gr_name': u'root', u'path':
u'/mnt/inventory/hostname999', u'xusr': True, u'atime': 1546930801.0,
u'isdir': True, u'ctime': 1546922126.0, u'wgrp': False, u'xgrp': True,
u'dev': 51, u'isblk': False, u'isfifo': False, u'mode': u'0555', u'islnk':
False})
Furthermore, my debug message of CONF OUTPUT never shows and I have no idea why not.
I have reviewed https://github.com/ansible/ansible/issues/5564 and other articles but they just seem to refer to items like shell commands that send stuff to stdout, which lineinfile does not.
But I get log output like this:
Then you likely want to use loop_control: with a label: "{{ item.path }}" child:
- lineinfile:
# as before
with_items: "{{ subdirs.files }}"
loop_control:
label: "{{ item.path }}"
which will get you closer to what you want:
ok: [localhost] => (item=/mnt/inventory/hostname999)
Furthermore, my debug message of CONF OUTPUT never shows and I have no idea why not.
The best guess I have for that one is that maybe the verbosity needs to be adjusted:
- debug:
msg: "CONF OUTPUT: {{ conf }}"
verbosity: 0
but it works for me, so maybe there is something else special about your ansible setup. I guess try the verbosity: and see if it helps. Given what you are actually doing with that msg:, you may be much happier with just passing conf directly to debug:
- debug:
var: conf
since it will render much nicer because ansible knows it is a dict, rather that just effectively calling str(conf) which (as you saw above) does not format very nicely.
I am trying to unmount filesystems with the below playbook.
vars:
unmountlist:
- "/DATA1"
- "/DATA2"
tasks:
- name: unmount
mount:
path: "{{ item }}"
state: unmounted
with_items:
- "{{ unmountlist }}"
register: output
ignore_errors: true
- debug:
msg: "{{ output }}"
- name: YE unmount persistant
mount:
path: "{{ item }}"
state: absent
with_items:
- "{{ unmountlist }}"
- name: Lazy unmount
command: umount -l "{{ item }}"
when: output.changed == false
with_items: "{{ unmountlist }}"
The debug section looks like this:
ok: [host001] => {
"msg": "error is {'msg': u'All items completed', 'failed': True, 'changed': False, 'results': [{'_ansible_parsed': True, 'changed': False, '_ansible_no_log': False, 'item': u'/DATA1', '_ansible_item_result': True, u'failed': True, u'invocation': {u'module_args': {u'src': None, u'dump': None, u'boot': u'yes', u'fstab': None, u'passno': None, u'fstype': None, u'state': u'unmounted', u'path': u'/DATA1', u'opts': None}}, u'msg': u'Error unmounting /DATA1: umount.nfs: /DATA1: device is busy\\n'}, {'_ansible_parsed': True, 'changed': False, '_ansible_no_log': False, 'item': u'/DATA2', '_ansible_item_result': True, u'failed': True, u'invocation': {u'module_args': {u'src': None, u'dump': None, u'boot': u'yes', u'fstab': None, u'passno': None, u'fstype': None, u'state': u'unmounted', u'path': u'/DATA2', u'opts': None}}, u'msg': u'Error unmounting /DATA2: umount.nfs: /DATA2: device is busy\\n'}]}"
I am trying to achieve the below.
lazy unmount the only the filesystem that returns error "device is busy". I cant see how to read the variable from above debug that contains string "device is busy" and how to unmount only the filesystem that returns this error.
What you can do is put the tasks in a tasks file and loop over the tasks file using unmountlist.
This is how your tasks file (umounts_tasks.yml) will look like - tasks file
Then within your play you can use include_tasks to include the above tasks file and loop over it with the unmountlist. So your play will look like this - play.yml
Let me know if this worked. :)
I have a playbook that looks like this:
- hosts: host1
gather_facts: false
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: [ 0, 2, 4, 6, 8, 10 ]
register: hello
- debug: "msg={{ hello.results }}"
Everything works correctly, and the output is returned, but there is tons and tons of output. It turns out that this:
- debug: "msg={{ hello.results.1.stdout }}"
does exactly what I want -- just grab the stdout from the command -- but only for one of the six times through the loop.
What I really want/need to do is this:
- debug: "msg={{ hello.results.*.stdout }}"
where it goes into the hello structure, accesses the results entry, goes to each member of that array, and pulls out the stdout value.
Is this possible?
UPDATE
- hosts: host1
gather_facts: false
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: [ 0, 2, 4, 6, 8, 10 ]
register: hello
- debug:
msg: "{{item.stdout}}"
with_items: "{{hello.results}}"
is no less verbose than my original example.
TASK [debug] *******************************************************************
ok: [host1] => (item={'_ansible_parsed': True, 'stderr_lines': [], u'cmd': [
u'echo', u'0'], u'end': u'2018-01-02 20:53:08.916774', '_ansible_no_log': False
, u'stdout': u'0', '_ansible_item_result': True, u'changed': True, 'item': 0,
u'delta': u'0:00:00.002137', u'stderr': u'', u'rc': 0, u'invocation': {u'module_
args': {u'warn': True, u'executable': None, u'_uses_shell': False, u'_raw_params
': u'echo 0', u'removes': None, u'creates': None, u'chdir': None, u'stdin': Non
e}}, 'stdout_lines': [u'0'], u'start': u'2018-01-02 20:53:08.914637', 'failed':
False}) => {
"item": {
"changed": true,
"cmd": [
"echo",
"0"
],
"delta": "0:00:00.002137",
"end": "2018-01-02 20:53:08.916774",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo 0",
"_uses_shell": false,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": 0,
"rc": 0,
"start": "2018-01-02 20:53:08.914637",
"stderr": "",
"stderr_lines": [],
"stdout": "0",
"stdout_lines": [
"0"
]
},
"msg": "0"
}
I get 6 copies of the above construct.
It feels like I'm close but I'm still doing something wrong. I see "msg": "0" at the bottom, which is what I want. I just don't want the rest of it.
Solution:
- debug: "msg={{ hello.results | map(attribute='stdout') | join('\n') }}"
Remark:
By default, Ansible will print visible \n two-character sequences instead of wrapping the lines, so either use a callback plugin for a human readable output (example) or verify the method with:
- copy:
content: "{{ hello.results | map(attribute='stdout') | join('\n') }}"
dest: ./result.txt
and check the contents of the result.txt.
I have used the keyword loop to get stdout from all iterations of the previous loop:
loop: "{{ hello | json_query('results[*].stdout') }}"
I find json_query easiest to use in such register-loop situations. Official documentation can be found here ==> json-query-filter
Sure. The ansible website has documentation that explains how to use register in a loop. You just need to iterate over the hello.results array, as in:
- debug:
msg: "{{item.stdout}}"
with_items: "{{hello.results}}"
What about:
- debug: "msg={{ item.stdout }}"
with_items: "{{ hello.results }}"
I think this construct works well enough for my needs.
- hosts: localhost
gather_facts: false
vars:
stuff: [ 0,2,4,6,8,10 ]
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: "{{ stuff }}"
register: hello
- debug: "var=hello.results.{{item}}.stdout"
with_sequence: "0-{{stuff|length - 1}}"
I was looking at a similar problem and was confused by getting lots of output when I was expecting a relatively small msg or var from debug:. Turns out most of that output was the 'label' with which Ansible was prefixing each of those small outputs. It being a few years after this question was originally asked I've been using loop rather than with_items; this also has a label: option in loop_control:, so in my case for a similar problem - getting any /etc/passwd entries for users 'alice' or 'bob',
- hosts: all
gather_facts: false
serial: 1 # output easier to read when grouped by host
tasks:
- name: Look for users in /etc/passwd
command: grep {{ item }} /etc/passwd
register: res
ignore_errors: true
loop:
- alice
- bob
- debug:
msg: "{{ item.stdout_lines }}"
when: not item.failed
loop: "{{ res.results }}"
loop_control:
label: "{{ item.item }}"