2026-06-28 10:00:23

This commit is contained in:
2026-06-28 12:00:23 +02:00
parent 37f5b7a787
commit 6979efd308
4 changed files with 665 additions and 99 deletions
@@ -0,0 +1,289 @@
---
# Playbook: ora_standalone_patch.yml
# Usage: ansible-playbook playbooks/ora_standalone_patch.yml -e "server=togoria oracle_sid=NABOOPRD opatch_zip=/mnt/yavin4/kit/Oracle/opatch/p6880880_190000_Linux-x86-64.zip dbru_zip=/mnt/yavin4/kit/Oracle/Oracle_Database_19/patch/GIRU_19.29/p38298204_190000_Linux-x86-64.zip"
- name: Gather facts for a specific inventory host
hosts: "{{ server }}"
gather_facts: yes
become: true
become_user: oracle
become_method: su
pre_tasks:
- name: Ensure 'server' variable is provided
assert:
that: server is defined
fail_msg: "Please provide the server via -e 'server=<server>'"
- name: Ensure 'oracle_sid' variable is provided
assert:
that: oracle_sid is defined
fail_msg: "Please provide the oracle_sid via -e 'oracle_sid=<oracle_sid>'"
- name: Ensure 'opatch_zip' variable is provided
assert:
that: opatch_zip is defined
fail_msg: "Please provide the opatch_zip via -e 'opatch_zip=<opatch_zip>'"
- name: Ensure 'dbru_zip' variable is provided
assert:
that: dbru_zip is defined
fail_msg: "Please provide the dbru_zip via -e 'dbru_zip=<dbru_zip>'"
tasks:
- name: Show selected system facts
debug:
msg:
hostname: "{{ (ansible_facts.nodename | default(ansible_facts.hostname)) | default('unknown') }}"
os_family: "{{ ansible_facts.os_family | default(ansible_facts.distribution | default('unknown')) }}"
operating_system: "{{ ansible_facts.distribution | default('unknown') }} {{ ansible_facts.distribution_version | default('') }}"
ip_address: "{{ ansible_default_ipv4.address | default('unknown') }}"
hardware_model: "{{ ansible_facts['machine'] | default('unknown') }}"
cpu_count: "{{ ansible_facts['processor_vcpus'] | default(ansible_facts['processor_count'] | default('unknown')) }}"
ram_gb: "{{ (ansible_facts['memtotal_mb'] / 1024) | int | default('unknown') }}"
- name: Source oraenv and display OPatch informations
shell: |
. oraenv <<< {{ oracle_sid }} && $ORACLE_HOME/OPatch/opatch version && echo && echo && $ORACLE_HOME/OPatch/opatch lspatches
register: opatch_output
when: oracle_sid is defined
- name: Display OPatch output
debug:
var: opatch_output.stdout_lines
when: oracle_sid is defined
- name: Check Oracle instance status via sqlplus as SYSDBA
shell: |
. oraenv <<< {{ oracle_sid }} >/dev/null 2>&1
$ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL'
set heading off feedback off verify off pagesize 0
select status from v$instance;
exit
SQL
register: instance_status
changed_when: false
when: oracle_sid is defined
- name: Show Oracle instance status
debug:
msg: "Oracle instance {{ oracle_sid }} status: {{ instance_status.stdout | trim | default('unknown') }}"
when: oracle_sid is defined
- name: Fail if Oracle instance is not OPEN
fail:
msg: "Oracle instance {{ oracle_sid }} is not OPEN (status: {{ instance_status.stdout | default('unknown') }})"
when: oracle_sid is defined and ("OPEN" not in (instance_status.stdout | default('')))
- name: List PDBs (if multitenant)
shell: |
. oraenv <<< {{ oracle_sid }} >/dev/null 2>&1
$ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL'
set heading off feedback off verify off pagesize 0
select name||':'||open_mode from v$pdbs where name!='PDB$SEED';
exit
SQL
register: pdb_list
failed_when: false
changed_when: false
when: oracle_sid is defined
- name: Determine if database is multitenant
set_fact:
is_multitenant: "{{ (pdb_list.stdout is defined) and ('ORA-' not in pdb_list.stdout) and (pdb_list.stdout | trim != '') }}"
when: oracle_sid is defined
- name: Show PDB list (if multitenant)
debug:
var: pdb_list.stdout_lines
when: oracle_sid is defined and is_multitenant
- name: Check for non-OPEN PDBs
shell: |
. oraenv <<< {{ oracle_sid }} >/dev/null 2>&1
$ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL'
set heading off feedback off verify off pagesize 0
select name||':'||open_mode from v$pdbs where name!='PDB$SEED' and upper(open_mode) not like '%READ WRITE%';
exit
SQL
register: pdb_not_open
failed_when: false
changed_when: false
when: oracle_sid is defined and is_multitenant
- name: Filter non-empty PDB results
set_fact:
pdb_not_open_filtered: "{{ pdb_not_open.stdout_lines | select('match', '^.+:.+$') | list }}"
when: oracle_sid is defined and is_multitenant
- name: Fail if any PDB is not OPEN
fail:
msg: "PDB(s) not OPEN: {{ pdb_not_open_filtered }}"
when: oracle_sid is defined and is_multitenant and (pdb_not_open_filtered | length) > 0
- name: Remove $ORACLE_HOME/OPatch directory
shell: |
. oraenv <<< {{ oracle_sid }}
if [ -d "$ORACLE_HOME/OPatch" ]; then
rm -rf "$ORACLE_HOME/OPatch"
echo removed
else
echo not_found
fi
args:
executable: /bin/bash
register: remove_opatch
changed_when: "'removed' in remove_opatch.stdout"
failed_when: false
when: oracle_sid is defined
- name: Show OPatch removal result
debug:
msg: "OPatch removal: {{ remove_opatch.stdout | default('no_output') }}"
when: oracle_sid is defined
- name: Copy OPatch zip to remote host
copy:
src: "{{ opatch_zip }}"
dest: "/tmp/{{ opatch_zip | basename }}"
mode: '0644'
when: opatch_zip is defined
- name: Extract OPatch zip into $ORACLE_HOME
shell: |
. oraenv <<< {{ oracle_sid }}
unzip -o "/tmp/{{ opatch_zip | basename }}" -d "$ORACLE_HOME"
args:
executable: /bin/bash
register: opatch_unzip
changed_when: opatch_unzip.rc == 0 and (opatch_unzip.stdout is defined)
failed_when: opatch_unzip.rc != 0
when: opatch_zip is defined
- name: Remove uploaded OPatch zip from remote
file:
path: "/tmp/{{ opatch_zip | basename }}"
state: absent
when: opatch_zip is defined
- name: Source oraenv and display OPatch informations
shell: |
. oraenv <<< {{ oracle_sid }} && $ORACLE_HOME/OPatch/opatch version && echo && echo && $ORACLE_HOME/OPatch/opatch lspatches
register: opatch_output
when: oracle_sid is defined
- name: Display OPatch output
debug:
var: opatch_output.stdout_lines
when: oracle_sid is defined
- name: Remove /app/oracle/staging_area if present
file:
path: /app/oracle/staging_area
state: absent
- name: Recreate /app/oracle/staging_area
file:
path: /app/oracle/staging_area
state: directory
mode: '0755'
- name: Check local dbru_zip path exists on controller (only if provided)
stat:
path: "{{ dbru_zip }}"
delegate_to: localhost
run_once: true
register: local_zip_stat
when: dbru_zip is defined
- name: Fail if local dbru_zip file not found on controller
fail:
msg: "Local zip file {{ dbru_zip }} not found on controller"
when: dbru_zip is defined and (local_zip_stat.stat is not defined or not local_zip_stat.stat.exists)
run_once: true
- name: Copy local dbru_zip to remote staging_area
copy:
src: "{{ dbru_zip }}"
dest: "/app/oracle/staging_area/{{ dbru_zip | basename }}"
mode: '0644'
when: dbru_zip is defined
- name: Unarchive uploaded zip into staging_area
unarchive:
src: "/app/oracle/staging_area/{{ dbru_zip | basename }}"
dest: /app/oracle/staging_area
remote_src: yes
when: dbru_zip is defined
- name: Remove uploaded zip from remote staging_area
file:
path: "/app/oracle/staging_area/{{ dbru_zip | basename }}"
state: absent
when: dbru_zip is defined
- name: Find extracted directories under staging_area
find:
paths: /app/oracle/staging_area
file_type: directory
depth: 1
register: staging_dirs
when: dbru_zip is defined
- name: Show staging directories found
debug:
var: staging_dirs.files
when: dbru_zip is defined
- name: Find all subdirectories inside extracted main dirs (depth=2)
shell: |
find /app/oracle/staging_area -mindepth 2 -maxdepth 2 -type d -print
args:
executable: /bin/bash
register: all_subdirs
changed_when: false
when: dbru_zip is defined
- name: Show all subdirectories to process
debug:
var: all_subdirs.stdout_lines
when: dbru_zip is defined and all_subdirs is defined
- name: Apply OPatch for each extracted subdirectory (clean chdir)
shell: |
. oraenv <<< {{ oracle_sid }}
export PATH=$PATH:$ORACLE_HOME/OPatch
if [ -x "$ORACLE_HOME/OPatch/opatch" ]; then
$ORACLE_HOME/OPatch/opatch apply -silent
else
echo "opatch_not_found"
exit 2
fi
args:
executable: /bin/bash
chdir: "{{ item }}"
loop: "{{ all_subdirs.stdout_lines | default([]) }}"
loop_control:
label: "{{ item }}"
register: opatch_apply_results
failed_when: false
changed_when: false
when: dbru_zip is defined and all_subdirs is defined and (all_subdirs.stdout_lines | length) > 0
- name: Show OPatch apply results (stdout lines)
debug:
var: opatch_apply_results.results | map(attribute='stdout_lines') | list
when: dbru_zip is defined and opatch_apply_results is defined
- name: Collect failed OPatch applies
set_fact:
failed_applies: "{{ opatch_apply_results.results | selectattr('rc','ne',0) | map(attribute='item') | list }}"
when: dbru_zip is defined and opatch_apply_results is defined
- name: Fail if any OPatch apply failed
fail:
msg: "OPatch apply failed in: {{ failed_applies }}"
when: dbru_zip is defined and failed_applies is defined and (failed_applies | length) > 0