Ansible: Insert word in GRUB cmdline - regex

I'd like to use Ansible's lineinfile or replace module in order to add the word splash to the cmdline in GRUB.
It should work for all the following examples:
Example 1:
Before: GRUB_CMDLINE_DEFAULT=""
After: GRUB_CMDLINE_DEFAULT="splash"
Example 2:
Before: GRUB_CMDLINE_DEFAULT="quiet"
After: GRUB_CMDLINE_DEFAULT="quiet splash"
Example 3:
Before: GRUB_CMDLINE_DEFAULT="quiet nomodeset"
After: GRUB_CMDLINE_DEFAULT="quiet nomodeset splash"
The post Ansible: insert a single word on an existing line in a file explained well how this could be done without quotes. However, I can't get it to insert the word within the quotes.
What is the required entry in the Ansible role or playbook in order to add the word splash to the cmdline as shown?

A possible solution is the definition of two entries as follows:
- name: "Checking GRUB cmdline"
shell: "grep 'GRUB_CMDLINE_LINUX_DEFAULT=.*splash.*' /etc/default/grub"
register: grub_cfg_grep
changed_when: false
failed_when: false
- name: "Configuring GRUB cmdline"
replace:
path: '/etc/default/grub'
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT="((\w.?)*)"$'
replace: 'GRUB_CMDLINE_LINUX_DEFAULT="\1 splash"'
when: '"splash" not in grub_cfg_grep'
Explanation: We first check if the splash keyword is present in the required line using grep. Since grep gives a negative return code when a string is not found, we suppress the errors using failed_when: false. The output of grep is saved to the grub_cfg_grep variable.
Next, we bind the replace module to the condition that the keyword splash is in the standard output of grep. The regular expression takes the old content in the quotes and adds the splash keyword behind it.
Note: In the case of an empty string before the execution, the result reads " splash" (with a space in front) but it is still a valid cmdline.

Related

Adding a parameter to a line with Ansible with lineinfile function and regexp

I want to add the parameter
--log-opt max-size=5M
to an existing line within a file and first want to check if this parameter is already set:
regexp: "^OPTIONS=(?!.*?(--log-opt max-size).*)"
I've tested with that teststring, on 3 different online testers it matched as required:
OPTIONS='--log-opt max-size=5M --selinux-enabled --signature-verification=False'
here's the complete playbook:
- name: "adding limiting parameter for container log-sizes"
lineinfile:
path: /etc/sysconfig/docker
backrefs: true
state: present
regexp: "^OPTIONS=(?!.*?(--log-opt max-size).*)"
line: "OPTIONS='--log-opt max-size=5M \\1'"
Ansible breaks with an exception, tried it with Ansible 2.6.5 and 2.7 guess it's rather a synthax than a binary issue.
Have been trying for so long :/
Want also to get the quotes after OPTIONS= to be checked in the regexp
(should be OPTIONS='any content ')
Any ideas ? thanks in advance
(?<=')[^']+
This pattern will get all the line after the quotes on OPTIONS=.
(?<='): Search the ' and pos it after found it.
[^']+ : Match everything one to unlimited times except if found a '.
Said that, if you want to check if the line, you can try another regex, like /--log-opt max-size=/ testing the output line got from the match above.
Found out, that Group 1 doesn't match, the exception was because of that.
Maybe not the smartest way, but worked around it and updated the regex to: ^OPTIONS='(?!.*?(--log-opt max-size))(.*) had to adapt the replacement line as well: OPTIONS='--log-opt max-size=5M\\2.
Complete playbook section now:
- name: "adding limiting parameter for container log-sizes"
lineinfile:
path: /etc/sysconfig/docker
backrefs: true
state: present
regexp: "^OPTIONS='(?!.*?(--log-opt max-size))(.*)"
line: "OPTIONS='--log-opt max-size=5M \\2"

Character escape single quote in a string in Expect Module

i have created a .YML Playbook.
Put some code in it, works fine.
Then i am hit with an issue - i am trying to automate the deployment of a certain application. The application prompts for user interaction. Unfortunatelly, "yes | command" does not work. It simply gets ignored, and still prompts.
So, i decided to use the Expect Module.
Current code looks like this:
- hosts: all
remote_user: someuser
gather_facts: yes
tasks:
- name: "Copy files"
copy: src=../somefiles/ dest=/tmp
- name: "Execute some script"
shell: sudo /tmp/script.sh
- name: "Execute app"
expect:
command: /bin/bash -c 'sudo /tmp/app arguments'
responses:
"'Press 'y' to confirm ('s' to skip, 'a' to abort):'": "y"
echo: y
I have encassed the Expected line, in double quotes. But, since the Expected Line, has Single quotes ('), it seems to be breaking the syntax.
The error output is as follow:
ERROR! Syntax Error while loading YAML.
The error appears to have been in 'deploy.yml': line 16, column 1, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
responses:
"'Press 'y' to confirm ('s' to skip, 'a' to abort):'": "y"
^ here
This one looks easy to fix. It seems that there is a value started
with a quote, and the YAML parser is expecting to see the line ended
with the same kind of quote. For instance:
when: "ok" in result.stdout
Could be written as:
when: '"ok" in result.stdout'
Or equivalently:
when: "'ok' in result.stdout"
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:
foo: "bad" "wolf"
Could be written as:
foo: '"bad" "wolf"'
I have tried both with backslash () to do character escaping for the single quotes, and double quoting the single quotes. None of them worked.
Depending on the order of the quotes i place, i either get This one looks easy to fix. or straight to bad wolf.
Problem are not quotes but special characters in the string. Expect module performs a regex matching, so the response string must be regex-compliant
That means special characters as parenthesis and commas must be escaped
The working example is like that:
/tmp/run.sh:
#!/bin/bash
echo "Press 'y' to confirm ('s' to skip, 'a' to abort):"
read response
echo $response
ansible playbook:
- connection: local
hosts: localhost
tasks:
- name: "Execute app"
expect:
command: /tmp/run.sh
responses:
Press 'y' to confirm \('s' to skip\, 'a' to abort\):: "y"
echo: yes

Ansible: Is it possible to search replace single word

In the lineinfile module, it replaces the full line.
If the line is long I have to repeat the whole line again.
Let us suppose I want to replace the single word in the file:
#abc.conf
This is my horse
this is the playbook:
- lineinfile: dest=abc.conf
state=present
regexp='horse'
line='This is my dog'
backup=yes
is there any way to achieve someting like sed 's/horse/dog/g' ?
You can use backreferences to retrieve other parts(that should not be changed) of the line:
- lineinfile: dest=abc.conf
state=present
regexp='^(.*)horse(.*)$'
line='\1dog\2'
backup=yes
backrefs=yes
New module replace available since 1.6 version:
- replace:
dest=abc.conf
regexp='horse'
replace='dog'
backup=yes
If you need to do more replace operations in one block and you have the file locally, you might want to consider using template, which substitutes variables in the template file and copies the file to the remote:
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf
In the local file you can write a variable with ansible sintax like
{{variable}}
and it will be substituted if it is in the scope of the script. Here the docs.

Copy a section within two keywords into a target file

I have thousand of files in a directory and each file contains numbers of defined variables starting with keyword DEFINE and ending with a semicolon (;), I want to copy all the occurrences of the data between this keyword(Inclusive) into a target file.
Example: Below is the content of the text file:
/* This code is for lookup */
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
END.
Now from the above content i just want to copy the section starting with DEFINE and ending with ; into a target file i.e. the output should be:
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
this needs to done for thousands of scripts and multiple occurences, Please help out.
Thanks a lot , the provided code works, but to a limited extent only when the whole sentence is in a single line but the data is not supposed to be in one single line it is spread in multiple line like below:
/* This code is for lookup */
DEFINE variable as a1 expr= if branchno > 55
then
extract (n123f1 using brach, code)
else
branchno = null
;
END.
The code is also in the above fashion i need to capture all the data between DEFINE and semicolon (;) after every define there will be an ending semicolon ;, this is the pattern.
It sounds like you want grep(1):
grep '^DEFINE.*;$' input > output
Try using grep. Let's say you have files with extension .txt in present directory,
grep -ho 'DEFINE.*;' *.txt > outfile
Output:
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
Short Description
-o will give you only matching string rather than whole line, if line also contains something else and want to ommit it.
-h will suppress file names before matching result
Read man page of grep by typing man grep on your terminal
EDIT
If you want capability to search in multiple lines, you can use pcregrep with -M option
pcregrep -M 'DEFINE.*?(\n|.)*?;' *.txt > outfile
Works fine on my system. Check man pcregrep for more details
Reference : SO Question
One can make a simple solution using sed with version :
sed -n -e '/^DEFINE/{:a p;/;$/!{n;ba}}' your-file
Option -n prevents sed from printing every line; then each time a line begins with DEFINE, print the line (command p) then enter a loop: until you find a line ending with ;, grab the next line and loop to the print command. When exiting the loop, you do nothing.
It looks a bit dirty; it seems that the version sed15 has a shorter (and more straightforward) way to achieve this in one line:
sed -n -e '/^DEFINE/,/;$/p' your-file
Indeed, only for this version of sed, both patterns are treated; for other versions of sed like mine under cygwin, the range patterns must be on separate lines to work properly.
One last thing to remember: it does not treat inclusive patterned ranges, i.e. it stops printing after the first encountered end-pattern even if multiple start patterns have been matched. Prefer something with awk if this is a feature you are looking for.

Match a whole string minus a couple of words from it using Perl

I have a Nagios check that is giving me a string with the space used by each mount on a server (the result of df -h). The string looks like:
/mnt/srv1: 22%used(1129MB/5191MB) /mnt/srv2: 59%used(1344MB/1713MB) /mnt/srv3: 97%used(2409MB/2576MB) /var/backup: 95%used(5976MB/69802MB) /mnt/srv1/dir1: 22%used(119MB/519MB) /: 22%used(119MB/514MB) /mnt/serv1: 59%used(144MB/1710MB) /mnt/srv1/dir2: 88%used(3034MB/3420MB) (>97%) : CRITICAL
I want to match the whole string without let's say the following two parts: /var/backup: 95%used(5976MB/69802MB) and /mnt/serv1: 59%used(144MB/1710MB) and I want a general expression because the strings I want to exculde may change.
The general rule is that I have a path followed by the space specifications of that path. I am trying to learn the regular expressions but it seems that in this case everything I have tried doesn't work.
The check works like this:
check_snmp_storage -H hostname -C comstring -m "this is the regular expression"
for example i have tried to
check_snmp_storage -H hostname -C comstring -m "\/[a-zA-Z0-9:]+"
but it doesn't match what I want
OK I have managed something. The following check:
check_snmp_storage -H hostname -C comstring -m "([\/a-zA-Z0-9]+:\s[\/a-zA-Z0-9\(%]*MB\)|\(>97%\) : CRITICAL)"
does its job and matches the things I'm interested in, but how can I exclude from those matches the ones I have mentioned earlier?

Resources