i have a simple ansible playbook that sets two ini variables.
- name: set Apache timeout
community.general.ini_file:
path: /etc/apache2/apache2.conf
section: null
option: Timeout
value: 900
state: present
exclusive: true
- name: set Proxy timeout
community.general.ini_file:
path: /etc/apache2/apache2.conf
section: null
option: ProxyTimeout
value: 900
state: present
exclusive: true
Problem is that it sets them like
Timeout = 900
ProxyTimeout = 900
But i need them to be set like, WITHOUT "="
Timeout 900
ProxyTimeout 900
EDIT This fixed it.
- name: set Timeout
ansible.builtin.lineinfile:
path: /etc/apache2/apache2.conf
regexp: '^Timeout '
insertafter: '^#Timeout '
line: Timeout 900
- name: set Proxy timeout
ansible.builtin.lineinfile:
path: /etc/apache2/apache2.conf
regexp: '^ProxyTimeout '
insertafter: '^Timeout '
line: ProxyTimeout 900
As far as I know, this file is not following an INI structure. Therefore, the "ini_file" module might be the wrong one.
Personally, I'd recommend using the "lineinfile" module with regex.
(Copied this example from the module page - use the link above for additional information)
- name: Ensure the default Apache port is 8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080
Related
I have a /etc/rhsm/rhsm.conf file with this block:
[rhsm]
# Content base URL:
baseurl=https://satellite.bcnconsulting.com
I want to check whether the line from baseurl ends with /pulp/repos or not. In case it doesn't add it to the end of the line.
The end result should be:
[rhsm]
# Content base URL:
baseurl=https://satellite.bcnconsulting.com/pulp/repos
For now I have,
- name: Check if baseurl in rhsm.conf is correct
shell: "grep -nr baseurl /etc/rhsm/rhsm.conf | grep -nr /pulp /etc/rhsm/rhsm.conf"
ignore_errors: yes
register: result
- name: If it has not the /pulp/repos ending add it
lineinfile:
path: /etc/rhsm/rhsm.conf
line: '/pulp/repos'
regexp: 'baseurl'
when: result.stdout == ""
But this is just removing the line and writing /pulp/repos.
How can I accomplish to put it just after the last letter of the line?
That is the problem regex "backreferences" are designed to solve
- name: If it has not the /pulp/repos ending add it
lineinfile:
path: /etc/rhsm/rhsm.conf
line: '\1/pulp/repos'
regexp: '^(baseurl=https://satellite.bcnconsulting.com)$'
backrefs: yes
when: result.stdout == ""
I am modifying /boot/cmdline.txt to add container features to a Raspberry Pi, so I need to add cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory into the file, within the same line.
I am trying to do it with the lineinfile module without much success:
- hosts: mypi
become: yes
tasks:
- name: Enable container features
lineinfile:
path: /boot/cmdline.txt
regex: " cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory"
line: " cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory"
insertafter: EOF
state: present
I have been trying modifying the insertafter to BOF, using insertbefore too, using a regex to match the last word... But it ends up adding a carriage return. I have been unable to find some way to not add a new line.
As Vladimir pointed out, Jack's answer unfortunately is not sufficient for empty files and also fails if the desired argument already exists at the beginning of the line.
The following suggested solution should address those issues. In particular, it is supposed to
support empty files,
support existing arguments at any position within the string,
be robust even with multi-line files (just in case...),
be idempotent, and
optionally update existing keys with the desired value.
# cmdline.yml
- name: read cmdline.txt
become: true
slurp: "src={{ cmdline_txt_path }}"
register: result_cmdline
- name: generate regular expression for existing arguments
set_fact:
regex_existing: '{{ "\b" + key|string + "=" + ("[\w]*" if update else value|string) + "\b" }}'
key_value_pair: '{{ key|string + "=" + value|string }}'
- name: generate regular expression for new arguments
set_fact:
regex_add_missing: '{{ "^((?!(?:.|\n)*" + regex_existing + ")((?:.|\n)*))$" }}'
- name: update cmdline.txt
become: true
copy:
content: '{{ result_cmdline.content
| b64decode
| regex_replace(regex_existing, key_value_pair)
| regex_replace(regex_add_missing, key_value_pair + " \1")
}}'
dest: "{{ cmdline_txt_path }}"
Usage:
- set_fact:
cmdline_txt_path: /boot/cmdline.txt
- include_tasks: cmdline.yml
vars:
key: cgroup_enable
value: memory
update: false
# will add the argument if the key-value-pair doesn't exist
- include_tasks: cmdline.yml
vars:
key: cgroup_enable
value: cpu
update: false
- include_tasks: cmdline.yml
vars:
key: cgroup_memory
value: 1
update: true
# will replace the value of the first matching key, if found;
# will add it if it's not found
However, I might have missed some edge cases - please let me know if you find any issues.
Since you only have the one line in the file, you can do that with either replace or lineinfile. Here is the replace version:
- name: Enable container features
replace:
path: cmdline.txt
regexp: '^([\w](?!.*\b{{ item }}\b).*)$'
replace: '\1 {{ item }}'
with_items:
- "cgroup_enable=cpuset"
- "cgroup_memory=1"
- "cgroup_enable=memory"
Stole the answer from here
I followed all of the different strategies laid out above, but in the end, I wanted something simple, as this is my first playbook, and I need to understand it now, and when I pick it up again later,.
My cmdline.txt contained multiple lines:
cat /boot/cmdline.txt -E
console=serial0,115200 console=tty1 rootfstype=ext4 fsck.repair=yes rootwait cgroup_enable=memory cgroup_enable=cpuset cgroup_memory=1$
dtoverlay=pi3-disable-bt$
dtoverlay=pi3-disable-wifi$
So, the approach I was looking for:
Would ignore the other configurations in the cmdline.txt
Would only add the add a specific key=value if it was missing
It had to be idempotent
I settled on a simple regex to decide if this was the row I wanted to edit:
If the row contaied console= (as this is the row I'm after)
AND.. If the row does not contain cgroup_memory=1
- name: Adding cgroup_enable=memory to boot parameters for k3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_enable=memory).*console.*)$'
line: '\1 cgroup_enable=memory'
backrefs: yes
notify: reboot
- name: Adding cgroup_enable=cpuset to boot parameters for K3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_enable=cpuset).*console.*)$'
line: '\1 cgroup_enable=cpuset'
backrefs: yes
notify: reboot
- name: Adding cgroup_memory=1 to boot parameters for K3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_memory=1).*console.*)$'
line: '\1 cgroup_memory=1'
backrefs: yes
notify: reboot
And at some point in future, I'll probably condense all three of these into a single loop task. But not today.
Q: "Ansible lineinfile module: Do not add new line. Find some way to not add a new line."
A: It's not possible. New line will be always added by module lineinfile. See source for example
b_lines.insert(index[1], b_line + b_linesep)
This is how a new line is added. Such additions will be terminated with b_linesep. See how the variable is defined
b_linesep = to_bytes(os.linesep, errors='surrogate_or_strict')
The os.linesep is used when you want to iterate through the lines of a text file. The internal scanner recognizes the os.linesep and replaces it with a single "\n".
See What is os.linesep for?.
The task with the module replace doesn't solve this problem either. Neither it creates the line without a newline, nor it modifies existing one this way. In addition to this it's not idempotent.
- name: Enable container features
replace:
path: cmdline.txt
regexp: '^([\w](?!.*\b{{ item }}\b).*)$'
replace: '\1 {{ item }}'
loop:
- "cgroup_enable=cpuset"
- "cgroup_memory=1"
- "cgroup_enable=memory"
It will do nothing if the file is empty
TASK [Enable container features]
ok: [localhost] => (item=cgroup_enable=cpuset)
ok: [localhost] => (item=cgroup_memory=1)
ok: [localhost] => (item=cgroup_enable=memory)
If the line is present in the file this task will change it
shell> cat cmdline.txt
cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
ASK [Enable container features] *****************************************
ok: [localhost] => (item=cgroup_enable=cpuset)
--- before: cmdline.txt
+++ after: cmdline.txt
## -1 +1 ##
-cgroup_memory=1 cgroup_enable=memory cgroup_enable=cpuset
+cgroup_memory=1 cgroup_enable=memory cgroup_enable=cpuset cgroup_memory=1
I need to add a block of the code into the configuration file on the remote servers if the content already exists in the conf file it should not add to the configuration file.
The code is working fine when the values are fixed but the timeout value and session value changes from one server to other for example on the first server Timeout are 100 and on the second server, it is 150 in that case code is not working.
- shell: cat /tmp/httpd.conf | egrep -i "Timeout 100| session 200"| wc -l
register: test_grep
- debug: var=test_grep.stdout
- blockinfile:
path: /tmp/httpd.conf
block: |
Timeout 100
session 200
when test_grep.stdout == "0"
Expected value always should be
Timeout 100
Session 200
One option would be to use the lineinfile module to remove any matching lines first. For example:
- lineinfile:
path: /tmp/httpd.conf
state: absent
regexp: '^ *(Timeout|Session) \d+'
- blockinfile:
path: /tmp/httpd.conf
block: |
Timeout 100
Session 200
This would remove any Timeout or Session lines from the configuration, and then add your desired block. The downside to this solution is that it will always result in a change.
If you don't want that, you could potentially do this using only lineinfile, like this:
- lineinfile:
path: /tmp/httpd.conf
state: present
regexp: '^ *{{ item.0 }} \d+'
line: '{{ item.0 }} {{ item.1 }}'
loop:
- [Timeout, 100]
- [Session, 200]
This has the advantage that the task won't show any changes if the file already contains the lines you want.
We have some redis configurations that only differs on port and maxmemory settings, so I'm looking for a way to write a 'base' config file for redis and then replace the port and maxmemory variables.
Can I do that with Ansible?
For such operations usually lineinfile module works best; for example:
- name: Ensure maxmemory is set to 2 MB
lineinfile:
dest: /path/to/redis.conf
regexp: maxmemory
line: maxmemory 2mb
Or change multiple lines in one task with with_items:
- name: Ensure Redis parameters are configured
lineinfile:
dest: /path/to/redis.conf
regexp: "{{ item.line_to_match }}"
line: "{{ item.line_to_configure }}"
with_items:
- { line_to_match: "line_to_match", line_to_configure: "maxmemory 2mb" }
- { line_to_match: "port", line_to_configure: "port 4096" }
Or if you want to create a base config, write it in Jinja2 and use a template module:
vars:
redis_maxmemory: 2mb
redis_port: 4096
tasks:
- name: Ensure Redis is configured
template:
src: redis.conf.j2
dest: /path/to/redis.conf
with redis.conf.j2 containing:
maxmemory {{ redis_maxmemory }}
port {{ redis_port }}
The best way i've found to do this (and i use the same technique everywhere)
is to create a role redis with a default vars file and then override the vars when you call the role.
So in roles/redis/default/main.yml :
redis_bind: 127.0.0.1
redis_memory: 2GB
redis_port: 1337
And in your playbook :
- name: Provision redis node
hosts: redis1
roles:
- redis:
redis_port: 9999
redis_memory: 4GB
- name: Provision redis node
hosts: redis2
roles:
- redis:
redis_port: 8888
redis_memory: 8GB
I'm trying insert a line in a property file using ansible.
I want to add some property if it does not exist, but not replace it if such property already exists in the file.
I add to my ansible role
- name: add couchbase host to properties
lineinfile: dest=/database.properties regexp="^couchbase.host" line="couchbase.host=127.0.0.1"
But this replaces the property value back to 127.0.0.1 if it exists already in the file.
What I'm doing wrong?
The lineinfile module ensures the line as defined in line is present in the file and the line is identified by your regexp. So no matter what value your setting already has, it will be overridden by your new line.
If you don't want to override the line you first need to test the content and then apply that condition to the lineinfile module. There is no module for testing the content of a file so you probably need to run grep with a shell command and check the .stdout for content. Something like this (untested):
- name: Test for line
shell: grep -c "^couchbase.host" /database.properties || true
register: test_grep
And then apply the condition to your lineinfile task:
- name: add couchbase host to properties
lineinfile:
dest: /database.properties
line: couchbase.host=127.0.0.1
when: test_grep.stdout == "0"
The regexp then can be removed since you already made sure the line doesn't exist so it never would match.
But maybe you're doing things back to front. Where does that line in the file come from? When you manage your system with Ansible there should be no other mechanisms in place which interfere with the same config files. Maybe you can work around this by adding a default value to your role?
This is possible by simply using lineinfile and check_mode:
- name: Check if couchbase.host is already defined
lineinfile:
state: absent
path: "/database.properties"
regexp: "^couchbase.host="
check_mode: true
changed_when: false # This just makes things look prettier in the logs
register: check
- name: Define couchbase.host if undefined
lineinfile:
state: present
path: "/database.properties"
line: "couchbase.host=127.0.0.1"
when: check.found == 0
This is the only way I was able to get this to work.
- name: checking for host
shell: cat /database.properties | grep couchbase.host | wc -l
register: test_grep
- debug: msg="{{test_grep.stdout}}"
- name: adding license server
lineinfile: dest=/database.properties line="couchbase.host=127.0.0.1"
when: test_grep.stdout == "0"
By a long way of "Trials and errors" I come to this:
- name: check existence of line in the target file
command: grep -Fxq "ip addr add {{ item }}/32 dev lo label lo:{{ app | default('app') }}" /etc/rc.local
changed_when: false
failed_when: false
register: ip_test
with_items:
- "{{ list_of_ips }}"
- name: add autostart command
lineinfile: dest=/etc/rc.local
line="ip addr add {{ item.item }}/32 dev lo label lo:{{ app | default('app') }}"
insertbefore="exit 0"
state=present
when: item.rc == 1
with_items:
- "{{ ip_test.results }}"
Looks like it does not work if you use backrefs.
Following sample does not add a line
- name: add hosts file entry
lineinfile:
path: "/etc/hosts"
regexp: "foohost"
line: "10.10.10.10 foohost"
state: present
backrefs: yes
After removing backrefs I got my line added to the file
- name: add hosts file entry
lineinfile:
path: "/etc/hosts"
regexp: "foohost"
line: "10.10.10.10 foohost"
state: present
Same idea as presented here : https://stackoverflow.com/a/40890850/7231194
Steps are:
Try to replace the line.
If replace mod change it, restore
If replace mod doesn't change, add the line
Example
# Vars
- name: Set parameters
set_fact:
ipAddress : "127.0.0.1"
lineSearched : "couchbase.host={{ ipAddress }}"
lineModified : "couchbase.host={{ ipAddress }} hello"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line not is here, I add it
- name: Add line
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed == false
# If the line is here, I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
Ok, here is mine naive solution... probably not a cross-platform and native Ansible (I've just started to use this tool and still learn it), but definitely shorter:
- name: Update /path/to/some/file
shell: grep -q 'regex' /path/to/some/file && echo exists || echo 'text-to-append' >> /path/to/some/file
register: result
changed_when: result.stdout.find('exists') == -1
We have tried the below and it worked well. our scenario need to entry "compress" in syslog file if it does not exist.
- name: Checking compress entry present if not add entry
lineinfile:
path: /etc/logrotate.d/syslog
regexp: " compress"
state: present
insertafter: " missingok"
line: " compress"
- name: add couchbase.host to properties, works like add or replace
lineinfile:
path: /database.properties
regexp: '^couchbase.host=(?!(127.0.0.1))\b'
line: 'couchbase.host=127.0.0.1'
This code will replace any line ^couchbase.host=* except couchbase.host=127.0.0.1 or will add new line if it does not exist
- name: add couchbase.host to properties, works like add or replace
lineinfile:
state: present
dest: /database.properties
regexp: '^couchbase.host'
line: 'couchbase.host=127.0.0.1'