I have a task which must check the database list of stores in the .ini file, and if it is not exist - create it from the file.
Currently I have this task which check the database present:
- name: Check DB (if necessary)
mysql_db:
name: "{{ item }}"
state=present
register: db_created
with_ini:
- databases[1-100]
- section: sites
- file: "lookup.ini"
- re: true
But it is just check the present of db and stay rise event "db_created".
And I need to create the new database with task like this:
- name: Import DB (if it was created)
mysql_db:
name=my_database
state=import
target=/tmp/database.sql
when: db_created.changed
But with this task it will be call once, not the for all databases which I need to create. And I need to know exactly which database need to be created.
Can you help, how to call second one with correct name of database, and for all database if there more then one?
Bad syntax apart, your first task does not just check, but ensures that db is present (create new db if it is not found).
In your db_created result you have per item changed status.
Something like this should do the job:
- name: Check DB (if necessary)
mysql_db:
name: "{{ item }}"
state: present
register: db_created
with_ini:
- databases[1-100]
- section: sites
- file: "lookup.ini"
- re: true
- name: Import DB (if it was created)
mysql_db:
name: "{{ item.item }}"
state: import
target: /tmp/database.sql
when: item is changed
with_items: "{{ db_created.results }}"
Or event like this (to iterate only over changed items):
- name: Import DB (if it was created)
mysql_db:
name: "{{ item.item }}"
state: import
target: /tmp/database.sql
with_items: "{{ db_created.results | select('changed') | list }}"
Note: about your syntax – forget =, always use :.
Related
I'm trying to copy a json file from my localhost to remote hosts, and use the json "version" key value as a parameter in the destination file name.
JSON:
{"name": "test", "version": 3}
YML:
---
- name: Get json file version
hosts: locahost
become: true
tasks:
- name: Register licenses file
command: "cat conf/licenses.json"
register: result
- name: Save the json data
set_fact:
jsondata: "{{ result.stdout | from_json }}"
- name: Save licenses file version
set_fact:
file_version: "{{ jsondata | json_query(jmesquery) }}"
vars:
jmesquery: 'version'
- name: Deploy Licenses File
hosts: hosts
become: true
tasks:
- name: Copy licenses file
copy:
src: "conf/licenses.json"
dest: "/tmp/licenses_{{ file_version }}.json"
When I run the playbook above, the Deploy Licenses File key doesn't find the file_version fact, even though I can see it is successfully saved in the Get json file version key.
Set file_version fact:
ok: [localhost] => {
"ansible_facts": {
"file_version": "1"
},
"changed": false
}
Error:
The task includes an option with an undefined variable. The error was: 'file_version' is undefined
I think the facts are saved on the given host granularity and are not global facts per playbook initiation.
My current workaround is to combine the keys to a single task and then it works correctly, but I prefer to get the version once instead of repeating it for each remote host.
To access facts of another host, you can always use the hostvars special variable.
So, in your case:
dest: "/tmp/licenses_{{ hostvars.localhost.file_version }}.json"
Now, you actually do not need that level of complication with two plays and could well do:
- name: Deploy Licenses File
hosts: hosts
become: true
tasks:
- name: Copy licenses file
copy:
src: "{{ _licence_file }}"
dest: "/tmp/licenses_{{ file_version }}.json"
vars:
_licence_file: conf/licenses.json
file_version: >-
{{ (lookup('file', _licence_file) | from_json).version }}
The first play runs at localhost only. Therefore the variables declared in the first play are available to localhost only. This is the reason that the variables are not available to host in the second play
The error was: 'file_version' is undefined
Run once the tasks in a block of the first play at all hosts. This way the variables will be available to all hosts in the second play. For example, the simplified playbook below does the job
- hosts: all
tasks:
- block:
- include_vars:
file: licenses.json
name: jsondata
- set_fact:
file_version: "{{ jsondata.version }}"
run_once: true
- hosts: hosts
tasks:
- copy:
src: licenses.json
dest: "/tmp/licenses_{{ file_version }}.json"
Created file at the remote host
shell> ssh admin#test_11 cat /tmp/licenses_3.json
{"name": "test", "version": 3}
The code can be further simplified. The single play below does the job as well
- hosts: hosts
tasks:
- include_vars:
file: licenses.json
name: jsondata
run_once: true
- copy:
src: licenses.json
dest: "/tmp/licenses_{{ jsondata.version }}.json"
I'm trying to find if any option to write to a particular cell in CSV file from Ansible. I can use Ansible lookup plugin type "csvfile" to lookup for a value in the csv file but I need to add text to that file. Consider below example:
empID,name,mark
111,user1
112,user2
113,user3
I need to add the mark for each user, running the playbook should prompt for the empID and marks:
---
- name: Update data
hosts: localhost
vars_prompt:
- name: empid
prompt: EMP_ID
private: no
- name: marks
prompt: MARKS
private: no
tasks:
- name: Lookup
debug:
msg: "{{ lookup('csvfile', '{{ empid }} file=data.csv delimiter=, col=2') }}"
Need help to write to CSV file column C rows 1,2,3....
As #mdaniel mentioned, there's no out-of-box write_csv module in Ansible. Without creating your own module, the only workaround I can think of is the following:
You read in the CSV file with read_csv module and register the data table as a dictionary variable.
You add the new values to the dict variable recursively with the key "mark". (Check this post for modifying values in dict variable)
You loop over the dict variable and output each line to a new file with the lineinfile module. The file can be created with.csv extension, and in each line, the values are separated with delimiters (, or ;).
Here is an example:
---
- hosts: localhost
gather_facts: no
tasks:
- name: Read from CSV file
community.general.read_csv:
path: data.csv
key: empID
delimiter: ','
register: response
- name: Create a dict variable from the CSV file
set_fact:
data_dict: "{{ response.dict }}"
- name: Update values in dictionary recursively
set_fact:
data_dict: "{{ data_dict | combine(new_item, recursive=true) }}"
vars:
new_item: "{ '{{ item.key }}': { 'mark': 'some_value' } }"
with_dict: "{{ data_dict }}"
- name: Debug
debug:
msg: "{{ data_dict }}"
- name: Save ddata_dict headers to a new CSV file
lineinfile:
path: new_data.csv
line: empID,name,mark
create: yes
- name: Save data_dict values to a new CSV file
lineinfile:
path: new_data.csv
line: "{{ item.value.empID }},{{ item.value.name }},{{ item.value.mark }}"
loop: "{{ data_dict | dict2items }}"
And the outputted CSV file is:
empID,name,mark
111,user1,some_value
112,user2,some_value
113,user3,some_value
There doesn't appear to be a provided write_csv mechanism in ansible, partially because ansible is not a general purpose computing platform. However, you can easily write your own module which behaves as you wish, or you may be able to find an existing one out in ansible galaxy
I'm facing the issue with ansible playbook, I want to collect the info about all servers to a single file. Simly speaking I need gather info from all servers specified under hosts file.
Here is my .yml file:
---
- hosts: idrac
connection: local
name: Get system inventory
gather_facts: False
collections:
- dellemc.openmanage
tasks:
- name: Get system inventory
dellemc_get_system_inventory:
idrac_ip: "{{ idrac_ip }}"
idrac_user: "root"
idrac_password: "root"
register: result
- name: Copy results locally to output file
copy:
content: "{{ result }}"
dest: "./output/system_inventory_output.json"
delegate_to: localhost
But the problem is that I check output file, it contains json data only from one server.
I've been browsing the Net but till now did not find any working solution for that...
Any idea how to achieve that?
Thanks!
Create the output file in a second play, and iterate over all the hosts using a template. Something like this:
---
- hosts: idrac
connection: local
name: Get system inventory
gather_facts: False
collections:
- dellemc.openmanage
tasks:
- name: Get system inventory
dellemc_get_system_inventory:
idrac_ip: "{{ idrac_ip }}"
idrac_user: "root"
idrac_password: "root"
register: system_inventory
- hosts: localhost
gather_facts: false
tasks:
- name: Write results to local output file
copy:
dest: "./output/system_inventory_output.json"
content: |
{% for host in groups.idrac %}
=== {{ host }} ==
{{hostvars[host].system_inventory}}
{% endfor %}
You might elect to use the template module rather than embedding the template in the content argument of the copy module, as I have done here.
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 am trying to use a variable in a when statement and ansible pops up a warning like this
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.name}}.changed
I use a loop first:
- include_tasks: anaconda_env.yml
with_items: "{{anaconda_templates}}"
and in the anaconda_env file.yml i have this:
- name: anaconda.templates
template:
owner: "{{item.owner|default(common_owner)}}"
group: "{{item.group|default(common_group)}}"
mode: "{{item.mode|default(common_mode)}}"
src: "{{item.template}}"
dest: "{{item.dest}}"
register: "{{item.name}}"
- name: anaconda.handler
command: echo '1'
notify: "{{item.name}}"
when: "{{ item.name}}.changed"
And in another situation I tried "{{ item.name}}.rc == 1" and I have the same issue. Any idea how can I avoid this Wanring message.
I found the issue here but no solution
https://github.com/ansible/ansible/issues/27225
My original answer didn't work, but I believe the one below will (or at least did with my limited mock data):
- set_fact:
current_template: "{{item}}"
- name: anaconda.handler
command: echo '1'
notify: "{{item.name}}"
when: current_template.changed is defined