Ansible: "this module requires key=value arguments" when key=value args supplied - ansible

I'm getting this error from Ansible 1.9.4:
TASK: [rabbitmq | add rabbitmq vhost] *****************************************
failed: [prod-sensu01] => (item={'name': u'/sensu'})
=> {"failed": true, "item": {"name": "/sensu"}}
msg: this module requires key=value arguments
(['name:', '/sensu', 'node:', 'rabbit',
'tracing:', 'no', 'state:', 'present'])
But as you can see from the error message, the item is actually a key=value argument (hash).
Here are the variables:
# playbooks/roles/rabbitmq/vars/main.yml
# Set the rabbitmq vhost
rabbitmq_vhost_definitions:
- name: "{{ sensu_server_rabbitmq_vhost }}"
And the role's task:
# playbooks/roles/rabbitmq/tasks/vhost.yml
- name: add rabbitmq vhost
rabbitmq_vhost: >
name: "{{ item.name }}"
node: "{{ item.node | default('rabbit') }}"
tracing: "{{ item.tracing | default('no') }}"
state: present
with_items: rabbitmq_vhost_definitions
What is going on here?

Key/value pairs of arguments for Ansible module passed as string should be separated by =, not by :. Correct task should look like this:
# playbooks/roles/rabbitmq/tasks/vhost.yml
- name: add rabbitmq vhost
rabbitmq_vhost: >
name="{{ item.name }}"
node="{{ item.node | default('rabbit') }}"
tracing="{{ item.tracing | default('no') }}"
state=present
with_items: rabbitmq_vhost_definitions
Why they're passed as string? Because you're using folded block scalar > after name of the module, in this line:
rabbitmq_vhost: >
You could also try to remove > scalar (and leave colons) if this module supports both ways of passing arguments.

Related

Issue with using Omit option

I am trying configure a attribute only if my_int_http is defined else I dont want it. So I coded it like below:
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(omit) }}"
However my execution fail and when check the arguments passed by the code in actual configuration it shows like below:
"profiles": [
"my_example.internal_tcp",
"__omit_place_holder__ef8c5b99e9707c044ac07fda72fa950565f248a4"
So how to pass absolutely no value where it is passing __omit_place_holder_****?
Q: "How to pass absolutely no value where it is passing omit_place_holder ?"
A1: Some filters also work with omit as expected. For example, the play
- hosts: localhost
vars:
test:
- "{{ var1|default(false) }}"
- "{{ var1|default(omit) }}"
tasks:
- debug:
msg: "{{ {'a': item}|combine({'b': true}) }}"
loop: "{{ test }}"
gives
msg:
a: false
b: true
msg:
b: true
As a sidenote, default(omit) is defined type string
- debug:
msg: "{{ item is defined }}"
loop: "{{ test }}"
- debug:
msg: "{{ item|type_debug }}"
loop: "{{ test }}"
give
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: true
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: true
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: bool
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: str
A2: No value in Ansible is YAML null. Quoting:
This is typically converted into any native null-like value (e.g., undef in Perl, None in Python).
(Given my_int_L4=bob). If the variable my_int_http defaults to null instead of omit
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(null) }}"
the list profiles will be undefined
profiles: VARIABLE IS NOT DEFINED!
Use None instead
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
The variable my_int_http will default to an empty string
profiles:
- bob
- ''
See also section "YAML tags and Python types" in PyYAML Documentation.
You can try something like this,
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
This will give you an empty string. And you can add a check while iterating over the profiles.
Please have a look at this GitHub Issue to get more understanding.

Ansible: how to check if a variable is being loaded in a playbook?

I'm trying to write a playbook that will load vars from a group vars file then check if a variable exists
my playbook is like this:
---
- hosts: "{{ target }}"
roles:
- app
tasks:
- name: alert if variable does not exist
fail:
msg: "{{ item }} is not defined"
when: "{{ item }}" is not defined
with_items:
- country
- city
- street
...
My inventory file contains
[app]
ansible-slave1
ansible-slave2
[db]
ansible-db
[multi:children]
app
db
and I have the roles/app/vars/main.yml containing
country: "France"
city: "Paris"
What I was expecting is the playbook to output "street is not defined" but I have a syntax issue I can't resolve
[vagrant#ansible-master vagrant]$ ansible-playbook --inventory-file=ansible_master_hosts test_variables.yml --extra-vars "target=ansible-slave1" --syntax-check
ERROR! Syntax Error while loading YAML.
The error appears to have been in '/vagrant/test_variables.yml': line 10, column 24, but may be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
msg: "{{ item }} is not defined"
when: "{{ item }}" is not defined
^ 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 }}"
I'd be happy with any hints.
Thanks
You have "" in invalid place of "when" statement. This should be like this:
msg: "{{ item }} is not defined"
when: "{{ item }} is not defined"
So the output will be:
failed: [hostname] (item=street) => {"changed": false, "item": "street", "msg": "street is not defined"}
there is on open issue conditional is defined fails to capture undefined var .
as a workaround I'd suggest to change the where condition to the following:
when: "{{ item }}" == ""

Ansible subelements lookup expects a dictionary

I have a variable/dictionary file as below:
cafu_analyze_bidprice:
artifacts_name:
- "forecast-measures-read-deploy"
- "forecast-measures-finalizer-deploy"
group_id: "com.lufthansa.cobra.cafu"
cafu_measurement:
artifacts_name:
- "forecast-exporter-read-deploy"
group_id: "com.lufthansa.cobra.cafu"
and playbook as below:
- name: Get deployable artifact from artifactory and copy
maven_artifact:
validate_certs: false
group_id: "{{ item.0.group_id }}"
artifact_id: "{{ item.1 }}"
version: "{{ version }}"
repository_url: http://10.127.130.82:8081/artifactory/cafu
dest: "/opt/cafu/target-test"
classifier: "exec"
with_subelements:
- "{{ module_name }}"
- artifacts_name
for which I am passing module name as variable:
ansible-playbook -C cafu-deploy.yml -i hosts -e module_name=cafu_analyze_bidprice -e version=1.1.17-SNAPSHOT
Getting below error:
FAILED! => {"msg": "subelements lookup expects a dictionary, got
'cafu_analyze_bidprice'"}
Please help if I am doing something wrong, task is to get module name from command line and than use those as dictionary variables.
Two mistakes:
You are passing a string (cafu_analyze_bidprice) instead of a reference to the variable named cafu_analyze_bidprice,
Using subelements lookup does not fit this use case, because you don't have a list of dictionaries.
What you should be doing instead:
use vars lookup to refer to a variable with a name stored in another variable (you use module_name),
iterate over the list defined in the artifacts_name key of the above lookup result.
As you also use group_id key, you can use a helper variable (called my_var in the example below) to avoid calling the lookup twice:
- name: Get deployable artifact from artifactory and copy
maven_artifact:
validate_certs: false
group_id: "{{ my_var.group_id }}"
artifact_id: "{{ item }}"
version: "{{ version }}"
repository_url: http://10.127.130.82:8081/artifactory/cafu
dest: "/opt/cafu/target-test"
classifier: "exec"
loop: "{{ my_var.artifacts_name }}"
vars:
my_var: "{{ lookup('vars', module_name) }}"

Ansible, list object has no attribute

I am using Ansible 2.3.0.0 and I have tested in Ansible 2.4.0.0, obtaining the same result. My problem is very simple, but I cannot see the problem.
I have defined a list of objects in Ansible as follows:
vars:
password_text_to_encrypt:
- { line: "{{truststore_pass }}" , regexp: '\${TRUSTSTORE_PASS}*'}
- { line: "{{ keystore_pass }}" , regexp: '\${KEYSTORE_PASS}*'}
- { line: "{{ gp_pass }}" , regexp: '\${GP_PASS}*'}
- { line: "{{ datasource_password }}" , regexp: '\${DATASOURCE_PASS}*'}
- { line: "{{ server_password }}" , regexp: '\${SERVER_PASSWORD}*'}
- { line: "{{ sftp_password }}" , regexp: '\${SFTP_PASSWORD}*'}
- { line: "{{ db_userpassword }}" , regexp: '\${DB_PASSWORD}*'}
roles:
- basic_role
My Basic_role just prints the items, and I would like to obtain the content of each line:
---
- name: "print password"
debug:
msg: "The content of the line is: {{ item.line}}"
with_nested:
- "{{password_text_to_encrypt}}"
But the result that I obtain is:
FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'list object' has no attribute 'line'\n\nThe error appears to have been in.....
If I change item.line to just item, it works but it prints:
ok: [localhost] => (item=[u'regexp', u'line']) => {
"item": [
"regexp",
"line"
],
"msg": "The content of the line is: [u'regexp', u'line']"
}
.
.
.
To summarize, Ansible does not consider the content of line o regexp. I have been doing tests and the variable which are used to init line and regexp are not empty.
Use with_items instead of with_nested.
I think you want loops and includes because you're getting a flattened list which is expected (as per the documentation).

Access ansible.cfg variable in task

How can I refer remote_tmp (or any other) value defined in ansible.cfg in my tasks? For example, in the my_task/defaults/main.yml:
file_ver: "1.5"
deb_file: "{{ defaults.remote_tmp }}/deb_file_{{ file_ver }}.deb"
produces an error:
fatal: [x.x.x.x]: FAILED! => {"failed": true,
"msg": "the field 'args' has an invalid value,
which appears to include a variable that is undefined.
The error was: {{ defaults.remote_tmp }}/deb_file_{{ file_ver }}.deb:
'defaults' is undefined\... }
You can't do this out of the box.
You either need action plugin or vars plugin to read different configuration parameters.
If you go action plugin way, you'll have to call your newly created action to get remote_tmp defined.
If you choose vars plugin way, remote_tmp is defined with other host vars during inventory initialization.
Example ./vars_plugins/tmp_dir.py:
from ansible import constants as C
class VarsModule(object):
def __init__(self, inventory):
pass
def run(self, host, vault_password=None):
return dict(remote_tmp = C.DEFAULT_REMOTE_TMP)
Note that vars_plugins folder should be near your hosts file or you should explicitly define it in your ansible.cfg.
You can now test it with:
$ ansible localhost -i hosts -m debug -a "var=remote_tmp"
localhost | SUCCESS => {
"remote_tmp": "$HOME/.ansible/tmp"
}
You can use lookup.
file_ver: "1.5"
deb_file: "{{ lookup('ini', 'remote_tmp section=defaults file=ansible.cfg' }}/deb_file_{{ file_ver }}.deb"
EDIT
In case you don't know the path to the configuration file, you can set that to a fact by running the following tasks.
- name: look for ansible.cfg, see http://docs.ansible.com/ansible/intro_configuration.html
local_action: stat path={{ item }}
register: ansible_cfg_stat
when: (item | length) and not (ansible_cfg_stat is defined and ansible_cfg_stat.stat.exists)
with_items:
- "{{ lookup('env', 'ANSIBLE_CONFIG') }}"
- ansible.cfg
- "{{ lookup('env', 'HOME') }}/.ansible.cfg"
- /etc/ansible/ansible.cfg
- name: set fact for later use
set_fact:
ansible_cfg: "{{ item.item }}"
when: item.stat is defined and item.stat.exists
with_items: "{{ ansible_cfg_stat.results }}"
You can then write:
file_ver: "1.5"
deb_file: "{{ lookup('ini', 'remote_tmp section=defaults file=' + ansible_cfg) }}/deb_file_{{ file_ver }}.deb"

Resources