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.