2026-06-28 12:00:23 +02:00
|
|
|
---
|
|
|
|
|
# Playbook: ora_stand_patch_oh.yml
|
|
|
|
|
# Usage example:
|
|
|
|
|
|
|
|
|
|
# ansible-playbook playbooks/ora_stand_patch_oh.yml \
|
|
|
|
|
# -e server=togoria \
|
|
|
|
|
# -e oracle_sid=NABOOPRD \
|
|
|
|
|
# -e opatch_zip=/mnt/yavin4/kit/Oracle/opatch/p6880880_190000_Linux-x86-64.zip \
|
|
|
|
|
# -e 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 if any oracle processes are using ORACLE_HOME files
|
|
|
|
|
shell: |
|
|
|
|
|
. oraenv <<< {{ oracle_sid }} >/dev/null 2>&1
|
|
|
|
|
lsof +D "$ORACLE_HOME" 2>/dev/null | grep -v COMMAND | awk '{print $1}' | sort -u || true
|
|
|
|
|
args:
|
|
|
|
|
executable: /bin/bash
|
|
|
|
|
register: oracle_processes_using_oh
|
|
|
|
|
changed_when: false
|
|
|
|
|
failed_when: false
|
|
|
|
|
when: oracle_sid is defined
|
|
|
|
|
|
|
|
|
|
- name: List oracle processes using ORACLE_HOME files and fail if any found
|
|
|
|
|
fail:
|
|
|
|
|
msg: |
|
|
|
|
|
Found oracle user processes using files from $ORACLE_HOME.
|
|
|
|
|
Cannot proceed with patching while processes are active:
|
|
|
|
|
{{ oracle_processes_using_oh.stdout_lines }}
|
|
|
|
|
when:
|
|
|
|
|
- oracle_sid is defined
|
|
|
|
|
- oracle_processes_using_oh.stdout_lines | default([]) | 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: 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 -not -name automation -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
|
|
|
|
|
|
|
|
|
|
- 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'
|
|
|
|
|
|
2026-06-28 21:28:40 +02:00
|
|
|
- name: Source oraenv and display OPatch informations (after OH patch)
|
|
|
|
|
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 (after OH patch)
|
|
|
|
|
debug:
|
|
|
|
|
var: opatch_output.stdout_lines
|
|
|
|
|
when: oracle_sid is defined
|
|
|
|
|
|