Ansible: to get specific lines from stdout_lines of ansible playbook - ansible

I would like to print specific lines form stdout_lines from ansible playbook with string e.g "systemd". Could you please someone help me on this ?

The select filter is a way of applying an existing test, such as the search test to a list of items, allowing through only those items which pass the test's criteria; thus:
- shell: |
for i in alpha system beta; do
echo "${i}d"
done
register: bob
- debug:
msg: '{{ bob.stdout_lines | select("search", "systemd") | list }}'
You need that final | list on the end because select and its map friend produce python generators, and thus need a terminating operation, either first or list or length to cause them to actually do work

Related

Is it possible to select an item within a list with a regex?

I am trying to create a script which is only selecting an item within a list, with a regex.
In my case, based on a certain condition which should be triggered wheter a specific character set is present of not within the name, then just select that item.
So far, I have produced the following code within an ansible Role:
- name: "Set_initial_regex"
set_fact:
name_regex: ".*((-a)+(\\d)?)\\..*\\.company\\..+"
# This regex should select any FQDN with "-a" followed by an optional number within its name (e.g. fqdn-test-a1.test.company.local)
- name: "Debug"
debug:
msg: "{{ name | split(',') | regex_search(name_regex) }}"
The name variable is provided with --extra-vars and could be multiple, but usually there are only two of them. They are separated by a comma - hence the split.
The code above works fine with | first and | last instead of regex_search, but I am looking for something more custom.
At the moment, the returned output is (for example):
"msg": [
"fqdn-test-a1.test.company.local",
"fqdn-test-b1.test.company.local"
]
And the desired output should -in this case- be just
"msg": [
"fqdn-test-a1.test.company.local"
]
Thanks in advance for any help or insights you might provide.
I found out the way to do this; the following code works fine for this purpose:
- name: "Debug"
debug:
msg: "{{ name | split(',') | select('search', name_regex) }}"
vars:
name_regex: ".*((-a)+(\\d)?)\\..*\\.company\\..+"
This returns the desired output mentioned above.
With select(), this works fine, as it should create a subset of a list, which is what I needed in this case.

How to provide tab space for ansible variables

I have a task and i am writing the task result to the file. While writing to the file I need to give indentation.
I have tried the below command but it is indenting only the first line of the variable. But, I need the entire lines in the variable need to be indent. Is there anyway to do that?
- name: task
shell: some command
register: result
- shell: 'echo -e \\t{{result.stdout | to_json}} >>file.txt'
As stated in #Seshadri comment, your shell output lines are available either globally in stdout or in a list containing one element for each line in stdout_lines.
You can use the latest to treat each line separately. You don't really have to loop in this case. There are functions to apply the same filter to each element of a list. The following should do the trick to add a tab to each line element:
result.stdout_lines | map('regex_replace', '(.*)', '\\t\\g<1>')
You simply have to join the resulting list with new lines to get your content as expected.
You should use the existing tools in ansible when they exist rather than using shell whenever possible. One reason is that modules will very often handle idempotence for you (e.g. write the file only when content actually changed). In this case, the copy module using the content option (rather than src) is probably the best solution. So your final solution would look like:
- name: Write my result file
copy:
dest: file.txt
content: "{{ result.stdout_lines
| map('regex_replace', '(.*)', '\\t\\g<1>') | join('\n') }}"

Ansible lookup plugin with special variables

Is Ansible lookup plugin able to support special characters ?
example:
vars:
stage: prod
debug:
msg: "{{ lookup('?', 'groups.' + stage + '.index(inventory_hostname)') }}"
If not, is it still possible to solve this problem any other way in order to get the index of the host which belong to a group stage ?
Thanks for the help here.
To get the index of an element in a list, you can indeed use index() in jinja2 as you seem to have already discovered. But there is no need to use a lookup for that (and there is no lookup named ? anyway). To do that on a dynamically named group like in your example, this gives.
debug:
msg: "{{ groups[stage].index(inventory_hostname) }}"
See the documentation on accessing complex variables for more info on the syntax.
Meanwhile, you should be aware that this method will fire an error if the value does not exist in the list (i.e. the host does not exist in the group) and that it cannot be recovered with the default filter like with normal undefined variables.
So if there is any chance your targe host is not in the stage group, you should add some extra jinja2 expression to make sure you always get a value without error (e.g. return -1 if host is not in list):
debug:
msg: "{%if machine in groups[stage] %}{{ groups[stage].index(inventory_hostname) }}{% else %}-1{% endif %}"
Since you mentioned lookups, there is an other solution using the indexed_items plugin. The idea here is to transform the group list in a list of (<index>, <host>) tuples, search tuples having second element equal to the current host, keep only first element of the result, default to a dummy tuple in case the result is empty and print the first element of the retained tuple:
debug:
msg: "{{ (lookup('indexed_items', groups[stage]) | selectattr('1', 'eq', inventory_hostname) | first | default([-1]))[0] }}"

Ansible, counting occurence of a word in a string

I am fairly new to Ansible, and I have been googling about this particular issue described below:
name: GETTING OUTPUT AND STORING IT INTO A VARIABLE
connection: network_cli
cli_command:
command: show configuration interface ge-0/0/0 | display set | match unit
register: A
Above, the task will run the command show configuration interface ge-0/0/0 on Juniper router, the out put will contain a bunch of key words unit. This output is then stored in a variable A.
I want to count the number of occurence key word unit appear in the output and store it in a variable COUNT. How can I do that? Ijust need an example.
Thanks and have a good weekend!!
If you have this task:
- name: get output and store
connection: network_cli
cli_command:
command: show configuration interface ge-0/0/0 | display set | match unit
register: show_config_result
Then you use a subsequent set_fact task to store the value you want in a variable:
- name: store unit count in unit_count variable
set_fact:
unit_count: "{{ (show_config_result.stdout_lines|length)-1 }}"

Ansible jinja2 filters '|'(pipe) what does it mean?

I have written a task as below but can not understand what '|' does?
tasks:
- shell: /usr/bin/foo
register: result
ignore_errors: True
- debug: msg="it failed"
when: result|failed
- debug: msg="it changed"
when: result|changed
Also I have found some examples on web but can not understand what '|' does?
debug: msg={{ ipaddr |replace(",", ".") }}
One more example:
- hosts: localhost
vars:
D:
1 : "one"
2 : "two"
tasks:
- debug: var=D
- debug: msg="D[1] is {{ D[1]|default ('undefined') }}"
Would be great if someone can explain in details or point me to some URL?
Any help would be appreciated.
Thanks.
With the pipe character you pass a value to a filter. There are numerous Jinja 2 filters but Ansible brings some additional filters.
The term filter might be confusing at times because all the filters work very differently. Some for example reduce a result set of a hash/array, some modify contents of a string, but then there are filters which simply return true or false.
A better explanation might be that those are modifiers and they can do anything with your passed data. You can even write your own filters.
Filters can be chained, passing the result from the first filter to the next and so forth. It works exactly like piping commands on a unix shell.
"value" | filter1 | filter2 | filterN
The failed filter returns true if the passed result has failed. It simply checks the failed property from result.
The changed filter is the same, but checks if the passed result has changes. It checks the changed property from result.
ipaddr | replace(",", ".") replaces all occurrences of , with .. So a value of 127,0,0,1 will be transformed to 127.0.0.1.
The default filter will set a default value if the input was null, e.g. an undefined variable. undefined_var | default("var was undefined") -> This will either print the contents of undefined_var or the string "var was undefined". In your given example above you output the value of the 2nd element of D (D[1]) and if it does not exist the sting "undefined" instead.
An update for anyone stumbling across this question trying to work out why lines such as when: result|failed has stopped working,
tl;dr: Try replacing | with is so,
when: result|failed
becomes,
when: result is failed
As of Ansible 2.9 and up ("Using Ansible-provided jinja tests as filters will be removed in Ansible 2.9."), using | in conditionals such as when: result|failed will trigger an error. The | is (was) to get Ansible to use a jinja2 filter, but these are now replaced with jinja2 tests, which have a slightly different syntax, with the old jinja2 filter names retained as jinja2 test names.
In some cases just replacing | with is looks a bit weird, so although (e.g.) when: result is success is valid, success and successful are aliases so when: result is successful looks better.
More detail on this in the Ansible 2.5 Porting Guide.

Resources