How to divide and multiply Ansible facts queried by json_query? - automation

"{{ ansible_facts | json_query('mounts[*].size_available') }} / {{ ansible_facts | json_query('mounts[*].size_total') }} * 100"
I am trying to get size available and divide it by size total and Multiply it by 100 to give me the percentage disc usage. This current code gives me the output something like this:
"msg": "[238273] / [483298433] * 100"
It completely ignores the / and the *.
How can I resolve this issue?

json_query is not needed. Simply map the attributes, e.g.
- debug:
msg: "{{ (item.0 / item.1 * 100)|round }}"
with_together:
- "{{ ansible_mounts|map(attribute='size_available')|list }}"
- "{{ ansible_mounts|map(attribute='size_total')|list }}"
gives
ok: [localhost] => (item=[2085240832, 41015336960]) =>
msg: '5.0'
ok: [localhost] => (item=[30278656, 100663296]) =>
msg: '30.0'
ok: [localhost] => (item=[21565116416, 109899771904]) =>
msg: '20.0'
A simpler option is to iterate the list of mounts, e.g.
- debug:
msg: "{{ (item.size_available / item.size_total * 100)|round }}"
loop: "{{ ansible_mounts }}"
loop_control:
label: "{{ item.mount }}"
gives
ok: [localhost] => (item=/) =>
msg: '5.0'
ok: [localhost] => (item=/boot/efi) =>
msg: '30.0'
ok: [localhost] => (item=/export) =>
msg: '20.0'

You need your math operator to be inside the Jinja expression {{ ... }}.
Moreover, since you are getting [238273] and [483298433] your JMESPath queries in json_query are returning you two lists, so you do want to use the first filter.
Last, but not least, those two values are still going to return you some string, so you do want to use the int filter.
"{{ (ansible_facts | json_query('mounts[*].size_available') | first | int) / (ansible_facts | json_query('mounts[*].size_total') | first | int) * 100 }}"

Related

Ansible uri module loop on files

I'm using the Ansible uri module to make a PUT API call and using all files in a directory as parameters.
I have a list of files in a directory, and I want to use the name and the content of each file in the API call
First of all i tried to list all files.
- name: "Find pipeline files in folder"
find:
paths: "/app/pipelines"
patterns: "pipeline-*.json"
file_type: "file"
register: pipe_files
- debug:
var: pipe_files
Then I want to make a loop on each file in the directory and call the API
- name: PUT PIPE
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/**FILE_NAME**"
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file','/app/pipelines/**FILE_NAME.json**') }}"
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
register: pipeline_created
until: pipeline_created.status == 200
When I deploy the content, I don't have the exact filename, how can I make the loop on each file to call the API?
Best regards,
Thanks in advance.
pipe_files is a register from a find task. You can have a look at returned values in the find module documentation. You can also examine your debug task output to better get accustomed with the content of the variable.
Anyway. The list of file objects returned will be in pipe_files.files. Each element is a dict where the information you need is in the path key.
You may test with
- name: PUT pipeline
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/{{ item.path | basename }}" # depends on input list content
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file', item.path) }}" # content
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
until: pipeline_created.status == 200
loop: "{{ pipe_files.files }}"
register: pipeline_created # result will become a list

Add Single Quotes around a list in Ansible

I have a list of items services and each needs to be wrapped in single quotes to configure some parameters.
The simplest solution I saw posted was:
"{{services | match('quote') | join(' OR ')}}"
This only wrapped the first element in the list in a single quote but not the remaining.
I also tried some variations of match regex.
Finally I tried adding the single quote manually in the data source, then joining. The first element retained the single quotes but subsequent were stripped off? What is going on here?
Right now they are static from the inventory
i.e.:
---
inventory:
hosts:
host1:
procs:
- splunkd.*
services:
- 'some service name'
- 'another service name'
- 'SplunkForwarder'
I need the end result to be
"Name='some service name' OR Name='another service name'"
Currently with the services single quoted in variable the quotes are stripped or ignored.
Result
Name=some service or Name=another service
you could cut your problem by using loop:
tasks:
- name: set var
set_fact:
result: "{{ result | d('') + _i + _o }}"
loop: "{{ services }}"
loop_control:
extended: yes
vars:
_i: "Name='{{ item }}'"
_o: "{{ '' if ansible_loop.last else ' OR ' }}"
- name: display result
debug:
var: result
result:
ok: [localhost] => {
"result": "Name='some service name' OR Name='another service name' OR Name='SplunkForwarder'"
}
with your vars:
- name: Join services
set_fact:
joined_services: "{{ joined_services | d('') + service + serviceAppend }}"
loop: "{{ services }}"
loop_control:
extended: yes
vars:
service: "'{{ item }}'"
serviceAppend: "{{ '' if ansible_loop.last else ' OR ' }}"

Retry Ansible URI PUT API Call with a POST API Call

Hello I am trying to run a PUT API call via Ansible URI module for a particular API Endpoint in my application, using a dictionary that contains the json files and that is defined as:
example: { 'example1' : 'v1', 'example2': 'v2''}
- name: Update existing
block:
- name: update existing
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
with_dict: "{{ example }}"
register: result
For the PUT api call, this api endpoint will fail is the {{item.key}} does not exist, e.g. if
"{{url}}/api/{{item.key}}/" endpoint does not exist, hence it will give a 4xx error.
Given the task fails and I get a 4xx error when the api endpoint for the item does not exist, I want to run a POST command for that same json file.
How can I do this in ansible, to retry a task that failed but only specifically for that {{item.key}} and {{item.value}} in dictionary?
or
Is there a better way to do this to retry a failed PUT with a POST command
I want to use the ansible URI module
Thanks!
You can ignore the error case and then loop through result.results with filtering to keep only errors. You can have access to the original item with item.item:
- name: update existing with PUT
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code:
- 200
- 404 # supposing it's the "normal" error case
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ example | items2dict }}"
register: result
- name: update existing with POST
uri:
url: "{{url}}/api/{{item.item.key}}/"
method: POSTT
body: "{{ lookup('file', 'example/{{item.item.key}}/{{item.item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ result.results | rejectattr('status', '200') }}" # filter out 200 status from previous task

How to disable Ansible's password obfuscating feature

I have a simple Ansible playbook to
Fetch a database connection config from an RestAPI,
Extract the config object from the payload,
Using the config JSON (as request body) to create a PUT request to another RestAPI.
At the 3rd stage I found that the database username and password combination is wrong. Later, while I print the outputs, I have found that the password has been replaced with a string named "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER".
After some googling, I found that this is a security feature by Ansible. Unfortunately, I haven't found any configuration or something like this to disable this feature. Is it possible to disable this feature? Or any other workaround?
---
- name: my-playbook
gather_facts: no
hosts: all
vars_files:
- secret
tasks:
- name: Fetch the config payload from the API
uri:
url: "{{get_config}}"
method: GET
user: "{{username}}"
password: "{{password}}"
validate_certs: no
return_content: yes
status_code: 200
body_format: json
register: config
- name: Extract the config object
set_fact:
config_raw: "{{ config.json | json_query(jmesquery) }}"
vars:
jmesquery: '{{name}}.config'
- name: print the config
debug:
msg: "{{config_raw}}"
- name: Creating object using config
uri:
url: "{{create_ocject}}"
method: PUT
user: "{{username}}"
password: "{{password}}"
validate_certs: no
body: "{{config_raw}}"
body_format: json
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
register: test_res
- name: output value
debug:
msg: "{{test_res.json}}"

Assign variable by referencing another

In the following Ansible Playbook, I am trying to create a user's password using predefined variables from defaults/main.yml which in return calls password from vars/passwords.yml. this file will be vaulted later.
vars/passwords
---
passwords:
foobar:
password: pass1234
defaults/main.yml
users:
- username: foobar
group: barfoo
password: "{{passwords.foobar}}"
tasks/main.yml
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{item.password | password_hash('sha512') }}"
When I run this playbook, I get the following error:
ERROR:
{
"msg": "[{u'username': u'foobar',
u'group': u'barfoo',
u'password': u'{{passwords.username}}'}]: 'list object' has no attribute 'username'"
}
Any idea how can I achieve assigning a variable by referencing another one.
the first file you provided, has passwords as a list variable, while in your defaults/main.yml file you are expecting a dictionary variable (passwords.foobar).
please change 1st file contents to:
---
passwords:
foobar: pass1234
cant comment about the rest, it looks to me that the tasks/main.yml is missing a line, probably a line including with_items statement. I dont imply its a problem in your code, you just probably didn't paste all your code to this question.
With the current variables files (defaults and vars), the solution for me was to call the password for user bar using the username as a key. I currently have:
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{item.password | password_hash('sha512') }}"
the new defaults/main.yml will not have a password key/value:
users:
- username: foobar
group: barfoo
Now with vars/passwords.yml :
---
passwords:
foobar:
password: pass1234
I can edit my change my task to:
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{passwords[item.username].password | password_hash('sha512') }}"
This solved my problem, and allows me to vault passwords.yml.
Please let me know if you have any improvements or suggestions.