I'm creating a playbook for an ACL update, where the existing ACL needs to be updated, but before adding the new set of IP addresses to that ACL, I need to make sure that the ACL is present and that the IP hasn't already been configured.
Process:
Need to add the below IP addresses
ACL NAME: 11, 13, DATA_TEST, dummy
Check if the list of ACL are present
commands: "show access-lists {{item}}"
Check if ACL Exist
Q: Can't figure out how to access each item in the result of the first action to see if ACL has been configured. Ex. We can see from the output that dummy has no output, how can I exclude that and process if exist. (refer code below)
Check if IP addresses already added
Q: What is the best approach here? I'm thinking using when then comparing the ACL output from stdout vs the given variables content (ex. parents/lines)?
Add the set of IP addresses on target ACL
Q: What is the best approach here? Need to match the ACL name and configure using the variable.
If somebody is knowledgeable about Ansible, perhaps you could assist me in creating this project? I'm still doing some research, so any assistance you can give would be greatly appreciated. Thanks
My Code:
---
- name: Switch SVU
hosts: Switches
gather_facts: False
vars:
my_acl_list:
- 11
- 13
- DATA_TEST
- dummy
fail: "No such access-list {{item}}"
UP_ACL11:
parents:
- access-list 11 permit 192.168.1.4
- access-list 11 permit 192.168.1.5
UP_ACL13:
parents: access-list 13 permit 10.22.1.64 0.0.0.63
UP_ACLDATA:
lines:
- permit 172.11.1.64 0.0.0.63
- permit 172.12.2.64 0.0.0.63
parents: ip access-list standard DATA_TEST
tasks:
- name: Check if the ACL Name already exists.
ios_command:
commands: "show access-lists {{item}}"
register: acl_result
loop: "{{my_acl_list}}"
- debug: msg="{{acl_result}}"
- name: Check if ACL Exist
debug:
msg: "{{item.stdout}}"
when: item.stdout.exists
with_items: "{{acl_result.results}}"
loop_control:
label: "{{item.item}}"
# Pending - Need to know how to match if ACL name exist on stdout.
- name: Check if IP addresses already added
set_fact:
when:
# pending - ansible lookup?
# when var: UP_ACL11, UP_ACL13, UP_ACLDATA IPs are not in ACL then TRUE
- name: Add the set of IP addresses on target ACL
ios_config:
# pending - if doest exist on particular ACL name then configure using the var: UP_ACL11, UP_ACL13, UP_ACLDATA
Given the simplified data for testing
acl_result:
results:
- item: DATA_TEST
stdout:
- "Standard ... 10 permit ... 20 permit ..."
stdout_lines:
- - "Standard ..."
- "10 permit ..."
- "20 permit ..."
- item: dummy
stdout:
- ""
stdout_lines:
- - ""
Q: "Check if ACL Exists"
A: If ACL doesn't exist the attribute stdout is a list of empty strings. Test it
- name: Check if ACL Exists
debug:
msg: "{{ item.item }} exists: {{ item.stdout|map('length')|select()|length > 0 }}"
loop: "{{ acl_result.results }}"
loop_control:
label: "{{item.item}}"
gives
TASK [Check if ACL Exists] ********************************************
ok: [localhost] => (item=DATA_TEST) =>
msg: 'DATA_TEST exists: True'
ok: [localhost] => (item=dummy) =>
msg: 'dummy exists: False'
Notes:
In the filter select, "If no test is specified, each object will be evaluated as a boolean". The number 0 evaluates to false.
Example of a complete playbook for testing
- hosts: localhost
vars:
acl_result:
results:
- item: DATA_TEST
stdout:
- "Standard ... 10 permit ... 20 permit ..."
stdout_lines:
- - "Standard ..."
- "10 permit ..."
- "20 permit ..."
- item: dummy
stdout:
- ""
stdout_lines:
- - ""
tasks:
- name: Check if ACL Exists
debug:
msg: "{{ item.item }} exists: {{ item.stdout|map('length')|select()|length > 0 }}"
loop: "{{ acl_result.results }}"
loop_control:
label: "{{item.item}}"
The test can be simplified if you're sure stdout is a list with a single line only
msg: "{{ item.item }} exists: {{ item.stdout|first|length > 0 }}"
Related
I have application details in respective vars like below. For example, myapp1 in "QA" environment would look like the below:
cat myapp1_QA.yml
---
APP_HOSTS:
- myapphost7:
- logs:
- /tmp/web/apphost7_access
- /tmp/web/apphost7_error
- myapphost9:
- logs:
- /tmp/web/apphost9_access
- /tmp/web/apphost9_error
- /tmp/web/apphost9_logs
WEB_HOSTS:
- mywebhost7:
- logs:
- /tmp/webserver/webhost7.pid
In this example I wish to create a dynamic host containing the 3 hosts
myapphost7
myapphost9
mywebhost7
and each host has variable log which can be looped to get the file paths:
Below is my ansible play:
---
- hosts: localhost
tasks:
- include_vars:
file: "{{ playbook_dir }}/{{ appname }}_{{ myenv }}.yml"
- name: Dsiplay dictionary data
debug:
msg: "{{ item[logs] }}"
loop: "{{ APP_HOSTS }}"
I get the below error:
ansible-playbook read.yml -e appname=myapp1 -e myenv=QA
TASK [Dsiplay dictionary data] *********************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'logs' is undefined\n\nThe error appears to be in '/root/read.yml': line 8, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Dsiplay dictionary data\n ^ here\n"}
My requirement is to store "myapphost7", "myapphost9", "mywebhost7" in group using add_hosts: hosts: while a variable logs: having the list of log files.
Note: if no hosts mywebhost7 is defined under WEB_HOSTS: or APP_HOSTS: then nothing should be added to the dynamic host.
Can you please suggest?
I'm trying to generate a list with the information of hosts that match a certain condition (for instance, that NTP is synched for an inventory of Cisco devices). So that the ones matching that condition will be added to a list with say hostname and IP, for later generating a CSV.
Checking the condition is quite easy, but I'm struggling on how to generate this list.
Adding them to a list in a var doesn't seem wise, as it requires of serial execution of tasks per device.
Should I set a boolean fact for each device (i.e., ntp_synched, and then generate a list with the ansible_net_hostname and ansible_host of each device? How to do this?
- name: CHECK NTP STATUS
ios_command:
commands:
- show ntp status
register: ntp_status
- name: NTP NOT SYNCH
debug:
msg: "{{ [ansible_net_hostname] }}"
when: '"Clock is synchronized" not in ntp_status.stdout[0]'
For example, given the inventory for testing
host01 status="Clock is synchronized"
host02 status="Clock is synchronized"
host03 status="Clock is not synchronized"
Create the dictionary of the hosts and statuses
- hosts: all
tasks:
- command: "echo {{ status }}"
register: ntp_status
- set_fact:
host_status: "{{ dict(_hosts|zip(_stats)) }}"
vars:
_hosts: "{{ ansible_play_hosts }}"
_stats: "{{ ansible_play_hosts|
map('extract', hostvars, ['ntp_status','stdout'])|list }}"
run_once: true
gives
host_status:
host01: Clock is synchronized
host02: Clock is synchronized
host03: Clock is not synchronized
List the synchronized hosts
- debug:
msg: "{{ host_status|dict2items|
selectattr('value', 'search', 'Clock is synchronized')|
map(attribute='key')|list }}"
run_once: true
gives
msg:
- host01
- host02
For a single directory my script is running fine, but how to check the same for multiple directories?
Code for a single directory:
---
- name: checking directory permission
hosts: test
become: true
tasks:
- name: Getting permission to registered var 'p'
stat:
path: /var/SP/Shared/
register: p
- debug:
msg: "permission is 777 for /var/SP/Shared/
when: p.stat.mode == "0777" or p.stat.mode == "2777" or p.stat.mode == "4777"
Reading stat_module shows that there is no parameter for recursion. Testing with_fileglob: did not gave the expected result.
So it seems you would need to loop over the directories in a way like
- name: Get directory permissions
stat:
path: "{{ item }}"
register: result
with_items:
- "/tmp/example"
- "/tmp/test"
tags: CIS
- name: result
debug:
msg:
- "{{ result }}"
tags: CIS
but I am sure there can be still more advanced solutions found.
So this is my dilemma... I am requiring a user to enter the name of a database (i.e. dbx) and the location (canada or america) through extra-vars (-e "dc=canada" -e "dbname=dbx". From that, I am going read the vars
vars:
dbx:
canada:
dbu: db1
home: /u01/app/oracle
america:
dbu: db2
home: /u01/app/oracle
to get the dbu. The dbu will then be compared to databases running on the host
- name: see if db is running on this host
command: echo database is running here
when: dbu == item.database_name
with_items:
- "{{custom python module}}"
I can get the value if I put
- name: output
register: x
debug:
msg: "{{ dbx[dc].dbu }}"
However if I change dbx to the value of dbname, it errors out.
Hope that makes sense.
Thanks Zeitounator and lxop.
By adding another nested loop info:
I was able to get the result by your suggestion:
{{ info[g_db][dc].dbu }}
I have this dictionary:
MyClouds:
Devwatt:
ExternalNetwork: PublicRSC
Flavors:
- Flavor_1cpu_1gb: Devwatt_1cpu_1gb
- Flavor_1cpu_2gb: Devwatt_1cpu_2gb
- Flavor_1cpu_4gb: Devwatt_1cpu_4gb
Fuga:
ExternalNetwork: Internet
Flavors:
- Flavor_1cpu_1gb: Fuga_1cpu_1gb
- Flavor_1cpu_2gb: Fuga_1cpu_2gb
- Flavor_1cpu_4gb: Fuga_1cpu_4gb
- Flavor_1cpu_8gb: Fuga_1cpu_8gb
I have to migrate from one Openstack cloud to another, and one of my problem is to find correspondances between flavors.
I want to find which flavor (key) has the value "Devwatt_1cpu_2gb" in "Devwatt", and after get the value of the same key in "Fuga"
I tried a lot of solution (with-dict, when, jija filters, json_query) but I can't find a way to do that.
Please, may you help me ?
Inspired by Eric's answer and this usefull resource, I, finally, used this solution:
I changed a little bit my data structure and put it in a file matrice.yml:
MyClouds:
Devwatt:
ExternalNetwork: PublicRSC
Flavors:
- name: Flavor_1cpu_1gb
FlavorName: Devwatt_1cpu_1gb
- name: Flavor_2cpu_1gb
FlavorName: Devwatt_2cpu_1gb
- name: Flavor_1cpu_2gb
FlavorName: Devwatt_1cpu_2gb
Fuga:
ExternalNetwork: Internet
Flavors:
- name: Flavor_1cpu_1gb
FlavorName: Fuga_1cpu_1gb
- name: Flavor_2cpu_1gb
FlavorName: Fuga_2cpu_1gb
- name: Flavor_1cpu_2gb
FlavorName: Fuga_1cpu_2gb
then I used these filters in my playbook:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
SourceFlavorName: "Devwatt_2cpu_1gb"
tasks:
- name: get flavors matrice
include_vars:
file: matrice.yml
- name: Get generic name from flavor name of source cloud
debug:
msg: "{{ MyClouds.Devwatt.Flavors | selectattr('FlavorName','search','^'+ SourceFlavorName +'$') |map (attribute='name') | list }}"
register: result
- name: Get flavor name for target cloud from generic name
debug:
msg: "{{ MyClouds.Fuga.Flavors | selectattr('name','search','^'+ result.msg[0] +'$') |map (attribute='FlavorName') | list }}"
With this solution I can have any number of clouds and find easily correspondances between flavor from one source cloud to target cloud.
Why not using a simple mapping using a dict where keys are "Devwatt" flavors and values are "Fuga" flavors, like this :
---
- hosts: localhost
vars:
FlavorsMapping:
Devwatt_1cpu_1gb: Fuga_1cpu_1gb
Devwatt_1cpu_2gb: Fuga_1cpu_2gb
Devwatt_1cpu_4gb: Fuga_1cpu_4gb
tasks:
- debug:
var: FlavorsMapping['Devwatt_1cpu_2gb']