--- # 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='" - name: Ensure 'oracle_sid' variable is provided assert: that: oracle_sid is defined fail_msg: "Please provide the oracle_sid via -e '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='" - name: Ensure 'dbru_zip' variable is provided assert: that: dbru_zip is defined fail_msg: "Please provide the dbru_zip via -e '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' - 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