diff --git a/ansible/swgalaxy/playbooks/ora_stand_datapatch.yml b/ansible/swgalaxy/playbooks/ora_stand_datapatch.yml new file mode 100644 index 0000000..477eb11 --- /dev/null +++ b/ansible/swgalaxy/playbooks/ora_stand_datapatch.yml @@ -0,0 +1,119 @@ +--- +# Playbook: ora_stand_datapatch.yml + +# Usage example: + +# ansible-playbook playbooks/ora_stand_datapatch.yml \ +# -e server=togoria \ +# -e oracle_sid=NABOOPRD + + +- 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='" + + 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 + diff --git a/ansible/swgalaxy/playbooks/ora_stand_patch_oh.yml b/ansible/swgalaxy/playbooks/ora_stand_patch_oh.yml new file mode 100644 index 0000000..06df2e7 --- /dev/null +++ b/ansible/swgalaxy/playbooks/ora_stand_patch_oh.yml @@ -0,0 +1,257 @@ +--- +# 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: 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 -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' + diff --git a/ansible/swgalaxy/playbooks/ora_standalone_patch.yml b/ansible/swgalaxy/playbooks/ora_standalone_patch.yml deleted file mode 100644 index 7a3bde5..0000000 --- a/ansible/swgalaxy/playbooks/ora_standalone_patch.yml +++ /dev/null @@ -1,99 +0,0 @@ ---- -# 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='" - - - 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 directory contents - 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 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 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 zip file 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 diff --git a/ansible/swgalaxy/playbooks/ora_standalone_patch.yml.old b/ansible/swgalaxy/playbooks/ora_standalone_patch.yml.old new file mode 100644 index 0000000..e6d30aa --- /dev/null +++ b/ansible/swgalaxy/playbooks/ora_standalone_patch.yml.old @@ -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='" + + - 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 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 + + +