Ansible read_csv module:How to provide path - csv

I have a csv file as below
Hostname,Permission,User,Group,file
lbserver1,-rw-------,root,root,/tmp/dir1/4
lbserver2,drwx------,root,root,/tmp/dir1
lbserver3,-rw-------,root,root,/tmp/dir2/8
I need to use path as the key. My playbook is as below
- name: read csv
read_csv:
path: "/tmp/test.csv"
key: file
register: file_details
- name: test
debug:
msg: "{{file_details.dict./tmp/dir2/5.Permission}}"
I get error as
"msg": "template error while templating string: expected name or number. String: {{file_details.dict./tmp/dir2/5'.Permission}}"
I gave quotes as well as escape char for the paths, but still no luck. Please advise.

You can change the code like below:
- name: test
debug:
msg: "{{ file_details.dict['/tmp/dir2/5'].Permission }}"
But then it will raise an error if the key doesn't exist which is the case in your example. In that case you may use some default.
- name: test
debug:
msg: "{{ file_details.dict['/tmp/dir2/5'].Permission | default('undefined') }}"

Related

How can I loop through json object REST API body in Ansible?

When using Ansible I am able to execute when passed one-by-one like this:
---
- name: Using a REST API
become: false
hosts: localhost
gather_facts: false
tasks:
- debug:
msg: “Let’s get list of Interfaces”
- name: Adding a Bridge-Interface
uri:
url: https://router/rest/interface/bridge
method: PUT
validate_certs: false
url_username: ansible
url_password: ansible
force_basic_auth: yes
body_format: json
status_code: 201
body: '{"name":"bridge_ansible"}'
register: results
- debug:
var: results
I want to iterate through a set of commands so I thought of looping, but that does not work for me, I am using this code:
---
- name: Using a REST API
become: false
hosts: localhost
gather_facts: false
tasks:
- debug:
msg: “Let’s get list of Interfaces”
- name: Adding a Bridge-Interface
uri:
url: "{{item.url}}"
method: PUT
validate_certs: false
url_username: ansible
url_password: ansible
force_basic_auth: yes
body_format: json
status_code: 201
body: "{{item.body}}"
register: results
loop:
- {body:'{"name":"bridge_ansible"}', url:'https://router/rest/interface/bridge'}
- {body:'{"address":"6.6.6.6", "interface":"bridge_ansible"}', url:'https://router/rest/ip/address'}
- debug:
var: results
I get an error for this {body:'{"name":"bridge_ansible"}', url:'https://router/rest/interface/bridge'} in the json object { I think my syntax is not correct but cannot understand the correct thing. Can someone please help
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
did not find expected ',' or '}'
The error appears to be in '/ansible-playbook/1-demo.yaml': line 23, column 19, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
loop:
- {body:'\{"name":"bridge_ansible"\}', url:'https://router/rest/interface/bridge'}
^ here
This one looks easy to fix. It seems that there is a value started
with a quote, and the YAML parser is expecting to see the line ended
with the same kind of quote. For instance:
when: "ok" in result.stdout
Could be written as:
when: '"ok" in result.stdout'
Or equivalently:
when: "'ok' in result.stdout"
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:
foo: "bad" "wolf"
Could be written as:
foo: '"bad" "wolf"'
Thanks

Writing to a particular cell in csv file from Ansible

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

How to use a url from a json formatted answer with the get_url module?

I am requesting a REST API to provide me with a download url for a specific item (this is solved by the uri-module). I then try to use the url in the get_url module but it fails with the following message
FAILED! => {"msg": "template error while templating string: expected name or number. String: {{ cora_response.json.['#odata.mediaReadLink'].stdout | from_json }}"}
How am i suppose to put the variable into the url parameter?
My task looks currently like this:
- name: Download Link from REST
debug: var=cora_response.json['#odata.mediaReadLink']
- name: Download the latest Release
get_url:
url: "{{ cora_response.json.['#odata.mediaReadLink'].stdout | from_json }}"
dest: "{{ installation_path }}/install/19.3.zip"
mode: 660
My debug task shows me the url i am trying to use, however the get_url module fails.
Please advise and thank you in advance.
Edit:
Tried the following suggested by #al76
- name: Download the latest CH Release
get_url:
url: "{{ cora_response.json['#odata.mediaReadLink'].stdout | from_json }}"
dest: "{{ installation_path }}/install/CH19.3.zip"
mode: 660
Error message:
fatal: [ln-lnxcelcon01.owo.company]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ cora_response.json['#odata.mediaReadLink'].stdout | from_json }}): expected string or buffer"}
try
url: "{{ cora_response.json['#odata.mediaReadLink'].stdout | from_json }}"
Blind luck took me out of my misery
url: "{{ cora_response.json['#odata.mediaReadLink'] }}"

Why is Ansible unable to read unicode string as JSON?

Summary
When retrieving data using the uri module in Ansible, I am unable to parse a section of it as JSON to retrieve a nested value.
The desired value is the ci field inside the content.data or json.data field (see output below).
Steps to Reproduce
site.yml
---
- hosts: localhost
gather_facts: false
tasks:
- name: Get String
uri:
url: "http://localhost/get-data"
method: POST
body_format: json
body: "{ \"kong-jid\": \"run-sn-discovery\" }"
return_content: yes
register: output
- set_fact:
ci: "{{ output.json.data.ci }}"
- debug:
msg: "{{ ci }}"
The {{ output }} variable
{
u'status': 200,
u'cookies': {},
u'url': u'http://kong-demo:8000/get-data',
u'transfer_encoding': u'chunked',
u'changed': False,
u'connection': u'close',
u'server': u'kong/0.34-1-enterprise-edition',
u'content':
u'{"data":"\\"{u\'ci\': u\'3bb8d625dbac3700e4f07b6e0f96195b\'}\\""}',
'failed': False,
u'json': {u'data': u'"{u\'ci\': u\'3bb8d625dbac3700e4f07b6e0f96195b\'}"'},
u'content_type': u'application/json',
u'date': u'Thu, 18 Apr 2019 15:50:25 GMT',
u'redirected': False,
u'msg': u'OK (unknown bytes)'
}
Result
[user#localhost]$ ansible-playbook site.yml
[WARNING]: Could not match supplied host pattern, ignoring: all
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [localhost] ***************************************************************************************************************
TASK [Pass Redis data to next task as output] **********************************************************************************
ok: [localhost]
TASK [set_fact] ****************************************************************************************************************
fatal: [localhost]: FAILED! => {}
MSG:
The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'ci'
The error appears to have been in 'site.yml': line 19, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- set_fact:
^ here
exception type: <class 'ansible.errors.AnsibleUndefinedVariable'>
exception: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'ci'
Important Troubleshooting Information
It appears the root issue is related to which Ansible type being interpreted. I desire to parse ci from the output in one task.
The two-task solution shown below works, but this leads me to believe this should be possible in one line...
Two-Task Solution
- set_fact:
ci: "{{ output.json.data | from_json }}"
- debug:
msg: "{{ ci['ci'] }}"
But the ci fact set from {{ output.json.data | from_json }} reports a different TYPE than the inline type...
Unicode or Dict?
- debug:
msg: "{{ output.json.data | from_json | type_debug }}" # returns unicode
- set_fact:
ci: "{{ output.json.data | from_json }}"
- debug:
msg: "{{ ci | type_debug }}" # returns dict
Why isn't {{ output.json.data | from_json | type_debug }}
the same as {{ ci | type_debug }}?
Although json and data are keys in their resp objects, ci is just part of a larger string (which happens to look like a JSON object
If the relevant line in your datastructure would be:
u'json': {u'data': {'ci': u'3bb8d625dbac3700e4f07b6e0f96195b'}},
then you could expect to use "{{ output.json.data.ci }}" but not when the .ci part is just a normal part of a string.

Ansible "set_fact" repository url from json file using filters like "from_json"

Using Ansible "set_fact" module, I need to get repository url from json file using filters like "from_json". I tried in couple ways, and still doesn't get it how is should work.
- name: initial validation
tags: bundle
hosts: localhost
connection: local
tasks:
- name: register bundle version_file
include_vars:
file: '/ansible/playbook/workbench-bundle/bundle.json'
register: bundle
- name: debug registered bundle file
debug:
msg: '{{ bundle }}'
I get json that I wanted:
TASK [debug registered bundle file] ************************************************
ok: [127.0.0.1] => {
"msg": {
"ansible_facts": {
"engine-config": "git#bitbucket.org/engine-config.git",
"engine-monitor": "git#bitbucket.org/engine-monitor.git",
"engine-server": "git#bitbucket.org/engine-server.git",
"engine-worker": "git#bitbucket.org/engine-worker.git"
},
"changed": false
}
}
And then I'm trying to select each value by key name to use this value as URL to "npm install" each package in separate instances.
- name: set_fact some paramater
set_fact:
engine_url: "{{ bundle.('engine-server') | from_json }}"
And then I get error:
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "template error
while templating string: expected name or number. String: {{
bundle.('engine-server') }}"}
I many others ways like this loopkup, and it still fails with others errors. Can someone help to understand, how I can find each parameter and store him as "set_fact"? Thanks
Here is a sample working code to set a variable like in the question (although I don't see much sense in it):
- name: initial validation
tags: bundle
hosts: localhost
connection: local
tasks:
- name: register bundle version_file
include_vars:
file: '/ansible/playbook/workbench-bundle/bundle.json'
name: bundle
- debug:
var: bundle
- debug:
var: bundle['engine-server']
- name: set_fact some paramater
set_fact:
engine_url: "{{ bundle['engine-server'] }}"
The above assumes your input data (which you did not include) is:
{
"engine-config": "git#bitbucket.org/engine-config.git",
"engine-monitor": "git#bitbucket.org/engine-monitor.git",
"engine-server": "git#bitbucket.org/engine-server.git",
"engine-worker": "git#bitbucket.org/engine-worker.git"
}