I'm not able to execute the shell command pvchange -u /dev/sde with ansible.
Can you pls help me to figure out what's wrong?
vars:
tgt_string_id: TGTDBID
action_vg: "clone_{{ tgt_string_id }}vg"
host_vars_dir: /home/abhn02a/ansible/host_vars
action_host: "{{ tgt_vm | default('localhost') }}"
tgt_disks: "/dev/sde /dev/sdf"
Tasks
- name: Display target disks
debug:
msg: "{{ item }}"
loop: "{{ tgt_disks.split(' ') }}"
- name: Change pv uuid
become: yes
shell: |
pvchange -u "{{ item }}"
loop: "{{ tgt_disks.split(' ') }}"
#changed_when: false
delegate_to: "{{ action_host }}"
Output:
......
.......
TASK [Display target disks]
*********************************************************************************************
ok: [localhost] => (item=/dev/sde) => {
"msg": "/dev/sde"
}
ok: [localhost] => (item=/dev/sdf) => {
"msg": "/dev/sdf"
}
!! This is not true, uuid stays same !!!
TASK [Change pv uuid]
***************************************************************************************************
changed: [localhost] => (item=/dev/sde)
changed: [localhost] => (item=/dev/sdf)
.......
.....
When I check the uuid of the disk, It has not been changed. Then, I type "pvchange -u /dev/sde", It changes the uuid of the disk with no problem!
Have ansible 2.9 with python2.7.5
Can you help, please?
(Oh, this is my 1st question on SO, sorry for the previous formatting)
Edit
So I have almost reproduced the error at home.
This happens when I try to do a pvchange on a cloned disk (But again, only with Ansible!!)
I have cloned 2 disks (/dev/xvdc and /dev/xvde) to (/dev/xvdf and /dev/xvdg), and re-run the task, and I have following output:
TASK [Change pv uuid]
*************************************************************************
failed: [localhost] (item=/dev/xvdf) => {"ansible_loop_var": "item", "changed": true, "cmd": "pvchange -u \"/dev/xvdf\"\n", "delta": "0:00:00.038053", "end": "2021-03-06 03:46:07.526239", "item": "/dev/xvdf", "msg": "non-zero return code", "rc": 5, "start": "2021-03-06 03:46:07.488186", "stderr": " Failed to find physical volume \"/dev/xvdf\".", "stderr_lines": [" Failed to find physical volume \"/dev/xvdf\"."], "stdout": " 0 physical volumes changed / 0 physical volumes not changed", "stdout_lines": [" 0 physical volumes changed / 0 physical volumes not changed"]}
This seems to be, because the cloned disks are still not "PVs" in LVM; I do not see /dev/xvdf and /dev/xvdg when I do pvs.
But at work, this was different, I could see the cloned disks in "pvs output"
Will check again next week.
Thank you
From the dub of the execution, you can see that the command executed is pvchange -u "/dev/xvdf", which raise an error message 'Failed to find physical volume "/dev/xvdf".', so looks like it doesn't like the quotes.
You can change your task to that, it should works better:
- name: Change pv uuid
become: yes
shell: |
pvchange -u {{ item }}
loop: "{{ tgt_disks.split(' ') }}"
pvscan --cache `, before doing` pvchange -u <PV>
Related
Loop through the items. And items are from Inventory groups.
I tried almost all the solutions posted in stackoverflow, but no luck.
Here is my playbook:
---
- hosts: dev
gather_facts: yes
become: true
become_method: su
become_user: xxxxx
tasks:
- name: Update the file contents
replace:
path: "/path/to/file{{ item.path }}.xml"
regexp: '(xxxxxxx[\s\S]*yyyyyyy$)'
replace: "/zzz/zzzz/{{ item.replace }}.dat /qqqq/qqqqq/q"
backup: yes
with_items:
- { path: "{{ groups['dev'] }}", replace: "{{ groups['pc'] }}" }
And this is my inventory:
[dev]
host1
host2
[pc]
1234
6789
I want to execute the task : "Update the file contents" for every hostname under dev group for item.path and item.pc
The result of my execution is:
failed: [host1] (item={u'path': [u'host1', u'host2'], u'replace': [u'1234', u'6789']}) => {"ansible_loop_var": "item", "changed": false, "
item": {"path": ["host1", "host2"], "replace": ["1234", "6789"]}, "msg": "Path /path/to/file[u'host1', u'host2'].xml does not exist !", "rc": 257}
failed: [host2] (item={u'path': [u'host1', u'host2'], u'replace': [u'1234', u'6789']}) => {"ansible_loop_var": "item", "changed": false, "
item": {"path": ["host1", "host2"], "replace": ["1234", "6789"]}, "msg": "Path /path/to/file[u'host1', u'host2'].xml does not exist !", rc": 257}
Expected output:
[host1] (item={u'path': [u'host1'], u'replace': [u'1234']}) "msg": "Path /path/to/file/host1.xml
[host2] (item={u'path': [u'host2'], u'replace': [u'6789']}) "msg": "Path /path/to/file/host2.xml
This works fine if I have only 1 hostname in the dev group. I need the task to work for all the hosts when the number of hosts are more than 1.
Can anyone help me solve this?
Thank you!
According to your desired output, there is no need to loop over all hosts.
There is even no need to run the task on the pc hosts group.
- name: Update the file contents
replace:
path: "/path/to/file/{{ inventory_hostname_short }}.xml"
regexp: '(xxxxxxx[\s\S]*yyyyyyy$)'
replace: "/zzz/zzzz/{{ groups['pc'][groups['dev'].index(inventory_hostname)].split('.')[0] }}.dat /qqqq/qqqqq/q"
backup: yes
when: inventory_hostname in groups['dev']
I avoided having the domain name in the hostnames.
{{ groups['pc'][groups['dev'].index(inventory_hostname)].split('.')[0] }}
Here I'm getting the host from group [pc], which its index number is equivalent to the host from group [dev] which the task is currently running on.
when running on host1 which has index number 0 in group [dev] ==> groups['pc'][0]
when running on host2 which has index number 1 in group [dev] ==> groups['pc'][1]
With .split('.')[0] I'm getting the hostname without domain name.
Team,
I am writing a validation task that is supposed to just check if a mount exists or not and report its state from output. so my task is below but it fails and am not sure how to handle it. any hint what adjustments do i need to make?
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
#failed_when: lvp_mount.rc != 0
#ignore_errors: yes
# - debug:
# var: lvp_mount
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }}
{{ lvp_mount.stderr }}
when: lvp_mount | failed
changed: [localhost -> ] => (item=hostA)
[WARNING]: Consider using the mount module rather than running 'mount'. If you
need to use command because mount is insufficient you can add 'warn: false' to
this command task or set 'command_warnings=False' in ansible.cfg to get rid of
this message.
failed: [localhost -> hostA.test.net] (item=hostA) => {"ansible_loop_var": "item", "changed": true, "cmd": "mount | grep sdd", "delta": "0:00:00.009284", "end": "2019-11-06 18:22:56.138007", "failed_when_result": true, "item": "hostA", "msg": "non-zero return code", "rc": 1, "start": "2019-11-06 18:22:56.128723", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
TASK [services-pre-install-checks : Report status of mounts] ************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'\n\nThe error appears to be in '/home/run_ansible_playbook/k8s/baremetal/roles/services-pre-install-checks/tasks/main.yml': line 265, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: \"Report status of mounts\"\n ^ here\n"}
Your task "Verify LVP Mounts on CPU Nodes for mount_device" is a loop so the register behavior is modified as specified in the documentation.
You can access the various outputs with lvp_mount.results.X.stdout where X is the index.
There is a cleaner way to write your script however. More specifically using:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
is bad practice. You can accomplish your desired outcome at the play level.
For example:
- hosts: kube-cpu-node # allows you to iterate over all hosts in kube-cpu-node group
tasks:
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
ignore_errors: yes
# notice there is no loop here
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }} # no loop so you can use lvp_mount.stdout
{{ lvp_mount.stderr }} # no loop so you can use lvp_mount.stderr
when: lvp_mount | failed
I am setting up a new play in my Ansible playbook that does some operations on a group of machines.
These operations require to play in serial mode 1 by 1.
My job is working well for the 1st machine but for the others one it seems that the variables are not valued.
- name: "My play"
hosts: my_hosts_group
tags: [test]
serial: 1
become: yes
become_user: root
tasks:
- shell: echo {{ MYVARIBLE.MYSOFTWARE.souche_path }} > /home/USER/test.txt
register: result
- debug: var=result
Here is my datamodel :
MYVARIBLE:
MYSOFTWARE:
souche_path: "/mybinpath/mybin"
For the 1st machine it's working well and I am getting this message :
ok: [host1] => {
"changed": false,
"result": {
"changed": true,
"cmd": "echo my_variable_value > /home/USER/test.txt",
"delta": "0:00:00.005541",
"end": "2019-04-26 16:27:09.415017",
"failed": false,
"rc": 0,
"start": "2019-04-26 16:27:09.409476",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
}
But fort he others one I got this one :
fatal: [host2]: FAILED! => {}
MSG:
The task includes an option with an undefined variable. The error was: 'MYVARIABLE' is undefined
The error appears to have been in '/my playbook/install.yml': line 85, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- shell: echo {{ MYVARIBLE }} > /home/USER/test.txt
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
exception type: <class 'ansible.errors.AnsibleUndefinedVariable'>
exception: 'MYVARIBLE' is undefined
I have played it in parallel mode and it's working well. So I am sure that the problem is the variable are only valued for the 1st loop.
Am I missing something ?
I have the following task which works perfectly as expected. I am wondering if the failure message can be a bit more informative since I am passing no_log: true without which I could see the entire result in the logs. Something like:
More than one access keys available for the cos account {{ item.name }}
- name: Fail if more than one key is available for any of the COS accounts
fail: msg="More than one access keys available for the cos account"
when: (item.json)|length > 1
with_items: '{{ old_existing_creds.results }}'
no_log: true
In fact I noticed I could not even see the msg. The o/p I got is:
TASK [Fail if more than one key is available for any of the COS accounts] *****************************************************************************
skipping: [localhost] => (item=None)
failed: [localhost] (item=None) => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
fatal: [localhost]: FAILED! => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
to retry, use: --limit #/root/deployment/generateSLKeys.retry
You've asked Ansible not to log the result of your task by setting no_log: true, so you're not going to be able to see the result of the fail task. You can hack around this by first creating a new variable that maps names from your old_existing_creds variables to the length of the json attribute, like this:
---
- hosts: localhost
gather_facts: false
vars:
old_existing_creds:
results:
- json: [secret1,secret2]
name: foo
- json: [secret1]
name: bar
tasks:
- name: check length of json array
set_fact:
key_check: "{{ key_check|default({})|combine({item.name: item.json|length}) }}"
loop: "{{ old_existing_creds.results }}"
- debug:
var: key_check
- name: Fail if more than one key is available for any of the COS accounts
fail:
msg: "More than one access keys available for the cos account {{ item.key }}"
when: (item.value > 1)
loop: "{{ key_check|dict2items }}"
This will output:
TASK [Fail if more than one key is available for any of the COS accounts] *********************
failed: [localhost] (item={'key': u'foo', 'value': 2}) => {"changed": false, "item": {"key": "foo", "value": 2}, "msg": "More than one access keys available for the cos account foo"}
skipping: [localhost] => (item={'key': u'bar', 'value': 1})
As you can see, it shows the message from the fail task, which includes the account name, but it does not expose credentials in the log.
I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message
One or more undefined variables: 'dict object' has no attribute 'stdout'
is there a way to register variable for each command and loop over them one by one ?
Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.
Testing playbook:
---
- hosts: localhost
gather_facts: no
tasks:
- command: "echo {{item}}"
register: result
with_items: [1, 2]
- debug:
var: result
Result:
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] **************************************************************
TASK: [command echo {{item}}] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
TASK: [debug ] ****************************************************************
ok: [localhost] => {
"var": {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": [
"echo",
"1"
],
"delta": "0:00:00.002502",
"end": "2015-08-07 16:44:08.901313",
"invocation": {
"module_args": "echo 1",
"module_name": "command"
},
"item": 1,
"rc": 0,
"start": "2015-08-07 16:44:08.898811",
"stderr": "",
"stdout": "1",
"stdout_lines": [
"1"
],
"warnings": []
},
{
"changed": true,
"cmd": [
"echo",
"2"
],
"delta": "0:00:00.002516",
"end": "2015-08-07 16:44:09.038458",
"invocation": {
"module_args": "echo 2",
"module_name": "command"
},
"item": 2,
"rc": 0,
"start": "2015-08-07 16:44:09.035942",
"stderr": "",
"stdout": "2",
"stdout_lines": [
"2"
],
"warnings": []
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.
Simple example, which will result in changed: false:
- action: command echo {{item}}
register: out
changed_when: "'z' in out.stdout"
with_items:
- hello
- foo
- bye
Another example:
- name: Create fulltext index for faster text searches.
mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
with_items:
- {tableName: Posts, columnName: Title}
- {tableName: Posts, columnName: Body}
- {tableName: Posts, columnName: Tags}
- {tableName: Comments, columnName: Text}
register: createfulltextcmd
changed_when: createindexcmd.msg.find('already exists') == -1
Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:
- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
- { index: 0, host: testhost1.test.dom }
- { index: 1, host: testhost2.test.dom }
- { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
Posting because I can't comment yet
Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.
From the docs:
Tracking progress through a loop with index_var
New in version 2.5.
To keep track of where you are in a loop, use the index_var directive
with loop_control. This directive specifies a variable name to contain
the current loop index:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
This also allows you to gather results from an array and act later to the same array, taking into account the previous results
- name: Ensure directories exist
file:
path: "{{ item }}"
state: directory
loop:
- "mouse"
- "lizard"
register: reg
- name: Do something only if directory is new
debug:
msg: "New dir created with name '{{ item }}'"
loop:
- "mouse"
- "lizard"
loop_control:
index_var: index
when: reg.results[index].changed
Please note that the "mouse lizard" array should be exactly the same
If what you need is to register the output of two commands separately, use different variable names.
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result0
changed_when: false
- debug: msg="{{result0.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result1
changed_when: false
- debug: msg="{{result1.stdout}}"