I have the following playbook that is trying to assert that a user prompt variable of NX-OS image equals the ansible_net_image captured from a Nexus switch. This is driving me crazy as to the correct format for the test condition.
---
- name: Upgrade NX-OS on switch with pre-checks to load image as required
hosts: nexus-7000
gather_facts: no
vars_prompt:
- name: NX_OS_Upgrade_Version
prompt: "Please enter NX-OS version to upgrade too - ensure that the filename is as used by boot variable"
private: no
##############################################################################
## Start of task execution
##############################################################################
tasks:
- name: Gather IOS configuration and software facts from switches
nxos_facts:
gather_subset: "!hardware"
################################################################################
## Display running image version to terminal
################################################################################
- debug:
msg: "{{ NX_OS_Upgrade_Version }}"
- assert:
that:
- "ansible_net_image | regex:/^bootflash:\\/\\/\\/(.*) == NX_OS_Upgrade_Version"
Upon execution this error is displayed.
fatal: [switcha]: FAILED! => {"msg": "The conditional check 'ansible_net_image | regex:/^bootflash:\/\/\/(.) == NX_OS_Upgrade_Version' failed. The error was: template error while templating string: expected token 'end of statement block', got '/'. String: {% if ansible_net_image | regex:/^bootflash:\/\/\/(.) == NX_OS_Upgrade_Version %} True {% else %} False {% endif %}"}
fatal: [switchb]: FAILED! => {"msg": "The conditional check 'ansible_net_image | regex:/^bootflash:\/\/\/(.) == NX_OS_Upgrade_Version' failed. The error was: template error while templating string: expected token 'end of statement block', got '/'. String: {% if ansible_net_image | regex:/^bootflash:\/\/\/(.) == NX_OS_Upgrade_Version %} True {% else %} False {% endif %}"}
The regex has been tested, but I am struggling to get the correct syntax for the Ansible play.
It would be great if someone could show me what I have got wrong.
Use 7.3.2. Single-Quoted Style to create regex.
See Testing strings.
For example
- hosts: localhost
vars:
ansible_net_image: 'bootflash:///n5000-uk9-kickstart.5.1.3.N2.1b.bin'
NX_OS_Upgrade_Version1: 'n5000-uk9-kickstart.5.1.3.N2.1b'
my_regex1: '^bootflash:///{{ NX_OS_Upgrade_Version1 }}.bin'
NX_OS_Upgrade_Version2: 'n5000-uk9-kickstart.5.1.4.N2.1b'
my_regex2: '^bootflash:///{{ NX_OS_Upgrade_Version2 }}.bin'
tasks:
- assert:
that: ansible_net_image is match(my_regex1)
success_msg: "Image is version {{ NX_OS_Upgrade_Version1 }}"
fail_msg: "Image is NOT version {{ NX_OS_Upgrade_Version1 }}"
- assert:
that: ansible_net_image is match(my_regex2)
success_msg: "Image is version {{ NX_OS_Upgrade_Version2 }}"
fail_msg: "Image is NOT version {{ NX_OS_Upgrade_Version2 }}"
gives
ok: [localhost] => {
"changed": false,
"msg": "Image is version n5000-uk9-kickstart.5.1.3.N2.1b"
}
fatal: [localhost]: FAILED! => {
"assertion": "ansible_net_image is match(my_regex2)",
"changed": false,
"evaluated_to": false,
"msg": "Image is NOT version n5000-uk9-kickstart.5.1.4.N2.1b"
}
Related
On an Ansible playbook, I'm trying to execute a shell command only if a service exist on the remote server.
I have 3 tasks :
service_facts
execution of shell command if tomcat is installed
display the output of the shell command if tomcat is installed
Here is my code :
- name: Get Infos
hosts: all
gather_facts: yes
become: false
remote_user: [MY_USER]
tasks:
- name: Get the list of services
service_facts:
- name: Get version of Tomcat if installed
become: true
shell: 'java -cp /opt/tomcat/lib/catalina.jar org.apache.catalina.util.ServerInfo | grep "Server version"'
register: tomcat_version
when: "'tomcat.service' in services"
- debug: msg="{{ tomcat_version.stdout_lines }}"
when: "'tomcat.service' in services"
The problem is on certains servers the service name is, for example, tomcat-8.1
How can i use regex in the when condition?
I tried regex(), regex_search(), either I'm doing it wrong or I don't know how to do it.
Have you any idea how to do it?
Thanks in advance!
Count matching items. For example
- service_facts:
- block:
- shell: smartctl --version | head -1
register: smart_version
- debug:
msg: "{{ smart_version.stdout_lines }}"
when: _srvcs|length > 0
vars:
_regex: '.*smart.*'
_srvcs: "{{ services|select('match', _regex) }}"
gives
msg:
- smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-73-generic] (local build)
The next option is to intersect the list of services, e.g.
when: _srvcs|length > 0
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
Debug
Q: "It gives me a failure on the server where my service doesn't exist, cause the playbook still tries to execute the shell. Is it normal?"
A: No. It is not normal. Print debug and find out why the condition evaluates to true, i.e. what service(s) match either the regex or the list. For example
- debug:
msg: |
_srvcs:
{{ _srvcs|to_nice_yaml|indent(2) }}
when: debug|d(false)|bool
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
gives
msg: |-
_srvcs:
- smartmontools.service
To enable the task run the playbook with the option -e debug=true.
I'm facing 2 problems:
Problem 1.
I'm trying to filter a list with jinja2 regex_search but I alse get None matches.
Problem 2.
Each elements of the new list seems to be list of one element (sigth!!!).
My code.
- name: Regex_Search Test
hosts: localhost
vars:
my_list:
- app-be-dev01-2
- app-be-dev02-2
- app-be-dev02-3
- app-be-dev03-2
- app-foo-2
- app-be-dev04-1
- app-be-dev04-2
tasks:
- name: Varsmng
set_fact:
customer_instances: >-
{% for instance in my_list -%} {{ customer_instances | default([]) + [ instance | string | regex_search('app-be-(.*)-([0-9]*)', '\1' ) ] }}
{%- endfor %}
- name: Debug
debug:
msg:
- "customer_instances: {{ customer_instances }}"
My output.
TASK [Varsmng] ****************************************************************************************************************************************
task path: /home/cin0633a/progetti/ansible/testenv/test.yml:19
ok: [localhost] => {
"ansible_facts": {
"customer_instances": "[[u'dev01']][[u'dev02']][[u'dev02']][[u'dev03']][None][[u'dev04']][[u'dev04']] "
},
"changed": false
As you can see, each element has a double square brackets. And can I avoid None values?
You get a list for each element because regex_search returns a list when you use the replace feature with capture groups in the expression.
$ ansible localhost -m debug -e toto=bla-bli-blo -a "msg={{ toto | regex_search('(bla).*') }}"
localhost | SUCCESS => {
"msg": "bla-bli-blo"
}
$ ansible localhost -m debug -e toto=bla-bli-blo -a "msg={{ toto | regex_search('(bla).*', '\\1') }}"
localhost | SUCCESS => {
"msg": [
"bla"
]
}
And you get None values because some items do not match your regex.
You can get your result with a better approach IMO using specific filters rather than a complex jinja2 template. The following playbook:
- name: Regex_Search Test
hosts: localhost
gather_facts: false
vars:
my_list:
- app-be-dev01-2
- app-be-dev02-2
- app-be-dev02-3
- app-be-dev03-2
- app-foo-2
- app-be-dev04-1
- app-be-dev04-2
searchreg: >-
app-be-(.*)-([0-9]*)
my_filtered_list: >-
{{
my_list |
select('regex', searchreg) |
map('regex_replace', searchreg, '\1')
}}
tasks:
- debug:
var: my_filtered_list
Gives:
PLAY [Regex_Search Test] *****************************************************************************
TASK [debug] *****************************************************************************
ok: [localhost] => {
"my_filtered_list": [
"dev01",
"dev02",
"dev02",
"dev03",
"dev04",
"dev04"
]
}
PLAY RECAP *****************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note1: you can pipe the unique filter if you want unique environment results
Note2: if you are using ansible < 2.10, you will have to add the list filter at the end of all others to get the actual result.
I have 3 tasks..
first task checks if a file contains <ip> <hostname> pattern
second task adds a line if the sought after string is not present.
third task corrects the line if it is bad.
the 3 tasks run well independently but I want to run them together somehow linked.
I have the following playbook using as model /etc/hosts.
---
- name: check hosts playbook
hosts: centos
tasks:
- name: check whether a line in the form of '<ip> <hostname>' exists
lineinfile:
path: /var/tmp/hosts
regexp: '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s\w+'
state: absent
check_mode: true
register: line_exists
- name: append_host_file
lineinfile:
path: /var/tmp/hosts
insertafter: '^(127\.0\.0\.1|)(?:\d{1,3}\.){3}\d{1,3}'
line: '{{ ansible_default_ipv4.address }} {{ansible_hostname }}'
backup: yes
when: not line_exists.changed
- name: correct_hosts_file
lineinfile:
path: /var/tmp/hosts
regexp: '^(?!{{ ansible_default_ipv4.address }}\s{{ ansible_hostname }})(?:\d{1,3}\.){3}\d{1,3}\s\w+'
line: '{{ ansible_default_ipv4.address }} {{ansible_hostname }}'
when: line_exists.changed
the issue i have is the correct task is running when the line is correct.. so i need to use some other sort of criteria to prevent it from running when the line in the file is correct...if the line in the file is wrong it work because it replaces it.
It's a common problem with lineinfile, it's not that useful as it looks.
My advice: load file content into variable (- command: cat /etc/hosts), register it (register: old_hosts) than iterate over each line of that variable in a template.
- name: get hosts
command: cat /etc/hosts
register: old_hosts
- name: write hosts
template:
src: hosts.j2
dest: /etc/hosts
hosts.j2:
{% for line in old_hosts.stdout_lines %}
{% if line (....) %}
...
{% endif %}
{% endfor %}
I have a users.ini file having below content:
[integration]
# My integration information
user=gertrude
pass=anotherpassword
I am trying to fetch the value in my below yml file using lookup plugin for INI:
- hosts: "{{vnf_ip}}"
connection: local
tasks:
debug: msg="User in integration is {{ lookup('ini', 'user section=integration file=users.ini') }}"
But I am getting output as
TASK [debug] ***********************************************************************************************************************************
ok: [10.10.10.10] => {
"msg": "User in integration is ['gertrude']"
}
Instead of ['gertrude'] it should simply be gertrude.
How to get gertrude simply????
What Ansible version do you use? On modern 2.3.2 it works as expected and returns just gertrude.
If you can't upgrade, you can use first filter to get an element from your resulting list:
{{ lookup('ini', 'user section=integration file=users.ini') | first }}
My problem is with ansible and parsing stdout. I need to capture the stdout from an ansible play and parse this output for a specific substring within stdout and save into a var. My specific use case is below
- shell: "vault.sh --keystore EAP_HOME/vault/vault.keystore |
--keystore-password vault22 --alias vault --vault-block |
vb --attribute password --sec-attr 0penS3sam3 --enc-dir |
EAP_HOME/vault/ --iteration 120 --salt 1234abcd"
register: results
become: true
This generates an output with the following line, the goal is to capture the masked key that jboss vault generates and save that in an ansible var so I can use it to configure the standalone.xml template:
vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/>
I need a way parse this string with possibly regex and save the "MASK-5dOaAVafCSd" substring into an ansible var using set_facts module or any other ansible module.
Currently my code looks like this
#example stdout
results: vault-option name=\"KEYSTORE_PASSWORD\" value=\"MASK-5dOaAVafCSd\"/>
- name: JBOSS_VAULT:define keystore password masked value variable
set_fact:
masked_value: |
"{{ results.stdout |
regex_replace('^.+(MASK-.+?)\\.+','\\\1') }}"
This code is defining masked_value as the results.stdout, not the expected capture group.
You are very close. I advice you to use regex101.com to test regular expressions.
Here is my solution:
---
- hosts: localhost
gather_facts: no
tasks:
- shell: echo 'vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"'
register: results
- set_fact:
myvalue: "{{ results.stdout | regex_search(regexp,'\\1') }}"
vars:
regexp: 'value=\"([^"]+)'
- debug:
var: myvalue
result:
ok: [localhost] => {
"myvalue": [
"MASK-5dOaAVafCSd"
]
}
Update:
regex_search returns a list of found matches, so to get only first one use:
{{ results.stdout | regex_search(regexp,'\\1') | first }}
The above solution worked for me, however I had to do some extra logic to filter shell command output to get to the line which contains following
<vault-option name="KEYSTORE_PASSWORD" value="MASK-6qcNdkIprlA"/>
because vault command output has many lines in it. Once this line is captured, the solution given by Konstantin works just fine. Below is the whole thing that needs to done in one place.
- name: Creating jboss vault
shell: |
{{ baseDir }}/bin/vault.sh -e {{ vaultDir }} -k {{ keystoreURL }} -p {{ keystorePassword }} \
-s {{ keystoreSalt }} -i {{ iterationCount }} -v {{ keystoreAlias }} -b {{ vaultBlock }} \
-a {{ attributeName }} -x {{ attributeValue }}
register: vaultResult
- set_fact:
jbossKeystorePassword: "{{ item | regex_search('value=\"([^\"]+)','\\1') | first }}"
when: item | trim | match('.*KEYSTORE_PASSWORD.*')
with_items:
- "{{ vaultResult.stdout_lines }}"
- debug:
var: jbossKeystorePassword
Be sure to replace all variables with your values in above vault.sh command.