Introduction
To administer an Oracle Database Appliance (ODA), you probably use the odacli commandline tool.
The principle of this tool is to run jobs in background. Fire-and-forget… or Fire-and-poll
odacli update-repository -f "/tmp/odacli-dcs-19.30.0.0.0-260210-GI-19.30.0.0.zip"
This command returns a jobId (e.g. f1338963-87a2-4cb9-8a3e-06e104270203) and the job is executed in background. To see if this job is (sucessfully) completed, you have to poll for the above jobId and wait for Status: Success.
odacli describe-job -i f1338963-87a2-4cb9-8a3e-06e104270203
watch -n 2 odacli describe-job -i f1338963-87a2-4cb9-8a3e-06e104270203
Automation with Ansible
For automation, e.g. with Ansible, that is not optimal. We have to implement a polling mechanism for the odacli commands.
For example. we will add new software to the ODA-repository. (version=19.30, software_zip=/tmp/odacli-dcs-19.30.0.0.0-260210-GI-19.30.0.0.zip)
First of all, we run odacli to create the job to add the software to the repository and extract the jobId from the output.
- name: import software in repository
ansible.builtin.shell: |
/opt/oracle/dcs/bin/odacli describe-dbsystem-image -j \
| jq -e '.[].dbSystemImageComponents[]|select(.componentName=="DB")|.availableVersions[]|select(startswith("{{version2}}")) ' \
|grep {{version2}} >&2 && echo 'ALREADY_INSTALLED' && exit 0
/opt/oracle/dcs/bin/odacli update-repository -f "{{software_zip}}"
register: repo
changed_when: "'ALREADY_INSTALLED' not in repo.stdout"
- name: set job-id
set_fact:
jobid: "{{ (repo.stdout | from_json).jobId }}
when: "'ALREADY_INSTALLED' not in repo.stdout"
Hint: the 1st command is to check if this software is already imported
Now, we can poll the job until it is completed. Ansible is optimized to work with json. So we will enforce odacli to return the output in json format (-j):
/opt/oracle/dcs/bin/odacli describe-job -i f1338963-87a2-4cb9-8a3e-06e104270203 -j
{
"jobId" : "f1338963-87a2-4cb9-8a3e-06e104270203",
"status" : "Created",
"message" : "/tmp/odacli-dcs-19.30.0.0.0-260210-GI-19.30.0.0.zip",
"reports" : [ ],
"createTimestamp" : "February 24, 2026 13:58:30 PM CET",
"resourceList" : [ ],
"description" : "Repository Update",
"updatedTime" : "February 24, 2026 13:58:30 PM CET",
"jobType" : null,
"cpsMetadata" : null
}
For polling, we can use the ansible loop control, see the Ansible documentatioon
- name: check until job completed
ansible.builtin.shell: /opt/oracle/dcs/bin/odacli describe-job -j -i {{jobid}}
register: check_status
until: "(check_status.stdout|from_json).status == 'Success'"
retries: 10
delay: 8
changed_when: false
when: "'ALREADY_INSTALLED' not in repo.stdout"
That means, Ansible will run the command every 8 seconds until status Success is returned (Success) or after 10 attemts (Failed)
TASK [check until job completed] ************************************************************************
FAILED - RETRYING: check until job completed (10 retries left).
FAILED - RETRYING: check until job completed (9 retries left).
FAILED - RETRYING: check until job completed (8 retries left).
FAILED - RETRYING: check until job completed (7 retries left).
FAILED - RETRYING: check until job completed (6 retries left).
FAILED - RETRYING: check until job completed (5 retries left).
FAILED - RETRYING: check until job completed (4 retries left).
ok: [server01]
Re-usability with roles
For re-usablility, I recommend to move the job-polling to a role, so that it can be used for all asynchronous jobs
# roles/odacli_job/tasks/main.yml
- name: set job-id
set_fact:
jobid: "{{ (job_stdout | from_json).jobId |default('') }}"
when: job_stdout|default('') != ''
- debug: var=jobid
- name: check until repo job completed
ansible.builtin.shell: /opt/oracle/dcs/bin/odacli describe-job -j -i {{jobid}}
register: check_status
failed_when: false
until: "(check_status.stdout|from_json).status == 'Success' or (check_status.stdout|from_json).status == 'Failure'"
retries: "{{retries}}"
delay: "{{delay}}"
changed_when: false
The role can be used as follows:
- name: include role to poll job
include_role:
role: odacli_job
when: "'ALREADY_INSTALLED' not in repo.stdout"
vars:
job_stdout: repo.stdout
retries: 50
delay: 3
The job to poll can be specified by variable “jobid”, or you can provide the json-output of launching the job via “job_stdout”, then the role extracts the jobid
After the import of the software (you will see it in /opt/oracle/oak/pkgrepos/orapkgs/clones/), you can now deploy an ORACLE_HOME with it. For that, we can use the same role to poll this asynchronous job:
- name: create database home
ansible.builtin.shell: |
/opt/oracle/dcs/bin/odacli create-dbhome -j -v {{version}}
register: home
- name: include role to poll job
include_role:
role: odacli_job
vars:
job_stdout: home.stdout
retries: 80
delay: 10
Errorhandling
What is not done in the role is the error-handling. It is up to you to define an adequate error-handling. You will get the result of the asynchronous job in the variable check_status.stdout which is of json format.
(check_status.stdout|from_json).status
If the status is
- “Success”, it is OK
- “Failure”, it is not OK
- Any other value (e.g. “Created”), you got a timeout (>retries*timeout sec.), then the result it is unknown (maybe the job completes at a later time, sucessful or not).