As you have completed the API walkthrough using the command line, below is an ansible playbook that performs exactly the same steps.
It’s best to run it with very verbose debugging and step-by-step (tag by tag) to understand the underlying functionality of the ansible module.
Example:
# set the IP, username and password
export F5OS_MGMT_IP=192.168.0.246
export F5OS_USER=admin
export F5OS_USER_PASSWORD='admin'
# run through each step, one by one
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-1
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-2
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-3
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-4
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-5
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-6
ansible-playbook -vvv f5os_restconf_api_journey.yaml -t step-7
{ .note }
You will still need a F5OS test device. For better reproducibility this documentation avoids the need for an inventory by using tricks to provide a fully runnable single playbook file.
# f5os_restconf_api_journey.yaml
---
- name: 'F5OS RESTCONF API journey'
#connection: httpapi # use this with an inventory
#hosts: all # use this with an inventory
connection: local # used for a single runnable playbook without an inventory, see above otherwise
hosts: localhost # used for a single runnable playbook without an inventory, see above otherwise
gather_facts: false
vars:
# the below overwrites the playbook 'connection' to allow the f5_ps_ansible collection to utilize the httpapi to connect to F5OS - this is only needed to provide a single runnable playbook
ansible_connection: httpapi
# the below typically belongs in the inventory
ansible_host: "{{ lookup('env', 'F5OS_MGMT_IP') }}" # The F5OS management IP
ansible_user: "{{ lookup('env', 'F5OS_USER') | default('admin') }}" # F5OS user with access rights to the API
ansible_httpapi_password: "{{ lookup('env', 'F5OS_USER_PASSWORD') }}" # Use a vault for credentials
ansible_httpapi_port: 443
ansible_httpapi_use_ssl: true
ansible_httpapi_validate_certs: false # Note: Security warning, always use cert validation unless running in a contained ans secure lab environment
ansible_network_os: f5networks.f5os.f5os
# f5os api prefix determination based on https port
f5os_api_prefix: "{{ '/restconf' if ansible_httpapi_port == '8888' else '/api' }}"
tasks:
- name: '1. Retrieve /data/openconfig-system:system/dns'
f5_ps_ansible.f5os.f5os_restconf_get:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns'
tags: ['step-1']
- name: 'Step 2. Add additional DNS resolvers'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns'
config:
openconfig-system:dns:
config:
search:
- internal.example.net
servers:
server:
- address: 8.8.4.4
config:
address: 8.8.4.4
port: 53
- address: 8.8.8.8
config:
address: 8.8.8.8
port: 53
- address: 9.9.9.9
config:
address: 9.9.9.9
port: 53
host-entries:
host-entry:
- hostname: server.internal.example.net
config:
hostname: server.internal.example.net
ipv4-address:
- 192.0.2.10
tags: ['step-2']
- name: 'Step 3. Item-level resource declaration'
block:
- name: 'Step 3. Item-level resource declaration - GET'
f5_ps_ansible.f5os.f5os_restconf_get:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns/servers/server=9.9.9.9'
- name: 'Step 3. Item-level resource declaration - PUT'
vars:
server:
address: 9.9.9.10
port: 53
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns/servers/server={{ server.address }}'
config:
openconfig-system:server:
- address: "{{ server.address }}"
config:
address: "{{ server.address }}"
port: "{{ server.port }}" # no need to change to int, the type is ignored by the ansible module
tags: ['step-3']
# Step 4 is more complicated in ansible as we need to implement idempotency
# and change detection not just blindly submit a PATCH request.
- name: 'Step 4. Using PATCH'
vars:
server:
address: 9.9.9.11
port: 53
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns'
method: 'PATCH'
# For change detection and idempotency to work we will need to filter the API response.
# This can be done using the below "config_query" using a jmespath query,
# the jmespath module is required!
#
# As the API returns the whole list of resources (dns servers) possibly along with
# 'host-entries' and 'config', we need to reduce the response to the item we expect
# to be created by the PATCH operation.
# f5os_restconf_config must be able to compare the API response (current_config) to
# the desired configuration (desired_config)
config_query: |-
"openconfig-system:dns".servers.server[?address == '{{ server.address }}'] | { "openconfig-system:dns": { servers: { server: @ } } }
config:
openconfig-system:dns:
servers:
server:
- address: "{{ server.address }}"
config:
address: "{{ server.address }}"
port: "{{ server.port }}" # no need to change to int, the type is ignored by the ansible module
tags: ['step-4']
- name: 'Step 5. PUT on dns/servers API endpoint'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns/servers'
config:
openconfig-system:servers:
server:
- address: 8.8.4.4
config:
address: 8.8.4.4
port: 53
- address: 8.8.8.8
config:
address: 8.8.8.8
port: 53
- address: 9.9.9.9
config:
address: 9.9.9.9
port: 53
- address: 9.9.9.10
config:
address: 9.9.9.10
port: 53
- address: 9.9.9.11
config:
address: 9.9.9.11
port: 53
- address: 149.112.112.112
config:
address: 149.112.112.112
port: 53
tags: ['step-5']
- name: 'Step 6. Remove configuration resources declaratively'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns/servers'
config:
openconfig-system:servers:
server:
- address: 8.8.8.8
config:
address: 8.8.8.8
port: 53
- address: 9.9.9.9
config:
address: 9.9.9.9
port: 53
- address: 9.9.9.10
config:
address: 9.9.9.10
port: 53
- address: 9.9.9.11
config:
address: 9.9.9.11
port: 53
tags: ['step-6']
- name: 'Step 7. Remove item-level resources'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: '{{ f5os_api_prefix }}/data/openconfig-system:system/dns/servers'
state: 'absent' # <-- we want this resource to be absent on the system!
config:
openconfig-system:servers:
server:
- address: 8.8.8.8
config:
address: 8.8.8.8
port: 53
tags: ['step-7']