How do I evaluate template expressions in my Ansible plugins? - ansible

I just wrote a small Ansible lookup plugin for fetching passwords from pass. I've set this variable in group_vars/all.yml:
pass_store: "{{ inventory_dir }}/passwords"
Inside my plugin I fetch this variable and pass it to os.environ["PASSWORD_STORE_DIR"].
The problem is that the value hasn't been evaluated. The literal value of the variable is used, not the evaluated value.
For example:
os.environ["PASSWORD_STORE_DIR"] = variables["pass_store"]
I now expect the environment variable PASSWORD_STORE_DIR to contain:
/home/tomas/my-project/passwords
Instead it contains:
{{ inventory_dir }}/passwords
Does the Ansible API provide some way of evaluating variables containing template expressions like this?

You can try this:
os.environ["PASSWORD_STORE_DIR"] = self._templar.template(variables["pass_store"])
This will evaluate the variable content and replace Jinja tokens by appropriate values.

Related

Expand complex ansible variable

I have distinct ansible variables which are combined in a single command. More precisely, the system defines a variable with default value:
# This exists in an ansible variable definition file
instrumentor = ''
At some the python script that "creates" the ansible commands specifies the extra arguments to override the default values. I'm jumping through some hoops (using \" and \') because the desired value contain spaces:
# This is a line from python script
instrumentation_specs =
'-e \'instrumentor=\"/software/cuda-memcheck --tool racecheck --log-file /tmp/memlog\"\''
On the ansible side then the command is structured as
;; This exists in a jinja template (.j2 extension)
;; command is what the start.yml script will be running
command="{{ instrumentor }}" my_program
Unfortunately I cannot get this to work. The command is reported to expand to something that contains extra ' and ":
start.yml (...) -e 'instrumentor="/software/cuda-memcheck --tool racecheck --log-file /tmp/memlog"' (...)
The weird thing is that
if I create another variable before command that only contains instrumentor, it is reported to have the exact value that I need
instr_tmp = "{{ instrumentor }}"
command = "{{ instr_tmp }}" my_program
i.e. the extra ' and " don't appear in instr_tmp but do appear in command
If I ommit the \" and \' on the python side (when creating the string that is used to provide the extra arguments) everything that's after a space gets omitted.
Anyone knows how to specify such a variable override, i.e. how the string should look like when I specify a variable that contains multiple space separated strings (instrumentor), but that variable is supposed to be plugged into another ansible variable (command).

How do I use a yaml anchor in an Ansible when conditional?

I'm trying to check the value of a variable in a role, but I don't know what the variable name will be beforehand, as it is set outside the role.
I tried to use a yaml anchor to achieve this like so:
- set_fact:
set_anchor: &job_status_data_key "{{ job_running_check_key }}"
[...]
- name: Set current state
set_fact:
loop_finished_successfully: true
when: *job_status_data_key != "Running"
Where job_running_check_key at the start, is set outside the role and contains the variable name I'll be checking against.
but ansible appears not to appreciate pointers in the conditionals
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
did not find expected key
The error appears to be in 'path/to/role/task.yml': line 15, column 30, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
loop_finished_succesfully: true
when: *job_status_data_key != "Running"
^ here
There's probably a proper syntax to achieve this, but I haven't managed to figure it out until now.
You're confusing ansible features with YAML features.
A YAML alias is a reference to an existing node. An ansible condition is a Jinja2 expression without quotes.
YAML doesn't know about ansible or Jinja. A YAML alias must be part of the YAML structure. Jinja, on the other hand, does not know about YAML. The consequences are:
*job_status_data_key != "Running"
is invalid YAML. The YAML syntax defines that an alias starts with * and contains a single name of an anchor. The following !=… content is therefore a syntax error. Now you might think „okay, let's quote it then so that YAML reads it as string and Jinja can evaluate it“:
'*job_status_data_key != "Running"'
This is now a valid YAML scalar. If given as value for when:, it will be evaluated as Jinja expression. The thing is, Jinja doesn't know that *job_status_data_key is supposed to be a YAML alias and in fact, at the time this expression is evaluated, the information about the anchored value of that name has already vanished.
The bottom line is: Don't use anchors and aliases for variables. They are a tool designed for serializing cyclic data structures inside YAML. Ansible offers variables and you should use those instead.

Ansible/Jinja2 asterisk conditional

Trying to update a LDAP entry via Ansible, but I want to make sure that the LDAP host wildcard ("*") is not in the host list. If I use "*" not in hosts or '*' not in hosts, Ansible does not like those. I've also tried Jinja2 search filters. Any help would be great!
The problem has nothing to do with the asterisk. It is a pure yaml syntax problem
In yaml, if you quote a variable, the representation of the value will start with a single or double quote, then you get the value itself and the closing quote. After that, the yaml parser is expecting a yank <block end>, in your specific case a new line to move on to the next value definition.
In your example, your when clause value starts with a quote. So you have to quote your entire value:
when: '"*" not in hosts'
# or
when: "'*' not in hosts"
# even
when: "\"*\" not in hosts"
A good and more readable alternative IMO is to use a yaml scalar block marker:
when: >-
"*" not in hosts
Figured this out. Was trying to do it with pipes and didn't need to.
when:
- var is not regex("\\*")

Ansible ambiguous env value

I'm getting the following warning in Ansible:
[WARNING]: Non-string value found for env option. Ambiguous env options should be wrapped in quotes to avoid YAML parsing. This will become an error in Ansible 2.8. Key: PORT; value will be treated as: 12345
So I went and looked up the origin of this value and wrapped all instances of it in quotes. Or so I thought. I'm still getting the warning.
So I went to the place in the code where it appeared and it seems to be this:
docker_container:
env: '{{ params | combine(extra_params, {"PORT": my_port|int + amount|int * 10 })}}'
This is a setup for dealing with multiple instances of the same container, each getting a unique port, as to not interfere with one another.
And I'm not sure how to fix that without breaking that setup. Can it be cast to string again after the calculation is done? Should I do it beforehand? What's the best option here?
As the ansible documentation for the docker_container module under env states
Values which might be parsed as numbers, booleans or other types by the YAML parser must be quoted (e.g. "true") in order to avoid data loss.
so you have to convert your result to a quoted string.
env: '{{ params | combine(extra_params, {"PORT": (my_port|int + amount|int * 10) | string })}}'

looping a shell command in ansible

I'm trying to get going with some more advanced Ansible playbooks and have hit a wall. I'm trying to get Ansible to do what this /bin/bash 'for' loop does;
for i in $(</filename.txt);do '/some/command options=1 user=usera server=$i';done
filesnames.txt contains 50-100 hostnames.
I can't use jinja templates as the command has to be run, not just the config file updated.
Any help would be greatly appreciated.
Thanks in advance,
Jeremy
you can use jinja templates, but differently
your specific code is not doing something that is most advisable
for multi-line code you should use shell module.
example of multi-code piece of call:
- name: run multiline stuff
shell: |
for x in "${envvar}"; do
echo "${x}"
done
args:
executable: /bin/bash
note I'm explicitly setting executable, which will ensure bash-isms would work.
I just used envvar as an example, of arbitrary environment variable available.
if you need to pass specific env variables, you should use environment clause of the call to shell module, refer to: http://docs.ansible.com/ansible/playbooks_environment.html
For simple variables you can just use their value in shell: echo "myvar: {{myvar}}"
If you wish to use an ansible list/tuple variable inside bash code, you can make it bash variable first. e.g. if you have a list of stuff in mylist, you can expand it and assign into a bash array, and then iterate over it. the shell code of the call to shell would be:
mylist_for_bash=({{mylist|join(" ")}})
for myitem in "${mylist_for_bash[#]}"; do
echo "my current item: ${myitem}"
done
Another approach would be to pass it as string env variable, and convert it into an array later in the code.
NOTE:
of course all this works correctly only with SPACELESS values
I've never had to pass array with space containing items

Resources