--- # 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||':'||restricted 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: msg: | PDB, OPEN, RESTRICTED {% for pdb in pdb_list.stdout_lines %} {{ pdb }} {% endfor %} when: oracle_sid is defined and is_multitenant - name: Check for non-OPEN PDBs or RESTRICTED 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||':'||restricted from v$pdbs where name!='PDB$SEED' and (upper(open_mode) not like '%READ WRITE%' or upper(restricted) = 'YES'); 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: Execute datapatch shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/OPatch/datapatch register: datapatch_output changed_when: false when: oracle_sid is defined - name: Show datapatch output debug: var: datapatch_output.stdout_lines when: oracle_sid is defined - name: Remove temporary SQL files from remote host file: path: "{{ item }}" state: absent loop: - /tmp/dba_invalid_objects.sql - /tmp/dba_registry.sql when: oracle_sid is defined - name: Execute utlrp.sql in CDB$ROOT shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' @$ORACLE_HOME/rdbms/admin/utlrp.sql exit SQL register: utlrp_root changed_when: false when: oracle_sid is defined - name: Get list of PDBs for utlrp execution 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 from v$pdbs where name!='PDB$SEED' and open_mode like '%READ WRITE%' and restricted='NO'; exit SQL register: pdb_names failed_when: false changed_when: false when: oracle_sid is defined and is_multitenant - name: Execute utlrp.sql in each PDB shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' alter session set container={{ item | trim }}; @$ORACLE_HOME/rdbms/admin/utlrp.sql exit SQL loop: "{{ pdb_names.stdout_lines | default([]) }}" loop_control: label: "{{ item }}" register: utlrp_pdb_results changed_when: false failed_when: false when: oracle_sid is defined and is_multitenant and (pdb_names.stdout_lines | default([]) | length) > 0 - name: Copy dba_invalid_objects.sql to remote host copy: src: ../files/scripts/sql/dba_invalid_objects.sql dest: /tmp/dba_invalid_objects.sql mode: '0644' when: oracle_sid is defined - name: Copy dba_registry.sql to remote host copy: src: ../files/scripts/sql/dba_registry.sql dest: /tmp/dba_registry.sql mode: '0644' when: oracle_sid is defined - name: Execute dba_invalid_objects.sql in CDB$ROOT shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' @/tmp/dba_invalid_objects.sql exit SQL register: dba_invalid_root changed_when: false when: oracle_sid is defined - name: Show dba_invalid_objects.sql results in CDB$ROOT debug: var: dba_invalid_root.stdout_lines when: oracle_sid is defined - name: Execute dba_registry.sql in CDB$ROOT shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' @/tmp/dba_registry.sql exit SQL register: dba_registry_root changed_when: false when: oracle_sid is defined - name: Show dba_registry.sql results in CDB$ROOT debug: var: dba_registry_root.stdout_lines when: oracle_sid is defined - name: Execute dba_invalid_objects.sql in each PDB shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' alter session set container={{ item | trim }}; @/tmp/dba_invalid_objects.sql exit SQL loop: "{{ pdb_names.stdout_lines | default([]) }}" loop_control: label: "{{ item }}" register: dba_invalid_pdb_results changed_when: false failed_when: false when: oracle_sid is defined and is_multitenant and (pdb_names.stdout_lines | default([]) | length) > 0 - name: Show dba_invalid_objects.sql results in PDBs debug: msg: | dba_invalid_objects.sql output for PDB: {{ item }} {{ dba_invalid_pdb_results.results[pdb_names.stdout_lines.index(item)].stdout }} loop: "{{ pdb_names.stdout_lines | default([]) }}" loop_control: label: "{{ item }}" when: oracle_sid is defined and is_multitenant and (pdb_names.stdout_lines | default([]) | length) > 0 - name: Execute dba_registry.sql in each PDB shell: | . oraenv <<< {{ oracle_sid }} >/dev/null 2>&1 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'SQL' alter session set container={{ item | trim }}; @/tmp/dba_registry.sql exit SQL loop: "{{ pdb_names.stdout_lines | default([]) }}" loop_control: label: "{{ item }}" register: dba_registry_pdb_results changed_when: false failed_when: false when: oracle_sid is defined and is_multitenant and (pdb_names.stdout_lines | default([]) | length) > 0 - name: Show dba_registry.sql results in PDBs debug: msg: | dba_registry.sql output for PDB: {{ item }} {{ dba_registry_pdb_results.results[pdb_names.stdout_lines.index(item)].stdout }} loop: "{{ pdb_names.stdout_lines | default([]) }}" loop_control: label: "{{ item }}" when: oracle_sid is defined and is_multitenant and (pdb_names.stdout_lines | default([]) | length) > 0 - name: Remove temporary SQL files from remote host file: path: "{{ item }}" state: absent loop: - /tmp/dba_invalid_objects.sql - /tmp/dba_registry.sql when: oracle_sid is defined