This will be the last part of my short series on the Ansible URI module and this time I will explain and show examples about when to use PUT or POST when interacting with REST APIs. I make use of the JSON_QUERY filter which I have explained in my previous article.
What is the difference between POST and PUT?
-
PUT – The PUT method is idempotent and needs the universal unique identifier (uuid) to update an API object. Example PUT /api/service/{{ object-uuid }}. The HTTP return code is 200.
-
POST – Is not idempotent and used to create an API object and an unique identifier is not needed for this. In this case the uuid is server-side generated. Example POST /api/service/. The HTTP return code is 201.
I am again using the example from AVI Network Software Load Balancers and their REST API.
---
password: 123
api_version: 17.2.13
openshift:
name: openshift-cloud-provider
openshift_cloud_json: "{{ lookup('template','openshift_cloud_json.j2') }}"
(Optional) Set ansible_host variable to IP address. I have had issues in the past using the DNS name and the task below overrides the variable with the IP address of the host:
- block:
- name: Resolve hostname
shell: dig +short "{{ ansible_host }}"
changed_when: false
register: dig_output
- name: Set ansible_host to IP address
set_fact:
ansible_host: "{{ dig_output.stdout }}"
when: ( inventory_hostname == groups ["controller"][0] )
Let’s start creating an object using POST and afterwards updating the existing object using PUT. The problem with POST is, that it is not idempotent so we need to first check if the object exists before creating it. We need to do this because creating the same object twice could be an issue:
- block:
- name: Avi | OpenShift | Check cloud config
uri:
url: "https://{{ ansible_host }}/api/cloud/?name={{ openshift.name }}"
method: GET
user: "{{ username }}"
password: "{{ password }}"
return_content: yes
body_format: json
force_basic_auth: yes
validate_certs: false
status_code: 200
timeout: 180
headers:
X-Avi-Version: "{{ api_version }}"
register: check
- name: Avi | OpenShift | Create cloud config
uri:
url: "https://{{ ansible_host }}/api/cloud/"
method: POST
user: "{{ username }}"
password: "{{ password }}"
return_content: yes
body: "{{ openshift_cloud_json }}"
body_format: json
force_basic_auth: yes
validate certs: false
status_code: 201
timeout: 180
headers:
X-Avi-Version: "{{ api_version }}"
when: check.json.count == 0
when: ( inventory_hostname == groups ["controller"][0] ) and update_config is undefined
Let’s continue with the example and using PUT to update the configuration of an existing object. To do this you need to define a extra variable update_config=true for the tasks below to be executed:
- block:
- name: Avi | OpenShift | Check cloud config
uri:
url: "https://{{ ansible_host }}/api/cloud/"
method: GET
user: "{{ username }}"
password: "{{ password }}"
return_content: yes
body_format: json
force_basic_auth: yes
validate_certs: false
status_code: 200
timeout: 180
headers:
X-Avi-Version: "{{ api_version }}"
register: check
- name: Avi | Set_fact for OpenShift name
set_fact:
openshift_cloud_name: "[?name=='{{ openshift.name }}').uuid"
- name: Avi | Set_fact for OpenShift uuid
set_fact:
openshift_cloud_uuid: "{{ check.json.results | json_query(penshift_cloud_name) }}"
- name: Avi | OpenShift | Update cloud config
uri:
url: "https://{{ ansible_host }}/api/cloud/{{ openshift_cloud_uuid [0] }}"
method: PUT
user: "{{ username }}"
password: "{{ password }}"
return_content: yes
body: "{{ openshift_cloud_json }}"
body_format: json
force_basic_auth: yes
validate_certs: false
status_code: 200
timeout: 180
headers:
X-Avi-Version: "{{ api_version }}"
when: ( inventory_hostname == groups ("controller"][0] ) and update_config is defined
Here you find the links to the other articles about Ansible URI module:
- Part one: Ansible URI module and Jinja2 templating
- Part two: Ansible URI module and json_query filter
Please share your feedback and leave a comment.