I'm struggling to find a solution for LAB project I'm working on right now. I'd like to use csv file to populate variables in my playbook when configuring Cisco ACI. I'm using read_csv module and the latest Ansible 2.9
Sample CSV:
tenant1;tenant1-vrf;tenant1-app
tenant1;tenant1-vrf2;tenant1-app2
tenant2;;tenant2-vrf2;tenant2-app2
UPDATE - based on Sai's code I'm not far from reaching the objective. This is the full tasks code.
UPDATE2 - eventually I went back to the read_csv module. It works nice even for complex things. Hope it helps someone as an example.
tasks:
- name: Read tenant from CSV file and return a list
read_csv:
path: "{{ filename }}"
delimiter: ;
register: tenantconfig
- name: TASK 1 - BUILD tenant
aci_tenant:
<<: *aci_login
validate_certs: no
use_ssl: yes
tenant: "{{ item.tenant }}"
description: "{{ item.tenant }} creation as per {{ filename }} source file"
state: present
with_items: "{{ tenantconfig.list }}"
- name: TASK 2 - BUILD Routing {{ vrf }} for {{ tenant }} on {{ apic_host }}
aci_vrf:
<<: *aci_login
state: present
validate_certs: no
use_ssl: yes
tenant: "{{ item.tenant }}"
vrf: "{{ item.vrf }}"
description: "{{ item.vrf }}"
with_items: "{{ tenantconfig.list }}"
i have changed answer to dynamically process your input file and assign tenant,vrf fields where ever you want to call.
tasks:
- name: split fields
command: /usr/bin/awk -F';' '!/^#/ && !/^$/ { print $1 }' tenant1.csv
register: tenants_out
#- debug:
# msg: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=0') }}"
# with_items: "{{ tenants_out.stdout_lines }}"
- name: TASK 1 - BUILD tenant
aci_tenant:
state: present
tenant: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=0') }}"
vrf: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=1') }}"
with_items: "{{ tenants_out.stdout_lines }}"
input file lines are spitted using initial task, and you can direct specify required tenent, vrf values using "with_items" looping. this is useful if your input file has multiple lines as well.
Related
Ansible lookup in file with semi colon; delimited & lookup based on 2
variable
#Input csv file:
Sysport;name;address;column1;port;column2;column3
host001$port0;host001;x.x.x.10;x.x.x.10:port0,x.x.x.11:port0;port0;port0;envq1
host001$port1;host001;x.x.x.10;x.x.x.10:port1,x.x.x.11:port1;port1;port1;envq1
host001$port2;host001;x.x.x.10;x.x.x.10:port2,x.x.x.11:port2;port2;port2;envq1
host001$port3;host001;x.x.x.10;x.x.x.10:port3,x.x.x.11:port3;port3;port3;envq1
host001$port4;host001;x.x.x.10;x.x.x.10:port4,x.x.x.11:port4;port4;port4;envq1
host001$port5;host001;x.x.x.10;x.x.x.10:port5,x.x.x.11:port5;port5;port5;envq1
code
---
- name: lookup example
# Include host, group of hosts
hosts: [dummy]
# Count of servers to run in batch
serial: 10
# Collect basic information from servers
gather_facts: True
#ignore_unreachable: true
# Execution tasks
tasks:
- shell: ls /directory
register: port
- debug:
msg: "{{ lookup('csvfile', inventory_hostname ~ '$' ~ item file=/ansible/files/repos.csv delimiter=; col=3 }}"
with_items:
"{{ port.stdout_lines }}"
Error:
TASK [debug] *******************************************************************
fatal: [dummy1]: FAILED! => {"failed": true, "msg": "ERROR! template error while templating string: expected token ',', got
'file'"}
expected: result
value of 'server_repo' in message
As shown in the fine manual, the csvfile lookup wants the 2nd argument as a string, not a freeform collection of tokens (to say nothing of the missing closing paren in your example):
- debug:
msg: "{{ lookup('csvfile', inventory_hostname ~ '$' ~ item ~ ' file=/ansible/files/vips.csv delimiter=; col=3') }}"
with_items:
"{{ port.stdout_lines }}"
at your discretion, you can also extract that csv key part to a var since the vars: blocks are evaluated for every iteration:
- debug:
msg: "{{ lookup('csvfile', my_csv_key ~ ' file=/ansible/files/vips.csv delimiter=; col=3') }}"
vars:
my_csv_key: "{{ inventory_hostname ~ '$' ~ item }}"
with_items:
"{{ port.stdout_lines }}"
both work the same, but the 2nd may be a little easier for folks to mentally parse
I'm trying to learn how to iterate through data from csv and put it into a dict rather than a list. I can make it work with a list, but doing so isn't as helpful for a CSV that needs to run multiple tasks that each use only some of the same data because then I'd have to make a new CSV for every task and call a different csv in every task. Since most of the data is the same in each task I'd rather pull all from the same csv and use a dict to specify which particular cell's data i want to use at that moment.
Is there a way to put the data pulled from the csv into a dict?
My Playbook:
tasks:
- name: Read CSV
read_csv:
path: ./constructs.csv
dialect: excel
register: csv_data
- name: Add a new BD
cisco.mso.mso_schema_template_bd:
<<: *login
state: present
schema: "{{ item.schema }}"
template: "{{ item.template }}"
bd: "{{ item.bd }}"
loop: "{{ csv_data.dict|dict2items }}"
- name: Add a new EPG
cisco.mso.mso_schema_template_anp_epg:
<<: *aci_login
state: present
schema: "{{ item.schema }}"
template: "{{ item.template }}"
anp: "{{ item.app_profile }}"
epg: "{{ item.epg }}"
bd:
name: "{{ item.bd }}"
loop: "{{ csv_data.dict|dict2items }}"
If i run the above (with appropriate headers/etc above task: it will simply skip all my tasks. The skip reason is 'no items in the list'.
I'm trying to get my playbook working with csv while using a dict due to the nature of the data [each task asks for different parts of the data in a row so i can't use a list?].
BD and EPG always will appear exactly once per file and can be used as a key if necessary.
I'm getting an error "the task includes an option with an undefined variable". Since the variable (schema) appears as a column header in the csv file I must have some sort of syntax issue.
What I am trying to do is loop, one row at a time, through the csv file and have schema: "{{ item.schema }}" evaluate to that particular rows value under the schema column, etc.
Playbook:
tasks:
- name: Read CSV
read_csv:
# Name of the csv
path: ./Create_EPGs_and_BDs.csv
dialect: excel
key: bd
# Creates register value to be used later
register: csv_data
#
##################################################
#Creates BD in MSO Template
##################################################
#
- name: Add a new BD
cisco.mso.mso_schema_template_bd:
<<: *aci_login
state: present
schema: "{{ item.schema }}"
template: "{{ item.template }}"
bd: "{{ item.bd }}"
vrf:
name: "{{ item.vrf }}"
loop: "{{ csv_data.dict|dict2items }}"
#
##################################################
#Creates EPG in MSO Template
##################################################
#
- name: Add a new EPG
cisco.mso.mso_schema_template_anp_epg:
<<: *aci_login
state: present
schema: "{{ item.schema }}"
template: "{{ item.template }}"
anp: "{{ item.app_profile }}"
epg: "{{ item.epg }}"
bd:
name: "{{ item.bd }}"
loop: "{{ csv_data.dict|dict2items }}"
CSV File:
https://i.stack.imgur.com/VlyLP.jpg
What is the correct syntax to pull the csv values for the entire row and then let me access each column's value in the playbook for that particular row?
The dict2items is going to transpose your dict into a list of dicts, shaped like [{"key": "the-value-of-bd-for-that-row", "value": {"schema": "schema1", ...}}, ...]
Thus, you just need to add .value in between your item and the dict key it references:
- name: Add a new BD
cisco.mso.mso_schema_template_bd:
<<: *aci_login
state: present
schema: "{{ item.value.schema }}"
template: "{{ item.value.template }}"
# or if prefer, item.value.bd
bd: "{{ item.key }}"
vrf:
name: "{{ item.value.vrf }}"
loop: "{{ csv_data.dict|dict2items }}"
it's your code style, but you'll want to be very careful using only one space for yaml indentation, as it's very easy to make a mistake doing that, and certainly makes asking questions on SO harder by doing so :-)
In the future, the use of debug: var=item will go a long way toward helping you understand the shape of your data when you encounter any such "has no attribute" error
I have a test playbook where I have 5 nodes and I need to create a dynamic host group if the Manufacturer of that nodes is XYZ company
So far, I have tried these and able to loop through nodes and get a Manufacturer using
- name: Filter the Manufacturer of Nodes
hosts: Test
tasks:
- name: Get facts from hosts
redfish_info:
category: Chassis
command: GetChassisInventory
baseuri: "{{ hostvars[item]['nodes_list'] }}"
username: "{{ }}"
password: "{{ }}"
register: chassis_facts
with_items:
- "{{ groups.webservers }}"
- "{{ groups.dbservers }}"
- debug:
var: chassis_facts.results
What should now happen is I need to create a dynamic host group using add_host module
- name: Add the hosts dynamically as per Manufacturer
add_host:
groups: xyz_hosts
hostname: "{{ item }}"
when: chassis_facts.results[*].redfish_facts | json_query(some_regex_filter)
with_items:
- "{{ groups.webservers }}"
- "{{ groups.dbservers }}"
based on the Manufacturer from above node list using json_query filter. Any help is highly appreciated.
So, after much digging I found an answer to this, Im using below module which did the trick.
- name: create a new host group based on Manufacturer
add_host:
groups: webservers
hostname: "{{ item }}"
inventory_dir: "{{inventory_dir}}"
when: '"xyz Company" in
result.results[0].redfish_facts.chassis.entries[0].Manufacturer'
with_items:
- "{{ groups.webservers }}"
I have a question, i got the sid list & the DB open_mode, i am trying to run a sql script on the DB when below two conditions satisfy:
DB name should end with '1'.
DB Open_Mode should be 'READ WRITE'.
i am using ansible dynamic inventory to get the sid's from the host & loop through that list, but i am unable to use the two conditions to work through the conditions i am adding.
- hosts: all
gather_facts: false
strategy: free
tasks:
- include_vars: roles/oracle/vars/install_vars.yaml
vars:
var_list:
- script_name
- set_fact:
ORACLE_HOMES_DIR: "/u01/app/oracle/product"
DB_HOME: "{{ ORACLE_HOMES_DIR }}/{{ ORACLE_VERSION }}/dbinst_1"
- name: Copy script to host
copy:
src: "{{ playbook_dir }}/{{ script_name }}"
dest: "/tmp/"
owner: "{{ USER_ORACLE }}"
group: "{{ GROUP_ORACLE }}"
mode: 0755
- name: Verify if the DB is open READ WRITE (or) not
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "echo \"set pagesize 0\n select trim(open_mode) from v\\$database;\" | {{ORACLE_HOME}}/bin/sqlplus -S / as sysdba"
with_items: "{{ hostvars[inventory_hostname]['sid_list'] }}"
loop_control:
loop_var: sid
register: om
- name: Get list of sid that are open in READ WRITE mode
set_fact:
sid_list: "{{ om.results | selectattr('sid','search','1$') | map (attribute='sid') | list }}"
- name: Get the OPEN MODE output of the sid's from the list
set_fact:
om_out: "{{ om.results | selectattr('stdout') | map (attribute='stdout') | list }}"
- name: execute sql script
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ item.0 }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "{{ ORACLE_HOME }}/bin/sqlplus / as sysdba #/tmp/{{ script_name }}"
when: item.1 == 'READ WRITE'
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
I am expected the playbook to execute the SQL script on the DB, but i am getting error saying "conditional result was False"
TASK [Get list of sid that are open in READ WRITE mode] ****************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:44
ok: [dwracdb1] => {
"ansible_facts": {
"sid_list": [
"abhitest1",
"dw1"
]
},
"changed": false
}
TASK [Get the SQL output from all the sid's] ***************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:48
ok: [dwracdb1] => {
"ansible_facts": {
"om_out": [
"READ WRITE",
"READ WRITE"
]
},
"changed": false
}
TASK [Print om out] ****************************************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:52
ok: [dwracdb1] => (item=[u'abhitest1', u'READ WRITE']) => {
"msg": "sid output is abhitest1 om output is READ WRITE"
}
ok: [dwracdb1] => (item=[u'dw1', u'READ WRITE']) => {
"msg": "sid output is dw1 om output is READ WRITE"
}
TASK [execute sql script] **********************************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:61
fatal: [dwracdb1]: FAILED! => {
"msg": "The conditional check 'item.1 == 'READ WRITE'' failed. The error was: error while evaluating conditional (item.1 == 'READ WRITE'): 'item' is undefined\n\nThe error appears to have been in '/uhome/abhi/ansible/sql_script_execute.yaml': line 61, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: execute sql script\n ^ here\n"
}
First, a formatting hint: move the when condition on your block up to the top so that it's obvious what it controls. When you put it at the bottom it's not obvious:
- block:
when:
- hostvars[inventory_hostname]['sid_list'] is defined
In this task, you're collecting multiple results (one for each entry in sid_list):
- name: Verify if the DB is open READ WRITE (or) not
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "echo \"set pagesize 0\n select trim(open_mode) from v\\$database;\" | {{ORACLE_HOME}}/bin/sqlplus -S / as sysdba"
with_items: "{{ hostvars[inventory_hostname]['sid_list'] }}"
loop_control:
loop_var: sid
register: om
- name: Get list of sid that are open in READ WRITE mode
set_fact:
sid_list: "{{ om.results | selectattr('sid','search','1$') | map (attribute='sid') | list }}"
That's why, when you run this task, you end up with a list of results:
- name: Get the SQL output from all the sid's
set_fact:
om_out: "{{ om.results | selectattr(\"stdout\",'equalto','READ WRITE') | map (attribute='stdout') | list }}"
You're doing the correct thing here in your debug task using with_together: you need that in order to associate a result in om_out with one of the entries in sid_list:
- name: Print om out
debug:
msg: sid output is {{ item.0 }} om output is {{ item.1 }}
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
You should do the same thing when trying to execute your sql script. Get rid of the block, because you only have a single task and you can't loop a block:
- name: execute sql script
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid.0 }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "{{ ORACLE_HOME }}/bin/sqlplus / as sysdba #/tmp/{{ script_name }}"
when:
- sid.1 == 'READ WRITE'
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
loop_control:
loop_var: sid
In this loop, sid.0 will be the value from sid_list, and sid.1 will be the corresponding value from om_out.