How to provide tab space for ansible variables - ansible

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') }}"

Related

Ansible tower survey transforming survey string values

I currently pass parameters to my script which an ansible tower job template/role calls. In order to make it more user friendly, I have decided to use a survey to do this. The script takes on filenames as parameters.
The script accepts the parameters in this format
'file1.txt','file2.txt','file3.txt'
However, here is the problem
The file could be one, it could be 2 or three up to possibly 5.
I have thought about a solution and I think the best design is to have a comma delimited list of files coming from ansible survey, for example file1, file2, file3
How can I have a logic whereby with the list of files, they can be split and a loop used to copy them one by one if there are more than one file provided in the list, then have a variable that will add single quotes and a comma to the file list. For example in the survey the values provided such as file1.txt, file2.txt, file3.txt will then be transformed into a variable which contains the following
'file1.txt','file2.txt','file3.txt'
The other issue is this.
The ansible role copies the given file name onto a directory, I know the split function can be used to split a comma separated list, how can I then copy them onto a folder in a loop ? If we look at the example below, it only works for a single file.
EDIT.
I have looked at the split function and combining it with a loop. I get an error when I run it. Template error while templating string
---
- name: Set file name
set_fact:
file1: "file1.txt"
file_list: "file1.txt"
- name: Set working directory
set_fact:
standard_path: "{{ansible_user_dir}}\\execution"
content_file: "{{standard_path}}\\{{file1}}"
- name: Copy file to working directory
win_copy:
src: "file1.txt"
dest: "{{content_file}}"
- name: Set parameters for script
set_fact:
params: "-filenames '{{content_file}}'"
- name: Run a loop to copy the files.
win_copy:
src: "{{ item }}"
dest: "{{standard_path \\ item }}"
with_items: "{{file_list.split(',') }}"
The section within the code that was throwing an exception has now been fixed.
The jinja2 standard requires the variables to be in separate {{}} as seen below.
- name: Run a loop to copy the files.
win_copy:
src: "{{ item }}"
dest: "{{standard_path}}\\{{item }}"
with_items: "{{file_list.split(',') }}"

Ansible: to get specific lines from stdout_lines of ansible playbook

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

How to loop not recursively over directories of directory with Ansible 2.x

Is there an other way than this one to loop not recursively over directories of a directory at root_dir:
- name: loop over directories
debug: var=item
with_filetree: "{{root_dir}}"
when: (item.path.split('/') | length == 1) and (item.state == 'directory')
Because this seems quite far-fetched, maybe there is a better way (that doesn't loop deeply over directories)?
Note: I also tried with a combination of with_fileglob and item.state = 'directory' but without success.
Edits:
Using a bash command and then parsing it with Ansible (like find as #techraf proposed in comments, or ls -l | grep '^d') can also answer the need but I was more asking for a use of an Ansible module different than shell or command.
Either use command: find with an argument to limit the search depth (afair, they differ in GNU and BSD's versions of find).
Or use two Ansible's native find tasks, both with recurse: false:
first one: plain with file_type: directory in your root_dir
second one: in a loop over the results of the first one with paths: "{{ item }}"
Because of a loop in the second task, you'll end up with results stored in a hierarchy, so to access flattened results use the following template (see this answer):
{{ second_task.results | sum(attribute='files', start=[]) | map(attribute='path') | list }}
You can add a third set_fact-task to simplify referring to the variable in subsequent tasks.

How do I loop over each line inside a file with ansible?

I am looking for something that would be similar to with_items: but that would get the list of items from a file instead of having to include it in the playbook file.
How can I do this in ansible?
I managed to find an easy alternative:
- debug: msg="{{item}}"
with_lines: cat files/branches.txt
Latest Ansible recommends loop instead of with_something. It can be used in combination with lookup and splitlines(), as Ikar Pohorský pointed out:
- debug: msg="{{item}}"
loop: "{{ lookup('file', 'files/branches.txt').splitlines() }}"
files/branches.txt should be relative to the playbook
Lets say you have a file like
item 1
item 2
item 3
And you want to install these items. Simply get the file contents to a variable using register. And use this variable for with_items. Make sure your file has one item per line.
---
- hosts: your-host
remote_user: your-remote_user
tasks:
- name: get the file contents
command: cat /path/to/your/file
register: my_items
- name: install these items
pip: name:{{item}}
with_items: my_items.stdout_lines
I am surprised that nobody mentioned the ansible Lookups, I think that is exactly what you want.
It reads contents that you want to use in your playbook but do not want to include inside the playbook from files, pipe, csv, redis etc from your local control machine(not from remote machine, that is important, since in most cases, these contents are alongside your playbook on your local machine), and it works with ansible loops.
---
- hosts: localhost
gather_facts: no
tasks:
- name: Loop over lines in a file
debug:
var: item
with_lines: cat "./files/lines"
with_lines here is actually loop with lines lookup, to see how the lines lookup works, see the code here, it just runs any commands you give it(so you can give it any thing like echo, cat etc), then split the output into lines and return them.
There are many powerful lookups, to get the comprehensive list, check out the lookup plugins folder.

Using ansible, what is the best way to add a line to an existing file that depends on the value of a variable passed by --extra-vars?

I have a numerical config (e.g., number of milliseconds for some config) that needs to be set in a standard system file. I don't want to keep the whole config file in version control since it's part of a standard install. Is there a way to add a line to a file and have some variable replacement text in the line that can depend on a specified variable (e.g., passed via command line when the playbook is run using --extra-vars.
For example, something like the following (my best effort so far):
- name: Set ring delay
lineinfile:
dest: /etc/cassandra/cassandra-env.sh
state: present
regexp: 'JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms=.*"'
line: 'JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms=${ring_delay}"'
backrefs: yes
when: ring_delay is defined
where the playbook is executed with ansible-playbook -e "ring_delay=10000"
The above example works fine if I don't have variable value for the config (e.g., I just hard code line: 'JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms=10000"', but I would like to be able to specify the value manually from the command line when I run the playbook. Is there a good way to do this? Ideally, rerunning the playbook would overwrite the ring_delay with the new value
EDIT: From this link, It appears that the ${ring_delay} notation I used above is not a feature of ansible, though there are a couple of examples on the web that suggest there is some related functionality for string replacement. The docs refer to "named backreferences", but I'm not sure what those are.
The proper syntax for interpolation is '{{ var }}'. The '${ var }' syntax has been deprecated for some time now.
Changing your task like below should do it :
- name: Set ring delay
lineinfile:
dest: /etc/cassandra/cassandra-env.sh
state: present
regexp: 'JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms=.*"'
line: 'JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms={{ring_delay}}"'
when: ring_delay is defined
You don't need backrefs here since there are no catching groups in the regexp.
Good luck.

Resources