Validates arguments passed to ansible-playbook with a defined argument specification - linux

We want to run the following playbook with the yml file - ansible-playbook install_os.yml
The playbook install_os.yml is working well , but now we want to add the validation of the arguments <machine name>,<machine IP>.
As the following:
ansible-playbook install_os.yml --limit RHEL01,73.22.3.44
From my point both arguments should be identify as strings (without verify of valid IP) and between <machine name> to <machine IP>, we should set the , separator
So, is it possible to validate the strings? And exit if one of them or both them are not defined?

You can access the limit specified in arguments to ansible-playbook with the help of the special variable ansible_limit.
From there on, you can assert the --limit values based on your business needs.
For example:
- hosts: all
gather_facts: no
tasks:
- assert:
that:
## We have exactly one comma, separating two hosts
- ansible_limit | split(',') | length == 2
## We have a string before the comma
- (ansible_limit | split(',')).0 is string
## We have a non-empty string before the comma
- (ansible_limit | split(',')).0 | length > 0
## We have a string after the comma
- (ansible_limit | split(',')).1 is string
## We have a non-empty string after the comma
- (ansible_limit | split(',')).1 | length > 0
## 'all', which has a wildcard meaning,
## is not one of the two hosts separated by the comma
- "'all' not in ansible_limit | split(',')"
## We do not have any character having a special meaning in the limit
## see: https://docs.ansible.com/ansible/latest/user_guide/intro_patterns.html#common-patterns
- "'#' not in ansible_limit"
- "':' not in ansible_limit"
- "'!' not in ansible_limit"
- "'&' not in ansible_limit"
- "'*' not in ansible_limit"
run_once: true
This would probably limit it to the use case you want.
This said, mind that --limit is an existing flag with its own behaviour, so, based on what you are aiming for, you could also be better with an extra parameter passed in command line.

Related

The bash shell uses the read loop to read the assignment in the text, the space separator problem

The content of the file.txt text is as follows
domain_wisgene.com keyword谷歌搜索 - This,is,keyword,google,search,awesome title谷歌搜索 - Google,Search,is,Awesome
code show as below
while IFS= read -r -d '' now_list_url now_list_keyword now_list_title; do
# Do something with the variables here
echo "domain_name:${now_list_url}"
echo "Key words:${now_list_keyword}"
echo "title:${now_list_title}"
done <file.txt
result
#domain
domain_name:domain_wisgene.com
#keywords
Key words:keyword谷歌搜索
#title
title:- This,is,keyword,google,search,awesome title谷歌搜索 - Google,Search,is,Awesome
wrong result
#domain
domain_name:domain_wisgene.com
#keywrods
Key words:keyword谷歌搜索 - This,is,keyword,google,search,awesome
#title
title:- This,is,keyword,google,search,awesometitle 谷歌搜索 - Google,Search,is,Awesome
I want the correct result
#domain
domain_name:domain_wisgene.com
#keywords
Key words:keyword谷歌搜索 - This,is,keyword,google,search,awesome
#title
title:title谷歌搜索 - Google,Search,is,Awesome"
Looking for help How do I solve this complicated delimiter problem, I want to use the default space as the delimiter, but there are also spaces in the variable value characters, how do I deal with the " - " in the variable without delimiting?
Example: This is one of the lines in file.txt
domain_wisgene.com keyword Google Search - This,is,keyword,google,search,awesome title Google Search - Google,Search,is,Awesome
Pass variable 1 variable 2 variable 3
Variable 1 = domain_wisgene.com
Variable 2 = keyword google search - This,is,keyword,google,search,awesome
Variable 3 = title Google Search - Google,Search,is,Awesome`
I want the correct result
domain_name:domain_wisgene.com
Key words:keyword谷歌搜索 - This,is,keyword,google,search,awesome
title:title谷歌搜索 - Google,Search,is,Awesome
Your delimiter needs are too complex for read. You can maybe use sed to separate the fields like so:
IFS=$'\n' read -rd '' now_list_url now_list_keyword now_list_title < <(sed -E 's/^(<domain-regex>)\s+(<keywords-regex>)\s+(<title-regex>)$/\1\n\2\n\3/g' file.txt)
Explanation:
sed replaces \1, \2, and \3 with the text that 1st, 2nd, and 3rd parentheses-wrapped expressions captured.
I capture the 3 variables then write them again with a newline delimiter. Now they are in a format suitable for read. You can replace the newline with whatever delimiter you know won't be in the input (personally I am fond of \x1F, the Unit Separator).
\s is shorthand for any whitespace character.
You will have to fill in the <regex>s yourself as I don't know what the general rule is that you want.

Filter out content from Ansible output

I want to filter out ip address alone from the ansible output. When I tried to filter out with the awk command, it failed. Please see my code, output, and required output.
- name: Gather all VMs from a specific folder
community.vmware.vmware_vm_info:
hostname: hostname_local
username: vmwarelogin
password: passwordvmware
folder: "/VMFS/"
validate_certs: False
delegate_to: localhost
register: vm_info
- debug:
var: ip
vars:
ip: "{{ vm_info.virtual_machines|
selectattr('guest_name', 'eq', 'My-Machine')|
map(attribute='ip_address')|first }}"
register: ip
- name: add ip
shell: echo "{{ip}}"| awk '{print $2}'
Output after running the above code
{'ip': '192.168.1.32', 'failed': False, 'changed': False}
Expected output is
192.168.1.32
Any help would be appreciated to use this IP address as a variable for other places in the same playbook
If I understand what you want to convey using YAML tag you are implying
{'ip': '192.168.1.32', 'failed': False, 'changed': False}
should be treated as YAML file. If this is case you should if allowed use YAML parser, if you want parser which can be used as part of pipelined command then I suggest trying yq. If you are forced to use AWK for this then be warned that it is best suited for working with entities which belong to Chomsky Type-3 while YAML is not Chomsky Type-3 contraption. Anyway it might suffice for your case. I would propose following heurestic: grab 1st thing which looks like IP address in decimal notation, this could be done following way in GNU AWK let say you are using standard input to deliver
{'ip': '192.168.1.32', 'failed': False, 'changed': False}
then
awk 'BEGIN{FPAT="[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}"}{print $1}'
output
192.168.1.32
Explanation: I use field pattern (FPAT) to inform GNU AWK that field is 1 to 3 digits followed by literal dot followed by 1 to 3 digits followed by dot followed by 1 to 3 digits followed by dot followed by 1 to 3 digits. Note that this does consider solely IP addresses in decimal notation and could also give false positives (e.g. it will find 999.999.999.999) but hopefully your input is not such malicious. I print 1st such field found for each line.
(tested in gawk 4.2.1)
Regarding how to
... use this IP address as a variable for other places in the same playbook?
and reviewing the provided example it looks that you can simplify your case as follows:
---
- hosts: localhost
become: false
gather_facts: false
vars:
IP: "192.168.2.1"
tasks:
- name: Show vars
debug:
var: IP
register: result
- name: Show result
debug:
var: result
- name: Show IP
debug:
msg: "{{ result.IP }}"
Just by using finally echo "{{ ip.ip }}" in your case.
I want to filter out IP address alone from the Ansible output.
There is no need for awk at all as the content is already there and direct accessible.
Expected output is ...
The result would be as required
TASK [Show vars] ***
ok: [localhost] =>
IP: 192.168.2.1
TASK [Show result] ***
ok: [localhost] =>
result:
IP: 192.168.2.1
changed: false
failed: false
TASK [Show IP] ***
ok: [localhost] =>
msg: 192.168.2.1
Further Documentation
Registering variables
Dictionary variables
debug module – Print statements during execution

Extract substring within a given string

I've read and attempted to extract a substring from a given string with awk, sed or grep but I am unable to get it working or think how to accomplish this.
I have the string below which describes configurations of my VMs:
config: diskSizeGb: 100 diskType: pd-standard imageType: COS_CONTAINERD machineType: e2-micro metadata: disable-legacy-endpoints: 'true' preemptible: true status: RUNNING version: 1.19.9
How can I extract a substring for example, "preemptible: true" or "status: RUNNING" knowing that the values can be different for each VM?
Thank you!
Assumptions:
the VM config name/value pairs may not be in the same order
config names and values are single strings with no embedded white space
each config name is preceded by (at least) one space, and followed immediately by a colon (:)
there may be multiple spaces between the colon (:) and the config value; we want to maintain these spaces in the output
One idea using sed and a capture groups:
# note: extra spaces placed between 'version:' and '1.19.9'
cfg_string="config: diskSizeGb: 100 diskType: pd-standard imageType: COS_CONTAINERD machineType: e2-micro metadata: disable-legacy-endpoints: 'true' preemptible: true status: RUNNING version: 1.19.9"
for config in preemptible status version
do
echo "++++++++++++++ ${config}"
sed -nE "s/.* (${config}:[ ]*[^ ]*).*/\1/p" <<< "${cfg_string}"
done
sed details:
-nE - disable default printing of the input (we'll use /p to explicitly print our capture group; enable Extended regex support
.* (${config}:[ ]*[^ ]*).* - match variable number of characters (.*) + a space ( ) + ${config} + a colon (:) + one or more spaces ([ ]*) + everything that follows that is not a space ([^ ]*) + the rest of the input (.*); the parens mark the start/end of the capture group (only one capture group in this case)
\1 - reference capture group #1 (ie, everything inside of the parens)
/p - print (the capture group)
This generates:
++++++++++++++ preemptible
preemptible: true
++++++++++++++ status
status: RUNNING
++++++++++++++ version
version: 1.19.9 # extra spaces maintained
NOTES:
obviously an invalid config name (eg, stat, versions) is going to produce no output
the sed results could be captured in a variable for further testing/processing (would address issue of an invalid config name)
Here a possibile solution:
#!/bin/bash
data="config: diskSizeGb: 100 diskType: pd-standard imageType: COS_CONTAINERD machineType: e2-micro metadata: disable-legacy-endpoints: 'true' preemptible: true status: RUNNING version: 1.19.9"
preemptible=$(echo ${data} | cut -d ' ' -f 14)
echo "preemptible = ${preemptible}"
status=$(echo ${data} | cut -d ' ' -f 16)
echo "status = ${status}"

Unexpected behavior with a string stored in a variable in PowerShell

I'm getting some odd behavior from Excel's Cells.Find() method:
Variable I'm searching on:
PS > $volumename
vol_01       
PS > $volumename.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
produces no results:
PS > $sheet.Cells.Find($volumename).Row
but if I manually copy and paste the value of that variable:
PS > $volumename = "vol_01"
PS > $volumename.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Gets the value I am expecting:
PS > $sheet.Cells.Find($volumename).Row
198
They appear to be exactly the same type in every way to me. This doesn't happen for every case. Some volume names passthrough fine while others do not. I did scrub the volume name for this post as it has a customers naming convention. It is the same format as above and the same format as the volume names that work.
The following snippet can be used to inspect a string for hidden control characters:
PS> & { [int[]] [char[]] $Args[0] |
% { '0x{0:x} [{1}]' -f $_, [char] $_ } } "vol_01`n"
0x76 [v]
0x6f [o]
0x6c [l]
0x5f [_]
0x30 [0]
0x31 [1]
0xa [
]
The first column is each character's Unicode code point ("ASCII code"), and the second column the character itself, enclosed in [...]
Note that I've added "`n" at the end of the string - a newline character (U+000A) - whose code point expressed as a hex. number is 0xa.
If, as in your case, the only unwanted part of the string is trailing whitespace, you can remove it as follows:
$volumename.TrimEnd() # trim trailing whitespace
In your case, the trailing whitespace is 0xa0, the NO-BREAK SPACE (U+00A0), which .TrimEnd() also removes, as Tom Blodget points out.
Simple function wrapper based on the above, for use with pipeline input:
filter debug-Chars { [int[]] [char[]] $_ | % { '0x{0:x} [{1}]' -f $_, [char] $_ } }
Sample use:
"vol_01`n" | debug-Chars
A more sophisticated function, named Debug-String, is available as an MIT-licensed Gist: It uses PowerShell's own escape-sequence notation to represent control characters in the input string, and also visualizes spaces as · by default; adding -UnicodeEscapes uses escape sequences for all non-ASCII-range characters.
Here's an example that first downloads and defines the function:
# Download and define the Debug-String function.
# NOTE:
# I can personally assure you that doing this is safe, but you
# you should always check the source code first.
irm https://gist.github.com/mklement0/7f2f1e13ac9c2afaf0a0906d08b392d1/raw/Debug-String.ps1 | iex
"A b`nThis is a hyphen: '-', this is an en dash '–'" | Debug-String -SingleLine
Output:

Comparing numbers in a text file to a list of numbers in an other textfile

I have a .txt file containing 4-digit numbers.
sometimes they only contain one 4-digit number, sometimes multiple 4-digit numbers, sometimes they are empty.
example1.txt file:
6304
6204
example2.txt file:
6308
example3.txt file:
6305
example4.txt file:
6300
6204
6301
example5.txt file:
6302
6234
6345
What I need to do, is to check if the numbers inside the example file are in a list of numbers I have in an other textfile.
this list looks something like this: (but with more numbers)
6300
6301
6302
6303
6304
6305
*for the 'example1.txt' file:
the number '6204' should be deleted out of the file*(because it's not in the list.)*
the number '6304' must stay in the example file (it is in the list)
*for the 'example2.txt' file:
the number should be deleted and the file should be empty.
*for the 'example3.txt' file:
the number stays in the example file.
*for the 'example4.txt' file:
There is more than 1 match in the example file. so everything should be deleted.
*for the 'example5.txt' file:
Only 6302 should be in the file. the other two should be deleted because they are not in the list.
So basicly I want to keep the files that have 1 single match. and those files should only contain the number that matches a number in the list. If there is more than 1 match, the file should be empty. if there are no matches the file should also be empty
On top of all this, I would like to do it in a sh script.
Now my question is:
Is this even possible and how? or do I need to work with a database and other programming language ?
Thanks in advance.
I think I have understood your logic now. I assume your list is stored in file list.txt and that you save the following as marksscript:
#!/bin/bash
#
# First count total number of matches and store in variable MATCHES
#
MATCHES=0
while read WORD
do
# Count number of matches for this word
N=$(grep -c $WORD list.txt)
[ $N -eq 1 ] && MATCHEDWORD=$WORD
echo DEBUG: $WORD $N
((MATCHES+=N))
done < "$1"
#
# Now we know total number of matches, decide what to do
#
echo DEBUG: Total matches $MATCHES
if [ $MATCHES -ne 1 ]; then
echo DEBUG: Zero out file - not exactly ONE match
> "$1"
else
echo DEBUG: $MATCHEDWORD remains as singleton match
echo $MATCHEDWORD > "$1"
fi
Run like this:
chmod +x marksscript
./marksscript example1.txt
OUTPUT
./go example1
DEBUG: 6204 0
DEBUG: 6304 1
DEBUG: Total matches 1
DEBUG: 6304 remains as singleton match
./go example2
DEBUG: Total matches 0
DEBUG: Zero out file - not exactly ONE match
./go example3
DEBUG: 6305 1
DEBUG: Total matches 1
DEBUG: 6305 remains as singleton match
./go example4
DEBUG: 6300 1
DEBUG: 6204 0
DEBUG: 6301 1
DEBUG: Total matches 2
DEBUG: Zero out file - not exactly ONE one match
This is certainly not the fastest solution but works:
while read line
do
sed -i "s/$line//" example1.txt
done < list_textfile.txt
It deletes every appearance of the string in each line from your "numbers to check" text file.
Update:
This did not what was asked: The above filters out the strings in the list_textfile.txt instead of keeping them.
This should do the right thing:
grep -o -f list_textfile.txt example1.txt
-o makes sure only the matching part is shown in the output
-f allows to specify a file which contains strings to grep for

Resources