1 - Vagrant

1.1 - Vagrant on Windows

1. Overview

Important
This documentation describes the installation of the required tools for ansible-oracle on a Windows based hostsystem.
chocolatey
  • Requirements

    • Intel/AMD x86 CPU is mandatory
      no support for Mac with M1

    • 16 GB RAM

    • 150 GB free Disk space
      The free space is needed in your home directory in Windows.

    • Admin privileges on Host machine

    • Hyper-V disabled

    • no WSL/WSL2 for installation
      While you may try it, the documentation states that Hyper-V should be completely disabled and that no WSL/WSL2 is used.

Warning
There are many issues when Hyper-V is active on the host machine.
Please switch Hyper-V off completely, before starting the boxes with Vagrant.
If you do find a really good working solution with Vagrant + VirtualBox and Hyper-V, feedback is welcome.

2. Installation

2.1. Disable Hyper-V in Windows

Warning
There are numerous problems with Vagrant & VirtualBox when Hyper-V is enabled.
It is therefore recommended to disable it for ansible-oracle.
Important
All commands require a cmd.exe with admin rights.
Check setting of Hyper-V
bcdedit

The output should contain a line with hypervisorlaunchtype.
If the line is not shown or Off, Hyper-V is completely disabled.

Output
# bcdedit

Windows-Start-Manager
---------------------
...

Windows-Startladeprogramm
-------------------------
...
hypervisorlaunchtype    Off

If it is Auto or On

Disable Hyper-V with immediate reboot
bcdedit /set {current} hypervisorlaunchtype off
shutdown -r -t 0

2.2. Chocolatey

Important
The installation of Chocolatey is not mandatory for ansible-oracle.
All tools could be installed manually. Please ensure, that they are all executable from git+bash.
It is highly recommended to use Chocolatey with ansible-oracle.
Important
See the Homepage of chocolatey for more details.
Execute in PowerShell with Admin rights
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

2.3. git+bash, VirutalBox and Vagrant

Execute in cmd.exe with admin rights
choco install git vagrant virtualbox

2.4. ConEmu - Handy Windows Terminal

Important
This step is optional.

This is a really good alternative terminal to cmd.exe for windows.
It is not recommended for ansible-oracle but you should give it a try and enjoy it - especially when VSCode is not used.

Execute in cmd.exe with admin rights
choco install conemu

2.5. Visual Studio Code

Important
This step is optional.
You should use VSCode - especially when Advanced or Expert setups are planned for a later time.
Execute in cmd.exe with admin rights
choco install vscode.install

3. Configuration

3.1. SSH-Setup

Warning
The example expects the private key at the default location.
Important
Create a custom key and place the public key in same directory as the Vagrantfile. Do not use the created keys from Vagrant.
You should always use the ssh-agent to connect to all machines.
Important
All commands are executed in git+bash on the Host machine - not inside a VM!
Create a key when needed
ssh-keygen
Output
ssh-keygen

Generating public/private rsa key pair.

Enter file in which to save the key (/c/Users/User/.ssh/id_rsa): Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/User/.ssh/id_rsa
Your public key has been saved in /c/Users/User/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:Xq9EGKMUEXFkxeli4NilfvwEkOAmvkV1PiRteA+NQRQ ubuntu@ansible-oracle-bastion
Important
Enable Agent Forwarding for all SSH-Sessions.
This is mandatory for Visual Studio Code.
Enable Agent Forwarding for all SSH-Sessions.
code ~/.ssh/config

Host *
  ForwardAgent yes
Start a ssh-agent (bash)
eval $(ssh-agent)
Output
eval $(ssh-agent)
Agent pid 16840
Import a key
ssh-add ~/.ssh/id_rsa
Output
$ ssh-add ~/.ssh/id_rsa
Identity added: /c/Users/User/.ssh/id_rsa (User@unknown)
Show imported keys
ssh-add -l
Output
ssh-add -l
2048 SHA256:EsVIFzfCLUDTO2cMTf4q4qt2Eu0w1pA2sdcXxh9jyBs User@unknown (RSA)

3.2. Vagrant SSH-Setup

Important
The Vagrantfile has a provisioner script block which copy a stored id_rsa.pub into authorized_keys on the target machine.

The exact command to copy the key is shown during the deployment description.

3.3. Vagrant Plugins

The following modules are used by the tutorial examples.

  • install* vagrant-disksize
    This is an experimental plugin which is needed to resize the 1. disk of a vagrant box.

vagrant plugin install vagrant-disksize

4. See also

1.2 - Vagrant Basics

What is vagrant?

Vagrant is a tool that makes it EASIER and FASTER to setup virtual machines. Vagrant Boxes are pre-built base images (e.g. Oracle Linux) that can be imported into Vagrant as a starting point. For our use-case, we will use Vagrant together with Virtualbox and Oracle Linux

Installation

  1. Download and install Vagrant
  2. Download and install Oracle Virtualbox

Let’s start

Using Tim Halls’s guide, let’s play https://oracle-base.com/articles/vm/vagrant-a-beginners-guide

  1. hmm, cannot download that first OL box, let’s see the oracle doc https://yum.oracle.com/boxes/
  2. Note also that the default provider for vagrant has changed to libvirt (was virtualbox), so we will need to add “–provider virtualbox” to our vagrant add commands

Download Vagrant boxes

  • Instead of downloading a large DVD image, we can download a small vagrant box.
  • We can manually download a new box using the vagrant box add command.
  • We only need to do this once for each vagrant box (e.g. one time for Oracle Linux 7 and one time for Oracle Linux 8).
  • This download process would also happen automatically if required, when you reference a new box in a Vagrantfile

In a DOS command prompt…

vagrant box add --provider virtualbox https://oracle.github.io/vagrant-projects/boxes/oraclelinux/7.json
vagrant box add --provider virtualbox https://oracle.github.io/vagrant-projects/boxes/oraclelinux/8.json

Some vagrant addons

  1. allows us to resize disks (optional)

In a DOS command prompt…

vagrant plugin install vagrant-disksize
  1. ensure the virtualbox guest additions is updated, required to ensure that the guest additions are automatically updated for all new vagrant boxes

In a DOS command prompt…

vagrant plugin install vagrant-vbguest
  1. Automatically update the /etc/hosts

In a DOS command prompt…

vagrant plugin install vagrant-hostsupdater

Create the initial vagrantfile

To create the virtual machine, we first of all create a new directory, change to it and create the initial “vagrantfile”

In a DOS command prompt…

mkdir D:\vagrant\vol7db19
cd /D D:\vagrant\vol7db19
vagrant init

Start the VM

edit the vagrantfile (see Tim Hall’s website above) and when ready with our configuration, we can install, create and start the VM with the following command…

In a DOS command prompt…

vagrant up

Changing the vagrantfile

if you change the vagrantfile, destroy and recreate your virtual machine

In a DOS command prompt…

vagrant destroy -f
vagrant up

accessing the VM

  1. use the command “vagrant ssh” to access the VM per shh in your DOS command prompt
  2. or connect with ssh to 127.0.0.1, port 2222 with your favourite tool

Removing Vagrant boxes

  • We can remove any old boxes we are no longer using, with the vagrant box remove command.
  • These boxes will be automatically downloaded again if required.
  • It is best not to remove them, if still used regularly, as it takes a few minutes to download them

In a DOS command prompt…

vagrant box remove oraclelinux/7
vagrant box remove oraclelinux/8

2 - Beginners Guide

This guide installs an Oracle AI Database 26ai with a container database DB1 and the PDB ORCLPDB on an Oracle Linux 9 VM with ansible-oracle.

1. Overview

1.1. Architecture overview

Architecture

1.2. What are we going to do?

  1. Prepare the Ansible controller: Ansible in Ubuntu on WSL2 on Windows.

  2. Configure the inventory with the IP address and login user of the Oracle Linux 9 VM.

  3. Start the playbook playbooks/do_all.yml. It executes all needed playbooks:

    1. opitzconsulting.ansible_oracle.os: prepare the VM for AutoUpgrade

    2. playbooks/autoupgrade.yml: install AutoUpgrade on the VM and download a gold image from My Oracle Support. The gold image is a complete ORACLE_HOME including the Release Update.

    3. opitzconsulting.ansible_oracle.single_instance_fs: configure the OS, install the ORACLE_HOME from the gold image and create the database.

1.3. Defaults

Table 1. Defaults used in this guide
Value Description

~/git/ansible-oracle

git repository on the Ansible controller

~/git/ansible-oracle/example/beginner/ansible

Working directory for all Ansible commands

beginner-dbfs

Name of the VM in the inventory

192.168.66.161

IP address of the VM (ansible_host)

ol

Login user on the VM (ansible_user)

/u01/sw

Installation media on the VM, target of the download (oracle_sw_source_local)

/u01/stage

Staging directory on the VM (oracle_stage)

2. Requirements

2.1. Oracle Linux 9 VM

Any Oracle Linux 9 VM matching the requirements below can be used. See Create the VM with Hyper-V and Packer for an example with Hyper-V on Windows.

Table 2. Requirements for the VM
Item Requirement

Operating system

Oracle Linux 9 x86_64

CPU

2 vCPUs (recommended)

Memory

8 GB RAM (recommended)

Disk

/dev/sda with the volume group ol on /dev/sda3. The volume group needs min. 81 GB free space for swap (16 GB), /u01 (40 GB) and /u02 (25 GB). See host_fs_layout in group_vars/all/host.yml.

Network

Fixed IP address, reachable from the Ansible controller with SSH

Internet

Access to the Oracle Linux yum repositories and to My Oracle Support for AutoUpgrade (updates.oracle.com, login-ext.identity.oraclecloud.com, aru-akam.oracle.com)

Login user

User with SSH login and sudo privileges, e.g. ol

2.2. Create the VM with Hyper-V and Packer

The repository packer-hyperv builds an Oracle Linux 9 template with Packer and creates the VM for this guide on Hyper-V. See the README of the repository for the installation of packer and task.

Important
All steps in this chapter are executed in PowerShell on Windows, not in WSL.

The template uses

  • the login user ol with sudo privileges and SSH key login,

  • the volume group ol on /dev/sda3, as expected by host_fs_layout in this guide, and

  • the SSH public key %USERPROFILE%\.ssh\id_ed25519.pub.

Clone the repository
git clone https://github.com/Rendanic/packer-hyperv.git
cd packer-hyperv
Allow Hyper-V for the current user (once, elevated PowerShell, log off and on afterwards)
task hyperv:hyperv-add-admin-user
Build the Oracle Linux 9 template
task hyperv:packer-ol9
Build the template with another SSH public key
task hyperv:packer-ol9 SSH_KEY_FILE=C:\path\to\key.pub
Create the VM from the template
cd OL9
.\create-vm.ps1 -NewVMName beginner-dbfs -Hostname beginner-dbfs -ExternalSwitch intern_66 -FixedIP 192.168.66.161 -CPUs 2 -MemoryGB 8

create-vm.ps1 creates the VM with two network adapters:

  • Default Switch with DHCP for the internet access

  • the switch from -ExternalSwitch with the fixed IP address from -FixedIP. Use this address for ansible_host in inventory/hosts.yml.

Important
The template has a disk with 80 GB. The volume group ol has approx. 60 GB free space then, but the guide needs 81 GB.
Increase the disk of the VM, e.g. to 130 GB, before you start the installation.
Note
The SSH public key of the template must belong to the private key of the Ansible controller. Copy the key pair from Windows into WSL or use the public key from WSL for SSH_KEY_FILE.

2.3. Ansible controller

This guide uses Ubuntu on WSL2 on Windows as Ansible controller.

Note
Optionally, the database VM can be used as Ansible controller as well. Install the packages with dnf instead of apt in this case, e.g. sudo dnf install -y git python3. ansible_host and ansible_user in the inventory are used the same way, the VM connects to itself with SSH.
Table 3. Requirements for the Ansible controller
Item Requirement

Python

Python 3 with venv

Ansible

ansible-core >= 2.16

Internet

Access to GitHub and Ansible Galaxy

AutoUpgrade is executed on the VM, not on the Ansible controller. Java and the gold image are not needed on the Ansible controller.

2.4. My Oracle Support

A My Oracle Support account is needed for the download of the gold image.

3. Setup Ansible controller

Important
All steps are executed on the Ansible controller.
Install packages
sudo apt update
sudo apt install -y git python3-venv
Clone git repository
mkdir -p ~/git
cd ~/git

git clone https://github.com/oravirt/ansible-oracle.git
Create a Python virtual environment with Ansible
python3 -m venv ~/venv/ansible-oracle
source ~/venv/ansible-oracle/bin/activate

cd ~/git/ansible-oracle
pip install -r tools/dev/requirements_dev_2.16.txt
Tip
tools/dev/requirements_dev_2.19.txt installs ansible-core 2.19 instead of 2.16.
Important
Activate the virtual environment in each new shell with source ~/venv/ansible-oracle/bin/activate.
Install collections
cd ~/git/ansible-oracle/example/beginner/ansible
ansible-galaxy collection install -r requirements.yml

4. Configure inventory

4.1. IP address and login user

The VM is configured in inventory/hosts.yml. Change ansible_host and ansible_user for your VM.

inventory/hosts.yml
---
all:
  children:
    dbfs:
      hosts:
        beginner-dbfs:
          # IP address or DNS name of the Oracle Linux 9 VM
          ansible_host: 192.168.66.161
          # Login user on the VM with sudo privileges (SSH key login)
          ansible_user: ol
Note
beginner-dbfs is only the name of the host in the inventory. The host specific variables are in inventory/host_vars/beginner-dbfs/. Rename the directory as well, when you change the name.

4.2. SSH key

Ansible connects with an SSH key to the VM.

Create SSH key (skip it, when a key exists already)
ssh-keygen -t ed25519
Copy the public key to the VM
ssh-copy-id ol@192.168.66.161

4.3. Check the connection

Connection test
cd ~/git/ansible-oracle/example/beginner/ansible
ansible all -m ansible.builtin.ping
Test of sudo
ansible all -b -m ansible.builtin.command -a id
Tip
Add --ask-become-pass (-K) to all ansible and ansible-playbook commands, when sudo requires a password for the login user.

4.4. My Oracle Support credentials

The My Oracle Support credentials are read from the environment variables MOS_USERNAME and MOS_PASSWORD.

My Oracle Support credentials
export MOS_USERNAME=<your My Oracle Support login>
read -s MOS_PASSWORD && export MOS_PASSWORD

Alternatively, store the credentials in the file .env in the working directory and load it into the shell before starting ansible-playbook.

~/git/ansible-oracle/example/beginner/ansible/.env
MOS_USERNAME=<your My Oracle Support login>
MOS_PASSWORD=<your My Oracle Support password>
Load .env into the shell
cd ~/git/ansible-oracle/example/beginner/ansible
set -a; . ./.env; set +a
Important
.env contains your password. Restrict the permissions with chmod 600 .env.
The file is ignored by git (.gitignore).
Note
The lookups are defined in inventory/group_vars/all/mos_login.yml. Optionally, set mos_username and mos_password there statically instead of the environment variables. Encrypt the password with ansible-vault in this case.

4.5. Database passwords

The password for all database users is default_dbpass in inventory/group_vars/all/password.yml.

Warning
The default Oracle_123 is for test environments only. Change it and use ansible-vault for other environments.

5. AutoUpgrade on the VM

AutoUpgrade is executed on the VM (autoupgrade_run_local: false in inventory/group_vars/all/autoupgrade.yml). It downloads the gold image directly into /u01/sw on the VM. No installation media are copied from the Ansible controller (oracle_sw_copy: false).

The execution of AutoUpgrade on the VM requires:

  • the Python module pexpect for the keystore with the My Oracle Support credentials,

  • a JDK for autoupgrade.jar and

  • the download directory /u01/sw on the filesystem /u01.

The playbook os creates the filesystem /u01 (host_fs_layout in the inventory). playbooks/autoupgrade.yml installs the packages for the JDK and pexpect before the installation of AutoUpgrade. Therefore do_all.yml executes the playbook os before AutoUpgrade. The playbook os is executed a second time, because it is a fixed part of opitzconsulting.ansible_oracle.single_instance_fs. The second execution changes nothing.

Table 4. Download configuration in inventory/group_vars/all/autoupgrade.yml
Variable Value Description

autoupgrade_download_latest

true

Download the latest autoupgrade.jar from Oracle

autoupgrade_download_patches

target_version: 26, patch: RECOMMENDED, gold_image: ALL

Gold image for 26ai with all recommended patches

autoupgrade_download_gold_image_rename

true

Rename the gold image to db_home_<release>.zip, e.g. db_home_26.04.zip

autoupgrade_download_gold_image_latest

true

Symlink db_home_26_latest.zip to the latest gold image of 26ai

See Oracle AutoUpgrade for details.

Important
The gold image is built by Oracle on request.
When no suitable image is cached, the download can take a few hours.

The release update of the gold image is known only after the download. Therefore the inventory uses the symlink db_home_26_latest.zip as imagename instead of the real filename. The symlink points to the gold image with the highest release update in /u01/sw. It is changed only, when a newer gold image is downloaded - the download of an older release update keeps it unchanged.

Check the downloaded gold image on the VM
ls -l /u01/sw/db_home_*.zip
Example
db_home_26.04.zip
db_home_26_latest.zip -> db_home_26.04.zip
inventory/group_vars/all/db-homes.yml
  db26_si_ee: &db26_si_ee
    version: 26.1.0.0
    oracle_home: /u01/app/oracle/product/db26_si_ee
    edition: EE
    imagename: db_home_26_latest.zip
Tip
Use the real filename, e.g. imagename: db_home_26.04.zip, when an ORACLE_HOME must be installed with a fixed release update.

6. Install database with Ansible

Start installation
cd ~/git/ansible-oracle/example/beginner/ansible
ansible-playbook playbooks/do_all.yml
Note
The execution takes approx. 60 minutes plus the download of the gold image.
Table 5. Configuration of the database
Parameter Value

ORACLE_BASE

/u01/app/oracle

ORACLE_HOME

/u01/app/oracle/product/db26_si_ee

ORACLE_SID

DB1

PDB

ORCLPDB

db_create_file_dest

/u02/oradata

db_recovery_file_dest

/u02/fra

Listener

LISTENER, port 1521

7. How to work with the installed database

Login into the VM
ssh ol@192.168.66.161
Switch to user oracle
sudo -iu oracle
Initialize ocenv to set the environment variables
ocenv

ocenv shows the ORACLE_HOMEs, databases and listeners on the host. envhelp shows an overview of the commands.

Set ORACLE_SID
DB1
Start SQL*Plus
sql

3 - Advanced Guide

Warning
This guide is a work in progress!

1. Advanced Guide

Important
The setup of Vagrant, VirtualBox and SSH is not part of this documentation.
Please follow the documentation.

1.1. Overview

1.1.1. Architecture overview

Architecturview

1.1.2. Defaults

Table 1. Used defaults in this guide
Value Description

~/git/ansible-oracle

git Repository on Host System

~/git/ansible-oracle

git Repository in VM

~/.ssh/id_rsa

private Key on Host System

vagrant

SSH-User for Login and Ansible

~/git/ansible-oracle/ansible-oracle/example/beginner

Vagrantfile location

1.2. Setup Host System

Important
All steps are executed on the Host System inside a git+bash.
clone git Repository
mkdir ~/git
cd ~/git

git clone https://github.com/oravirt/ansible-oracle.git

1.2.1. Build Oracle Software tree

Important
It is highly recommended to use the following structure.
Do not change this until you really know what you are doing.

All installation media are searched in oracle_stage_remote on the database server.

Warning
It is assumed that oracle_stage_remote is set to /sw
1.2.1.1. Installation media
Important
Only LINUX.X64_193000_db_home.zip is needed as base media for the Advanced Guide.
Example oracle_stage_remote: /sw
/sw/LINUX.X64_180000_db_home.zip
/sw/LINUX.X64_193000_grid_home.zip
/sw/LINUX.X64_213000_grid_home.zip
/sw/LINUX.X64_193000_db_home.zip
/sw/LINUX.X64_213000_db_home.zip
1.2.1.2. OPatch archive
Important
Only p6880880_190000_Linux-x86-64.zip is needed for patching in the Advanced Guide.
Example oracle_stage_remote: /sw for OPatch
/sw/oracle/linux64/p6880880_112000_Linux-x86-64.zip
/sw/oracle/linux64/p6880880_180000_Linux-x86-64.zip
/sw/oracle/linux64/p6880880_190000_Linux-x86-64.zip
/sw/oracle/linux64/p6880880_210000_Linux-x86-64.zip
1.2.1.3. Patch structure

<todo wip>

Directory Description

1.2.2. Configure SSH-Key

Important
Do not skip the preparation for Vagrant and SSH.
Please follow the documentation.
Copy Public key into Vagrantfile directory
cp ~/.ssh/id_rsa.pub ~/git/ansible-oracle/ansible-oracle/example/beginner
Start ssh-Agent
eval $(ssh-agent)
Import the generated key
ssh-add ~/.ssh/id_rsa

1.2.3. Start Vagrantbox

Important
The Deployment und provisioning of the VM takes some minutes.
It highly depends on the speed of the internet connection.
Important
The SSH public key is configured during the provisioning phase of the VM.
Do not forget to copy the file before starting the box with vagrant up.
vagrant up
cd ~/git/ansible-oracle/ansible-oracle/example/beginner
VAGRANT_EXPERIMENTAL=disks vagrant up
1.2.3.1. What happens when I start vagrant?
  1. Using the vagrantfile, an Oracle Linux VM is installed (2x CPU, 4096MB RAM, 150GB HDD). IP = 192.168.56.161

  2. A mount point /vagrant is available in the Oracle Linux VM. This is a shared folder from your Windows machine.

  3. Your ssh key is used to connect to the VM in a secure way

  4. docker is installed in the Oracle Linux VM

  5. docker-compose is installed in the Oracle Linux VM

  6. The ansible-oracle repository is cloned to the Oracle Linux VM

  7. A docker container "Ansible Container" is built with docker-compose. This runs in the Oracle Linux VM

  8. No Oracle database is yet installed. See below

1.3. Working inside the VM

1.3.1. Connect to VM

Important
The next steps are done inside the VM.
The SSH-Setup from the previous chapter must be completed before continuing!
Connect into VM with SSH from git+bash
ssh -A vagrant@192.168.56.161

1.3.2. Note about Installation media

The installation media is mounted with vboxsf from Vagrant into the Linux VM, mounted as /vagrant.
Use the following command to check this…​

check for Installation media
[vagrant@beginner-dbfs-151-192-168-56-161 ~]$ ls /vagrant/LINUX.X64_193000_db_home.zip

/vagrant/LINUX.X64_193000_db_home.zip

1.3.3. Start Ansible-Container inside VM

Important
Check the prompt shown in this documentation, as to whether you should start the commands in the shell or inside the Ansible-Container.
ansible@ansible-oracle is for working inside the container. .Start Ansible-Container
cd ~/git/ansible-oracle/docker
docker-compose run --rm -w /git/ansible-oracle/example/beginner/ansible ansible bash
Example
[vagrant@beginner-dbfs-151-192-168-56-161 docker]$ docker-compose run --rm -w /git/ansible-oracle/example/beginner/ansible ansible bash
[+] Running 2/0
 ⠿ Network docker_default          Created
 ⠿ Volume "docker_ansible_galaxy"  Created

ansible@ansible-oracle:/git/ansible-oracle/example/beginner/ansible$
Important
The collection is installed once and stored on the docker volume.
Install Collections
ansible@ansible-oracle:/git/ansible-oracle/example/beginner/ansible$ ansible-galaxy collection install -r requirements.yml
Example
ansible@ansible-oracle:/git/ansible-oracle/example/beginner/ansible$ ansible-galaxy collection install -r requirements.yml
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/devsec-hardening-8.2.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/devsec-hardening-8.2.0-0j9481f1
Installing 'devsec.hardening:8.2.0' to '/ansible/galaxy/ansible_collections/devsec/hardening'
Downloading https://galaxy.ansible.com/download/opitzconsulting-ansible_oracle-3.2.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/opitzconsulting-ansible_oracle-3.2.0-x3wo4c3b
devsec.hardening:8.2.0 was installed successfully
Installing 'opitzconsulting.ansible_oracle:3.2.0' to '/ansible/galaxy/ansible_collections/opitzconsulting/ansible_oracle'
Downloading https://galaxy.ansible.com/download/ansible-posix-1.4.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/ansible-posix-1.4.0-1jub71c6
opitzconsulting.ansible_oracle:3.2.0 was installed successfully
Installing 'ansible.posix:1.4.0' to '/ansible/galaxy/ansible_collections/ansible/posix'
Downloading https://galaxy.ansible.com/download/community-mysql-3.5.1.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/community-mysql-3.5.1-lkcnbkd5
ansible.posix:1.4.0 was installed successfully
Installing 'community.mysql:3.5.1' to '/ansible/galaxy/ansible_collections/community/mysql'
Downloading https://galaxy.ansible.com/download/community-crypto-2.8.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/community-crypto-2.8.0-6sco_75m
community.mysql:3.5.1 was installed successfully
Installing 'community.crypto:2.8.0' to '/ansible/galaxy/ansible_collections/community/crypto'
Downloading https://galaxy.ansible.com/download/community-general-5.8.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-25z8isd809/tmp25w_hn4o/community-general-5.8.0-up2j_3iq
community.crypto:2.8.0 was installed successfully
Installing 'community.general:5.8.0' to '/ansible/galaxy/ansible_collections/community/general'
community.general:5.8.0 was installed successfully

1.3.4. Start Installation with Ansible

Important
Make sure to start the installation inside the Ansible Container.
The prompt shows the correct location.
The execution takes ~60 minutes.
Start installation with Ansible
cd /git/ansible-oracle/example/beginner/ansible
ansible-playbook -i inventory/ -e hostgroup=dbfs playbooks/single-instance-fs.yml

The Database creation is now complete.
See the following chapter for details about how to work with the VM. === How to work with installed database

1.3.5. Login as vagrant

Login with SSH from git+bash
ssh -A vagrant@192.168.56.161

1.3.6. Sudo oracle

sudo oracle
[vagrant@beginner-dbfs-151-192-168-56-161 ~]$ sudo su - oracle
Example
[vagrant@beginner-dbfs-151-192-168-56-161 ~]$ sudo su - oracle
Last login: Sun Nov  6 11:16:54 UTC 2022 on pts/2
execute ocenv to source Oracle Environment

1.3.7. Start ocenv

Initialize ocenv to set the environment variables
[oracle@beginner-dbfs-151-192-168-56-161 ~]$ ocenv
Example
[oracle@beginner-dbfs-151-192-168-56-161 ~]$ ocenv
#####################################################################################
## Version: 2022-08-10
## get command overview by typing envhelp
#####################################################################################
home   oracle       OraHome_OraDB19Home1                       /u01/app/oracle/product/19/db1
db     oracle       DB1              (up)                      /u01/app/oracle/product/19/db1
lsnr   oracle       LISTENER                                   /u01/app/oracle/product/19/db1
[oracle@beginner-dbfs-151-192-168-56-161] [] [~]

1.3.8. Switch to DB1

Set ORACLE_SID
[oracle@beginner-dbfs-151-192-168-56-161] [] [~]
$ DB1
Example
[oracle@beginner-dbfs-151-192-168-56-161] [] [~]
$ DB1
=================================
INSTANCE NAME       : DB1
INSTANCE STATUS     : OPEN (READ WRITE)
INSTANCE START TIME : 2022-11-06 11:34:06
DB NAME             : DB1
DB UNIQUE NAME      : DB1
DB ID               : 1713907024
DB LOGGING MODE     : NOARCHIVELOG
DB ROLE             : PRIMARY
DB FORCE LOGGING    : YES
DB FLASHBACK ON     : NO
DB is CDB           : YES
PDBs
   PDB NAME        STATUS     RESTRICTED OPEN TIME           LOCAL UNDO
   --------------- ---------- ---------- ------------------- ----------
   ORCLPDB         READ WRITE NO         2022-11-06 11:34:28 YES
=================================
[oracle@beginner-dbfs-151-192-168-56-161] [DB1] [~]

1.3.9. Database Login

Start SQLPlus
[oracle@beginner-dbfs-151-192-168-56-161] [DB1] [~]
$ sql
Example
[oracle@beginner-dbfs-151-192-168-56-161] [DB1] [~]
$ sql

SQL*Plus: Release 19.0.0.0.0 - Production on Sun Nov 6 11:41:40 2022
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

11:41:40 SYS@DB1
SQL>

4 - Ansible as a Docker Container

Important
This documentation is a work in progress!

The development and test of ansible-oracle is done in a Docker Container. This allows a simple switch between ansible versions and an easy implementation at a customer site, because you can use your own chosen version of Ansible for ansible-oracle.

Requirements:

  • Docker

  • docker-compose

  • ssh-agent
    The ssh-agent is configured inside the docker-compose.yml. It simplifies the connection to the target machines via SSH.

Build

cd docker
docker-compose build

Start Container

cd docker
docker-compose run --rm ansible bash

Install Collection

Important
ansible-oracle is designed to run as a container.
cd docker
docker-compose run --rm ansible bash
ansible-galaxy collection install -r requirements.yml

5 - HOWTO: Local Installation of Ansible-Oracle

1. Intro

This guide will show you how to get Ansible-Oracle up and running with a local installation (as opposed to a container or Vagrant setup).

It is assumed that the server is airgapped and doesn’t have Internet access and therefore it’s demonstrated how to install all necessary binaries manually.

Note
This guide aims at developers who want to test different setups with different dependencies. To enable you to run the Ansible-Oracle environment independently of other versions of Ansible, Python and Galaxy modules, everything will be encapsulated within a Python Virtual Environment (venv). If you prefer to install everything globally, you’ll find info on how to do so below.

The steps in this guide were tested with OL9.5, Python 3.9, Ansible 2.14 and Ansible-Oracle 4.11.1.

2. What you need

2.1. Server and OS

  • Oracle Linux 9 or RHEL 9 (this guide was tested with OL 9.5)

  • OS install DVD or ISO attached to your machine.

  • At least 50 MB free space in the /home filesystem.

2.2. Mandatory RPMs

These RPMs can be installed from the OS install image (DVD ISO).

  • git

  • ansible-core

  • python3-pip

2.3. Other mandatory files

Download these files on a device with Internet access and transfer them to the target server’s /tmp directory.

2.3.1. Optional:

  • tig, for easier navigation of git repos.

3. Setup Ansible

3.1. User

dnf install -y git ansible-core python3-pip
useradd ansible

su - ansible
mkdir .ssh
cd .ssh/
vi authorized_keys
ssh-keygen -t ed25519
## No password
## Paste the contents of id_ecdsa.pub into /root/.ssh/authorized_keys on target machine

3.2. Python Virtual Environment and Ansible Collections

# As ansible user
mkdir -p /home/ansible/ansible/inventory
cd ~/ansible
unzip /tmp/ansible-oracle-4.11.1.zip
mv ansible-oracle-4.11.1 ansible-oracle
cd ansible-oracle
echo ".venv/" >> .gitignore
python3 -m venv .venv

# Activate the virtual environment
source .venv/bin/activate

Install packages in the virtual environment:

Note
If you don’t want the dependencies within the venv, change the -p ~/ansible/ansible-oracle/.venv/lib/collections to -p ~/ansible/ansible-oracle/playbooks/collections. This is the default path for Ansible Navigator.
ansible-galaxy collection install /tmp/ansible_collections/ansible-posix-1.5.4.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/ansible-utils-5.1.0.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/community-general-9.5.4.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/community-crypto-2.24.0.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/community-mysql-3.12.0.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/devsec-hardening-8.8.0.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
ansible-galaxy collection install /tmp/ansible_collections/opitzconsulting-ansible_oracle-4.11.1.tar.gz -p ~/ansible/ansible-oracle/.venv/lib/collections
Starting galaxy collection install process
[WARNING]: The specified collections path '/home/ansible/ansible/ansible-oracle/collections' is not part of the configured Ansible collections paths
'/home/ansible/.ansible/collections:/usr/share/ansible/collections'. The installed collection will not be picked up in an Ansible run, unless within a
playbook-adjacent collections directory.

For background info on Ansible in a Python venv, see: How to set up and use Python virtual environments for Ansible

3.3. Shell Alias

This alias for Bash sets the environment and jumps to the ansible-oracle directory.

alias aov='cd ~/ansible/ansible-oracle ; source .venv/bin/activate ; export ANSIBLE_COLLECTIONS_PATHS=".venv/lib/collections:$ANSIBLE_COLLECTIONS_PATHS"'

You could also define the path to the ansible collections in ansible.cfg:

[defaults]
collections_paths = .venv/lib/collections

3.4. SSH Key

For passwordless login, the ansible user needs a SSH key. This is optional, if you run on local connections or if you prefer to provide a password.

ssh-keygen -q -t ed25519 -N '' <<< $'\ny' >/dev/null 2>&1

Copy the contents of id_ed25519.pub to the target host’s root user into ~/.ssh/authorized_keys.

4. Target Host

4.1. python-oracledb

The target (database) host needs python-oracledb installed. The modules of Ansible-Oracle use this package in thick mode (Oracle Client libraries of the ORACLE_HOME) to issue database queries. The Playbook os.yml installs this package with the role pyoracledb. The version depends on the Python version of the target host (1.4.2 for Python 3.6 on OL8, 4.0.2 for Python 3.9 on OL9). Without access to the Python package index, put the wheel files into a directory and point to it from your inventory, e.g., pyoracledb_wheel_dir: /tmp/python-packages. pip selects the wheel file matching the Python version of the target host.

Otherwise, the package must be manually installed as root.

4.1.1. Optional: install python-oracledb manually

As shown above, on the target machine:

pip install /tmp/python-packages/oracledb-*.whl

Skip the installation with the role pyoracledb in this case: pyoracledb_install: false.

5. Ready to go!

Activate the virtual environment by issuing aov. Then, run your playbooks as usual.

6 - Installation & Patching Settings for the Inventory

1. Introduction

This guide explains the variables used in the inventory to configure RDBMS installation and patching. Usually, both are done at the same time, because for most scenarios you wouldn’t install just the base release of the Oracle RDBMS.

Not described are the details of patch provisioning, e. g., how to obtain the patch files and how to find and enter patch IDs in the inventory.

2. Typical Scenarios

Let’s look at some typical setups concerning where the Oracle software and patch binaries are placed and how they should be processed:

  1. The binaries are stored locally on the Ansible control node. From there they are copied over to the target, where they get unpacked and installed.

  2. The binaries are stored in compressed form on NFS and should be copied over to the target, where they get unpacked and installed.

  3. The binaries are stored in compressed form on NFS. The NFS share will be mounted on the target host and the files will be unpacked from there to /tmp on the target host, then installed from the temp directory.

  4. The binaries are stored uncompressed on NFS. The NFS share will be mounted on the target host and the files will be installed from there.

Here is how the inventory variables interact and what to set for each scenario.

3. Variable Reference

Variable Default Role

is_sw_source_local

true

true = copy from control node; false = download via HTTP

oracle_sw_copy

false if install_from_nfs, else true

Transfer archive to oracle_stage on the target

oracle_sw_unpack

false if install_from_nfs, else true

Unpack archive on the target

oracle_sw_source_local

/tmp

Path on the Ansible control node where archives are read from

install_from_nfs

false

Mount NFS on the target; sets both copy/unpack flags to false by default

oracle_stage

/u01/stage

Local staging directory on the target

oracle_stage_remote

{{ oracle_stage }}

NFS mount point on the target

oracle_patch_stage

{{ oracle_stage }}/patches

Local patch staging directory on the target

oracle_patch_stage_remote

{{ oracle_stage_remote }}/patches

Patch directory on the NFS share

4. Scenario Comparison Table

Setting Sc. 1: Local control node Sc. 2: NFS via control node Sc. 3: NFS on target, unpack first Sc. 4: NFS on target, direct install

install_from_nfs

false

false

true

true

is_sw_source_local

true

true

n/a

n/a

oracle_sw_copy

true

true

false

false

oracle_sw_unpack

true

true

false (*)

false

oracle_sw_source_local

path on control node

NFS mount on control node

—

—

oracle_stage_remote

—

—

NFS mount on target

NFS mount on target

oracle_sw_patches (patching)

required

required

not needed

not needed

Patches pre-extracted on NFS

no

no

yes

yes

(*) For Sc. 3 (installation only): set oracle_sw_unpack: true to unpack from the NFS mount to oracle_stage. For *patching*, this override has no effect — the unpack block requires oracle_sw_copy: true as well, so patches must be pre-extracted on NFS regardless.

5. Scenario Configurations

5.1. Scenario 1 — Binaries on the Ansible control node

# Installation + Patching
is_sw_source_local: true
oracle_sw_copy: true
oracle_sw_unpack: true
oracle_sw_source_local: /path/to/archives   # must contain RDBMS ZIP, patch ZIPs, OPatch ZIP
install_from_nfs: false

# Patching only
oracle_sw_patches:
  - filename: pXXXXXXXX_190000_Linux-x86-64.zip
    patchid: XXXXXXXX
    version: 19.3.0.0
    description: Database Release Update 19.30

db_homes_config:
  db19-si-ee:
    opatch:
      - patchid: XXXXXXXX
        patchversion: 19.30.0.0.260120
        stop_processes: true
        state: present

5.2. Scenario 2 — Compressed archives on NFS, copied via control node

Identical to Scenario 1. oracle_sw_source_local points to the NFS mount point on the control node.

is_sw_source_local: true
oracle_sw_copy: true
oracle_sw_unpack: true
oracle_sw_source_local: /mnt/nfs/orasw      # NFS mount point on the control node
install_from_nfs: false

oracle_sw_patches:
  - filename: pXXXXXXXX_190000_Linux-x86-64.zip
    patchid: XXXXXXXX
    version: 19.3.0.0

5.3. Scenario 3 — NFS mounted on target, unpack to local stage (installation), pre-extracted (patching)

install_from_nfs: true
oracle_sw_copy: false
oracle_sw_unpack: true          # installation only: unpack from NFS to oracle_stage
                                # patching: has no effect; patches must be pre-extracted on NFS
nfs_server_sw: nfs-server.example.com
nfs_server_sw_path: /orasw
oracle_stage_remote: /mnt/nfs  # oracle_patch_stage_remote = /mnt/nfs/patches

# oracle_sw_patches is NOT required
# Required NFS structure for patching:
#   /mnt/nfs/patches/19.3.0.0/XXXXXXXX/        <- extracted patch
#   /mnt/nfs/p6880880_190000_Linux-x86-64.zip  <- OPatch ZIP

5.4. Scenario 4 — Uncompressed binaries on NFS, install and patch directly from NFS

install_from_nfs: true
oracle_sw_copy: false           # default when install_from_nfs: true
oracle_sw_unpack: false         # default when install_from_nfs: true
nfs_server_sw: nfs-server.example.com
nfs_server_sw_path: /orasw
oracle_stage_remote: /mnt/nfs  # oracle_patch_stage_remote = /mnt/nfs/patches

# oracle_sw_patches is NOT required
# Required NFS structure for patching:
#   /mnt/nfs/patches/19.3.0.0/XXXXXXXX/        <- extracted patch
#   /mnt/nfs/p6880880_190000_Linux-x86-64.zip  <- OPatch ZIP

Note that the opatch part of the db home to be patched doesn’t need a path to be set when the required NFS path structure is as above. The patchid is enough in this case. Here’s an example of the settings for the DBRU 19.25:

apply_patches_db: true
db_homes_config:
  db1925-si-ee:
    version: 19.3.0.0
    oracle_home: /u01/app/oracle/product/19/dbhome_1
    edition: EE
    opatch_minversion: 12.2.0.1.44
    state: present
    opatchauto: []
    opatch:
      - patchid: 36912597
        # Database Release Update 19.25.0.0.240716
        patchversion: 19.25.0.0.241015
        stop_processes: true
        state: present
      - patchid: 36878697
        # Oracle JavaVM Component Release Update (OJVM RU) 19.25.0.0.241015
        stop_processes: true
        state: present

However, you could set a path attribute for each patchid if you want to deviate from the standard setups. This might be necessary when you use a "combo patch" instead of separate patch archives for DBRU, OJVM RU et al.

6. Key Behavioral Differences: Installation vs. Patching

For installation, setting oracle_sw_copy: false + oracle_sw_unpack: true (Scenario 3) causes the installer to read the archive from oracle_stage_remote (NFS) and extract directly to oracle_home_db.

For patching, the unpack block unconditionally requires oracle_sw_copy: true. When oracle_sw_copy: false, no copy or unpack step runs at all — the patch must already be extracted on the NFS share at oracle_patch_stage_remote/<version>/<patchid>/. Scenarios 3 and 4 are therefore identical from a patching perspective.

7 - Setup APEX and ORDS

Important
The installation of APEX and ORDS with ansible-oracle is currently experimental.

1. Requirements

1.1. APEX

  • RDBMS 19c or newer

  • APEX 20.x or newer is mandatory for RDBMS 19c

  • Tested with APEX 23.2, 24.1 - may work with older versions as well

  • Installation is tested with PDB only.

  • CDB and nonCDB is not supported at the moment.

  • Download ZIP from Oracle with Playbook patch_download.yml supported for Version 22.1 or newer

  • APEX only with ORDS - no old APEX Listener

Important Notes:

  • Primary Note for Oracle APEX Upgrades (Doc ID 1088970.1)

Known Issues:

  • ADMIN password of INTERNAL Workspace is wrong after APEX upgrade Silent script to reset ADMIN password depends on APEX Release.
    There was no reliable solution found for the moment…​

1.2. ORDS

  • OracleLinux 8 or 9 only

  • Installation with ords.rpm from the Oracle Linux oracle/software repository on yum.oracle.com

  • Limited database configuration - see documentation

2. APEX Configuration

Important
Install APEX before ORDS - otherwise, the ORDS will not detect and configure APEX.
ORDS is only tested with existing APEX installation.

2.1. Mandatory variables

The following global variables are needed for an APEX installation.

Table 1. Mandatory variables
Value Description

oraapex_default_admin_password

The parameter is mandatory, because it is the only way to configure a password for the admin user at the moment.

Table 2. Optional variables
Value Description

oraapex_base

Default: {{ oracle_base }}/product
The unzip is done into {{ oraapex_base }}/apex_<APEX Version>

oraapex_default_tablespace

Default: SYSAUX
Could be set in each PDB.

oraapex_default_files_tablespace

Default: SYSAUX
Could be set in each PDB.

oraapex_default_temp_tablespace

Default: TEMP
Could be set in each PDB.

oraapex_image_path

Default: /i/
Image path for apxsilentins.sql.
Could be set in each PDB.

2.2. APEX in PDB

APEX can be installed in each PDB.

Table 3. mandatory Variables in oracle_pdbs
Value Description

state

Must be present.
APEX is only installed in PDBs with state: present.

apex_state

State for APEX installation.
Only present supported at the moment.

apex_version

Version of APEX.
Is used to download the Archive from Oracle.

Table 4. optional Variables in oracle_pdbs
Value Description

oraapex_tablespace

Custom APEX Tablespace.

oraapex_files_tablespace

Custom APEX Tablespace for files.

oraapex_temp_tablespace

Custom APEX temporary Tablespace.

oraapex_image_path

Custom image path for apxsilentins.sql.

apex_use_cdn

Default: true
Set the APEX IMAGE_PREFIX to the Oracle CDN https://static.oracle.com/cdn/apex/<APEX Version>/.
The images are not loaded from ORDS when the CDN is used.

apex_patchid

Patchid for PSE BUNDLE FOR APEX

Important
The example shows the minimum attributes for an APEX installation.
That’s not the minimum needed to create a PDB.
Example oracle_pdbs
oracle_pdbs:
  - cdb: CDB1
    pdb_name: PDB1
    state: present
    apex_state: present
    apex_version: 24.1
    apex_patchid: 36695709
Important
oraapex does not use the default passwords from ansible-oracle.
You have to define them in dbpasswords.

oraapex needs the passwords for APEX_PUBLIC_USER and APEX_REST_PUBLIC_USER.
The keys could be written in lower or upper case.
ords_public_user is not used by oraapex, but could be referenced in oraords_db_pools_password (see ORDS Configuration).

The password of APEX_LISTENER is generated with the password lookup into /tmp/passwordfile on the Ansible controller.

Example dbpasswords for APEX in a PDB
dbpasswords:
  CDB1:
    PDB1:
      apex_public_user: ords123
      apex_rest_public_user: ords123
      ords_public_user: ords123
Important
Do not forget to download the APEX Patch when apex_patchid is set in oracle_pdbs.
The patch is not applied with OPatch. oraapex copies the patch into the APEX software directory and executes catpatch.sql in the PDB.

oraapex expects the archive p<apex_patchid>_<apex_version without dots>0_Generic.zip in oracle_sw_source_local, e.g. p36695709_2410_Generic.zip for APEX 24.1.

Example oracle_sw_patches
oracle_sw_patches:
  - filename: p36695709_2410_Generic.zip
    patchid: 36695709
    version: 24.1
    description: PSE BUNDLE FOR APEX 24.1

2.3. Playbook execution

The installation of APEX is part of playbooks/manage_db.yml.
This allows the creation of custom Tablespaces, Profiles etc. for APEX, without the need to use a dedicated Playbook.

3. ORDS Configuration

Important
The configuration of ORDS is limited to the following variables at the moment.
Table 5. Mandatory variables in oraords_db_pools
Value Description

db_pool

Database Pool.
default is the default pool of ORDS, all other names are created with --db-pool.

service

Database Service Name

Table 6. Optional variables in oraords_db_pools
Value Description

host

Database Hostname
Default: {{ inventory_hostname }}

port

Listener Port
Default: oraords_default_port (1521)

admin_user

Admin user for the ORDS installation
Default: oraords_default_admin_user (sys)

feature_db_api

true / false (Default)

feature_rest_enabled_sql

true / false (Default)

feature_sdw

true / false (Default)

Table 7. Optional global variables for ORDS
Value Description

oraords_apex_image_path

Directory with the APEX images on the ORDS host (standalone.static.path).
The role fails, when <oraords_apex_image_path>/css/apex.css does not exist.
Not needed when APEX uses the CDN (apex_use_cdn).

oraords_apex_image_context

Context path for the APEX images (standalone.static.context.path)

oraords_java_rpm

Default: java-21-openjdk

oraords_ords_bin

Default: /opt/oracle/ords/bin

oraords_ords_config

Default: /etc/ords/config

oraords_ords_logs

Default: /etc/ords/logs

oraords_db_pools:
  - db_pool: default
    admin_user: sys
    service: pdb1
    port: 1521
    host: "{{ inventory_hostname }}"
    feature_db_api: true
    feature_rest_enabled_sql: true
    feature_sdw: true

The passwords for Admin- and ORDS-User are defined in oraords_db_pools_password.
They are set in a dedicated variable to prevent no_log=true in loops over oraords_db_pools.
Referencing dbpasswords helps to reduce redundancy for passwords.

oraords_db_pools_password:
  default:
    admin_password: "{{ dbpasswords['CDB1']['sys'] }}"
    ords_password: "{{ dbpasswords['CDB1']['PDB1']['ords_public_user'] }}"

3.1. Playbook execution

The Playbook playbooks/manage_ords.yml is used to install ORDS.

Note
ords install is executed for each pool and the ORDS service is reloaded on every execution of the Playbook.

8 - Oracle AutoUpgrade

Important
The role autoupgrade is currently experimental.
At the moment it supports the installation of AutoUpgrade, the download of patches from My Oracle Support and the Unplug/Plug upgrade of PDBs.
Patching databases with AutoUpgrade is not implemented yet.
The upgrade of PDBs is described in Upgrade of PDBs with AutoUpgrade.

The role autoupgrade uses Oracle AutoUpgrade (autoupgrade.jar) for:

  • Installation of autoupgrade.jar on the database servers or on the Ansible controller

  • Download of the latest autoupgrade.jar from Oracle into the software repository

  • Management of the AutoUpgrade keystore with the My Oracle Support credentials

  • Download of patches from My Oracle Support (-patch -mode download)

  • Unplug/Plug upgrade of PDBs into a CDB of a new release (-mode analyze, -mode fixups, -mode deploy), see autoupgrade_upgrade.adoc

The variables of the role are described in roles/autoupgrade/README.md.

1. Requirements

  • Java on the host executing AutoUpgrade

    • Database server: e.g. the JDK from an ORACLE_HOME (autoupgrade_java_home)

    • Ansible controller: java from PATH or autoupgrade_local_java_home

  • Python module pexpect on the host executing AutoUpgrade for the keystore management (used by ansible.builtin.expect). It is checked by install.yml, but not installed by the role.

  • My Oracle Support account (mos_username, mos_password) for downloading patches

  • Downloads with AutoUpgrade are supported for the releases 19, 21, 23 and 26 (AutoUpgrade 26.6)

2. Proxy

The download of autoupgrade.jar (get_url) and AutoUpgrade itself (keystore, download of patches) use the proxy variables of ansible-oracle: http_proxy, https_proxy and no_proxy. AutoUpgrade reads the lowercase environment variable https_proxy.

AutoUpgrade needs access to updates.oracle.com, login-ext.identity.oraclecloud.com and aru-akam.oracle.com. Use DNS names in the proxy/firewall configuration - the addresses are part of a CDN.

Proxy with authentication
https_proxy: http://proxy.example.com:3128
http_proxy: http://proxy.example.com:3128
no_proxy: localhost,.example.com

autoupgrade_proxy_username: proxyuser
autoupgrade_proxy_password: "{{ vault_proxy_password }}"

The role adds the credentials URL encoded to http_proxy and https_proxy (http://proxyuser:<password>@proxy.example.com:3128). Credentials already contained in http_proxy / https_proxy are not changed.

Java disables Basic authentication for HTTPS tunneling through a proxy by default. With proxy credentials the role adds -Djdk.http.auth.tunneling.disabledSchemes= to the Java options of AutoUpgrade.

Table 1. Variables for the proxy
Variable Description

autoupgrade_proxy_username

Username for the proxy (optional)

autoupgrade_proxy_password

Password for the proxy (optional). Use Ansible Vault.

autoupgrade_java_options

Additional Java options for AutoUpgrade, e.g. a truststore for a proxy with TLS inspection

Truststore for a proxy with TLS inspection
autoupgrade_java_options:
  - -Djavax.net.ssl.trustStore=/etc/pki/java/cacerts
  - -Djavax.net.ssl.trustStorePassword=changeit
Note
A proxy with TLS inspection leads to unable to find valid certification path to requested target. The certificate of the proxy must be in the truststore used by Java.

2.1. Check of the connection

Configuration errors of the proxy lead to misleading errors of AutoUpgrade, e.g. "My Oracle Support credentials in keystore …​ are not valid" for a wrong proxy password. Therefore the role checks the connection to Oracle before (proxy_check.yml):

  • download_patches.yml: before the keystore is used. URLs: autoupgrade_proxy_check_urls.

  • install.yml with autoupgrade_download_latest: true: before the download. URL: autoupgrade_jar_url.

The check is executed on the same host, as the same user and with the same proxy settings (http_proxy, https_proxy, no_proxy, autoupgrade_proxy_username, autoupgrade_proxy_password) as the later download. Any HTTP response of the target counts as success (e.g. 302, 401). The upgrade of PDBs does not need the internet and does not execute the check.

Result of a successful check
https://login-ext.identity.oraclecloud.com => HTTP 401
https://transport.oracle.com => HTTP 302
https://updates.oracle.com => HTTP 302
https://aru-akam.oracle.com => HTTP 302
Proxy: http://proxyuser:***@proxy.example.com:3128

The password of the proxy is never shown. A failed check shows the error and a hint for each URL, then the play fails:

Failed check: wrong password of the proxy
No connection to Oracle
Executed on: dbserver1
Proxy:       http://proxyuser:***@proxy.example.com:3128
no_proxy:    localhost,.example.com
--- https://login-ext.identity.oraclecloud.com ---
Error: Request failed: <urlopen error Tunnel connection failed: 407 Proxy Authentication Required>
Hint:  Proxy authentication failed (407): check autoupgrade_proxy_username / autoupgrade_proxy_password or the credentials in https_proxy.
Table 2. Hints of the check
Error Hint

407 Proxy Authentication Required

wrong or missing credentials for the proxy

Tunnel connection failed: 403

the proxy does not allow the target

Name resolution failed

wrong hostname of the proxy - or no proxy configured, but the internet is reachable through a proxy only

Connection refused

wrong port of the proxy or proxy not running

Timeout, no route

firewall to the proxy - or no proxy configured

CERTIFICATE_VERIFY_FAILED

proxy with TLS inspection: CA of the proxy missing in the trust store

Note
The check uses Python (ansible.builtin.uri), AutoUpgrade uses Java. Problems specific to Java (e.g. the truststore of Java for a proxy with TLS inspection) are not detected by the check.
Table 3. Variables for the check
Variable Default Description

autoupgrade_proxy_check

true

false disables the check

autoupgrade_proxy_check_urls

hosts of AutoUpgrade 26

URLs for download_patches.yml

autoupgrade_proxy_check_timeout

30

Timeout in seconds per URL

The molecule scenario download tests the proxy: download has no route to the internet and reaches Oracle through a Squid with Basic authentication only. side_effect.yml checks the connection with the correct and with a wrong password of the proxy, also with autoupgrade_proxy_check: false (check by keystore.yml after a connection failure, the keystore stays unchanged).

3. Task files

Important
The role has no tasks/main.yml.
Each area of AutoUpgrade has its own task file, which must be called with tasks_from.
Table 4. Task files of role autoupgrade
Task file Description

assert.yml

Check variables and Java. Included by the other task files.

install.yml

Install autoupgrade.jar. Optional download of the latest version from Oracle.

keystore.yml

Create/update the keystore with the My Oracle Support credentials. Included by download_patches.yml.

download_patches.yml

Download patches with AutoUpgrade. Includes install.yml and keystore.yml.

upgrade_analyze.yml

Analyze phase for the Unplug/Plug upgrade of PDBs (analyze). Includes install.yml.

upgrade_fixups.yml

Fixups phase (fixups) in the source CDB/PDBs. Includes upgrade_analyze.yml.

upgrade_deploy.yml

Unplug/Plug upgrade of PDBs. Executes upgrade_analyze.yml first.

Example: call a task file
- name: Install AutoUpgrade
  ansible.builtin.import_role:
    name: opitzconsulting.ansible_oracle.autoupgrade
    tasks_from: install.yml

4. Playbooks

Table 5. Playbooks for AutoUpgrade
Playbook Description

playbooks/autoupgrade_install.yml

Install AutoUpgrade. hostgroup is mandatory. Use autoupgrade_run_local: false for the database servers.

playbooks/autoupgrade_download_patches.yml

Download patches with AutoUpgrade.

playbooks/autoupgrade_upgrade_analyze.yml

Analyze phase for the upgrade of PDBs. hostgroup is mandatory.

playbooks/autoupgrade_upgrade_fixups.yml

Fixups phase for the upgrade of PDBs. hostgroup is mandatory.

playbooks/autoupgrade_upgrade.yml

Unplug/Plug upgrade of PDBs (analyze phase + deploy). hostgroup is mandatory.

Install AutoUpgrade on database servers
ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_run_local=false \
  playbooks/autoupgrade_install.yml
Download patches and the latest autoupgrade.jar
ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_download_latest=true \
  playbooks/autoupgrade_download_patches.yml

5. Execution on Ansible controller or database server

autoupgrade_run_local defines, where AutoUpgrade is executed. The switch is valid for all task files.

Table 6. Execution of AutoUpgrade
autoupgrade_run_local Executed on Directory Java

true (default)

Ansible controller, run_once, no become

autoupgrade_local_home

autoupgrade_local_java_home

false

target host as oracle_user

autoupgrade_home

autoupgrade_java_home

Important
AutoUpgrade is executed on the Ansible controller by default.
Set autoupgrade_run_local: false for the execution on the database servers, e.g. for the installation on the database servers.
Install AutoUpgrade on the database servers and download patches on the Ansible controller
ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_run_local=false \
  playbooks/autoupgrade_install.yml

ansible-playbook -i inventory/ -e hostgroup=dbservers playbooks/autoupgrade_download_patches.yml

The host executing AutoUpgrade needs:

  • Java (see autoupgrade_java_home / autoupgrade_local_java_home)

  • Python module pexpect for the Python interpreter used by Ansible (keystore console). install.yml checks it at the beginning and fails with a clear message. The role does not install pexpect - install it e.g. with the package python3-pexpect or with pip.

  • With autoupgrade_run_local: false: autoupgrade_download_dir must be a directory on the target host, writable for oracle_user. The default oracle_sw_source_local is a directory on the Ansible controller.

  • With autoupgrade_run_local: false: oracle_user must exist (playbook os). oracle_base and oracle_base/cfgtoollogs are created by install.yml with the same attributes as in oraswdb_install. AutoUpgrade can be used before the installation of the Oracle software, e.g. for downloading the patches.

Important
With autoupgrade_run_local: true the tasks are executed only once (run_once) with the variables of the first host in the play.
oracle_sw_patches, db_homes_installed, db_homes_config, oracle_install_version_gi and autoupgrade_download_patches must be the same for all hosts in the play - e.g. defined in group_vars/all for the whole environment.
When they are defined per host, limit the playbook to one host (--limit <host>) or execute it for each host.

6. Installation of autoupgrade.jar

autoupgrade.jar is read from the software repository like all other installation media:

  • is_sw_source_local: true: copy from oracle_sw_source_local on the Ansible controller

  • is_sw_source_local: false: download from oracle_sw_source_www

  • oracle_sw_copy: false: copy from oracle_stage_remote on the database server (e.g. NFS mount)

  • autoupgrade_jar_filesource: full path to autoupgrade.jar, when it is not placed in the software repository

On the Ansible controller oracle_sw_copy is ignored and the file is always taken from oracle_sw_source_local or oracle_sw_source_www.

6.1. Download the latest version

autoupgrade_download_latest: true downloads the latest autoupgrade.jar from Oracle (autoupgrade_jar_url) into the software repository before the installation. The target depends on the location of the installation media:

Table 7. Target for the download of the latest version
Installation media Download on Target

oracle_sw_copy: true or autoupgrade_run_local: true

Ansible controller, run_once, no become. Requires is_sw_source_local: true.

autoupgrade_jar_filesource or oracle_sw_source_local/autoupgrade_jar

oracle_sw_copy: false

target host as oracle_user

autoupgrade_jar_filesource or oracle_stage_remote/autoupgrade_jar

Important
With oracle_sw_copy: false the target directory must exist on the target host and must be writable for oracle_user.
On a shared NFS directory each host downloads the file.

6.2. Update an existing installation

An existing autoupgrade.jar is not replaced by default.
autoupgrade_force_update: true replaces the file, when it differs from the file in the software repository.

Update AutoUpgrade on all database servers with the latest version from Oracle
ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_run_local=false \
  -e autoupgrade_download_latest=true -e autoupgrade_force_update=true \
  playbooks/autoupgrade_install.yml

7. Keystore for My Oracle Support

AutoUpgrade reads the My Oracle Support credentials from a keystore (autoupgrade_keystore). The keystore is managed by keystore.yml with mos_username and mos_password.

Note
AutoUpgrade only offers an interactive console (-load_password) for the keystore. The role uses ansible.builtin.expect for this console.
Table 8. Mandatory variables for the keystore
Variable Description

mos_username

Login for My Oracle Support

mos_password

Password for My Oracle Support

The role stores a fingerprint of mos_username in autoupgrade_keystore/.ansible_oracle_fingerprint. The keystore is created again, when mos_username is changed.

Note
AutoUpgrade writes the input of the keystore console into its log file - including mos_username, but not the passwords.

The keystore is handled as follows:

  1. Keystore exists: check the credentials with list (online check against My Oracle Support). Nothing is changed, when the credentials are valid.

  2. Credentials missing or invalid: add the credentials with add. This requires the keystore password.

  3. Keystore password rejected, keystore missing or fingerprint changed (mos_username): remove the keystore and create it again.

  4. Verify the credentials. The play fails, when the login is not possible (autoupgrade_keystore_fail_on_invalid_credentials: true).

The keystore password is autoupgrade_keystore_password. When it is empty (default), the SHA256 hash of mos_password is used. A changed mos_password changes the keystore password as well - the keystore is recreated automatically.

Important
The keystore is converted to auto-login. AutoUpgrade opens it without password.
The keystore password is only needed for changes.
Table 9. Optional variables for the keystore
Variable Default Description

autoupgrade_keystore_manage

true

false: the keystore must exist and is not changed by the role

autoupgrade_keystore_password

''

Keystore password (min. 16 characters). Empty: SHA256 of mos_password

autoupgrade_keystore_recreate

false

Remove and create the keystore in each execution

autoupgrade_keystore_fail_on_invalid_credentials

true

Fail, when the login into My Oracle Support is not possible

autoupgrade_keystore_no_log

true

false shows the output of tasks with passwords - for debugging only!

8. Download of patches

download_patches.yml creates the AutoUpgrade config file download_patches.cfg and executes autoupgrade.jar -patch -mode download. The patches are placed in autoupgrade_download_dir (default: oracle_sw_source_local).

Each entry in the patch list creates a patch<n> section in the config file. The patch list is built from:

  1. autoupgrade_download_patches: explicit entries with AutoUpgrade keywords

  2. oracle_sw_patches, db_homes_installed, db_homes_config and oracle_install_version_gi (same variables as role orasw_download_patches), when autoupgrade_download_use_oracle_sw_patches: true (default)

8.1. autoupgrade_download_patches

Table 10. Attributes in autoupgrade_download_patches
Attribute Mandatory Description

target_version

yes

Release, e.g. 19, 26

patch

yes

AutoUpgrade keywords and/or patch numbers, e.g. RECOMMENDED, RU,OPATCH,OJVM,DPBP, RU:19.24,OPATCH

platform

no

Default: autoupgrade_download_platform (LINUX.X64)

gold_image

no

NO, AUTO, YES, ALL. Default: autoupgrade_download_gold_image

Example autoupgrade_download_patches
autoupgrade_download_use_oracle_sw_patches: false

autoupgrade_download_patches:
  - target_version: 19
    patch: RU,OPATCH,OJVM,DPBP
    gold_image: "NO"
Resulting section in download_patches.cfg
patch1.target_version=19
patch1.platform=LINUX.X64
patch1.patch=RU,OPATCH,OJVM,DPBP
patch1.gold_image=NO
Important
RU without a version downloads the latest Release Update. A new Release Update from Oracle leads to new downloads. Use RU:<version> for a fixed release.

The attribute patch is passed unchanged to AutoUpgrade. It is a comma separated list of keywords and patch numbers.

Table 11. Keywords for patch (Oracle documentation, AutoUpgrade 26)
Keyword Description

RECOMMENDED

Alias for RU,OPATCH,OJVM,DPBP,AU. Default of AutoUpgrade, when patch is not set.

RU[:x.y]

Release Update. Without version: latest Release Update. Example: RU:19.24

OPATCH

Latest OPatch for the release

OJVM[:x.y]

OJVM Release Update. Without version: version of RU

OCW[:x.y]

Oracle Clusterware (Grid Infrastructure) patch. Without version: version of RU

DPBP

Data Pump Bundle Patch for the Release Update

MRP

Latest Monthly Recommended Patch. Linux only, 19c and 26ai (from 23.26)

AU

AutoUpgrade

<patch number>

One-off patch, e.g. 34672698

Note
Not every keyword is available for every release (e.g. MRP not for 21c). The Oracle documentation lists further keywords (e.g. SDOBP, TOOLS, OEM). See Common Parameters for the AutoUpgrade Configuration File in the Oracle documentation.

8.2. Additional patches

There are three ways to download additional patches. They can be combined.

Table 12. Ways to add patches
Way When to use

Patch number in patch of autoupgrade_download_patches

One-off patches without entry in oracle_sw_patches

Keyword in patch of autoupgrade_download_patches

Patch types, e.g. OCW for Grid Infrastructure, MRP

New entry in oracle_sw_patches

Patches, which are installed by ansible-oracle later (e.g. with db_homes_config)

Example: one-off patches and Clusterware patch in autoupgrade_download_patches
# patch numbers are placeholders
autoupgrade_download_patches:
  - target_version: 19
    patch: RU:19.24,OPATCH,OJVM,OCW,DPBP,34672698,34774667
    gold_image: "NO"
Example: additional patches in oracle_sw_patches
# one-off and Data Pump patch numbers/filenames are placeholders
oracle_sw_patches:
  # Release Update => RU:19.24
  - filename: p36582781_190000_Linux-x86-64.zip
    patchid: 36582781
    version: 19.3.0.0
    ru_version: 19.24.0.0.240716
    description: Database Release Update 19.24.0.0.240716
  # One-off patch => 34672698
  - filename: p34672698_1924000DBRU_Linux-x86-64.zip
    patchid: 34672698
    version: 19.3.0.0
    description: One-off patch for RU 19.24
  # Data Pump Bundle Patch: the keyword is forced with autoupgrade_patch
  - filename: p36682332_1924000DBRU_Generic.zip
    patchid: 36682332
    version: 19.3.0.0
    autoupgrade_patch: DPBP
    description: Data Pump Bundle Patch 19.24
Important
Only entries matching autoupgrade_download_filename_regex are used.
The Data Pump Bundle Patch in the example is a *_Generic.zip and is ignored with the default regex. Use the keyword DPBP in autoupgrade_download_patches or extend autoupgrade_download_filename_regex.

Hints for additional patches:

  • AutoUpgrade selects the file of a one-off patch for the platform and release itself. The filename can differ from filename in oracle_sw_patches, e.g. when the one-off patch exists for several Release Updates. The check after the download reports missing files as warning.

  • Release Updates must use the keyword RU:<x.y>. A patch number of a Release Update is rejected by AutoUpgrade.

  • Each entry of autoupgrade_download_patches creates its own section. Use separate entries for different platforms (platform) or different gold image settings (gold_image).

  • An explicit entry and the entries derived from oracle_sw_patches / db_homes_installed for the same release create two sections. This is accepted by AutoUpgrade. Set autoupgrade_download_use_oracle_sw_patches: false to use autoupgrade_download_patches only.

8.3. oracle_sw_patches

The patches from oracle_sw_patches are added to the section of the release from version (e.g. 19.3.0.0 ⇒ target_version=19).

Only entries are used, where

  • filename matches autoupgrade_download_filename_regex (database patches for Linux x86-64, e.g. p36582781_190000_Linux-x86-64.zip) and

  • the release from version is in autoupgrade_download_supported_releases.

Other entries (e.g. APEX patches *_Generic.zip, cvupack_linux_ol7_x86_64.zip) are ignored with a warning.

AutoUpgrade does not accept Release Updates by patch number. The entries are converted into AutoUpgrade keywords:

Table 13. Conversion of oracle_sw_patches into AutoUpgrade keywords
Entry in oracle_sw_patches AutoUpgrade

attribute autoupgrade_patch

value of the attribute, e.g. RU:19.24, OJVM, DPBP

attribute ru_version, e.g. 19.24.0.0.240716

RU:19.24

description contains OJVM

OJVM

others

patchid (one-off patch)

Important
Set ru_version for each Release Update in oracle_sw_patches.
A Release Update without ru_version is passed as patch number and rejected by AutoUpgrade (RU patches are not allowed to be specified individually).

OPATCH is added for each release of oracle_sw_patches, db_homes_installed and Grid Infrastructure/Oracle Restart (oracle_install_option_gi: CRS_CONFIG or HA_CONFIG).

Example oracle_sw_patches
oracle_sw_patches:
  - filename: p36582781_190000_Linux-x86-64.zip
    patchid: 36582781
    version: 19.3.0.0
    ru_version: 19.24.0.0.240716
    description: Database Release Update 19.24.0.0.240716
  - filename: p36414915_190000_Linux-x86-64.zip
    patchid: 36414915
    version: 19.3.0.0
    description: OJVM 19.24
Resulting section in download_patches.cfg
patch1.target_version=19
patch1.platform=LINUX.X64
patch1.patch=OPATCH,RU:19.24,OJVM

After the download, the role checks if all files from oracle_sw_patches exist in autoupgrade_download_dir. Missing files are reported as warning.

8.4. Gold Image

A gold image is a complete, patched database home (approx. 5 GB). AutoUpgrade requests it from the Oracle Update Advisor service (OUA) in the download mode. The service creates the gold image on demand, when no suitable image is cached.

Table 14. Values for gold_image in download mode (Oracle documentation)
Value Description

AUTO

Default of AutoUpgrade. Resolved to YES, when the connection to the Oracle Update Advisor service is possible, the platform is LINUX.X64 and the Release Update is available for the service. Otherwise NO.

NO

No gold image. All patches are downloaded from My Oracle Support. For 23.x/26ai NO forces the download of the Release Update instead of the gold image.

YES

Gold image with the latest OCW and MRP patches. Other patches (e.g. one-offs) are downloaded separately.

ALL

Gold image with all required patches, including the one-off patches.

The value is set for all sections with autoupgrade_download_gold_image or per entry with gold_image in autoupgrade_download_patches. An empty autoupgrade_download_gold_image (default of the role) does not write the parameter ⇒ AUTO.

8.4.1. Requirements

  • My Oracle Support credentials (mos_username, mos_password)

  • Platform LINUX.X64

  • Enough space in autoupgrade_download_dir

8.4.2. Download a gold image

Example: gold image with all patches for 19c
autoupgrade_download_patches:
  - target_version: 19
    patch: RU:19.24,OPATCH,OJVM,DPBP
    gold_image: ALL
Download the gold image in a dedicated run, default is NO
# inventory
autoupgrade_download_gold_image: "NO"

# command line
ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_download_gold_image=YES \
  playbooks/autoupgrade_download_patches.yml
Note
gold_image in an entry of autoupgrade_download_patches overrides autoupgrade_download_gold_image.
Remove gold_image from the entry, when the gold image should be controlled from the command line.

8.4.3. Hints for gold images

  • Time: When the Oracle Update Advisor service has to create the gold image, this can take a few hours. AutoUpgrade waits for the gold image. Request gold images well in advance of the maintenance window.
    One-off patches (ALL) reduce the chance that a suitable gold image is already cached.

  • Individual images: The gold images are built by Oracle on request of AutoUpgrade. Their content depends on the requirements sent by AutoUpgrade (e.g. release update, patch list, one-off patches, gold_image YES or ALL). Each download can therefore result in an individual image.

  • Filename: Because each gold image can be individual, AutoUpgrade generates a new filename for each download. The filename contains a timestamp (db_goldimage_<release>_<update>_<timestamp>.zip). It cannot be changed.

  • Repeated downloads: Because of the new filename, AutoUpgrade cannot detect an existing gold image. It downloads the gold image again in each execution with AUTO, YES or ALL.
    Recommendation: autoupgrade_download_gold_image: "NO" in the inventory and a gold image only in dedicated runs.

  • Disk space: Old gold images are not removed by AutoUpgrade or the role. Remove them manually from autoupgrade_download_dir.

  • Usage: AutoUpgrade uses gold images from download_folder for the creation of new Oracle homes (create_home mode). This is not implemented in the role yet.
    db_homes_config supports golden images (imagename, filesource) for the installation with oraswdb_install. Whether a gold image from AutoUpgrade can be used there, is not tested.

  • Information: patches_info.json in autoupgrade_download_dir lists the downloaded gold images with release update and included patches.

Disable the download of gold images for all sections
autoupgrade_download_gold_image: "NO"
Note
Quote "NO" in YAML. An unquoted NO is a boolean - the role converts true/false into YES/NO anyway.

8.5. Repeated downloads

AutoUpgrade does not download patches again, when they already exist in autoupgrade_download_dir. The output shows LOCATED for existing files and VALIDATED for downloaded files. The task is reported as changed, when at least one file was downloaded.

AutoUpgrade writes patches_info.json into autoupgrade_download_dir.

9. Known limitations

  • Patching databases with AutoUpgrade is not implemented yet.

  • The download on the Ansible controller uses the variables of the first host in the play (see above).

  • An explicit entry in autoupgrade_download_patches and the derived entry from db_homes_installed / oracle_sw_patches for the same release create two sections in the config file. Set autoupgrade_download_use_oracle_sw_patches: false, when only autoupgrade_download_patches should be used.

  • The filename of a downloaded gold image cannot be changed. Gold images are downloaded again in each execution.

9 - Upgrade of PDBs with AutoUpgrade (Unplug/Plug)

Important
The upgrade of PDBs is experimental.
Supported is the Unplug/Plug upgrade of PDBs from a CDB of the old release (e.g. 19c) into an existing CDB of the new release (e.g. 26ai) on the same host.

The role autoupgrade upgrades PDBs with Oracle AutoUpgrade (autoupgrade.jar). AutoUpgrade unplugs the PDBs from the source CDB, plugs them into the target CDB and upgrades them there. The installation of AutoUpgrade and the general settings are described in autoupgrade.adoc.

The upgrade is split into 3 phases. The names are the AutoUpgrade modes:

Table 1. Phases of the upgrade
Phase Task file Playbook AutoUpgrade Downtime Database changed

Analyze

upgrade_analyze.yml

autoupgrade_upgrade_analyze.yml

-mode analyze

no

no

Fixups

upgrade_fixups.yml

autoupgrade_upgrade_fixups.yml

-mode fixups

no

yes, the source CDB/PDBs (e.g. statistics, recyclebin)

Deploy

upgrade_deploy.yml

autoupgrade_upgrade.yml

analyze phase, then -mode deploy

yes, for the PDBs

yes

Important
The analyze phase is always the first step.
upgrade_deploy.yml executes upgrade_analyze.yml first and starts the upgrade only, when the analyze phase was successful.
The fixups phase is optional. It applies the fixups some time before the deploy and shortens the downtime. Without it, AutoUpgrade applies the fixups during the deploy.

1. End state in oracle_pdbs

oracle_pdbs always defines the end state of a PDB. For the upgrade this is the PDB in the target CDB of the new release:

  • cdb: the target CDB

  • home: the target ORACLE_HOME

The source of the PDB is defined with the attribute autoupgrade of the PDB. It is used by the role autoupgrade only.

Warning

Do not execute playbook manage_db between the change of oracle_pdbs and the upgrade!

As soon as a PDB in oracle_pdbs points to the target CDB, oradb_manage_pdb (playbook manage_db) tries to reach this end state. It does not know AutoUpgrade and creates a new, empty PDB in the target CDB instead of migrating the existing PDB.

  • The analyze phase detects this situation (PDB exists in source and target CDB, state conflict) and fails. The empty PDB must be dropped manually before the upgrade.

  • Execute manage_db again only after a successful upgrade (autoupgrade_upgrade.yml).

  • Prepare everything, which needs manage_db (e.g. the target CDB), before oracle_pdbs is changed.

2. Upgrade vs. Out-of-Place Patching

Both procedures move a database into a new ORACLE_HOME. They must not be mixed up.

Table 2. Upgrade vs. Out-of-Place Patching
Topic Upgrade (this guide) Out-of-Place Patching

Release of the new ORACLE_HOME

new release, e.g. 19c ⇒ 26ai

same release, new Release Update, e.g. 19.32 ⇒ 19.33

Object

PDB, moved into another CDB

CDB with all PDBs

Executed by

role autoupgrade (upgrade_deploy.yml)

role oradb_manage_db (switch_home.yml)

Inventory

cdb/home in oracle_pdbs (end state) + attribute autoupgrade (source)

change of home in oracle_databases

Playbook

autoupgrade_upgrade.yml - not manage_db

manage_db

Dictionary

upgraded by AutoUpgrade

datapatch

oradb_manage_db refuses the switch of the ORACLE_HOME of a CDB, when the release of the old and the new ORACLE_HOME differs (oraversion -majorVersion).

3. Requirements

  • Source and target ORACLE_HOME are installed (db_homes_config, db_homes_installed, playbook swdb)

  • The source CDB is running from the source ORACLE_HOME

  • The target CDB exists and is running from the target ORACLE_HOME. It is created with playbook manage_db before the PDBs are changed in oracle_pdbs. 26ai supports Container Databases only.

  • AutoUpgrade (autoupgrade.jar) in the software repository. It is installed on the database server by the task files.

  • Java: the JDK of the target ORACLE_HOME is used, when autoupgrade_java_home is empty

  • AutoUpgrade is executed on the database server as oracle_user. The playbooks set autoupgrade_run_local: false. The task files fail with autoupgrade_run_local: true.

  • The Python module pexpect is not needed for the upgrade

  • Upgrade paths, minimum Release Updates, character sets, components etc. follow the rules of Oracle for AutoUpgrade

4. Configuration

4.1. Attribute autoupgrade in oracle_pdbs

Only PDBs with the attribute autoupgrade and state: present are processed.

Table 3. Attributes of autoupgrade in oracle_pdbs
Attribute Mandatory Description

source_cdb

yes

oracle_db_name of the source CDB. Must differ from cdb.

source_home

no

Key in db_homes_config of the source ORACLE_HOME. Default: home of source_cdb in oracle_databases. Mandatory, when the source CDB is not in oracle_databases.

source_sid

no

SID of the source CDB. Default: oracle_db_instance_name, oracle_db_unique_name or oracle_db_name of source_cdb in oracle_databases, otherwise source_cdb.

source_pdb_name

no

Name of the PDB in the source CDB. Default: pdb_name. A different name renames the PDB (upg1.target_pdb_name.<pdb>).

copy_option

no

upg1.target_pdb_copy_option.<pdb>, e.g. file_name_convert=NONE. Default: NOCOPY - the datafiles are reused, no fallback with the source CDB. See Datafiles: NOCOPY or copy.

restoration

no

true: Guaranteed Restore Point before the upgrade. Default: autoupgrade_upgrade_restoration (false)

parameters

no

Dict with AutoUpgrade parameters (upg1.<key>=<value>). Overrides autoupgrade_upgrade_parameters.

The target is taken from the PDB itself (end state):

  • cdb: target CDB. It must be defined in oracle_databases.

  • home: target ORACLE_HOME. It must be the same as home of the target CDB in oracle_databases.

Example: move ORCLPDB1 from DB2 (19c) into DB26 (26ai)
db_homes_config:
  db1932-si-ee:
    version: 19.3.0.0
    oracle_home: /u01/app/oracle/product/19/db_19.32
    edition: EE
    readonly_home: true
    oracle_home_name: db1932siee
  db26-si-ee:
    version: 26.1.0.0
    oracle_home: /u01/app/oracle/product/26/db_26.1
    edition: EE
    readonly_home: true
    oracle_home_name: db261siee

oracle_databases:
  - oracle_db_name: DB2          # source CDB in 19c
    home: db1932-si-ee
    state: present
    # ...
  - oracle_db_name: DB26         # target CDB in 26ai - created before the upgrade
    home: db26-si-ee
    is_container: true
    state: present
    # ...

oracle_pdbs:
  - cdb: DB26                    # end state: target CDB
    home: db26-si-ee             # end state: target ORACLE_HOME
    pdb_name: ORCLPDB1
    state: present
    autoupgrade:
      source_cdb: DB2            # source CDB
      parameters:
        timezone_upg: "yes"

4.2. AutoUpgrade jobs

All PDBs with the same source CDB and the same target CDB are upgraded in one AutoUpgrade job.

  • Config file: autoupgrade_home/cfg/upgrade_<source SID>_<target SID>.cfg

  • Log directory: autoupgrade_log_dir/<source SID>_<target SID>

  • restoration and parameters must be identical for all PDBs of a job

  • Only PDBs, which are still in the source CDB, are written into upg1.pdbs. An interrupted upgrade continues with the remaining PDBs.

Table 4. Parameters set by the role
AutoUpgrade Value

upg1.sid

SID of the source CDB

upg1.source_home

oracle_home of the source ORACLE_HOME in db_homes_config

upg1.target_home

oracle_home of home of the PDB in db_homes_config

upg1.target_cdb

SID of cdb of the PDB

upg1.pdbs

PDBs in the source CDB (source_pdb_name)

upg1.target_pdb_name.<pdb>

pdb_name, when it differs from source_pdb_name

upg1.target_pdb_copy_option.<pdb>

copy_option

upg1.log_dir

autoupgrade_log_dir/<job>

upg1.restoration

restoration or autoupgrade_upgrade_restoration ⇒ yes/no

global.global_log_dir

autoupgrade_log_dir/<job> - recovery data of AutoUpgrade per job

sid, source_home, target_home, target_cdb, pdbs, log_dir and restoration must not be used in parameters.

Resulting config file upgrade_DB2_DB26.cfg
global.global_log_dir=/u01/app/oracle/cfgtoollogs/autoupgrade

upg1.sid=DB2
upg1.source_home=/u01/app/oracle/product/19/db_19.32
upg1.target_home=/u01/app/oracle/product/26/db_26.1
upg1.target_cdb=DB26
upg1.pdbs=ORCLPDB1
upg1.log_dir=/u01/app/oracle/cfgtoollogs/autoupgrade/DB2_DB26
upg1.restoration=no
upg1.timezone_upg=yes

4.3. Further parameters

All other AutoUpgrade parameters are passed unchanged. Booleans are converted into yes/no.

Table 5. Parameters for the upgrade
Variable Scope Result

autoupgrade_upgrade_parameters

all jobs

upg1.<key>=<value>

autoupgrade.parameters in oracle_pdbs

one job, overrides autoupgrade_upgrade_parameters

upg1.<key>=<value>

autoupgrade_upgrade_global_parameters

config file

global.<key>=<value>

Table 6. Frequently used AutoUpgrade parameters (see Oracle documentation)
Parameter Description

timezone_upg

yes: upgrade the time zone file of the PDB during the upgrade

drop_grp_after_upgrade

yes: drop the guaranteed restore point after a successful upgrade

run_utlrp

Recompile invalid objects after the upgrade

before_action / after_action

Scripts before/after the upgrade

4.4. Variables of the role

Table 7. Variables for the upgrade
Variable Default Description

autoupgrade_upgrade_pdbs

[]

Limit to these PDBs (pdb_name). Empty: all PDBs with attribute autoupgrade.

autoupgrade_upgrade_restoration

false

Guaranteed Restore Point before the upgrade (upg1.restoration)

autoupgrade_upgrade_parameters

{}

AutoUpgrade parameters for all jobs

autoupgrade_upgrade_global_parameters

{}

Additional global.* parameters

autoupgrade_upgrade_fail_on_precheck_errors

true

Fail the analyze/fixups phase on errors without automatic fixup

autoupgrade_upgrade_clear_recovery_data

false

Discard the recovery data of AutoUpgrade before the analyze phase. See Recovery data of AutoUpgrade.

autoupgrade_upgrade_timeout

21600

Maximum runtime in seconds of the deploy per job

autoupgrade_upgrade_poll

60

Poll interval in seconds during the deploy

autoupgrade_java_home

''

JAVA_HOME. Empty: <target ORACLE_HOME>/jdk

Note
The attribute autoupgrade in oracle_databases is not supported. The analyze phase fails, when it is found.

5. Datafiles: NOCOPY or copy

During the Unplug/Plug upgrade AutoUpgrade unplugs the PDB from the source CDB into an XML file and creates the PDB in the target CDB with create pluggable database …​ using '<xml file>'. The attribute copy_option in oracle_pdbs (AutoUpgrade parameter target_pdb_copy_option.<pdb>) defines, what happens with the datafiles.

Table 8. Options for the datafiles
copy_option Datafiles Disk space Duration Fallback into the source CDB

not set (default)

NOCOPY: the target CDB uses the existing datafiles. They stay in their directory, e.g. oradata/DB2/<GUID>/datafile.

none

short, independent of the size

no - the datafiles are upgraded to the new release

file_name_convert=NONE

copied as Oracle Managed Files into db_create_file_dest of the target CDB

size of the PDB

copy of all datafiles

yes - the source datafiles stay unchanged

file_name_convert=('<old>', '<new>')

copied, <old> in the path is replaced by <new>

size of the PDB

copy of all datafiles

yes - the source datafiles stay unchanged

5.1. NOCOPY (default)

oracle_pdbs:
  - cdb: DB1
    home: db26-si-ee
    pdb_name: ORCLPDB1
    state: present
    autoupgrade:
      source_cdb: DB2
      # no copy_option => NOCOPY
  • Fastest way. Recommended for big PDBs, when a backup of the PDB exists.

  • After the upgrade the datafiles of the PDB in the target CDB are still in the directory of the source CDB, e.g. /u01/app/oracle/oradata/DB2/<GUID of the PDB>/datafile/. They can be moved later online with alter database move datafile in the target CDB.

  • There is no way back. Oracle: "Backups are the only fallback strategy." Flashback Database (restoration) does not work across the Unplug/Plug.

Warning

With NOCOPY the target CDB uses datafiles in the directories of the source CDB.
Do not remove these directories (e.g. rm -rf /u01/app/oracle/oradata/DB2), when the source CDB is dropped later. Check the datafiles of the target CDB first:

select name from v$datafile where con_id = (select con_id from v$pdbs where name = 'ORCLPDB1');

5.2. Copy as Oracle Managed Files

oracle_pdbs:
  - cdb: DB1
    home: db26-si-ee
    pdb_name: ORCLPDB1
    state: present
    autoupgrade:
      source_cdb: DB2
      copy_option: file_name_convert=NONE
  • Recommended for OMF and ASM (AutoUpgrade: "For ASM/OMF, use: file_name_convert=none").

  • The target CDB needs db_create_file_dest (or pdb_file_name_convert). Otherwise the analyze phase reports "The target database …​ requires the PDB_FILE_NAME_CONVERT or DB_CREATE_FILE_DEST system parameter defined when the target_pdb_copy_option config parameter is set to file_name_convert=NONE". Set it with init_parameters of the target CDB in oracle_databases.

  • The datafiles are created in the directory of the target CDB, e.g. oradata/DB1/<GUID>/datafile.

  • Needs disk space and time for a complete copy of the PDB. The downtime of the PDB includes the copy.

5.3. Copy with path conversion

oracle_pdbs:
  - cdb: DB1
    home: db26-si-ee
    pdb_name: ORCLPDB1
    state: present
    autoupgrade:
      source_cdb: DB2
      copy_option: "file_name_convert=('/u01/app/oracle/oradata/DB2/', '/u01/app/oracle/oradata/DB1/')"
  • For datafiles with fixed paths (no OMF). Each occurrence of <old> in the path of a datafile is replaced by <new>.

  • More than one pair is possible: file_name_convert=('/a/', '/x/', '/b/', '/y/').

  • The target directories must exist and must be writable for oracle_user.

  • Quote the value in YAML because of the brackets and quotes.

5.4. Fallback after a copy

With a copy the datafiles of the source PDB stay unchanged in the release of the source CDB. AutoUpgrade has unplugged the PDB into an XML file in the job directory:

<autoupgrade_log_dir>/<job>/<source SID>/<job number>/drain/<source SID>-<PDB>.xml

keep_source_pdb (AutoUpgrade parameter, set with parameters) controls the source CDB:

Table 9. keep_source_pdb
Value Source CDB after the upgrade

no (default)

The PDB is dropped from the source CDB (drop pluggable database …​ keep datafiles). The datafiles are kept.

yes

The PDB is kept in the source CDB in a closed state (AutoUpgrade: "kept in a closed state instead of being removed from the source CDB").

    autoupgrade:
      source_cdb: DB2
      copy_option: file_name_convert=NONE
      parameters:
        keep_source_pdb: "yes"

The role ignores PDBs with status UNPLUGGED in the source CDB. A kept source PDB is not reported as conflict.

The fallback itself is a manual task and is not implemented in the role:

  1. Drop the upgraded PDB from the target CDB, if needed (drop pluggable database ORCLPDB1 including datafiles - only with a copy, never with NOCOPY!)

  2. Plug the PDB into the source CDB again, e.g. with the XML file and the unchanged datafiles: create pluggable database ORCLPDB1 using '<xml file>' nocopy; and open it

  3. Change oracle_pdbs back to the source state

Note
The fallback with keep_source_pdb and the copy options is not tested with molecule yet. Test the procedure before you use it in production.

5.5. Recommendation

Table 10. Which option to use
Situation Option

Big PDB, short downtime, backup available

NOCOPY (default)

Fallback into the source CDB needed, enough disk space

file_name_convert=NONE + keep_source_pdb: "yes"

OMF/ASM

file_name_convert=NONE

Fixed paths without OMF, new directory for the target CDB

file_name_convert=('<old>', '<new>')

Important
Take a backup of the PDB before the deploy phase - independent of the option.

6. Process overview

  1. Preparation with manage_db (no downtime) - oracle_pdbs still points to the source CDB

    1. Install the target ORACLE_HOME with playbook swdb (db_homes_installed)

    2. Add the target CDB to oracle_databases and create it with playbook manage_db

  2. Change the inventory to the end state

    1. cdb and home of the PDBs in oracle_pdbs: target CDB and target ORACLE_HOME

    2. Add the attribute autoupgrade with source_cdb to the PDBs

    3. From now on: no manage_db until the upgrade is done!

  3. Analyze phase (no downtime): autoupgrade_upgrade_analyze.yml

    1. Install autoupgrade.jar on the database server

    2. Read the PDBs of the source and the target CDB (v$pdbs)

    3. AutoUpgrade analyze for the PDBs in the source CDB

    4. Evaluate the prechecks. Fail on errors without automatic fixup.

  4. Fix the reported problems and repeat the analyze phase until it is successful

  5. Fixups phase (no downtime, optional): autoupgrade_upgrade_fixups.yml

    1. AutoUpgrade fixups for the PDBs in the source CDB, e.g. some days before the deploy

    2. Evaluate the prechecks like the analyze phase

  6. Deploy phase (downtime of the PDBs): autoupgrade_upgrade.yml

    1. Analyze phase (again)

    2. AutoUpgrade deploy: prechecks, fixups, unplug, plug, upgrade, postupgrade

    3. Check that the PDBs are open READ WRITE in the target CDB

  7. Post tasks

    1. Execute playbook manage_db - the PDBs are in their end state now

    2. Remove the attribute autoupgrade from oracle_pdbs (optional - upgraded PDBs are skipped)

    3. Remove the source CDB, when it is not needed anymore (state: absent in oracle_databases)

7. Analyze phase

ansible-playbook -i inventory/ -e hostgroup=dbservers playbooks/autoupgrade_upgrade_analyze.yml

The role reads the PDBs of the source and the target CDB from v$pdbs. The ORACLE_HOME of a CDB is taken from the running instance (/proc/<pid of pmon>/exe).

Table 11. State of a PDB in the analyze phase
Source CDB Target CDB State Action

PDB exists

PDB missing

pending

AutoUpgrade analyze (fixups phase: fixups), upgrade in the deploy phase

PDB missing

PDB exists

upgraded

nothing - the PDB is skipped

PDB exists

PDB exists

conflict

fail. Usually manage_db was executed after the change of oracle_pdbs and created an empty PDB in the target CDB.

PDB missing

PDB missing

missing

fail

Further checks:

  • The target CDB must be running from the target ORACLE_HOME.

  • The source CDB must be running from the source ORACLE_HOME, when PDBs are pending. It is not needed anymore, when all PDBs are upgraded.

7.1. Evaluation of the prechecks

AutoUpgrade writes the results of the prechecks into the job directory:

<autoupgrade_log_dir>/<job>/<source SID>/<job number>/
  autoupgrade_<date>.log
  autoupgrade_<date>_user.log
  prechecks/
    *_checklist.json
    *_preupgrade.html

The role reads all *_checklist.json of the newest job and evaluates each check with the attribute severity:

Table 12. Evaluation of the prechecks
severity fixup_available runfix Result

ERROR

not YES

-

blocking

ERROR

YES

NO

blocking

ERROR

YES

YES or missing

fixed by AutoUpgrade in the deploy

WARNING, RECOMMEND, INFO

-

-

reported only

The analyze phase fails, when at least one check is blocking (autoupgrade_upgrade_fail_on_precheck_errors: true) or when no *_checklist.json was found.

Note
Oracle does not document the structure of *_checklist.json. The role collects all objects with an attribute severity at any level of the files.

At the end of the analyze phase the role shows for each job:

  • Logfile: detailed logfile of the AutoUpgrade job (<job number>/autoupgrade_<date>.log)

  • User log: summary of the AutoUpgrade job (<job number>/autoupgrade_<date>_user.log)

  • Reports: HTML reports of the prechecks (*_preupgrade.html)

The paths are shown before the evaluation fails, so they are available in case of blocking checks, too.

Result of the analyze phase
"_autoupgrade_upgrade_state": {
    "DB2_DB26": {
        "blocking": [],
        "checks": 51,
        "errors": 0,
        "warnings": 12,
        "log_dir": "/u01/app/oracle/cfgtoollogs/autoupgrade/DB2_DB26",
        "logfile": "/u01/app/oracle/cfgtoollogs/autoupgrade/DB2_DB26/DB2/100/autoupgrade_20261025.log",
        "user_logfile": "/u01/app/oracle/cfgtoollogs/autoupgrade/DB2_DB26/DB2/100/autoupgrade_20261025_user.log",
        "mode": "analyze",
        "pdbs": ["ORCLPDB1"],
        "upgraded_pdbs": [],
        "reports": ["..."],
        "state": "analyzed"
    }
}

8. Fixups phase

ansible-playbook -i inventory/ -e hostgroup=dbservers playbooks/autoupgrade_upgrade_fixups.yml

The fixups phase is the analyze phase with AutoUpgrade in mode fixups (upgrade_fixups.yml includes upgrade_analyze.yml):

  • Same checks of the source and the target CDB and the same states of the PDBs

  • AutoUpgrade executes the prechecks and applies the fixups (e.g. dictionary statistics, purge recyclebin) in the source CDB/PDBs. The PDBs are changed, but stay in the source CDB.

  • Same evaluation of the prechecks, logfiles and reports as the analyze phase. It fails on errors without automatic fixup.

The fixups phase is optional. Execute it some time before the deploy to shorten the downtime. Without it, AutoUpgrade applies the fixups during the deploy.

9. Deploy phase

ansible-playbook -i inventory/ -e hostgroup=dbservers playbooks/autoupgrade_upgrade.yml

# one PDB only
ansible-playbook -i inventory/ -e hostgroup=dbservers -e '{"autoupgrade_upgrade_pdbs": ["ORCLPDB1"]}' \
  playbooks/autoupgrade_upgrade.yml

upgrade_deploy.yml:

  1. executes the analyze phase (upgrade_analyze.yml)

  2. executes autoupgrade.jar -config upgrade_<job>.cfg -mode deploy -noconsole for each job in state analyzed (async: autoupgrade_upgrade_timeout, poll: autoupgrade_upgrade_poll)

  3. checks that all PDBs of the job are open READ WRITE in the target CDB and are gone from the source CDB

The jobs are executed one after the other.

Follow the progress on the database server
tail -f /u01/app/oracle/cfgtoollogs/autoupgrade/DB2_DB1/cfgtoollogs/upgrade/auto/autoupgrade.log

9.1. Repeated execution

  • PDBs in the target CDB are skipped (state upgraded). The playbook can be executed again without changes.

  • An interrupted or failed deploy is resumed by the next execution of autoupgrade_upgrade.yml, see Recovery data of AutoUpgrade.

9.2. Recovery data of AutoUpgrade

AutoUpgrade keeps recovery data after each deploy in autoupgrade_log_dir/<job>/cfgtoollogs/upgrade/auto (config_files/upgradeJobs.bin, state.txt) - for a finished deploy as well as for an interrupted or failed one. As long as the recovery data exists, AutoUpgrade refuses analyze, fixups and a new deploy with the same config file:

There is an unfinished execution of a deploy mode. Run the AutoUpgrade in deploy mode to resume from failure point

The role reads [Status] of the jobs from state.txt before the PDBs are checked:

Table 13. Handling of the recovery data
Recovery data Analyze/fixups phase Deploy phase

none

normal

normal

finished deploy (FINISHED), PDBs pending

cleared automatically (-clear_recovery_data), then normal

cleared automatically, then normal

finished deploy (FINISHED), all PDBs upgraded

kept - needed for -restore

kept

unfinished deploy (other status or no state.txt)

fails with a hint to resume with the deploy phase

state resume: no checks of the PDBs, AutoUpgrade deploy resumes the job with the existing config file

The checks of the PDBs are skipped for resume, because the PDB can be in any intermediate state during a deploy (e.g. unplugged from the source CDB, but not plugged into the target CDB yet). After the resumed deploy the PDBs are checked like after a normal deploy.

autoupgrade_upgrade_clear_recovery_data: true discards the recovery data in any case. Use it only, when the PDBs were restored manually and the upgrade must start from the beginning. An unfinished deploy cannot be resumed anymore afterwards.

ansible-playbook -i inventory/ -e hostgroup=dbservers -e autoupgrade_upgrade_clear_recovery_data=true \
  playbooks/autoupgrade_upgrade_analyze.yml

9.3. Fallback

  • See Datafiles: NOCOPY or copy: a fallback into the source CDB is possible with a copy of the datafiles only.

  • restoration: true / autoupgrade_upgrade_restoration: true: AutoUpgrade creates a Guaranteed Restore Point (upg1.restoration=yes). Requires a Fast Recovery Area and Enterprise Edition. Check the Oracle documentation, how the restore point is used for an Unplug/Plug upgrade.

The fallback is not implemented in the role.

Important
Take a backup before the upgrade.

10. After the upgrade

oracle_pdbs describes the end state already. Nothing has to be changed for the PDBs.

  • Execute playbook manage_db. It manages the PDBs in the target CDB now (tablespaces, users, services, …​).

  • The attribute autoupgrade can be removed from oracle_pdbs. Upgraded PDBs are skipped anyway.

  • Services, tnsnames.ora entries and wallets, which point to the source CDB, must be changed to the target CDB.

  • Remove the source CDB, when it is not needed anymore (state: absent in oracle_databases), and the source ORACLE_HOME (db_homes_installed).

11. Molecule scenario upgrade

The scenario extensions/molecule/upgrade tests the Unplug/Plug upgrade of ORCLPDB1 from DB2 (19c, db1932-si-ee) into DB1 (26ai, db26-si-ee). The inventory is in extensions/molecule/shared_config/inventory/host_vars/dbfs-upg.yml.

Table 14. Steps in scenario upgrade
Step Playbooks Result

converge

os, swdb, sql_zauberkasten, manage_db

19c and 26ai installed, DB2 with ORCLPDB1 in 19c, empty target CDB DB1 in 26ai, listener from 26ai. oracle_pdbs points to DB2 (source state).

side_effect

autoupgrade_upgrade_analyze, autoupgrade_upgrade_fixups, autoupgrade_upgrade

oracle_pdbs is set to the end state in side_effect.yml (never in the inventory used by converge). Analyze, fixups and deploy phase for ORCLPDB1, controlled by AUTOUPGRADE_PHASE.

verify

ORCLPDB1 is open READ WRITE in DB1 (26ai). Only valid after the deploy phase.

The listener runs from the 26ai ORACLE_HOME: a listener can serve databases of the same or an older release only.

Installation media in MOLECULE_SW_DIR:

  • db_home_19.32.zip (Golden Image 19.32, e.g. from scenario download with autoupgrade_download_gold_image_rename: true)

  • LINUX.X64_2326100_db_home.zip (26ai)

  • autoupgrade.jar

Execute scenario upgrade
cd extensions
task converge S=upgrade
task side-effect S=upgrade

The phases in side_effect.yml are controlled with the environment variable AUTOUPGRADE_PHASE. It is a comma separated list of phases. The phases are always executed in the order analyze, fixups, deploy.

Table 15. AUTOUPGRADE_PHASE for side_effect.yml
Value Executed Use case

all (default, not set)

analyze, fixups, deploy

complete test

analyze

analyze phase only. The databases are not changed.

analyze changes before the deploy, repeat as often as needed

fixups

fixups phase only. The source CDB/PDB is changed, the PDB stays in the source CDB.

test the fixups

deploy

deploy phase only (upgrade_deploy.yml executes the analyze checks internally)

test the deploy without the separate analyze/fixups plays

analyze,fixups

analyze and fixups phase

everything before the deploy

cd extensions
AUTOUPGRADE_PHASE=analyze task side-effect S=upgrade
AUTOUPGRADE_PHASE=fixups task side-effect S=upgrade
AUTOUPGRADE_PHASE=analyze,fixups task side-effect S=upgrade
AUTOUPGRADE_PHASE=deploy task side-effect S=upgrade
task side-effect S=upgrade        # analyze + fixups + deploy

The deploy phase moves ORCLPDB1 into DB1. A new test of the deploy needs a new environment (task destroy S=upgrade and task converge S=upgrade).

Warning
Do not execute task converge S=upgrade during or after the deploy phase without task destroy S=upgrade. The inventory of converge contains the source state (ORCLPDB1 in DB2). manage_db creates a new, empty ORCLPDB1 in DB2 after AutoUpgrade has unplugged it. The deploy phase reports this as conflict.
Important
side_effect.yml must never import playbook manage_db. With oracle_pdbs in the end state it would create an empty ORCLPDB1 in DB1 (see warning above).

12. Known limitations

  • Unplug/Plug upgrade of PDBs into an existing CDB on the same host only. Upgrade of a whole CDB, non-CDB to PDB conversion, RAC, Oracle Restart and Data Guard are not supported.

  • The jobs are executed one after the other. AutoUpgrade can execute jobs in parallel - not used by the role.

  • The fallback is not implemented in the role.

  • manage_db must not be executed between the change of oracle_pdbs and the upgrade (see warning above).

  • The evaluation of the prechecks depends on *_checklist.json, which is not documented by Oracle.

  • Patching of databases with AutoUpgrade (-patch) is not implemented.

10 - Oracle Autonomous Health Framework (AHF)

Important
The roles ahf and ahf_fleet_insights are under development and experimental.
Breaking changes can happen at any time and are not always documented (e.g. in Breaking Changes - Upgrade Guide).

Oracle Autonomous Health Framework (AHF) bundles the diagnostic and health check tools of Oracle:

  • Trace File Analyzer (TFA): collection of diagnostic data for Service Requests, automatic collections after incidents

  • Compliance checks with orachk (exachk on Engineered Systems)

  • AHF Insights: offline HTML reports about the state of the server and the databases

  • OS Watcher, Cluster Health Monitor and Cluster Health Advisor (Grid Infrastructure)

AHF is not needed in every environment. This guide helps with the decisions for an installation and describes the usage in ansible-oracle:

  • Role ahf: installation, upgrade and configuration of AHF on the database servers

  • Role ahf_fleet_insights: AHF Fleet Insights as central web interface for many database servers

The variables are described in roles/ahf/README.md and roles/ahf_fleet_insights/README.md.

1. Architecture overview

Architecture

The role ahf installs AHF on every database server. The role ahf_fleet_insights is optional and installs the central web interface AHF Fleet Insights on a separate server. AHF works without Fleet Insights - the data is then evaluated locally on each database server.

Table 1. Data storage
Location Content Owner Remark

ahf_loc (/opt/oracle.ahf) on the database server

AHF software: TFA, orachk, ahfctl

root

Replaced by an upgrade. No data.

ahf_data_dir (/opt/oracle.ahf/data) on the database server

AHF repository: TFA collections, Compliance results, Insights reports, logs of AHF

root

Primary storage of the data. Local filesystem (no NFS), size limit with reposizeMB, purge with minfileagetopurge.

ahf_ansible_state_file (/opt/oracle.ahf.ansible.json) on the database server

Settings applied by the role ahf

root

Detection of changed settings, see Settings.

ahf_fi_install_dir ({{ oracle_base }}/ahffi) on the Fleet Insights server

Fleet Insights software, embedded Nginx, logs, processing of uploads

ahf_fi_install_user

Plan 4 GB per registered cluster.

Repository database (19c or newer)

Uploaded data of all clients: Compliance results and TFA collections

schema AHFFI

Central copy for the evaluation. Available when a database server is down.

The data flows in one direction only: from the database servers to Fleet Insights (push over HTTPS). The local AHF repository stays the primary storage - a database server keeps working when Fleet Insights is not reachable. Fleet Insights needs no access to the database servers, neither for the collection nor for the evaluation in the browser.

Note
The diagram is rendered by plantuml.com from the master branch on GitHub. Changes of doc/guides/ahf.puml are visible after the merge.

2. When is AHF useful?

Table 2. Use cases for AHF
Situation Recommendation

Grid Infrastructure (RAC, Oracle Restart)

AHF is recommended. Oracle Support requests TFA collections for most Service Requests. GI contains an older TFA - a current AHF replaces it.

Single instance on filesystem with Oracle Support contract

Useful for faster Service Requests (tfactl diagcollect) and regular Compliance checks.

Many database servers

AHF with AHF Fleet Insights gives a central view of the Compliance results and the collections.

Test/development VMs, short-lived environments

Normally not needed. The TFA daemon and the Compliance scheduler need CPU, memory and disk space.

3. Decisions before the installation

3.1. root or non-root installation

Table 3. Installation modes (ahf_install_mode)
Feature root (default) extract (non-root)

User for the installation

root

oracle_user

TFA daemon (systemd), automatic collections

yes

no

Compliance scheduler (ahfctl startahf -compliance)

yes

no

Access to OS logs (/var/log/messages)

yes

no

AHF Fleet Insights client

yes

no

Default ahf_loc / ahf_data_dir

/opt

oracle_base

Recommended by Oracle

yes

only when root is not possible

3.2. Installation directory and data directory

ahf_setup creates oracle.ahf below ahf_loc (software) and oracle.ahf/data below ahf_data_dir (repository with collections, Compliance results and Insights reports).

Table 4. Default directories
Variable Default Reason

ahf_loc

/opt ⇒ /opt/oracle.ahf

Default of Oracle. The binaries are executed by root.

ahf_data_dir

ahf_loc ⇒ /opt/oracle.ahf/data

Default of Oracle. At least 5 GB, 10 GB recommended.

Important
With ahf_install_mode: root every directory in the path of ahf_loc and ahf_data_dir must be owned by root. ahf_setup rejects a data directory in a path of another owner (AHF-00014: AHF Data Location …​ is not owned by root in directory hierarchy).
A directory below oracle_base or below a mount point of host_fs_layout (e.g. /u01, owned by oracle_user by default) is not possible: oracle_user could rename or replace a directory in the path and get the binaries executed by root. The role checks the owner of all directories of ahf_loc and ahf_data_dir before the installation.

The software and the data need about 10 GB in /opt/oracle.ahf. Use a logical volume of its own for /opt/oracle.ahf, owned by root, when the root filesystem has not enough space. A full AHF repository does not fill the root filesystem then.

Logical volume of its own for AHF (role orahost)
host_fs_layout:
  - vgname: vgora
    state: present
    filesystem:
      - { mntp: /u01, lvname: lvorabase, lvsize: 25G, fstype: xfs }
      - { mntp: /opt/oracle.ahf, lvname: lvahf, lvsize: 10G, fstype: xfs, owner: root, group: root, mode: "0755" }
    disk:
      - { device: /dev/sdb, pvname: /dev/sdb1 }
Note
The installer appends oracle.ahf to ahf_loc and oracle.ahf/data to ahf_data_dir. With the defaults, software and data are both in /opt/oracle.ahf and therefore on this logical volume.

3.3. Resource requirements

The values are from the documentation of Oracle and measured in the molecule scenario ahf (AHF 26.10.2, AHF Fleet Insights 25.2.1, freshly installed, idle, 8 CPUs).

Table 5. Database server (role ahf)
Resource Value Source

Disk: software /opt/oracle.ahf

1.2 GB

measured

Disk: data /opt/oracle.ahf/data

at least 5 GB, 10 GB recommended. Grows with collections and Compliance results, limit with reposizeMB

Oracle

Memory: TFA daemon

about 170 MB RSS (Java with -Xmx256m)

measured

CPU: TFA daemon

limit by default: the lower of 4 CPUs or 75% of the CPUs (ahfctl getresourcelimit: cpu 4.0)

Oracle, measured

Compliance checks (orachk)

only during the scheduled runs (default: daily 02:03, Sunday 03:03)

not measured

Table 6. AHF Fleet Insights server (role ahf_fleet_insights)
Resource Value Source

CPU / memory

4 cores, 16 GB RAM (minimum recommendation)

Oracle

Memory idle

about 2.5 GB RSS (34 processes)

measured

Disk: software ahf_fi_install_dir

0.8 GB

measured

Disk: data

4 GB per registered cluster

Oracle

Repository database

separate database (19c or newer), e.g. Oracle Database Free: about 2.2 GB memory

measured

The CPU and memory of TFA can be limited with ahfctl setresourcelimit (only on Linux with a root installation):

Limit TFA to 2 CPUs and 1 GB memory
ahfctl setresourcelimit -value 2
ahfctl setresourcelimit -resource kmem -value 1024
ahfctl getresourcelimit
Note
The documentation of Oracle has different ranges for the memory limit in different releases. Check the allowed values with the installed release. The role ahf does not manage the limits.

3.4. Which components should be active?

Table 7. Components and variables
Component Variable Default Recommendation

TFA daemon

ahf_tfa_enabled

true

Active for GI and production. Disable on small test systems - tfactl stays usable on-demand.

Automatic diagnostic collections

ahf_tfa_settings.autodiagcollect

Oracle: OFF

ON for production with Service Requests. Needs space in the repository.

Compliance scheduler (orachk)

ahf_compliance_autostart

false

Active, when the results are evaluated (mail or AHF Fleet Insights).

AHF Insights

ahf_tfa_settings.autoinsights

Oracle: ON

Insights are created with collections. Usable for the static web server (see below).

AHF Fleet Insights client

ahf_fleet_insights_client

false

Active, when an AHF Fleet Insights server exists.

Minimal installation: TFA on-demand only
ahf_tfa_enabled: false
ahf_compliance_autostart: false
Full installation for production
ahf_tfa_enabled: true
ahf_tfa_settings:
  autodiagcollect: "ON"
  reposizeMB: 10240
  ahfnotificationaddress: dba@example.com
ahf_compliance_autostart: true
ahf_compliance_settings:
  autostart_client:
    COLLECTION_RETENTION: 30
ahf_fleet_insights_client: true
ahf_fleet_insights_url: https://ahffi.example.com:5005
ahf_fleet_insights_user: ahfclient
ahf_fleet_insights_password: "{{ vault_ahf_fleet_insights_password }}"

3.5. systemd, cron or nothing?

The role creates no cron jobs or systemd units for AHF:

  • The TFA daemon is started by AHF itself (oracle-tfa.service / init.tfa).

  • The Compliance checks have their own scheduler (ahfctl startahf -compliance). Oracle recommends the scheduler instead of cron. The schedule is changed with AUTORUN_SCHEDULE of an autorun ID in ahf_compliance_settings.

  • AHF Fleet Insights clients upload their data automatically.

  • The AHF Fleet Insights server starts and monitors its processes with cron entries of ahf_fi_install_user - created by the installer.

A systemd timer is only needed for the static web server without AHF Fleet Insights (see below).

4. Installation media

AHF should be up to date. Oracle releases a new version every quarter.

Table 8. Sources of the installation media
Source Filename Remark

AutoUpgrade (role autoupgrade)

name from My Oracle Support

AHF in autoupgrade_download_patches - always the latest version

My Oracle Support Doc ID 2550798.1 / patch 30166242

AHF-LINUX_v<version>.zip or p30166242_*.zip

manual download into the software repository

Download AHF with AutoUpgrade into the software repository
autoupgrade_download_patches:
  - target_version: 26
    patch: AHF
    platform: LINUX.X64
ansible-playbook -i inventory/ opitzconsulting.ansible_oracle.autoupgrade_download_patches

The role ahf uses the newest file (modification time) matching ahf_installer_patterns in oracle_sw_source_local (Ansible controller) or oracle_stage_remote (oracle_sw_copy: false, e.g. NFS). A fixed file is set with ahf_installer_archive.

The version is taken from the filename AHF-LINUX_v<version>.zip. Patch 30166242 is extracted to find the version of the inner AHF-LINUX_v<version>.zip. Set ahf_installer_version, when the version is not part of the filename.

Note
AHF can upgrade itself (ahfctl setupgrade -autoupgrade on -swstage <dir>). This is not used by ansible-oracle. An upgrade with the role is reproducible, uses the software repository and the signature check, and is executed at a planned time.

5. Installation and configuration with role ahf

Requirements:

  • Perl 5.10 or newer with all core modules - the package perl, perl-interpreter alone is not enough (AHF-00074: Required Perl Modules not found). The role installs perl, openssl and unzip (ahf_packages, ahf_manage_packages).

  • Python module pexpect on the database server for the registration in AHF Fleet Insights

The role executes these steps:

  1. Check of variables, Perl and the owner of ahf_loc

  2. Installation with ahf_setup -silent -local when AHF is missing, or upgrade when the installation media is newer (ahf_upgrade)

  3. Check of the signature of ahf_setup (ahf_verify_signature)

  4. Start/stop of TFA and the Compliance scheduler, tfactl set and orachk -set for changed settings

  5. Registration or deregistration in AHF Fleet Insights

Install AHF on all database servers
ansible-playbook -i inventory/ opitzconsulting.ansible_oracle.ahf -e hostgroup=dbfs

5.1. Settings

ahf_tfa_settings is executed with tfactl set <key>=<value>, ahf_compliance_settings with orachk -set "<key>=<value>;…​" -id <autorun ID> - one call per autorun ID. orachk -set requires AUTORUN_SCHEDULE in every call: the role adds the current schedule, when it is not part of the settings.

The Compliance settings are stored per autorun ID by AHF. The autoruns exist only with a running Compliance scheduler: ahfctl stopahf -compliance removes them, ahfctl startahf -compliance does not recreate them. The role creates the default autoruns of Oracle with orachk -autostart, when they are missing, and applies ahf_compliance_settings again. ahf_compliance_settings requires ahf_compliance_autostart: true.

A new installation has two autorun IDs (orachk -get all -id all shows them with the prefix orachk.):

Table 9. Autoruns of a new installation (AHF 26.10)
Autorun ID AUTORUN_SCHEDULE Checks COLLECTION_RETENTION

autostart_client_oratier1

3 2 * * 1,2,3,4,5,6 (Monday - Saturday 02:03)

profile oratier1

7 days

autostart_client

3 3 * * 0 (Sunday 03:03)

all checks

14 days

Daily checks at 01:30 and a longer retention of the weekly results
ahf_compliance_settings:
  autostart_client_oratier1:
    AUTORUN_SCHEDULE: "30 1 * * 1-6"
  autostart_client:
    COLLECTION_RETENTION: 30

The role stores the applied settings in ahf_ansible_state_file (/opt/oracle.ahf.ansible.json). A setting is executed only, when it differs from the last run. Manual changes with tfactl are not detected. -e ahf_settings_force=true executes all settings again.

Note
Removing a key from ahf_tfa_settings does not reset the setting in AHF. Set the default value of Oracle instead.

5.2. Cluster

The role installs AHF with -local on each host of the play. Execute tfactl syncnodes once after the installation on all nodes of a cluster. An existing TFA of Grid Infrastructure is upgraded by ahf_setup.

ahf_setup uses the Perl of the Grid Infrastructure home on hosts with Oracle Restart or RAC. This Perl is owned by the GI owner and is rejected for a root installation (AHF-00026: Perl is not owned by root). The role passes the Perl of the OS with -perlhome /usr (ahf_perl_home) and checks its owner.

5.3. Uninstall

Uninstall AHF
ansible-playbook -i inventory/ opitzconsulting.ansible_oracle.ahf -e hostgroup=dbfs -e ahf_state=absent

The repository in ahf_data_dir is kept. ahf_uninstall_delete_repo: true removes it.

6. Central evaluation of many servers

6.1. Architecture

The data stays on the database servers and is pushed to a central server:

  • The AHF repository is a local directory on the database server. An NFS share for the repository is not needed - a hanging NFS server has no impact on the database server.

  • The database servers send their data to the central server (push). The central server needs no access to the database servers - neither for the collection nor for the evaluation.

  • The data is available on the central server, when the database server is down.

A pull from the central server (e.g. rsync over SSH from each database server) is possible as well, but requires access of the central server to all database servers.

6.2. AHF Fleet Insights

AHF Fleet Insights is the web interface of Oracle for many AHF clients (My Oracle Support Doc ID 3043060.1).

  • Registered clients upload Compliance results (orachk) and TFA collections automatically.

  • The data is stored in a repository database (Oracle Database 19c or newer). Oracle AI Database 26ai needs no additional license for the repository.

  • The web application (with an embedded Nginx) runs as non-root user on port ahf_fi_port (5005) with HTTPS.

  • Requirements: Oracle Linux / RHEL 8 or newer (x86_64), Java 22 or newer, Oracle Instant Client (Basic, SQL*Plus), 4 cores, 16 GB RAM and 4 GB disk space per registered cluster.

  • Clients need AHF 24.9 or newer.

Important
The Oracle documentation describes the registration for clusters ("AHF Client must be running on a cluster"), but describes the fleet as "database clusters or single-instance systems". Check the registration of single instance servers before planning AHF Fleet Insights for them.

6.2.1. Repository database

Fleet Insights needs a schema user in the repository database, before the role ahf_fleet_insights is executed. The role does not create it. The installation media contains the script create_db_user.sql (in ahf_fleet_setup_onprem.zip) with placeholders for the password and the tablespace:

create_db_user.sql of AHFFI-LINUX_v25.2.1.zip
CREATE USER AHFFI IDENTIFIED BY <password> DEFAULT TABLESPACE <tablespace>;
GRANT CREATE SESSION TO AHFFI;
GRANT CONNECT, RESOURCE TO AHFFI;
GRANT CREATE TABLE TO AHFFI;
GRANT INSERT ANY TABLE TO AHFFI;
alter user AHFFI quota unlimited on <tablespace>;

Optionally, the repository database and the user are managed with ansible-oracle, e.g. a PDB in oracle_pdbs with the user in users (role oradb_manage_users). The password is taken from dbpasswords like for every other user.

Schema user for Fleet Insights in a PDB of ansible-oracle
oracle_pdbs:
  - cdb: DB1
    home: db26-si-ee
    pdb_name: AHFFI
    pdbadmin_user: PDBADMIN
    listener_port: 1521
    state: present
    users:
      - schema: AHFFI
        state: present
        default_tablespace: USERS
        grants:
          - create session
          - connect
          - resource
          - create table
          - insert any table
        quotas:
          - tablespace: USERS
            size: unlimited

dbpasswords:
  DB1:
    AHFFI:
      AHFFI: "{{ vault_ahf_fi_db_password }}"

ahf_fi_db_service: AHFFI
ahf_fi_db_user: AHFFI
ahf_fi_db_password: "{{ vault_ahf_fi_db_password }}"
Note
Check the grants of create_db_user.sql of a new release of Fleet Insights. The molecule scenario ahf creates the user directly with SQL*Plus in an Oracle Database Free container.

6.2.2. Installation

Inventory for AHF Fleet Insights
ahf_fi_db_host: ahffi.example.com
ahf_fi_db_port: 1521
ahf_fi_db_service: ahffi.example.com
ahf_fi_db_user: AHFFI
ahf_fi_db_password: "{{ vault_ahf_fi_db_password }}"
ahf_fi_registration_user: ahfclient
ahf_fi_registration_password: "{{ vault_ahf_fleet_insights_password }}"
Install AHF Fleet Insights
ansible-playbook -i inventory/ opitzconsulting.ansible_oracle.ahf_fleet_insights -e hostgroup=ahffi

The role:

  1. installs cronie, iproute, hostname, the Oracle JDK and Oracle Instant Client (ahf_fi_packages)

  2. installs Fleet Insights with ahf_fleet_setup -quiet <properties> as ahf_fi_install_user and creates the registration user, or patches an existing installation when the installation media is newer

  3. opens ahf_fi_port in firewalld

  4. starts Fleet Insights (ahffi start), when ahffi status does not report it as running, and waits for the web application on ahf_fi_port (ahf_fi_start_timeout)

  5. provides the public certificate as fact ahf_fi_public_cert and optionally as file on the Ansible controller (ahf_fi_public_cert_fetch)

  6. shows ahffi status and the registration command from ahffi info

The properties file with the passwords is removed after the installation.

Fleet Insights has no systemd unit. Its processes are started by cron entries of ahf_fi_install_user, created by the installer - crond must be running.

The installer creates a self-signed certificate with the hostname as CN, but without Subject Alternative Name (SAN). AHF clients (AHF 26.x, Python 3.13) check only the SAN and reject it with CERTIFICATE_VERIFY_FAILED …​ Hostname mismatch, certificate is not valid for '<host>'. The role replaces it (ahffi importcert, Fleet Insights is restarted):

  • ahf_fi_cert_generate: true (default): self-signed certificate with the SAN ahf_fi_cert_names (default: hostname and FQDN). The clients must use one of these names in ahf_fleet_insights_url.

  • ahf_fi_cert_file / ahf_fi_cert_key_file: own certificate, e.g. from an internal CA.

6.2.3. Login to the web interface

The URL of the web interface is shown by ahffi info (Application URL), e.g. https://ahffi.example.com:5005/ahfservice.

Table 10. Default login of the installer
User Password Remark

fleet_admin

welcome1

UI administrator with access to the data of the whole fleet. The password must be changed at the first login.

Important
Change the password of fleet_admin immediately after the installation. The generic registration user (ahf_fi_registration_user) is only used by the AHF clients for the registration and the upload, not for the web interface.
Note
In the molecule scenario ahf the web interface is published on the host: https://localhost:5005/ahfservice.
6.2.3.1. Reset the password of fleet_admin

Oracle documents no reset of a forgotten password of fleet_admin. The UI users are stored in the table AHFFI_USERS of the repository schema (ahf_fi_db_user). The installer writes the hash of the default password welcome1 into this table (install_ahf_service.sh, variable ahffi_admin_default). Setting this hash again resets the password to welcome1, the new password is set at the next login.

Warning
This procedure is derived from the installer of AHF Fleet Insights 25.2.1 and is not documented by Oracle.
Reset the password of fleet_admin to welcome1 (as ahf_fi_install_user on the Fleet Insights server)
cd <ahf_fi_install_dir>
HASH=$(sed -n 's/^ahffi_admin_default="\(.*\)"/\1/p' install_ahf_service.sh)

sqlplus <ahf_fi_db_user>@//<ahf_fi_db_host>:<ahf_fi_db_port>/<ahf_fi_db_service> <<EOF
update ahffi_users
   set user_password = '${HASH}', status = 'ENABLED', update_date = sysdate
 where user_id = 'fleet_admin';
commit;
EOF
Note
The installer sets the default password, when the hash in AHFFI_USERS differs from it (register_admin_user). Check the password of fleet_admin after a patch of Fleet Insights.

6.2.4. Registration of the clients

Register the database servers
ahf_fleet_insights_client: true
ahf_fleet_insights_url: https://ahffi.example.com:5005
ahf_fleet_insights_user: ahfclient
ahf_fleet_insights_password: "{{ vault_ahf_fleet_insights_password }}"
# self-signed certificate of Fleet Insights (ahf_fi_public_cert_fetch of role ahf_fleet_insights)
ahf_fleet_insights_public_cert: "{{ lookup('ansible.builtin.file', inventory_dir + '/files/ahffi_cert.pem') }}"

The client checks the certificate of Fleet Insights. With the self-signed certificate of the installer the registration fails with CERTIFICATE_VERIFY_FAILED, unless the certificate is passed with ahf_fleet_insights_public_cert (ahf configuration set --public-cert). Fetch it with ahf_fi_public_cert_fetch while installing Fleet Insights:

Fetch the certificate to the Ansible controller
ahf_fi_public_cert_fetch: "{{ inventory_dir }}/files/ahffi_cert.pem"

The URL must use a name of the SAN of the certificate (ahf_fi_cert_names). The certificate file on the client must be readable by the owner only (AHF-07275), the role writes it with mode 0600.

The registration asks twice for the password of the registration user, the role answers it with ansible.builtin.expect (Python module pexpect on the database server). It is executed with --os-user {{ oracle_user }} (ahf_fleet_insights_os_user). Without an OS user for the REST access, the registration fails with AHF-02119: Fleet Insights configuration succeeded, but the REST Agent was not started.

ansible-playbook -i inventory/ opitzconsulting.ansible_oracle.ahf -e hostgroup=dbfs

The URL is the URL of ahffi info without /ahfservice. ahf_fleet_insights_force_register: true registers again, e.g. after a password change. ahf_fleet_insights_client: false deregisters a client registered by the role.

6.2.5. Data in AHF Fleet Insights

A registered client uploads its data automatically. The views of Fleet Insights are filled by different data:

Table 11. Data and views of Fleet Insights
Data View in Fleet Insights Automatic on the client Manual start (as root) Inventory (role ahf)

Topology: database servers, databases (CDB, PDB), ASM, clusters

Fleet Topology, Databases, Database Servers

AHF Insights collection at 05:00 and 17:00

tfactl diagcollect -insights -last 1h -silent

ahf_fleet_insights_client: true (no further setting)

Problems, events, OS issues, system changes, patches, RPMs, space

Insights (Detected Problems, Events, …​), Space Analysis

AHF Insights collection at 05:00 and 17:00

tfactl diagcollect -insights -last 1h -silent

ahf_fleet_insights_client: true (no further setting)

Compliance results (orachk)

Insights > Best Practice Issues

Compliance scheduler

orachk -usediscovery -profile ahf -showpass -tag manual_check -readenvconfig

ahf_compliance_autostart: true, schedule in ahf_compliance_settings

Diagnostic collections with log and trace files

collections of the clients

automatic collections after incidents

tfactl diagcollect -database <db> -last 1h -silent

ahf_properties: {ahf.fleet-insights.upload-only-minimal-files: "false"}, ahf_tfa_settings: {autodiagcollect: "ON"}

Capacity

Fleetwide Capacity Analysis, Cluster-Wise Usage Trends

capacity plugin of Fleet Insights and exachk (Exadata)

-

not supported

Table 12. Collector options on the CLI and in the inventory of the role ahf
Option CLI (as root) Inventory (role ahf)

Automatic diagnostic collections after incidents

tfactl set autodiagcollect=ON

ahf_tfa_settings: {autodiagcollect: "ON"}

AHF Insights in automatic collections

tfactl set autoinsights=ON

ahf_tfa_settings: {autoinsights: "ON"}

Size of the TFA repository

tfactl set reposizeMB=10240

ahf_tfa_settings: {reposizeMB: 10240}

Upload of complete collections (log and trace files)

ahf configuration set --property ahf.fleet-insights.upload-only-minimal-files --value false

ahf_properties: {ahf.fleet-insights.upload-only-minimal-files: "false"}

Alert History: timeline of ASM events (only ASM: dismount, dismount_force, offline, mount, last 30 days), view with ahf analysis create --type alert-history --component ASM

ahf configuration set --property ahf.collectors.alert-history --value on

ahf_properties: {ahf.collectors.alert-history: "on"}

Events of the alert logs of databases, ASM and Clusterware (base of the automatic collections), view with tfactl events

always active with a running TFA daemon

ahf_tfa_enabled: true

Enhanced OS metrics

ahf configuration set --property ahf.collectors.enhanced_os_metrics --value on

ahf_properties: {ahf.collectors.enhanced_os_metrics: "on"}

Checks excluded from the Compliance checks

ahf configuration set --property ahf.compliance.excluded-check-ids --value <id>

ahf_properties: {ahf.compliance.excluded-check-ids: "<id>"}

Compliance scheduler

ahfctl startahf -compliance / ahfctl stopahf -compliance

ahf_compliance_autostart: true / false

Schedule and retention of a Compliance autorun

orachk -set "AUTORUN_SCHEDULE=30 1 * * 1-6;COLLECTION_RETENTION=30" -id autostart_client_oratier1

ahf_compliance_settings: {autostart_client_oratier1: {AUTORUN_SCHEDULE: "30 1 * * 1-6", COLLECTION_RETENTION: 30}}

Additional directory for collections

tfactl directory add <dir> -public -noexclusions (asks for component, database, instance)

ahf_tfa_directories: [{path: <dir>, component: RDBMS, database: <db>, instance: <sid>, collection: noexclusions}]

TFA daemon

tfactl enable + ahfctl startahf -tfa / ahfctl stopahf -tfa + tfactl disable

ahf_tfa_enabled: true / false

Registration in Fleet Insights

ahf configuration set --type fleet-insights --user-name <user> --url <url> --public-cert <file> --os-user oracle

ahf_fleet_insights_client: true, ahf_fleet_insights_url, ahf_fleet_insights_user, ahf_fleet_insights_password, ahf_fleet_insights_public_cert

CPU and memory limit of TFA

ahfctl setresourcelimit -value 2

not supported, see Resource requirements

The role executes a setting only, when it differs from the last run (state file ahf_ansible_state_file). Manual changes on the CLI are not detected - -e ahf_settings_force=true executes all settings again.

The data is processed about 2 minutes after the upload. A new client shows its databases after the first Insights collection. The role ahf does not start a collection - start one manually, when the data is needed immediately.

6.2.5.1. Log files and diag_dest of the databases

TFA discovers the directories of Grid Infrastructure, ASM, listeners and the running databases automatically, including the ADR (diag_dest) of each database with the database and instance name:

Directories discovered by TFA (example from Oracle Restart, shortened)
tfactl print directories
| /u01/app/oracle/diag/rdbms/dbhas/DBHAS/trace | [RDBMS]{RDBMS|database=dbhas, RDBMS|instance=DBHAS} | public | root |
| /u01/app/oracle/diag/asm/+asm/+ASM/trace     | [ASM]{ASM|instance=+ASM}                            | public | root |

Directories, which are not discovered (e.g. audit files or log files of an application), are added with ahf_tfa_directories. The role answers the questions of tfactl directory add (component, database, instance) and removes directories, which it has added and which are no longer in the list.

By default the clients upload only minimal files into Fleet Insights: the Insights data and the Compliance results. Diagnostic collections with the log and trace files are uploaded only with ahf.fleet-insights.upload-only-minimal-files: false.

Note
The property ahf.fleet-insights.upload-only-minimal-files is not documented by Oracle. The behavior is observed with AHF 26.10: with true (default) a tfactl diagcollect -database is not uploaded, with false it is uploaded. Complete collections need more space in the repository database and on the Fleet Insights server.
Upload of log files and an additional directory
ahf_properties:
  ahf.fleet-insights.upload-only-minimal-files: "false"

ahf_tfa_directories:
  - path: /u01/app/oracle/admin/DB1/adump
    component: RDBMS
    database: db1
    instance: DB1
    collection: noexclusions

The properties of AHF are set with ahf configuration set --property <name> --value <value> (ahf_properties). ahf configuration get --help lists the supported properties, ahf configuration get --property <name> shows the value.

6.2.6. Check the upload on the client

ahf configuration check --type fleet-insights tests the upload of a client into Fleet Insights. The commands for the clients and the Fleet Insights server, the manual start of a collection with upload and the known errors are described in Troubleshooting.

6.3. Alternative: static web server with AHF Insights

This variant is not implemented in ansible-oracle. It is an option for environments without a repository database.

AHF Insights reports are offline HTML reports (zip file with index.html) - they need no server component.

  1. A systemd timer on the database server creates a report and copies it to the web server:

    # /etc/systemd/system/ahf-insights-push.service
    [Unit]
    Description=Create AHF Insights report and push it to the web server
    
    [Service]
    Type=oneshot
    ExecStart=/opt/oracle.ahf/bin/ahf analysis create --type insights --last 24h
    ExecStart=/usr/bin/rsync -a --include='*/' --include='*insights*.zip' --exclude='*' \
      /u01/app/oracle/ahf/oracle.ahf/data/repository/ ahfpush@web.example.com:
    # /etc/systemd/system/ahf-insights-push.timer
    [Timer]
    OnCalendar=daily
    RandomizedDelaySec=1h
    Persistent=true
    
    [Install]
    WantedBy=timers.target
  2. The SSH key of the database server is restricted to a directory on the web server with rrsync (command="rrsync -wo /srv/ahf/<host>" in authorized_keys).

  3. The web server (Nginx/httpd) extracts the reports (e.g. with a systemd path unit) and serves the directories as static files.

A systemd timer is preferred to cron: random delay (RandomizedDelaySec), catch-up after downtime (Persistent) and the output in the journal.

Table 13. Comparison of the variants
Criteria AHF Fleet Insights Static web server

Product

Oracle, supported

own solution

Data

Compliance results and TFA collections, aggregated

single Insights reports per host and day

Requirements

repository database, Java, Instant Client, 16 GB RAM

web server, SSH/rsync

Transport

HTTPS by the AHF client

rsync over SSH

Overview of the fleet

yes

no - one report per host

The Health Check Collection Manager (APEX application for orachk -setdbupload) is an older alternative for Compliance results only. It is not supported by ansible-oracle.

7. Troubleshooting

The commands are executed as root on the database server (/opt/oracle.ahf/bin with the default ahf_loc) and as ahf_fi_install_user in ahf_fi_install_dir on the Fleet Insights server.

7.1. Database server (AHF client)

7.1.1. Status

Table 14. Status of AHF on the database server
Command Description

ahfctl statusahf

Status of TFA (PID, port, version) and the Compliance scheduler

ahf software get-version --component all

Installed versions of AHF, TFA and Compliance

tfactl print config

Settings of TFA (ahf_tfa_settings)

tfactl print directories

Trace directories discovered by TFA, e.g. CRS, ASM, RDBMS with Oracle Restart

ahfctl getresourcelimit

CPU and memory limits of TFA

orachk -get all -id all

Compliance autoruns with AUTORUN_SCHEDULE and COLLECTION_RETENTION (ahf_compliance_settings)

cat /opt/oracle.ahf.ansible.json

Settings and registration applied by the role ahf (ahf_ansible_state_file)

7.1.2. Connection to Fleet Insights

Test the upload into Fleet Insights
ahf configuration check --type fleet-insights

The command uploads a test file (TestUploadFile.txt) into Fleet Insights and checks the complete path including the certificate:

  • Parameters for 'odbda' are configured correctly (rc 0): the upload works.

  • Parameters are not configured correctly to upload (rc 1): the upload fails. The message has no details, check the known errors below. After a new installation or a new certificate of Fleet Insights, run the role ahf again - it registers the client again, when the certificate has changed. ahf_fleet_insights_force_register: true registers again in any case.

The client has no log of its uploads. Failed uploads of Compliance results are kept and can be uploaded again:

Failed uploads of Compliance results
orachk -checkservicefaileduploads    # list failed uploads
orachk -uploadservicefailed          # upload them again

7.1.3. Collect data and upload it immediately

The registered client uploads Compliance results and TFA collections automatically. For a check in the web interface, a collection can be started manually:

Compliance check with upload (profile ahf: about 1 minute)
orachk -usediscovery -profile ahf -showpass -tag manual_check -readenvconfig

The output ends with <name> successfully uploaded to Autonomous Health Framework Service. Other profiles (orachk -profile -h), e.g. dba or clusterware, take longer. Without -profile, all checks are executed.

TFA collection of the last hour with AHF Insights and upload
tfactl diagcollect -insights -last 1h -silent

The dashboard of Fleet Insights is built from AHF Insights data. The clients collect it at 05:00 and 17:00, -insights uploads it immediately (about 2 minutes until the data is processed). -silent starts the collection in the background without questions. Without it, tfactl diagcollect asks for an event or an event time (e.g. on hosts with Grid Infrastructure). The status is shown by tfactl print actions and tfactl print collections.

7.1.4. Logs and directories

Table 15. Logs on the database server (default ahf_loc / ahf_data_dir)
Path Content

/opt/oracle.ahf/data/<host>/diag

Logs of AHF (requested by Oracle Support for AHF-00005)

/opt/oracle.ahf/data/<host>/orachk/user_root/output

Compliance results (orachk_<host>_<date>_<tag>.zip, HTML report)

/opt/oracle.ahf/data/repository

TFA collections and Insights reports

/opt/oracle.ahf/data/ahf/diag/ahf

Log of the installation (ahf_install_*.log)

7.1.5. Known errors

Table 16. Known errors on the database server
Message Cause Solution

AHF-00014: AHF Data Location …​ is not owned by root in directory hierarchy

ahf_data_dir below a directory of oracle_user, e.g. oracle_base or /u01

ahf_data_dir in a path owned by root (default /opt)

AHF-00026: Perl is not owned by root

ahf_setup uses the Perl of the GI-Home (Oracle Restart, RAC)

ahf_perl_home: /usr (default)

AHF-00074: Required Perl Modules not found

only perl-interpreter is installed

package perl (ahf_packages)

No scheduler for any ID

ahfctl stopahf -compliance removed the autoruns

ahf_compliance_autostart: true, the role recreates them with orachk -autostart

AUTORUN_SCHEDULE is a mandatory field to setup autorun entries

orachk -set without AUTORUN_SCHEDULE or with an unknown autorun ID

the role adds the current schedule, the ID is used without orachk.

CERTIFICATE_VERIFY_FAILED …​ self-signed certificate

certificate of Fleet Insights is unknown to the client

ahf_fleet_insights_public_cert

CERTIFICATE_VERIFY_FAILED …​ Hostname mismatch

certificate of Fleet Insights without SAN or the URL uses another name

ahf_fi_cert_generate / ahf_fi_cert_names on the server, a name of the SAN in ahf_fleet_insights_url

AHF-07275: Restrict the certificate file to the owner only

certificate file readable by others

mode 0600 (done by the role)

AHF-02119: …​ the REST Agent was not started

registration without OS user for the REST access

ahf_fleet_insights_os_user (default oracle_user)

7.2. AHF Fleet Insights server

7.2.1. Status

Table 17. Status of Fleet Insights (as ahf_fi_install_user in ahf_fi_install_dir)
Command Description

./ahffi status

Status of the web application and the collection processing engine

./ahffi info

URL of the web interface and registration command for the clients

./ahffi version

Installed version

./ahffi diagnose

Diagnostics of Fleet Insights

./ahffi start / ./ahffi stop / ./ahffi restart

Start and stop of all processes

crontab -l

Cron entries, which start and monitor the processes (no systemd unit)

ss -ltnp | grep 5005

Nginx listens on ahf_fi_port

Certificate of Fleet Insights (CN and SAN)
echo | openssl s_client -connect <host>:5005 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName -dates

7.2.2. Uploads of the clients

The access log of Nginx shows the registrations and uploads of all clients:

Registrations and uploads in the access log
LOG=<ahf_fi_install_dir>/ahf_service/third_party/nginx/logs/access.log
grep '"POST /ahfservice/register' $LOG                    # registrations
grep '"PUT /ahfservice/v1/collectionstorage' $LOG         # uploads

The uploads contain the hostname of the client in the path, e.g. …​/collections/tfa/<host>/orachk_<host>_<date>_<tag>.zip (Compliance results), …​/PERIODICPERFDATA_srdc_insights_collection_…​zip (Insights data) and checkserviceupload_*.txt / TestUploadFile.txt (tests).

7.2.3. Logs and directories

Table 18. Logs on the Fleet Insights server
Path Content

<ahf_fi_install_dir>/ahf_service/log/manage_app.log

Start, stop, status and importcert

<ahf_fi_install_dir>/ahf_service/third_party/nginx/logs/access.log

Requests of the browsers and the clients

<ahf_fi_install_dir>/ahf_service/third_party/nginx/logs/error.log

Errors of Nginx

<ahf_fi_install_dir>/ahf_service/certs/cert.pem

Certificate of the web interface

<ahf_fi_stage_dir>/tmp/ahf_fleet_insights_log

Log of the installation (removed with the stage directory after the installation)

7.2.4. Known errors

Table 19. Known errors on the Fleet Insights server
Message Cause Solution

Path to Java, 'JAVA_HOME' must be a valid directory

ahf_fi_java_home does not exist. The Oracle JDK RPM installs into a directory with the full version

ahf_fi_java_home: "" (default): the role uses the newest /usr/lib/jvm/jdk-*-oracle-x64

The installer will fail if run with root privileges

installation as root

ahf_fi_install_user (default oracle_user)

web interface not reachable after a reboot

processes are started by cron, crond is not running

systemctl enable --now crond, ./ahffi start (the role starts Fleet Insights, when it is not running)

Collection Processing Engine | Not Running after a reboot, uploads are not processed

the cron jobs of Fleet Insights restart the server and the collection processing engine at the same time. The restart of the engine removes its own cron entry and is interrupted (observed with Fleet Insights 25.2.1)

./ahffi start - starts all processes and adds the cron entries again. The role ahf_fleet_insights does it, when not all processes are running. Check with ./ahffi status and crontab -l (4 entries) after a reboot

clients fail with Hostname mismatch

certificate of the installer has no SAN

ahf_fi_cert_generate: true (default), ahf_fi_cert_names

login with fleet_admin not possible

password forgotten

see Reset the password of fleet_admin

11 - Out-of-Place Patching - Switch ORACLE_HOME

Important
Out-of-Place Patching is supported for Single Instance primary databases without and with Oracle Restart.
The switch of RAC and RAC One Node databases is not supported.
The Grid Infrastructure home of Oracle Restart and of Grid Infrastructure (cluster) 26ai can be switched as well, see Switch of the Grid Infrastructure home (Oracle Restart and cluster).
Terms (Oracle Restart, Grid Infrastructure, RAC): see Featurelist.
The guide describes the concept, all dependencies and the gaps in the current implementation.

Out-of-Place Patching switches a database from its current ORACLE_HOME to another, already installed and patched ORACLE_HOME. The installation and patching of the new ORACLE_HOME is not part of Out-of-Place Patching. It is done before with other roles (oraswdb_install, oraswdb_manage_patches or from a Golden Image).

Advantages compared to In-Place Patching with opatch apply:

  • The new ORACLE_HOME is installed and patched before the switch without downtime, because no database is running from it.

  • The downtime is reduced to a restart of the database and datapatch.

  • Fallback is easy: the old ORACLE_HOME is still available.

Note
A Golden Image is recommended, but not required.
The new ORACLE_HOME can also be installed and patched with ansible-oracle on the database server. The switch of the database is independent from the way the new ORACLE_HOME was created. See Preparation of the new ORACLE_HOME.

1. Requirements

  • Oracle Database 19c or newer

  • Read-Only Oracle Homes (readonly_home: true) are recommended, but not required

    • Configuration files like SPFile and password file are not part of the ORACLE_HOME and are shared between all Read-Only `ORACLE_HOME`s.

    • The switch copies SPFile, PFile and password file, when the location differs between old and new ORACLE_HOME (e.g. switch from a Read-Write into a Read-Only ORACLE_HOME).

    • readonly_home must not be changed for an installed ORACLE_HOME.

  • Each Read-Only ORACLE_HOME needs 2 attributes in db_homes_config:

    db_homes_config:
      db1932-si-ee:
        readonly_home: true
        oracle_home_name: db1932siee
  • oracle_home_name is mandatory:

    • It cannot be derived from the key in db_homes_config. Oracle allows only characters, numbers and _ in the name of an ORACLE_HOME in the Oracle Inventory. YAML keys are much less restrictive (e.g. db1932-si-ee is a valid key, but not a valid oracle_home_name).

    • It must be unique over all ORACLE_HOME`s on a server. It defines the `ORACLE_BASE_HOME ($ORACLE_BASE/homes/<oracle_home_name>), which must not be shared between the old and the new ORACLE_HOME.

  • Enough disk space for at least 2 `ORACLE_HOME`s at the same time

2. Read-Only Oracle Home

A Read-Only Oracle Home separates the software from the configuration. The locations are reported by orabasehome and orabaseconfig.

Table 1. Locations in a Read-Only Oracle Home
Location Path Shared between ORACLE_HOMEs Content

ORACLE_HOME

e.g. /u01/app/oracle/product/19/db19_2610

no

Software only

ORACLE_BASE_CONFIG

$ORACLE_BASE/dbs

yes

SPFile, password file, hc_*.dat, lk*

ORACLE_BASE_HOME

$ORACLE_BASE/homes/<oracle_home_name>

no

network/admin (listener.ora, sqlnet.ora, tnsnames.ora), log, rdbms/log

Important
ORACLE_BASE_CONFIG is shared, but ORACLE_BASE_HOME is not.
All files in $ORACLE_BASE/homes/<oracle_home_name>/network/admin must exist in the new ORACLE_HOME before the switch.

3. Process overview

  1. Prepare the new ORACLE_HOME (no downtime)

    1. Install the new ORACLE_HOME with oraswdb_install / oraswdb_manage_patches (not part of Out-of-Place Patching), one of:

      1. from a Golden Image (recommended)

      2. from the base release, followed by opatch apply for all patches

    2. Create the network configuration in the new ORACLE_BASE_HOME

  2. Switch the database (downtime) - Out-of-Place Patching with oradb_manage_db

    1. Stop the database (and listener) from the old ORACLE_HOME

    2. Change the ORACLE_HOME in /etc/oratab or in Oracle Clusterware (srvctl)

    3. Start the listener and database from the new ORACLE_HOME

    4. Execute datapatch

  3. Post tasks

    1. APEX, time zone, statistics, etc. (optional)

    2. Remove the old ORACLE_HOME after a successful test period

4. Preparation of the new ORACLE_HOME

The new ORACLE_HOME can be prepared in 2 ways. Both are supported by ansible-oracle and both lead to the same result for the switch.

Table 2. Ways to prepare the new ORACLE_HOME
Topic Golden Image Install and patch on the server

Source

Golden Image (imagename or filesource in db_homes_config)

Base release (e.g. 19.3) + patches from oracle_sw_patches

Patching

Included in the Golden Image. Additional patches are possible like for the base release.

oraswdb_manage_patches with opatch/opatchauto from db_homes_config

Duration on the database server

Short (unzip + runInstaller)

Long (opatch apply for each patch)

Identical `ORACLE_HOME`s on all servers

Guaranteed by the Golden Image

Depends on identical patch lists in the inventory

Software needed on the server

Golden Image only

Base release, OPatch and all patches

Relink options (e.g. Unified Auditing, dNFS)

Included in the Golden Image, checked by oraswdb_install

Set by oraswdb_install

Downtime

None

None - no database is running from the new ORACLE_HOME, stop_processes is not needed

Recommendation: download the Golden Image created by Oracle with the role autoupgrade (gold_image in autoupgrade_download_patches or autoupgrade_download_gold_image). This is the way with the least effort, because no ORACLE_HOME has to be installed and patched for creating the Golden Image.

Alternative: create the Golden Image once from an ORACLE_HOME installed and patched by ansible-oracle (role oraswdb_golden_image), e.g. when one-off patches are needed, which are not part of the Golden Image from Oracle.

Use the Golden Image for all servers.

Important
The implementation of the switch must not depend on the way the new ORACLE_HOME was prepared.
The only requirement for the switch is an installed and fully patched ORACLE_HOME.

5. Dependencies

5.1. Registration of the ORACLE_HOME

Table 3. Registration of the ORACLE_HOME for a database
Environment Registration Switch

Single Instance without Oracle Restart

/etc/oratab

Replace the line <SID>:<old ORACLE_HOME>:<Y/N> with the new ORACLE_HOME.

Oracle Restart

Oracle Clusterware (OCR/OLR)

srvctl modify database -db <db_unique_name> -oraclehome <new ORACLE_HOME>
/etc/oratab is not maintained correctly by Oracle Clusterware. ansible-oracle writes the new ORACLE_HOME into /etc/oratab (flag N), but never reads it for the state of the database.

Grid Infrastructure (cluster): RAC, RAC One Node

Oracle Clusterware (OCR)

Rolling switch node by node. Not supported.

The following parts of ansible-oracle read the ORACLE_HOME from /etc/oratab and follow the switch automatically:

  • systemd service / manage_oracle_rdbms_procs.sh (start/stop of databases, without Oracle Restart only)

  • ocenv / .profile_<ORACLE_SID>

  • rman_backup.sh from role oradb_rman (with Oracle Restart from the database resource, crsctl)

5.2. Stop and Start

  • The database must be stopped from the old ORACLE_HOME.

  • With Oracle Restart the stop must be done with srvctl from the old ORACLE_HOME, before srvctl modify database is executed.

  • The start is done from the new ORACLE_HOME.

5.3. Listener

  • A listener running from the database ORACLE_HOME must be restarted from the new ORACLE_HOME.

  • All databases served by this listener must be switched together. Recommendation: run the listener from the Grid Infrastructure home of Oracle Restart or from a dedicated ORACLE_HOME.

  • Static registrations in SID_LIST_<LISTENER> contain the ORACLE_HOME:

    • Data Guard Broker (<db_unique_name>_DGMGRL)

    • extproc

    • Static service entries

5.4. Network configuration and wallets

  • sqlnet.ora, tnsnames.ora and listener.ora must exist in $ORACLE_BASE/homes/<new oracle_home_name>/network/admin. Alternative: a central TNS_ADMIN.

  • WALLET_LOCATION or ENCRYPTION_WALLET_LOCATION in sqlnet.ora must not point into the old ORACLE_HOME.

  • TDE with the init parameter WALLET_ROOT is independent from the ORACLE_HOME.

  • local_listener must not be an alias from tnsnames.ora of the old ORACLE_HOME. DBCA sets local_listener to LISTENER_<DB_NAME> and writes this alias into tnsnames.ora of the ORACLE_HOME. The alias is missing in the new ORACLE_HOME, the instance does not register at the listener (ORA-12514). orasw_meta requires local_listener with EZConnect in init_parameters of Single Instance databases, e.g. //localhost:1521 (see breaking_changes.adoc). Other entries from DBCA in tnsnames.ora of the old ORACLE_HOME are not copied.

5.5. datapatch

  • datapatch is mandatory after the start from the new ORACLE_HOME.

  • datapatch must be executed for the CDB and all PDBs.

  • OJVM patches may need a startup in upgrade mode, depending on the release.

  • Data Guard: datapatch is executed on the primary database only.

5.6. Directory objects

Directory objects can point into the old ORACLE_HOME. The OPatch directories are changed by datapatch, own directories are not.

Find directory objects in the old ORACLE_HOME
select directory_name, directory_path
from dba_directories
where directory_path like '/u01/app/oracle/product/19/db19_2601%';

5.7. Content of the ORACLE_HOME

The new ORACLE_HOME must contain all customizations of the old ORACLE_HOME:

  • Relink options (e.g. Unified Auditing, dNFS) - included in a Golden Image or set by oraswdb_install

  • One-off patches - included in a Golden Image or defined in db_homes_config[].opatch for the new ORACLE_HOME

  • oranfstab

  • Custom DBCA templates

5.8. Data Guard

  • The standby databases are switched first.

  • The primary database is switched afterwards, followed by datapatch.

  • Static listener registrations for the Data Guard Broker must be changed (see Listener).

5.8.1. Physical Standby

Important
Not implemented yet.
The current implementation opens every database after the switch and executes datapatch.

A physical standby database must be handled differently from a primary database:

  • The standby database is started in MOUNT only. Opening the database while Redo Apply is running uses Active Data Guard, which needs an extra license. This is valid even when the standby database was opened READ ONLY WITH APPLY before the switch.

  • datapatch must not be executed on a standby database. The SQL changes are applied with Redo Apply from the primary database.

The role of the database is read after startup mount from the controlfile:

startup mount
select database_role from v$database;
Table 4. Actions after startup mount depending on DATABASE_ROLE
DATABASE_ROLE Open database datapatch

PRIMARY

yes (+ all PDBs)

yes

PHYSICAL STANDBY

no, remains in MOUNT

no

Redo Apply after the switch:

  • With Data Guard Broker (dg_broker_start=TRUE) the broker restarts Redo Apply automatically.

  • Without Data Guard Broker Redo Apply must be restarted manually. ansible-oracle does not start Redo Apply, because it does not manage Data Guard configurations.

    alter database recover managed standby database disconnect;

5.9. External dependencies

  • Enterprise Manager and other monitoring targets

  • Scripts and crontabs with a hard coded ORACLE_HOME

  • Applications with a local ORACLE_HOME (e.g. Bequeath connections)

6. Implementation in ansible-oracle

6.1. Existing features

Out-of-Place Patching itself is the switch of the ORACLE_HOME in oradb_manage_db. The other features prepare the new ORACLE_HOME and are used before the switch.

Table 5. Existing features used for Out-of-Place Patching
Feature Role Comment

Read-Only Oracle Home

oraswdb_install

readonly_home: true and oracle_home_name in db_homes_config

Install from base release

oraswdb_install

Without imagename in db_homes_config

Patch the new ORACLE_HOME

oraswdb_manage_patches

opatch/opatchauto in db_homes_config, apply_patches: true in db_homes_installed. No stop_processes needed, because no database is running from the new ORACLE_HOME

Download Golden Image from Oracle (recommended)

autoupgrade (download_patches.yml)

gold_image in autoupgrade_download_patches or autoupgrade_download_gold_image

Create Golden Image (optional)

oraswdb_golden_image

Install from Golden Image (optional)

oraswdb_install

imagename or filesource in db_homes_config

Relink options

oraswdb_install

Enforced for every ORACLE_HOME, independent from the source

Network configuration

oradb_manage_db

Files are created in ORACLE_BASE_HOME of the ORACLE_HOME of the listener (listener_installed[].home)

datapatch

oradb_datapatch

Remove ORACLE_HOME

oraswdb_install

state: absent in db_homes_installed

Switch ORACLE_HOME of database

oradb_manage_db (switch_home.yml)

Change of oracle_databases[].home. Single Instance without and with Oracle Restart, not Grid Infrastructure (cluster). Stop from old ORACLE_HOME, copy of SPFile/PFile/password file (when the location differs), change of /etc/oratab, start from new ORACLE_HOME, datapatch. Oracle Restart: stop/start with srvctl, srvctl modify database -oraclehome (and -spfile/-pwfile, when copied), start with the start option of the resource, datapatch for the role PRIMARY only. Flag file $ORACLE_BASE/switch_home_<SID>.pending for an incomplete switch.

Switch ORACLE_HOME of listener

oradb_manage_db (loop_listener_details.yml)

Change of listener_installed[].home. Stop from old ORACLE_HOME, start from new ORACLE_HOME.

/etc/oratab without duplicate entries

oradb_manage_db (manage-db.yml)

lineinfile with regexp replaces the entry for the SID. With Oracle Restart the entry <db_unique_name>:<ORACLE_HOME>:N is written as well.

ORACLE_HOME without database

orasw_meta, oraswdb_install

An ORACLE_HOME in db_homes_installed without a database in oracle_databases is valid (e.g. new ORACLE_HOME before the switch, dedicated listener home).

ORACLE_HOME of listener, tnsnames and sqlnet must be installed

orasw_meta (assert_sqlnet.yml)

home in listener_installed, sqlnet_installed and tnsnames_installed must be in db_homes_installed without state: absent (unless state: absent or use_gi_home: true for the entry).

local_listener with EZConnect

orasw_meta (assert_oracle_databases.yml)

local_listener is mandatory in init_parameters of Single Instance databases with state: present.

Switch Grid Infrastructure home of Oracle Restart

oraswgi_install (switch_home.yml)

Change of oracle_home_gi. Oracle Restart 26ai only. See Switch of the Grid Infrastructure home (Oracle Restart and cluster).

6.2. Gaps

Table 6. Missing features for Out-of-Place Patching
Gap Location Description

Incomplete check of oracle_home_name

roles/oraswdb_install/tasks/roohctl.yml

Only the existence of oracle_home_name is checked for readonly_home: true, and only during the installation. Missing: check for allowed characters, unique value over all ORACLE_HOME`s in `db_homes_installed, check in orasw_meta before any change on the server.

6.3. ORACLE_HOME without database

An ORACLE_HOME in db_homes_installed without a database in oracle_databases is a valid configuration:

  • The new ORACLE_HOME has no database before the switch.

  • The old ORACLE_HOME has no database after the switch, until it is removed.

  • Listeners can run from a dedicated ORACLE_HOME without database.

The oracle_db_type must still be identical for all databases in the same ORACLE_HOME. All roles and asserts must support an ORACLE_HOME without database.

7. Switch of the Grid Infrastructure home (Oracle Restart and cluster)

The Grid Infrastructure home of Oracle Restart and of Grid Infrastructure (cluster) is switched by oraswgi_install, when oracle_home_gi differs from crs_home in /etc/oracle/olr.loc. The new home is created from oracle_install_image_gi, normally a gold image of the new Release Update.

Important
Supported for Oracle Restart 26ai and Grid Infrastructure (cluster) 26ai inside the same release (e.g. 23.26.3 ⇒ 23.26.4).
Not supported for 19c: -switchGridHome is not supported by Oracle for Oracle Restart 19c (INS-45101, MOS 2764906.1), 19c Grid Infrastructure (cluster) is not implemented.
Grid Infrastructure (cluster): tested with 2 nodes and a RAC database (molecule scenario rac, side_effect.yml).
Table 7. Steps of oraswgi_install (switch_home.yml)
Step User Description

Extract oracle_install_image_gi into oracle_home_gi

grid

Without downtime.
Grid Infrastructure (cluster): on every node.

gridSetup.sh -silent -setupHomeAs <old home>

grid

Software only installation with Oracle base and groups of the old home. Registers the new home in the central inventory.
Without it, -switchGridHome fails with INS-45127. Without downtime.
Grid Infrastructure (cluster): on every node, one after the other. -setupHomeAs works on the local node only, -switchGridHome needs the new home on all nodes (INS-42016 otherwise).

gridSetup.sh -silent -switchGridHome

grid

Prepares the switch. Without downtime.
Grid Infrastructure (cluster): on the primary node (1st host of orasw_meta_cluster_hostgroup).

root.sh of the new home

root

Oracle Restart: roothas.pl -prepatch/-postpatch -dstcrshome <new home>: stops the stack in the old home and starts it in the new home.
Downtime of ASM, the listeners in the Grid Infrastructure home and all databases managed by Oracle Restart.
The databases are started with their start option. oracle-ohasd.service and the +ASM entry in /etc/oratab point to the new home.
Grid Infrastructure (cluster): rootcrs.pl -prepatch/-postpatch -dstcrshome <new home>, rolling: primary node first, then the other nodes one after the other (throttle: 1). Only the instances of the node in progress are stopped, the RAC databases stay available. rootcrs.pl -prepatch runs cluvfy stage -pre patch: /etc/oraInst.loc must be owned by root (PRVG-2031/PRVG-2032), oraswgi_install sets the owner before root.sh.

runInstaller -updateNodeList

grid

CRS=TRUE for the new home, CRS=FALSE for the old home in the central inventory (not changed by root.sh).
Grid Infrastructure (cluster): in the central inventory of every node (-local).

gi_ignoreprereq: true adds -ignorePrereqFailure to gridSetup.sh and -ignoreSysPrereqs to runInstaller.

Listeners running from the Grid Infrastructure home (e.g. LISTENER) are moved with the stack, listener.ora is copied into the new home. Listeners and databases in database homes are not changed.

The old Grid Infrastructure home is not removed by the switch.

Oracle Restart: a switch back to the old home is done in the same way by setting oracle_home_gi to the old home.

Grid Infrastructure (cluster):

  • A switch back into a former home is not implemented: the former running home is locked by root.sh (e.g. bin/crsctl.bin owned by root), oraswgi_install stops with an assert. Use a new oracle_home_gi.

  • When root.sh fails before the switch (e.g. the CVU check of rootcrs.pl -prepatch), the next run resumes with root.sh: -switchGridHome is skipped, when rootconfig.sh of the new home is already owned by root.

7.1. Removal of obsolete Grid Infrastructure homes

oraswgi_install_remove_obsolete_homes: true removes obsolete Grid Infrastructure homes with deinstall. Without it, old homes stay for a switch back.

The source is the central inventory only. Obsolete is every home in the central inventory

  • with the component oracle.crs (1st component in <home>/inventory/ContentsXML/comps.xml), database homes are never removed,

  • which is not crs_home in /etc/oracle/olr.loc and not oracle_home_gi.

Homes or directories, which are not registered in the central inventory, are not touched and must be removed manually. The removal is only done, when Oracle Restart runs from oracle_home_gi (completed switch).

Table 8. Steps of oraswgi_install (remove_obsolete_home.yml) for each obsolete home
Step User Description

<old home>/deinstall/deinstall -silent -checkonly -o <dir>

grid

Creates the response file. The role stops with an error, when deinstall detects a configuration in the home:
CRS_HOME=false, ASM_IN_HOME=false, ASM_DROP_DISKGROUPS=false and ORA_CRS_HOME=<running home> are required, the output must contain The home being deconfigured is NOT a configured Grid Infrastructure home.

<old home>/crs/install/roothas.sh -unlock -dstcrshome <old home>

root

Only for a locked home (owner root), a former running home.

<old home>/deinstall/deinstall -silent -paramfile <response file>

grid

Removes the software and the home from the central inventory. No downtime.

rootdeinstall.sh

root

Only, when requested in the output of deinstall. Removes the files owned by root and the directory of the home.

Note
deinstall of an obsolete home prints Oracle Restart is stopped and de-configured successfully and requests rootdeinstall.sh "to remove the configuration of the Oracle Restart". This is the standard text of deinstall: the running Oracle Restart in crs_home is not changed (tested with 23.26.3 ⇒ 23.26.4: resources, /etc/oracle/olr.loc, oracle-ohasd.service, +ASM in /etc/oratab and listener.ora of the running home unchanged).
Note
Oracle documents deinstall of the home itself (same release) after an unlock for the previous release Grid home after an upgrade ("Unlocking and Deinstalling the Previous Release Grid Home"). The removal of an old home of the same release after -switchGridHome is not described explicitly.
Inventory example
oracle_install_option_gi: HA_CONFIG
oracle_install_version_gi: 26.1.0.0
# 1st installation: gi_goldimage_26_03.zip, /u01/app/oracle/product/26/restart_26.03
oracle_install_image_gi: gi_goldimage_26_04.zip
oracle_home_gi: /u01/app/oracle/product/26/restart_26.04

8. Inventory example

The ORACLE_HOME for each Release Update gets its own entry in db_homes_config with a unique oracle_home and oracle_home_name. Both ways to prepare the new ORACLE_HOME can be mixed, e.g. the old ORACLE_HOME from a Golden Image and the new one installed and patched on the server.

Example db_homes_config with Golden Image
db_homes_config:
  db19_2601:
    version: 19.3.0.0
    oracle_home: /u01/app/oracle/product/19/db19_2601
    oracle_home_name: db19_2601
    readonly_home: true
    edition: EE
    imagename: db_home_19.30.zip
Example db_homes_config installed and patched on the server
db_homes_config:
  db19_2604:
    version: 19.3.0.0
    oracle_home: /u01/app/oracle/product/19/db19_2604
    oracle_home_name: db19_2604
    readonly_home: true
    edition: EE
    opatch_minversion: 12.2.0.1.48
    opatch:
      - patchid: <RU patch id>
        # Database Release Update 19.31
        state: present
      - patchid: <OJVM patch id>
        # Oracle JavaVM Component Release Update 19.31
        state: present
Note
stop_processes is not needed for the patches of the new ORACLE_HOME.
Example: switch of database ORCL from db19_2601 to db19_2604
db_homes_installed:
  - home: db19_2601
    state: present
  - home: db19_2604
    state: present

oracle_databases:
  - home: db19_2604    # (1)
    oracle_db_name: ORCL
    state: present
  1. Changing home is the trigger for the switch. See Existing features.

9. Open questions

  • oraswdb_install derives the type of an ORACLE_HOME (SI or cluster installation) from the databases in oracle_databases. An ORACLE_HOME without database is installed as SI. A new RAC ORACLE_HOME needs the cluster installation before the switch ⇒ explicit type in db_homes_config (e.g. oracle_db_type)?

  • Handling of a listener shared by databases in different `ORACLE_HOME`s

  • Fallback to the old ORACLE_HOME (datapatch rollback)

12 - Standby Databases

Important
Standby databases are experimental in ansible-oracle (Issue #65).
Implemented: inventory structure with asserts, creation of a physical standby database and of the Data Guard Broker configuration (role oradb_standby). See Open Points and TODOs for missing features. Variable names and attributes may change.

The inventory structure is shared by different standby technologies:

  • Oracle Data Guard (type: dataguard)

  • Dbvisit StandbyMP (type: dbvisit) - reserved, not planned for the first implementation

Attributes that apply to every technology are defined at the top level. Attributes for one technology are placed in a sub-dictionary named after it (dg for Data Guard, dbvisit for Dbvisit). No additional variables are needed when a new technology is added.

Only Oracle Database 19c and newer are supported.

1. Overview

Variable Location Description

db_standby_config

group_vars of all members

Standby configurations with technology, members and topology

oracle_databases[].standby_config

host_vars

Reference from a database to an entry in db_standby_config

oracle_databases[].oracle_db_unique_name

host_vars

db_unique_name of the local member

oracle_tnsnames_config / tnsnames_installed

group_vars

TNS aliases for redo transport, broker and RMAN duplicate

oracle_listeners_config / listener_installed

group_vars / host_vars

Listener with static entries for the instances

db_standby_config must be identical on all members of a configuration. Each member needs the complete topology, e.g. for log_archive_config, fal_server or ADD DATABASE in the broker. The topology is never collected from hostvars of other hosts. Only the current role of the other members (database_role from oradb_facts) is read from hostvars.

2. db_standby_config

List of standby configurations.

Table 1. Attributes of a standby configuration
Attribute Mandatory Description

name

yes

Name of the configuration. Referenced by oracle_databases[].standby_config. Used as name of the broker configuration for Data Guard.

type

yes

dataguard or dbvisit. No default value.

members

yes

List of members, see below.

dg

dataguard: for archivelog_deletion_policy

Data Guard attributes of the configuration. Only allowed with type: dataguard. Not needed, when archivelog_deletion_policy is defined in each member.

dbvisit

no

Dbvisit attributes of the configuration. Only allowed with type: dbvisit.

Table 2. Attributes of a member
Attribute Mandatory Description

hosts

yes

List of inventory_hostname of the member. More than 1 host for RAC.

db_unique_name

dataguard: yes, dbvisit: no

db_unique_name of the member. Default for dbvisit: oracle_db_name.

initial_role

yes

primary or physical_standby. Used during creation only, see initial_role.

dg

no

Data Guard attributes of the member. Only allowed with type: dataguard.

dbvisit

no

Dbvisit attributes of the member. Only allowed with type: dbvisit.

2.1. Data Guard attributes (dg)

Table 3. Configuration (db_standby_config[].dg)
Attribute Default Description

protection_mode

MaxPerformance

MaxPerformance, MaxAvailability or MaxProtection

create_method

rman_duplicate_active

Default method for the creation of the standby databases, see Creation of a standby database

create_tasks_file

-

Tasks file for create_method: custom

archivelog_deletion_policy

- (mandatory here or in each member)

RMAN CONFIGURE ARCHIVELOG DELETION POLICY TO <value> of all members, see Archivelog deletion policy

Table 4. Member (db_standby_config[].members[].dg)
Attribute Default Description

connect_identifier

-

TNS alias from oracle_tnsnames_config. Used for redo transport and as broker property DGConnectIdentifier.

log_xpt_mode

ASYNC

Broker property LogXptMode: ASYNC, SYNC or FASTSYNC

delay_mins

0

Broker property DelayMins (delay of redo apply in minutes)

reopen_secs

300

Broker property ReopenSecs: seconds until the redo transport to this member is retried after an error, e.g. while the standby database is restarted during a switchover

create_method

create_method of the configuration

Method for the creation of this standby database

create_tasks_file

create_tasks_file of the configuration

Tasks file for create_method: custom

archivelog_deletion_policy

archivelog_deletion_policy of the configuration

RMAN CONFIGURE ARCHIVELOG DELETION POLICY TO <value> of this member

Note
MaxAvailability and MaxProtection need SYNC or FASTSYNC for at least one standby member.

2.2. Dbvisit attributes (dbvisit)

Reserved for a later implementation.

3. oracle_databases

A database becomes a member of a standby configuration with the attribute standby_config.

Table 5. Additional attributes in oracle_databases
Attribute Description

standby_config

name of an entry in db_standby_config. The type is inherited from that entry.

oracle_db_unique_name

Mandatory for type: dataguard. Must match db_unique_name of the local member.

archivelog

Mandatory true for type: dataguard (ARCHIVELOG mode for redo transport and RMAN DUPLICATE).

force_logging

Must not be false for type: dataguard. oradb_standby enables FORCE LOGGING on the primary database, force_logging: false would disable it again.

listener_name

Mandatory for type: dataguard. Adds the static entries for the instance to listener.ora, including <db_unique_name>_DGMGRL (used by RMAN DUPLICATE and the broker). See LISTENER.md.

All technology specific attributes of a database are defined in its member in db_standby_config (e.g. members[].dg.connect_identifier), not in oracle_databases.

4. Assignment of a database to a member

The member of a database is found by the following rules:

  1. Search all members of the referenced configuration with inventory_hostname in hosts.

  2. When more than 1 member matches (e.g. primary and standby on the same host), the member with db_unique_name = oracle_db_unique_name is used.

  3. Exactly 1 member must match. Otherwise the play fails.

Setup Unique by

Data Guard, different hosts

host

Data Guard, same host

host + db_unique_name

Dbvisit (same db_unique_name on all members)

host

RAC

host (more than 1 entry in hosts)

5. initial_role

initial_role is only used when a member is created:

  • primary: the database is created with dbca.

  • physical_standby: the database is created as standby of the primary.

The role is never enforced afterwards. After a switchover or failover the inventory is not changed and ansible-oracle does not switch back. All decisions at runtime are based on database_role from v$database.

oradb_manage_db does not execute oracle_db (dbca) for a database of a standby configuration, when

  • the instance is running and database_role is not PRIMARY, or

  • the instance is not running/mounted and initial_role of the member is physical_standby.

Listener, tnsnames.ora, oratab and the systemd units are configured for all members.

Before dbca, oradb_manage_db creates the missing directories of a database with standby_config (orasw_meta/tasks/standby_prepare.yml): file destinations, audit_file_dest, diagnostic_dest and the directories of dg_broker_config_file1/2 from init_parameters. oradb_manage_parameters sets dg_broker_start=TRUE on the primary before oradb_standby is executed, the broker configuration files must be accessible at this time. Existing directories are not changed (owner, group and mode are kept).

The systemd unit of a database with standby_config uses ORACLE_START_MODE=auto: the database is started in MOUNT and only a PRIMARY database is opened. A physical standby database is never opened read only by the autostart (no Active Data Guard).

Important

Standby databases are supported with the systemd units of oradb_manage_db only (autostartup_service_mode: units, default since 5.0.0).

The legacy solution (oracle-rdbms.service with dbstart) does not know ORACLE_START_MODE=auto: it opens every database, a physical standby database is opened read only (Active Data Guard, separate license).

oradb_facts stores database_role in oracledb_facts[<db_unique_name>]. state is present for a PRIMARY database only and standby for all other roles. All roles which check state == 'present' (e.g. oradb_manage_pdb, oradb_manage_redo, oradb_manage_users) skip a standby database. oradb_manage_parameters changes the init_parameters of oracle_databases on a standby database too (state: standby), but not the parameters of PDBs and without a restart for scope: spfile. A database of a standby configuration that is not reachable (e.g. not created yet) gets state: unreachable without a failure of oradb_facts.

6. Creation of a standby database

The role oradb_standby (playbook manage_db.yml, after oradb_manage_parameters, only when db_standby_config is not empty) creates a missing standby database: a member with initial_role: physical_standby without an SPFile of the instance. An existing database is never changed by oradb_standby.

Table 6. create_method
Method Description

rman_duplicate_active

RMAN DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE USING COMPRESSED BACKUPSET from the standby host (default)

external

The standby database is created outside of ansible-oracle. A warning is shown while the database is missing.

custom

The tasks file create_tasks_file is included. The result must be a mounted physical standby database.

The SPFile of the standby database is created by rman_duplicate_active only. With external and custom the SPFile is the responsibility of the DBA.

Steps for rman_duplicate_active and custom:

  1. The primary database is searched in the facts of the other members: hostvars[<host>].oracledb_facts[<db_unique_name>].database_role = PRIMARY (oradb_facts). The hosts of the primary database must be part of the play.

  2. The primary database is prepared over its connect_identifier (idempotent): FORCE LOGGING and standby redo logs (number of online redo log groups + 1 per thread, OMF is needed). log_archive_config, fal_server and log_archive_dest_n are set by the Data Guard Broker (dgmgrl).

  3. The standby database is created with the method.

  4. The result is checked: SPFile exists and database_role = PHYSICAL STANDBY.

Details of rman_duplicate_active:

  • Password file in orabaseconfig/dbs, PFile in $ORACLE_BASE/admin/<db_unique_name>/pfile/init<SID>_duplicate.ora (an existing PFile in dbs is not overwritten), instance in NOMOUNT. The PFile contains db_name and audit_file_dest, diagnostic_dest from init_parameters (when defined). Without them the instance uses the defaults (e.g. $ORACLE_BASE/admin/<db_name>/adump) and the start fails, when these directories cannot be written. The PFile is kept after DUPLICATE for a manual repeat of RMAN DUPLICATE on the host.

  • target: connect_identifier of the primary, auxiliary: //<host>:<port>/<db_unique_name>_DGMGRL (host and port from oracle_tnsnames_config of the connect_identifier of the standby)

  • The SPFile of the primary is cloned (SPFILE PARAMETER_VALUE_CONVERT '<primary db_unique_name>','<standby db_unique_name>'). Different or additional parameters of the standby are read from init_parameters of the standby database in oracle_databases (state: present, scope not memory, sid *) and set with SET. The role sets in addition:

    • db_unique_name of the member (always)

    • control_files: <db_create_file_dest>/<db_unique_name>/controlfile/control01.ctl, unless defined in init_parameters

    • local_listener: empty, unless defined in init_parameters. The value of the primary is usually a TNS alias (e.g. LISTENER_<SID> from dbca), which does not exist on the standby host. An empty local_listener registers the instance at the default listener (local host, port 1521).

  • Missing directories of db_create_file_dest, db_create_online_log_dest_n, db_recovery_file_dest, audit_file_dest, diagnostic_dest, dg_broker_config_file1/2 from init_parameters are created before NOMOUNT. Existing directories are not changed (owner, group and mode are kept). Parameters of the cloned SPFile that are not defined in init_parameters keep the value of the primary (after PARAMETER_VALUE_CONVERT): their directories must exist on the standby host.

  • A failed DUPLICATE stops the auxiliary instance and removes the SPFile created by DUPLICATE. The next run starts a new DUPLICATE.

  • RMAN script duplicate_<db_unique_name>.rman and shell script duplicate_<db_unique_name>.sh in $ORACLE_BASE/admin/<db_unique_name>/pfile, without passwords. The shell script reads the SYS password from the environment variable ORADB_STANDBY_SYS_PASSWORD (or from stdin, when the variable is not set and stdin is not a terminal) and passes the connections with stdin to RMAN (not visible in the process list, connect target * in the logfile). Ansible uses the same scripts and passes the password with stdin, because the environment of a task with become is visible in the process list. Both files are kept for a manual repeat on the host:

    sqlplus / as sysdba <<< "startup nomount pfile='$ORACLE_BASE/admin/<db_unique_name>/pfile/init<SID>_duplicate.ora'"
    read -rs ORADB_STANDBY_SYS_PASSWORD; export ORADB_STANDBY_SYS_PASSWORD
    $ORACLE_BASE/admin/<db_unique_name>/pfile/duplicate_<db_unique_name>.sh
      The logfile is in `oradb_standby_rman_logdir`.
    - Redo Apply is started after `DUPLICATE` (`oradb_standby_start_apply`).
Important
local_listener of the standby database is empty after DUPLICATE, unless it is defined in init_parameters of the standby database in oracle_databases.
The DBA is responsible for the inventory: a listener with a different address or port than the default (local host, port 1521) needs local_listener in init_parameters of the standby database. Otherwise the instance is not registered at the listener and the dynamic services (e.g. <db_unique_name>) are missing.
Tip
Define local_listener with EZConnect in init_parameters of all members, e.g. - {name: local_listener, value: '//dbhost1:1521', scope: both, state: present}.
The primary gets a clean value instead of the alias LISTENER_<SID> from dbca (set by oradb_manage_parameters), the standby gets its own value with SET in RMAN DUPLICATE.

The SYS password is read from dbpasswords[<oracle_db_name>]['sys'] or default_dbpass, like dbca in oradb_manage_db.

7. Archivelog deletion policy

archivelog_deletion_policy is mandatory for type: dataguard: in dg of the configuration (all members) or in dg of each member. The policy decides, when RMAN and the Fast Recovery Area may delete an archive log, e.g. not before it is applied on all standby databases. It belongs to the Data Guard configuration and must be defined consciously for every member.

oradb_standby executes RMAN on every member, primary and physical standby, because the policy is stored in the controlfile of each member:

show archivelog deletion policy;
configure archivelog deletion policy to <archivelog_deletion_policy>;
  • CONFIGURE is only executed when the current policy differs (idempotent). SHOW prints TO DISK instead of TO DEVICE TYPE DISK, both are compared normalized.

  • The instance must be mounted. A missing standby database (e.g. create_method: external) is skipped.

  • The controlfile of a new standby database from RMAN DUPLICATE contains the policy of the primary, the policy of the member is configured afterwards.

  • The warning RMAN-08591: warning: invalid archived log deletion policy (e.g. no standby destination yet) is no error.

Table 7. Valid values (case insensitive)
Value Description

NONE

RMAN default: no deletion policy

APPLIED ON [ALL] STANDBY

Eligible after the archive log is applied on (all) standby databases

SHIPPED TO [ALL] STANDBY

Eligible after the archive log is shipped to (all) standby databases

BACKED UP <n> TIMES TO DEVICE TYPE DISK|SBT

Eligible after <n> backups, can be repeated for several devices

APPLIED ON ALL STANDBY BACKED UP <n> TIMES TO DEVICE TYPE DISK

Combination: APPLIED/SHIPPED first, then BACKED UP

Important
BACKED UP needs an RMAN catalog in a Data Guard configuration: without catalog the controlfile of a member knows only its own backups, e.g. the archive logs of a standby database are never eligible for deletion, when the backups run on the primary.
Example without RMAN catalog
db_standby_config:
  - name: DG1
    type: dataguard
    dg:
      archivelog_deletion_policy: APPLIED ON ALL STANDBY
Example with RMAN catalog and backups on the standby database
db_standby_config:
  - name: DG1
    type: dataguard
    dg:
      archivelog_deletion_policy: APPLIED ON ALL STANDBY BACKED UP 1 TIMES TO DEVICE TYPE DISK

8. Data Guard Broker configuration file

oradb_standby creates a dgmgrl script for each database with type: dataguard: $ORACLE_BASE/admin/<db_unique_name>/pfile/dgmgrl.cfg.

Each member gets its own file with the local database as primary database. The file is valid on the host where the database is the PRIMARY at execution time (e.g. after a switchover the file on the new primary is used). The content depends on the inventory only, not on the current role ⇒ it is identical on every run.

Content
  • CREATE CONFIGURATION '<name>' AS PRIMARY DATABASE IS '<local db_unique_name>' CONNECT IDENTIFIER IS '<connect_identifier>'

  • ADD DATABASE …​ MAINTAINED AS PHYSICAL for all other members

  • EDIT DATABASE …​ SET PROPERTY LogXptMode (log_xpt_mode), DelayMins (delay_mins) and ReopenSecs (reopen_secs) for all members

  • ENABLE CONFIGURATION

  • EDIT CONFIGURATION SET PROTECTION MODE (only when protection_mode is not MaxPerformance)

  • SHOW CONFIGURATION

Usage on the host of the primary database
dgmgrl / "@$ORACLE_BASE/admin/<db_unique_name>/pfile/dgmgrl.cfg"

Requirements: dg_broker_start=TRUE on all members and no existing broker configuration.

For an existing broker configuration oradb_standby compares the properties LogXptMode, DelayMins and ReopenSecs of all members on the host of the primary database (SHOW DATABASE …​ '<property>') and changes only the differing properties (EDIT DATABASE …​ SET PROPERTY).

Important
dg_broker_config_file1, dg_broker_config_file2, dg_broker_start and standby_file_management must be defined in init_parameters of all members (state: present, sid: '*'). orasw_meta checks this with an assert at the start of every play (before oradb_manage_parameters):
init_parameters:
  # before dg_broker_start: the files cannot be changed while the broker is running
  # read-only ORACLE_HOME: ORACLE_BASE/dbs (location of SPFile and password file)
  - {name: dg_broker_config_file1, value: '/u01/app/oracle/dbs/dr1<db_unique_name>.dat', scope: both, state: present, dbca: false}
  - {name: dg_broker_config_file2, value: '/u01/app/oracle/dbs/dr2<db_unique_name>.dat', scope: both, state: present, dbca: false}
  - {name: dg_broker_start, value: 'TRUE', scope: both, state: present, dbca: false}
  - {name: standby_file_management, value: 'AUTO', scope: both, state: present}
  • dg_broker_config_file1/2 must be different and must not be below the ORACLE_HOME: the default ($ORACLE_HOME/dbs) is lost with Out-of-Place Patching. Recommendation:

    • read-only ORACLE_HOME: $ORACLE_BASE/dbs (orabaseconfig, location of SPFile and password file)

    • read-write ORACLE_HOME: $ORACLE_BASE/admin/<db_unique_name>/dgbroker (dbs is below the ORACLE_HOME)

        The directories are created by `oradb_standby`.
      - `dg_broker_start` must be `TRUE`, the value of `standby_file_management` is not checked (e.g. `MANUAL` is allowed).
Warning
dg_broker_config_file1/2 can only be changed while the broker is stopped (dg_broker_start=FALSE), otherwise ORA-16573: attempt to change or access configuration file for an enabled broker configuration.
oradb_manage_parameters changes the parameters in the order of init_parameters ⇒ dg_broker_start must always be defined below dg_broker_config_file1 and dg_broker_config_file2 (checked by the assert in orasw_meta).
When the parameters are added to an existing environment with a running broker, the DBA must stop the broker on each member before the next run of ansible-oracle (alter system set dg_broker_start=FALSE) and copy the existing broker configuration files to the new location, if the configuration should be kept. The value is set by oradb_manage_parameters on the primary and on a mounted standby database (state: standby), a new standby database gets it with SET in RMAN DUPLICATE. oradb_standby executes the file once on the host of the primary database (oradb_standby_broker_create: true, default), after the creation of the standby databases, when
  • the local database is the PRIMARY (oradb_facts),

  • no broker configuration exists (show configuration returns ORA-16532) and

  • all other members are PHYSICAL STANDBY databases (oradb_facts of the hosts of the members). Otherwise a warning is shown, e.g. for a standby database with create_method: external that does not exist yet.

An existing broker configuration is never changed. Errors in the output of dgmgrl (Error:, ORA-, Syntax error, …​ was not found) fail the task, all members must be part of the configuration afterwards. A warning is shown, when the broker is not available (e.g. dg_broker_start not TRUE).

9. Role transitions outside of ansible-oracle

Important
This section describes the planned behavior. Only the current behavior is implemented.

Switchover, failover and Fast-Start Failover are usually executed outside of ansible-oracle (DBA, broker observer). ansible-oracle must handle the result of a role transition on the next run.

Current behavior after a failover
  • The old primary database needs a reinstatement (ORA-16661 in the broker configuration).

  • oradb_standby does nothing: the database of the old primary has an SPFile and initial_role: primary.

  • oradb_manage_db skips oracle_db while the instance of the old primary is running with a role other than PRIMARY.

  • oradb_manage_db does not start the systemd unit of a database that is skipped as standby database (it may not exist yet or be damaged), a warning is shown when the instance is not running.

    Warning
    When the instance of the old primary is not running, its role is unknown and initial_role is primary ⇒ oracle_db in oradb_manage_db would start and open the database (split brain). oradb_manage_db must check the facts of the other members before oracle_db, see Open Points and TODOs.

9.1. Case 1: Reinstate (automatic, not destructive)

The broker reports ORA-16661 for a member ⇒ REINSTATE DATABASE '<member>' on the current primary.

  • Requires Flashback Database on the old primary (flashback: true in oracle_databases).

  • Typical case after a failover, no RMAN DUPLICATE needed.

  • Planned switch: oradb_standby_reinstate.

9.2. Case 2: Missing member (automatic, not destructive)

The database of a member does not exist (e.g. a reinstalled server of the old primary) and another member is the PRIMARY (oradb_facts) ⇒ the member is created as standby database, independent of initial_role. initial_role is only used for the initial setup (no member is PRIMARY).

  • ENABLE DATABASE '<member>' (or ADD DATABASE) in the broker after the creation.

  • oradb_manage_db must use the same rule to skip dbca.

9.3. Case 3: Rebuild of an existing standby database (on request only, destructive)

Most frequent case in practice: a reinstatement is not possible (no Flashback Database) or the standby database is damaged.

The rebuild is never executed automatically. It is requested at runtime with the db_unique_name of the member, never in the inventory (otherwise every run would rebuild the standby database):

ansible-playbook -i <inventory> playbooks/standby_rebuild.yml -e '{"oradb_standby_rebuild": ["DB1_SITE1"]}'

The rebuild uses the current creation of a standby database (create_method: rman_duplicate_active). create_method: custom is not supported for a rebuild at the moment.

Table 8. Steps of the rebuild on the host of the member
Step Description

Checks

The member is not the PRIMARY (local state). Another member is the PRIMARY (oradb_facts). Otherwise the rebuild fails.

Broker

DISABLE DATABASE '<member>' on the primary (no failure, when the member is already disabled, e.g. after a failover).

Autostart

systemctl stop oracle-db@<SID> ⇒ no restart by systemd.

Drop

shutdown abort, startup mount restrict exclusive, SQL DROP DATABASE (data files, redo logs, control files, SPFile with OMF), remove the broker configuration files (dg_broker_config_file1/2). Backups and archived redo logs are not removed: backups of a standby database are valid for the whole configuration. A database that cannot be mounted (e.g. lost control files or data files) is not dropped: the SPFile and the broker configuration files are removed, a warning lists the remaining files below the file destinations of the member.

Creation

The SPFile does not exist ⇒ creation with rman_duplicate_active, see Creation of a standby database.

Broker

ENABLE DATABASE '<member>' (or ADD DATABASE) on the primary, check of the member in SHOW CONFIGURATION.

The playbook standby_rebuild.yml executes oradb_facts and oradb_standby on all hosts of the configuration only: oradb_manage_db is not executed (see the warning above), the hosts of the primary database must be part of the play.

10. Validation

The rules are checked by the role orasw_meta (roles/orasw_meta/tasks/assert_standby.yml). The asserts can be disabled with orasw_meta_assert_db_standby_config: false.

db_standby_config:

  • db_standby_config is a list.

  • name is defined and unique in db_standby_config.

  • type is defined and has an allowed value.

  • Only the sub-dictionary matching type is defined (dg or dbvisit) in the configuration and its members.

  • members is a non empty list.

  • hosts is a non empty list for each member. Each host exists in the inventory.

  • initial_role is primary or physical_standby. Exactly 1 member has initial_role: primary.

  • When the current host is part of a member, at least 1 database in oracle_databases references the configuration.

type: dataguard:

  • db_unique_name is defined for each member and unique in the configuration (case insensitive).

  • protection_mode is MaxPerformance, MaxAvailability or MaxProtection (case sensitive).

  • log_xpt_mode is ASYNC, SYNC or FASTSYNC.

  • delay_mins and reopen_secs are integers >= 0.

  • MaxAvailability and MaxProtection need SYNC or FASTSYNC for at least 1 member with initial_role: physical_standby.

  • connect_identifier is defined for each member and exists in oracle_tnsnames_config.

  • create_method is rman_duplicate_active, external or custom. custom needs create_tasks_file in the member or in the configuration.

  • archivelog_deletion_policy is defined for each member (in the member or in the configuration) and is a valid policy, see Archivelog deletion policy.

oracle_databases with standby_config:

  • dg and dbvisit are not defined.

  • standby_config references an existing entry.

  • Exactly 1 member matches the database, see Assignment of a database to a member.

  • type: dataguard: oracle_db_unique_name and listener_name are defined.

  • type: dataguard: archivelog: true, force_logging is not false.

  • oracle_db_unique_name matches db_unique_name of the member (case insensitive). Both default to oracle_db_name.

  • type: dataguard: init_parameters contain dg_broker_config_file1/2 (different, not below ORACLE_HOME), dg_broker_start=TRUE below both file parameters and standby_file_management.

11. Example: Data Guard with 2 hosts

Tip
A complete example with 2 VMs is the Data Guard Guide (example/dataguard).

Hosts dbhost1 (primary) and dbhost2 (standby) are members of the group dg_db1.

inventory/hosts.yml
all:
  children:
    dg_db1:
      hosts:
        dbhost1:
        dbhost2:
inventory/group_vars/dg_db1/standby.yml
db_standby_config:
  - name: DG1
    type: dataguard
    dg:
      protection_mode: MaxPerformance
      archivelog_deletion_policy: APPLIED ON ALL STANDBY
    members:
      - hosts: [dbhost1]
        db_unique_name: DB1_SITE1
        initial_role: primary
        dg:
          connect_identifier: DB1_SITE1
          log_xpt_mode: ASYNC
      - hosts: [dbhost2]
        db_unique_name: DB1_SITE2
        initial_role: physical_standby
        dg:
          connect_identifier: DB1_SITE2
          log_xpt_mode: ASYNC
          delay_mins: 0

oracle_tnsnames_config:
  DB1_SITE1:
    connect:
      service_name: DB1_SITE1
    address:
      - host: dbhost1
        port: 1521
  DB1_SITE2:
    connect:
      service_name: DB1_SITE2
    address:
      - host: dbhost2
        port: 1521

tnsnames_installed:
  - tnsname: DB1_SITE1
    home: db19-si-ee
    state: present
  - tnsname: DB1_SITE2
    home: db19-si-ee
    state: present
inventory/host_vars/dbhost1/databases.yml
oracle_databases:
  - home: db19-si-ee
    oracle_db_name: DB1
    oracle_db_unique_name: DB1_SITE1
    listener_name: LISTENER
    standby_config: DG1
    init_parameters:
      - {name: local_listener, value: '//dbhost1:1521', scope: both, state: present}
      - {name: dg_broker_config_file1, value: '/u01/app/oracle/dbs/dr1DB1_SITE1.dat', scope: both, state: present, dbca: false}
      - {name: dg_broker_config_file2, value: '/u01/app/oracle/dbs/dr2DB1_SITE1.dat', scope: both, state: present, dbca: false}
      - {name: dg_broker_start, value: 'TRUE', scope: both, state: present, dbca: false}
      - {name: standby_file_management, value: 'AUTO', scope: both, state: present}
    # ... other attributes of oracle_databases
inventory/host_vars/dbhost2/databases.yml
oracle_databases:
  - home: db19-si-ee
    oracle_db_name: DB1
    oracle_db_unique_name: DB1_SITE2
    listener_name: LISTENER
    standby_config: DG1
    init_parameters:
      - {name: local_listener, value: '//dbhost2:1521', scope: both, state: present}
      - {name: dg_broker_config_file1, value: '/u01/app/oracle/dbs/dr1DB1_SITE2.dat', scope: both, state: present, dbca: false}
      - {name: dg_broker_config_file2, value: '/u01/app/oracle/dbs/dr2DB1_SITE2.dat', scope: both, state: present, dbca: false}
      - {name: dg_broker_start, value: 'TRUE', scope: both, state: present, dbca: false}
      - {name: standby_file_management, value: 'AUTO', scope: both, state: present}
    # ... other attributes of oracle_databases

listener_name adds a static entry for the instance to listener.ora (see LISTENER.md). The static entry is needed for RMAN duplicate, because the auxiliary instance is in NOMOUNT.

12. Open Points and TODOs

12.1. Open Points

The following points are not decided yet:

Topic Description

oracle_db_unique_name in host_vars

oracle_databases must be placed in host_vars, because oracle_db_unique_name differs per member. Alternative: derive it in orasw_meta from the member. Then oracle_databases could be placed in group_vars, but all roles must use the derived value.

List vs. dictionary

db_standby_config is a list with name. oracle_listeners_config and oracle_tnsnames_config are dictionaries.

12.2. TODO

  • ❏ Standby redo logs with oradb_manage_redo
    The standby redo logs are created by oradb_standby on the primary database before DUPLICATE (number of online redo log groups + 1 per thread, size of the largest online redo log). They should be managed by oradb_manage_redo on all members (primary and standby), based on redolog_size and redolog_groups in oracle_databases. oradb_manage_redo skips a standby database at the moment.

  • ❏ RMAN backups with oradb_rman
    Backups depending on the current role of the database (e.g. backups on the standby database only), archivelog deletion policy APPLIED ON ALL STANDBY / SHIPPED TO ALL STANDBY, RMAN configuration (db_unique_name in the RMAN catalog).

  • ❏ Support for Oracle Restart
    srvctl add database -role PHYSICAL_STANDBY -startoption MOUNT for the standby database, SPFile and password file in ASM, no systemd units of oradb_manage_db. standby_state.yml and rman_duplicate_active support databases on filesystem without Oracle Restart only.

  • ❏ Management of database services
    Role based services with oradb_manage_services: services for the role PRIMARY or PHYSICAL_STANDBY (srvctl -role with Oracle Restart, a trigger or the broker for databases on filesystem), e.g. a service for the application on the primary database only.

  • ❏ Split brain protection in oradb_manage_db
    oracle_db must not be executed for a member, when another member is the PRIMARY (oradb_facts), see Role transitions outside of ansible-oracle.

  • ❏ Reinstate after a failover (case 1 in Role transitions outside of ansible-oracle), flashback: true in the molecule scenario.

  • ❏ Creation of a missing member independent of initial_role (case 2 in Role transitions outside of ansible-oracle).

  • ❏ Rebuild of a standby database on request (case 3 in Role transitions outside of ansible-oracle): oradb_standby_rebuild, playbooks/standby_rebuild.yml, molecule test: failover, rebuild, switchover back.

  • ❏ Changes of an existing broker configuration
    dgmgrl.cfg is executed only when no broker configuration exists. Changes of properties, protection mode or new members are not applied to an existing configuration.

  • ❏ Out-of-Place Patching
    switch_home.yml in oradb_manage_db starts the database with startup ⇒ opens a standby database read only. Datapatch must run on the primary database only.

  • ❏ RAC
    Members with more than 1 host (hosts) are allowed by the inventory structure, but not supported by oradb_standby.

13 - Breaking Changes - Upgrade Guide

This guide lists all changes in ansible-oracle which require an action in your inventory, playbooks or environment when upgrading to a new major version.

How to use this guide:

  1. Find your current version and the target version.

  2. Read the overview table of every major version in between.

  3. Check the column Affected if against your inventory. Only the rows that match need an action.

Note
The complete list of changes is in CHANGELOG.rst. This guide only covers the changes from the section Breaking Changes / Porting Guide and explains the required actions.

1. Version 5.0.0

1.1. Overview

Change Affected if Action

Minimum ansible-core 2.16

ansible-core 2.14 or 2.15 is used on the controller or in the execution environment

Upgrade to ansible-core 2.16 or newer

Supported operating systems on target hosts

ansible-core 2.17 or newer and target hosts with Oracle Linux / RHEL 8 (Python 3.6)

Use ansible-core 2.16 for these hosts

Collection dependencies community.general and community.crypto

community.general < 11.0.0 or >= 12.0.0, or community.crypto >= 3.0.0 is installed or pinned in your requirements.yml

Use community.general 11.x and community.crypto 2.x

Strict boolean conditionals with ansible-core 2.19

ansible-core 2.19 or newer is used and your own playbooks or roles use variables directly in conditionals

Use | bool in your conditionals. ansible-oracle itself accepts strings like "yes" or -e var=false

Templating changes with ansible-core 2.19

ansible-core 2.19 or newer is used and your inventory contains templates ({% if %}, {{ }}) which create values for ansible-oracle

Check the result types of your templates

Default of oracle_sw_copy and oracle_sw_unpack with install_from_nfs

install_from_nfs: true and oracle_sw_copy or oracle_sw_unpack are not set in inventory

Set oracle_sw_copy: true and oracle_sw_unpack: true to keep the old behavior or test the installation directly from NFS

Firewall enabled by default on RHEL/OL

Target hosts with RHEL/OL and firewalld is installed and disable_firewall is not set in inventory

Check the permanent firewalld config before the upgrade, or set disable_firewall: true and firewall_open_listener_ports: false to keep the old behavior

Supported versions in db_homes_config

db_homes_config contains an ORACLE_HOME with a version other than 19.3.0.0 or 26.1.0.0

Upgrade the databases to 19c or 26ai, then remove the old ORACLE_HOMEs from db_homes_config. Emergency switch: assert_db_homes_config: false

python-oracledb replaces cx_Oracle

Always: the modules require python-oracledb instead of cx_Oracle on the target hosts. Action needed if the target hosts have no access to the Python package index, a proxy is needed (use_proxy, http_proxy), install_cx_oracle / cx_oracle_source / cx_oracle3_source / cx_oracle_umask are set in inventory or the role cxoracle is used in own playbooks

Set the new variables pyoracledb_wheel_dir, pyoracledb_use_proxy and pyoracledb_http_proxy, replace the role cxoracle by pyoracledb

oracle_job, oracle_directory, oracle_grants and oracle_privs: names in double quotes are case sensitive

A name of a profile, user, role, object or directory is enclosed in " by mistake (profiles and grants, object_privs, directory_privs of users in oracle_databases / oracle_pdbs, or the modules in own playbooks), or an existing profile, user, role or object has a name in lower or mixed case

Remove the " from the name, or write the name exactly as stored in the data dictionary in "

oracle_db uses hostname for the database connection

The listener does not listen on oracle_hostname (default: ansible_fqdn), or own playbooks use oracle_db with hostname / host

Set oracle_hostname to an address of the listener, this is also the way to use another listener address

Quotas for users in oracle_user and oradb_manage_users

quotas is set for a user (new option, without quotas no quota is removed or changed), or own playbooks use quotas from the old module PR ('<tablespace>:<size>')

No action without quotas, the quotas of the user are not changed. With quotas: add the default tablespace to quotas, if the user needs a quota there, and use the format {tablespace: <name>, size: <size>}

Internal variables of oraasm_manage_diskgroups removed

Own playbooks or inventories use the internal variables _oraasm_manage_diskgroups_attr_name or _oraasm_manage_diskgroups_attr_value

No action for inventories with asm_diskgroups. Otherwise use attributes of asm_diskgroups

Autostart of databases with one systemd unit per database and listener

autostartup_service: true on a Single Instance without Grid Infrastructure / Oracle Restart with systemd

No action for the new solution, it replaces oracle-rdbms.service with the next run of oradb_manage_db. A crashed database is restarted after 2 minutes, set autostartup_service_restart: false to disable it. Set autostartup_service_mode: legacy before the upgrade to keep the solution of 4.x

Autostart of databases is enabled by default

Single Instance without Grid Infrastructure / Oracle Restart and autostartup_service is not set in the inventory

No action, if the databases should start with the server. Otherwise set autostartup_service: false

init_parameters for one instance: sid and dbca

An entry in init_parameters of oracle_databases has an attribute sid other than *and dbca: true

Remove dbca: true from the entry. The parameter is set by oradb_manage_parameters after the creation of the database

oradb_rman: control_file_record_keep_time in init_parameters

A database in oracle_databases has rman_jobs and control_file_record_keep_time is missing in init_parameters or smaller than the days of RECOVERY WINDOW OF <n> DAYS

Add control_file_record_keep_time to init_parameters. Emergency switch: oradb_rman_assert_control_file_record_keep_time: false

oradb_rman: logfiles of rman_backup.sh

A database in oracle_databases has rman_jobs and the RMAN logfiles are monitored, evaluated or rotated (e.g. checkmk logwatch, logrotate, own scripts), or oradb_rman_cron_logdir / rman_cron_logdir is set

Change the monitoring to the new logfile names in RMAN_LOGDIR, remove oradb_rman_cron_logdir / rman_cron_logdir and the old logfiles

listener_installed with Grid Infrastructure / Oracle Restart: listener in its home

Grid Infrastructure / Oracle Restart and an entry in listener_installed with home

Use use_gi_home: true for a listener, which must stay in the GI / Oracle Restart home. Check the ORACLE_HOME of the listener resources (srvctl config listener)

local_listener in init_parameters of Single Instance databases

A Single Instance database (also with Oracle Restart) in oracle_databases with state: present has no local_listener in init_parameters

Add local_listener with EZConnect to init_parameters, e.g. {name: local_listener, value: '//localhost:1521', scope: both, state: present}. Emergency switch: orasw_meta_assert_oracle_databases: false (disables all asserts of oracle_databases)

oracle_databases: is_container and omf are true by default

A 19c database in oracle_databases without is_container (non-CDB) or without omf (created without OMF)

Set is_container: false for a new non-CDB. OMF is strongly recommended, omf: false needs an own DBCA template without OMF (dbca_templatename) and is not tested

oracle_databases: db_create_file_dest and db_recovery_file_dest in init_parameters

A database in oracle_databases with state: present and without omf: false and db_create_file_dest or db_recovery_file_dest is missing in init_parameters

Add db_create_file_dest and db_recovery_file_dest to init_parameters

oracle_databases: datafile_dest and recoveryfile_dest are removed

A database in oracle_databases with datafile_dest or recoveryfile_dest

Move the values to db_create_file_dest and db_recovery_file_dest in init_parameters and remove datafile_dest and recoveryfile_dest

oracle_dbf_dir_asm and oracle_reco_dir_asm removed

A database in oracle_databases with storage_type: ASM and omf: false, or oracle_dbf_dir_asm / oracle_reco_dir_asm are set in the inventory

Use OMF with ASM (db_create_file_dest and db_recovery_file_dest in init_parameters), remove oracle_dbf_dir_asm and oracle_reco_dir_asm

oracle_databases: max_pdbs in init_parameters of Container Databases

A Container Database (is_container: true, default) in oracle_databases with state: present and max_pdbs is missing in init_parameters or greater than 3 (SE2) / 1024 (EE)

Add max_pdbs with the number of PDBs allowed by your license to init_parameters, e.g. {name: max_pdbs, value: '3', scope: both, state: present}. EE: more than 3 PDBs only with a license for the Multitenant Option

oracle_databases: control_management_pack_access in init_parameters of Enterprise Edition databases

A database of an Enterprise Edition home in oracle_databases with state: present and control_management_pack_access is missing in init_parameters or has a value other than NONE, DIAGNOSTIC or DIAGNOSTIC+TUNING, or a database of a Standard Edition 2 home with a value other than NONE

Add control_management_pack_access with the value allowed by your license to init_parameters, e.g. {name: control_management_pack_access, value: 'NONE', scope: both, state: present}

oracle_databases: sga_target and pga_aggregate_target in init_parameters

A database in oracle_databases with state: present and sga_target or pga_aggregate_target is missing in init_parameters, or oracle_db_mem_totalmb is set

Add sga_target and pga_aggregate_target to init_parameters, remove oracle_db_mem_totalmb

oracle_databases: no memory_target and memory_max_target with Hugepages

A database in oracle_databases with state: present and memory_target or memory_max_target in init_parameters and configure_hugepages is not false

Replace memory_target / memory_max_target by sga_target and pga_aggregate_target (state: absent for the old parameters), or set configure_hugepages: false

oracle_databases: processes in init_parameters

A database in oracle_databases with state: present and processes is missing in init_parameters

Add processes to init_parameters, e.g. {name: processes, value: '640', scope: spfile, state: present}. Usually irrelevant in practice, the default of processes is too small in most cases anyway

Role oraswahf_install replaced by ahf

Own playbooks use the role oraswahf_install

Use the new role ahf (playbook opitzconsulting.ansible_oracle.ahf)

Default of oracle_install_version_gi is 26ai

oracle_install_option_gi is HA_CONFIG or CRS_CONFIG and oracle_install_version_gi is not set in inventory

Set oracle_install_version_gi to the base release of the installation media, e.g. 19.3.0.0 for 19c. Usually irrelevant in practice, the version must match the installation media anyway

1.2. Minimum ansible-core 2.16

ansible-oracle 5.x supports ansible-core 2.16 - 2.19. ansible-oracle 4.x supports ansible-core 2.14 - 2.18, so ansible-core 2.19 requires ansible-oracle 5.x.

ansible-oracle ansible-core

4.x

2.14 - 2.18

5.x

2.16 - 2.19

The minimum version is defined by requires_ansible in meta/runtime.yml and min_ansible_version in meta/main.yml of all roles.

Check the version on the controller or in your execution environment:

ansible --version

1.2.1. Python on controller and target hosts

Each ansible-core version supports a fixed range of Python versions on the controller and on the target hosts:

ansible-core Python on controller Python on target host

2.16

3.10 - 3.12

2.7, 3.6 - 3.12

2.17

3.10 - 3.12

3.7 - 3.12

2.18

3.11 - 3.13

3.8 - 3.13

2.19

3.11 - 3.13

3.8 - 3.13

The Python on the target host is the system Python or the interpreter from ansible_python_interpreter.

1.2.2. Supported operating systems on target hosts

OS System Python ansible-core 2.16 ansible-core 2.17 - 2.19

Oracle Linux / RHEL 8

3.6

yes

no - see below

Oracle Linux / RHEL 9

3.9

yes

yes

Oracle Linux / RHEL 10

3.12

yes

yes

SLES / openSUSE Leap 15

3.6

yes

only with python311 (SLES 15 SP4 or newer) and ansible_python_interpreter: /usr/bin/python3.11 - not tested with ansible-oracle

Important
Use ansible-core 2.16 for Oracle Linux / RHEL 8.
The system Python 3.6 is not supported by ansible-core 2.17 or newer, even fact gathering fails. A newer Python from AppStream (e.g. python3.12 with ansible_python_interpreter: /usr/bin/python3.12) runs most modules, but ansible.builtin.dnf and ansible.builtin.yum fail, because the dnf Python bindings exist for Python 3.6 only: Could not import the dnf python module using /usr/bin/python3.12. The roles common, orahost, oraords and oraswgi_install install packages with these modules. Tested with ansible-core 2.19.14 and Oracle Linux 8.10.

The controller can use another OS than the target hosts. Oracle Linux 8 and 9 provide Python 3.11 and 3.12 as additional packages (python3.11, python3.12) for a controller with ansible-core 2.19.

1.3. Collection dependencies community.general and community.crypto

The dependencies in galaxy.yml are limited to versions, which support the minimum ansible-core version and Python 3.6 on the target hosts:

Collection 4.x 5.0.0

community.general

>=8.6.0

>=11.0.0,<12.0.0

community.crypto

>=2.21.1

>=2.21.1,<3.0.0 (3.x needs ansible-core >= 2.17)

ansible-galaxy collection install fails with a dependency conflict, when your requirements.yml pins a version outside of these ranges. Remove the pinning or adjust it to the ranges above.

1.4. Strict boolean conditionals with ansible-core 2.19

ansible-core 2.19 requires a real boolean as result of a conditional (when, failed_when, changed_when, until, assert: that). Older versions converted strings like "false" or "yes" silently. Now the task fails:

Conditional result (True) was derived from value of type 'str' at '...'. Conditionals must have a boolean result.

ansible-oracle 5.0.0 evaluates all boolean variables from the inventory with | bool. Strings like "true", "yes", "false" or extra-vars like -e oracle_sw_copy=false work as before.

Your own playbooks and roles are affected, when they use variables directly in conditionals:

# fails with ansible-core 2.19, when my_flag is a string ("true", "yes", -e my_flag=true)
when: my_flag

# works with all versions
when: my_flag | bool

The bool filter accepts true/false, yes/no, on/off, 1/0 (case-insensitive). Other values (e.g. "enabled" or an empty string) result in a deprecation warning and false. This will be an error in ansible-core 2.23.

Tip
ANSIBLE_ALLOW_BROKEN_CONDITIONALS=true restores the old behavior temporarily. Use it only to find the affected conditionals, not as permanent solution.

1.5. Templating changes with ansible-core 2.19

ansible-core 2.19 has a new templating engine. The result of a template has a different type in some cases. ansible-oracle 5.0.0 is adapted, but templates in your inventory are used as they are.

Template ansible-core < 2.19 ansible-core 2.19

"{% if x %}True{% else %}False{% endif %}"

bool

string "True" / "False"

"{% if x %}{'key': 1}{% endif %}", "{% if x %}[1, 2]{% endif %}"

dict / list

string

"{% if x %}-flag{% endif %}" with x false (empty result)

empty string ""

None

"{{ 1521 }}"

string "1521"

int 1521

Examples of failing expressions with ansible-core 2.19:

my_flag: "{% if foo %}True{% else %}False{% endif %}"
my_param: "{% if foo %}-flag{% endif %}"
my_port: "{{ 1521 }}"

# when: my_flag              -> fails: string instead of boolean
# when: my_param == ''       -> false, because the value is None
# "{{ my_param | length }}"  -> fails: None has no length
# "{{ 'host:' + my_port }}"  -> fails: string + int
# when: my_port == '1521'    -> false, because the value is an int

Recommendations:

  • Write expressions instead of text: my_flag: "{{ foo | bool }}" or my_flag: "{{ foo == 'x' }}".

  • Use YAML for lists and dictionaries instead of creating them as text.

  • Use | default('', true) when a template can be empty and a string is needed.

  • Use | int or | string for comparisons.

An empty template in environment: sets the environment variable to the text None, e.g. _JAVA_OPTIONS=None lets Java fail with Unrecognized option: None. Use "{{ my_value | default('', true) }}" for environment variables, which can be empty.

Embedding these values into other strings is not affected: "gridSetup.sh {{ my_param }}" renders an empty string for None in all versions.

1.6. Other ansible-core 2.19 changes for own playbooks and inventories

ansible-oracle 5.0.0 is checked for these changes. They are relevant for your own playbooks, inventories and ansible.cfg:

  • File modes must be quoted strings in inventories: mode: '0775' instead of mode: 775. An unquoted 775 is an integer and is passed through templates (for example mode: "{{ item.mode }}") as decimal 775, which is mode 1407. Before 2.19 the template converted the value to the string "775", which was interpreted as octal.

  • with_nested handles strings differently (not mentioned in the porting guide): an empty string "" is an empty list (the task is skipped completely), a templated string "{{ var }}" is split into single characters. Use lists: - [""] or - ["{{ var }}"].

  • A loop over an undefined list fails, although when is false, when delegate_to uses the loop variable (not mentioned in the porting guide). Use loop: "{{ my_list | default([]) }}".

  • Templates inside conditionals fail: that: swap >= {{ min_swap }} must be written as that: swap >= min_swap | int.

  • The ssh connection uses SSH_ASKPASS instead of sshpass for password authentication. Set password_mechanism = sshpass in section [ssh_connection] of ansible.cfg to keep the old behavior.

  • The bool filter warns for values other than true/false, yes/no, on/off, 1/0 (and None). This will be an error in ansible-core 2.23.

  • A timeout of become is an unreachable error. Use ignore_unreachable instead of ignore_errors.

  • The magic variable play_hosts is deprecated, use ansible_play_batch.

The complete list is in the ansible-core 2.19 Porting Guide.

1.7. Default of oracle_sw_copy and oracle_sw_unpack with install_from_nfs

The defaults of oracle_sw_copy and oracle_sw_unpack depend on install_from_nfs. Until 4.x the defaults were the strings "true" or "false". The string "false" is not empty and was evaluated as true in most conditionals. The documented behavior for install_from_nfs: true (no copy, no unpack) was therefore not active, unless both variables were set explicitly in the inventory.

As of 5.0.0 both defaults are real booleans.

install_from_nfs: true and …​ 4.x 5.0.0

oracle_sw_copy/oracle_sw_unpack not set

installation media are copied and unpacked to oracle_stage

installation media are used from oracle_stage_remote (NFS mount)

oracle_sw_copy/oracle_sw_unpack set as boolean

no change

no change

Affected variables with changed default value: oracle_sw_copy, oracle_sw_unpack, oracle_stage_install, oracle_patch_install.

Keep the behavior of 4.x:

install_from_nfs: true
oracle_sw_copy: true
oracle_sw_unpack: true

1.8. Firewall enabled by default on RHEL/OL

Until 4.x the role orahost stopped and disabled firewalld on RHEL/OL by default (disable_firewall: true). As of 5.0.0 firewalld stays active and the role opens the ports of the Oracle listeners:

Variable 4.x 5.0.0

disable_firewall

true

false

firewall_open_listener_ports

- (not available)

true (new)

The change applies only to RHEL/OL target hosts with installed firewalld. SUSE is not changed.

What orahost does with the new defaults:

  1. Checks, if the permanent firewalld config allows the SSH connection of Ansible (service ssh, port and target of the zone). The play fails, when starting firewalld would block SSH. Rich rules and zones bound to sources are not evaluated by this check.

  2. Starts and enables firewalld.

  3. Opens the following TCP ports in the permanent and the running config:

    • all ports from address in oracle_listeners_config with a host (IPC addresses are ignored, listener_port is used for addresses without port),

    • all ports from the new variable oracle_extra_firewall_ports,

    • oracle_scan_port with oracle_install_option_gi: CRS_CONFIG.

Other ports are not opened automatically, e.g. EM Express (5500), ORDS/APEX or the Enterprise Manager agent. Add them to oracle_extra_firewall_ports:

oracle_extra_firewall_ports:
  - 5500  # EM Express
Warning
Oracle RAC needs unrestricted network traffic on the private interconnect. Allow the interconnect in firewalld (for example with a trusted zone for the interconnect interfaces) or set disable_firewall: true on the cluster nodes.

Combinations of both variables:

disable_firewall firewall_open_listener_ports Result

false

true

Default in 5.0.0: firewalld is started and enabled, ports are opened immediately and permanently

true

false

Behavior of 4.x: firewalld is stopped and disabled

true

true

firewalld is stopped and disabled, ports are only added to the permanent config (prepared for a later start)

false

false

firewalld is not changed by orahost

Keep the behavior of 4.x:

disable_firewall: true
firewall_open_listener_ports: false

1.9. Supported versions in db_homes_config

The role orasw_meta checks db_homes_config. It is a dependency of nearly all roles, so the check runs in nearly every playbook. As of 5.0.0 only Oracle 19c and 26ai are accepted as version:

Version Allowed version in db_homes_config

4.x

26.1.0.0, 21.3.0.0, 19.3.0.0, 18.3.0.0, 12.2.0.1, 12.1.0.2, 11.2.0.4, 11.2.0.3

5.0.0

26.1.0.0, 19.3.0.0

version is the base release of the ORACLE_HOME. Release Updates are not part of it, e.g. an ORACLE_HOME with 19.32 uses version: 19.3.0.0.

An ORACLE_HOME with another version stops the playbook with a failed assert, even when the ORACLE_HOME is not used by the playbook. Upgrade the databases to 19c or 26ai, e.g. with the role autoupgrade, and remove the old ORACLE_HOMEs from db_homes_config afterwards.

The assert can be disabled in emergency cases:

assert_db_homes_config: false
Warning
assert_db_homes_config: false disables all checks of db_homes_config, not only the check of the version. Missing oracle_home, edition or opatch_minversion are no longer detected. ORACLE_HOMEs with other versions are not tested with ansible-oracle 5.x.

1.10. python-oracledb replaces cx_Oracle

python-oracledb (Python package oracledb) is the successor of cx_Oracle. All modules of ansible-oracle are migrated from cx_Oracle to python-oracledb. cx_Oracle is no longer used.

The new role pyoracledb installs python-oracledb with pip3 on the target hosts. The playbook os.yml executes pyoracledb instead of cxoracle.

Important
The role cxoracle and its variables are removed. Variables like install_cx_oracle or cx_oracle_source in the inventory have no effect anymore. Own playbooks using the role opitzconsulting.ansible_oracle.cxoracle fail, replace it by opitzconsulting.ansible_oracle.pyoracledb. cx_Oracle is not removed from existing target hosts.

The modules require python-oracledb on the target hosts instead of cx_Oracle. python-oracledb is used in thick mode, so the Oracle Client libraries must be found (ORACLE_HOME and LD_LIBRARY_PATH), like with cx_Oracle before.

Migrated modules:

  • oracle_asmdg

  • oracle_asmvol

  • oracle_awr

  • oracle_db

  • oracle_directory

  • oracle_facts

  • oracle_grants

  • oracle_job

  • oracle_jobclass

  • oracle_jobschedule

  • oracle_jobwindow

  • oracle_ldapuser

  • oracle_parameter

  • oracle_pdb

  • oracle_privs

  • oracle_profile

  • oracle_redo

  • oracle_role

  • oracle_rsrc_consgroup

  • oracle_services (python-oracledb is only used without Grid Infrastructure)

  • oracle_sql

  • oracle_stats_prefs

  • oracle_tablespace

  • oracle_user

The role pyoracledb installs a fixed version of python-oracledb, depending on the Python version of the target host (ansible_python_interpreter, needs gather_facts):

Python on the target host Variable Default

3.6 (Oracle Linux / RHEL 8, SLES 15)

pyoracledb_version_py36

1.4.2

3.7

pyoracledb_version_py37

2.3.0

3.8

pyoracledb_version_py38

3.0.0

3.9 (Oracle Linux / RHEL 9)

pyoracledb_version_py39

4.0.2

all other versions

pyoracledb_version

26.1.0 (updated by renovate)

Without changes in the inventory, pyoracledb installs this version from the Python package index (pip install oracledb==<version>). The installation fails, when the target hosts have no access to the package index.

The wheel files of python-oracledb need pip 19.3 or newer (manylinux2014). The system pip of Oracle Linux / RHEL 8 is 9.0.3, it would try to build python-oracledb from source, which fails. Therefore pyoracledb installs pip>=19.3 first, if pip is older (Python 3.6: pip 21.3.1 in /usr/local).

The role pyoracledb uses own variables with the prefix pyoracledb_. The variables of the removed role cxoracle are not used by pyoracledb, even use_proxy, http_proxy and extra_args:

cxoracle pyoracledb Description

install_cx_oracle

pyoracledb_install

Install the package? Default: true

cx_oracle_source, cx_oracle3_source

pyoracledb_wheel_dir (and pyoracledb_version*)

Directory with wheel files instead of a single package or file. Default: package index

cx_oracle_umask

pyoracledb_umask

umask for the pip installation

use_proxy

pyoracledb_use_proxy

Use a proxy for the pip installation. Default: false

http_proxy

pyoracledb_http_proxy

Proxy for the pip installation

extra_args

pyoracledb_extra_args

Additional arguments for pip, e.g. --index-url=…​ for an internal mirror. The default contains the proxy and the client certificates get_url_ssl_client_cert / get_url_ssl_client_key

cx_oracle_source / cx_oracle3_source pointed to one package or file. pyoracledb_wheel_dir points to a directory with wheel files of python-oracledb, pip selects the file matching the Python version (pip install --no-index --find-links <pyoracledb_wheel_dir> oracledb==<version>). One directory can contain the wheel files for all Python versions of the target hosts (e.g. cp36 and cp39):

  • local directory on the Ansible controller, copied to <oracle_stage>/pyoracledb when is_sw_source_local: true and oracle_sw_copy: true. With oracle_sw_copy: false the directory must exist on the target host (e.g. NFS).

  • URL (http://, https://) of a page with links to the wheel files, used directly by pip

use_proxy, http_proxy and extra_args can be kept in the inventory, if they are used for other purposes.

Example of the migration of the inventory:

# before (cxoracle)
cx_oracle_source: "file:///sw/cx_Oracle-8.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
use_proxy: true
http_proxy: proxy.example:3128

# after (pyoracledb)
pyoracledb_wheel_dir: /sw/python-oracledb
pyoracledb_use_proxy: true
pyoracledb_http_proxy: proxy.example:3128

Download the wheel files for the target hosts on a host with access to the package index, one command per Python version with the matching version from the table above:

pip download --no-deps --only-binary=:all: --python-version 3.6 \
  --platform manylinux2014_x86_64 -d /sw/python-oracledb oracledb==1.4.2
# pip for Python 3.6 (the system pip of OL8 is too old for the wheel files)
pip download --no-deps --only-binary=:all: --python-version 3.6 \
  -d /sw/python-oracledb pip==21.3.1
pip download --no-deps --only-binary=:all: --python-version 3.9 \
  --platform manylinux2014_x86_64 -d /sw/python-oracledb oracledb==4.0.2

Skip the installation of python-oracledb:

pyoracledb_install: false

1.11. oracle_profile, oracle_job, oracle_directory, oracle_grants and oracle_privs: names in double quotes are case sensitive

The modules oracle_profile, oracle_job, oracle_directory, oracle_grants and oracle_privs handle names like SQL (ANSI): a name in double quotes (") is case sensitive, a name without quotes is converted to uppercase. All other modules are not changed. The functions for this are in plugins/module_utils/oracle_identifier.py and can be used by more modules in the future. Such a change will be listed here.

Module Before Now

oracle_profile

The name was not quoted in the SQL statements, but compared case insensitive with DBA_PROFILES. A name in " was created case sensitive, but not found in the next run (ORA-02379).

A name in " is used case sensitive, e.g. name: '"Gestion/Monitorizacion"'. A name without " is used in uppercase and compared exactly with DBA_PROFILES.

oracle_job

A job_name with " failed with Invalid job name.

A part of job_name in " is case sensitive, e.g. job_name: 'SYSTEM."My_Job"'. convert_to_upper works as before for parts without ".

oracle_directory

The name was not quoted in the SQL statements and compared in uppercase with DBA_DIRECTORIES. A name in " was created case sensitive, but not found in the next run (ORA-00955).

A name in " is used case sensitive, e.g. directory_name: '"MyDir"'. "MyDir" and MYDIR are two different directories. A name without " is used in uppercase as before.

oracle_grants

The grantee (schema, role) was compared in uppercase, roles and objects in grants and object_privs were compared case insensitive, but not quoted in the SQL statements. A grantee or object in " was not found. Granting or revoking a privilege on an object with a lower or mixed case name failed (ORA-00942). With state: absent the names in grants had to be in lowercase. directory_privs needed sys.<directory>.

Grantee, roles in grants, owner and object in object_privs and the directory in directory_privs can be in " (case sensitive), e.g. schema: '"MyUser"', grants: ['"MyRole"'], object_privs: ['select:"MyOwner"."MyTable"']. Names without " are used in uppercase and compared exactly. state: absent compares in any case. An object without owner is in the current schema of the connection, sys. is optional for directories.

oracle_privs

Names with " failed with Invalid privilege, Invalid user/role or Invalid object. convert_to_upper was valid for all names in roles and objs. A role in privs was always converted to uppercase.

A name in " is case sensitive in roles, objs (owner and object, also with the wildcard %) and for a role in privs, e.g. roles: ['"MyUser"'], objs: ['"MyOwner".%'], privs: ['"MyRole"']. convert_to_upper works as before for names without ". No action needed, names without " are handled as before.

PDB names are not affected: Oracle stores them always in uppercase, oracle_pdb converts the name to uppercase as before.

For most inventories nothing changes, because names without " are stored in uppercase as before.

Important
Check names which are enclosed in " by mistake, e.g. from a copied SQL statement. They are case sensitive now and refer to a different object than before, so the module creates a new profile or directory with the exact name, or grants a privilege on a different object or role. The roles of ansible-oracle do not use oracle_directory, so only own playbooks are affected for directories.

An existing profile with a name in lower or mixed case (created outside of ansible-oracle) was found before with a name in any case. Now the name must be given exactly in ", otherwise the module creates a new profile in uppercase.

The same applies to oracle_grants: a grant on an existing object or role with a lower or mixed case name, which was found before with a name in any case, must be given exactly in ". Otherwise the module tries to grant the privilege on the uppercase name (ORA-00942 or ORA-01919) and, with grants_mode: enforce, revokes the existing grant.

Find objects and roles with a name in lower or mixed case
select owner, object_name, object_type from dba_objects where object_name <> upper(object_name) or owner <> upper(owner);
select role from dba_roles where role <> upper(role);
Find profiles with a name in lower or mixed case
select distinct profile from dba_profiles where profile <> upper(profile);
Profile with a mixed case name in the inventory
oracle_pdbs:
  - cdb: DB1
    pdb_name: ORCLPDB
    profiles:
      # before: MyProfile was found case insensitive
      # - name: MyProfile
      - name: '"MyProfile"'
        state: present
        attributes:
          - {name: password_life_time, value: unlimited}

1.12. oracle_db uses hostname for the database connection

This is a bugfix (#542), but it changes the address used for the database connection.

oracle_db ignored the option hostname (alias host) and always connected to the local host name (os.uname()[1]). This forced a listener on that address, which is not allowed in all environments. Now oracle_db connects to hostname, without hostname to os.uname()[1] as before.

The roles oradb_manage_db, oradb_manage_parameters and oradb_datapatch pass oracle_hostname as hostname, like all other oradb_manage_* roles already do. oracle_hostname defaults to ansible_fqdn, so all roles connect to ansible_fqdn unless oracle_hostname is set in inventory.

For most inventories nothing changes, because the other oradb_manage_* roles already need a listener on oracle_hostname.

Use another listener address
# default: ansible_fqdn
oracle_hostname: dbhost-lsnr.example.com
Note
oracle_hostname is also used as host name in the RDBMS installation and as node name for Grid Infrastructure.
Important
An explicit hostname: localhost for oracle_db in own playbooks was ignored before. Now the module connects to localhost, which works only, if the listener listens on this address. Remove hostname to keep the old behavior.

1.13. Quotas for users in oracle_user and oradb_manage_users

This is a new feature (#312), not a breaking change.

Note
Without quotas no quota is removed or changed, the existing quotas of a user are kept. quotas_mode has no effect without quotas. A user with default_tablespace gets QUOTA UNLIMITED on this tablespace as before.

Quotas are only removed, if the inventory says so: with size: 0 for a tablespace in quotas, or with quotas_mode: enforce for all tablespaces not in quotas.

oracle_user and the users of oracle_databases / oracle_pdbs (role oradb_manage_users) have two new options:

  • quotas: list of tablespace and size. size is the number of bytes with an optional unit K, M, G, T, P, E or unlimited. size: 0 removes the quota on the tablespace.

  • quotas_mode: append (default) changes only the tablespaces in quotas, enforce removes all other quotas of the user.

Important
With quotas, the default tablespace gets no automatic QUOTA UNLIMITED. Add the default tablespace to quotas, if the user needs a quota there. An existing quota on the default tablespace is kept with quotas_mode: append, but removed with quotas_mode: enforce, if the tablespace is not in quotas.

Oracle rounds a quota up to whole blocks of the tablespace, e.g. size: 1000 is stored as 8192 bytes with a block size of 8K. The comparison uses blocks, so the module is idempotent for such sizes.

User with quotas, all other quotas are removed
oracle_pdbs:
  - cdb: orcl
    pdb_name: ORCLPDB
    users:
      - schema: app
        state: present
        default_tablespace: app_data
        quotas_mode: enforce
        quotas:
          # without this entry, app has no quota on its default tablespace
          - tablespace: app_data
            size: unlimited
          - tablespace: app_index
            size: 500M
Note
The old pull request for quotas in ansible-oracle-modules (#158) used a list of strings '<tablespace>:<size>'. This format is not supported, use {tablespace: <name>, size: <size>}.

1.14. Internal variables of oraasm_manage_diskgroups removed

This is not a breaking change for inventories, the variables were internal (prefix _). It is listed here for own playbooks or roles which used them.

The role oraasm_manage_diskgroups built the attributes for oracle_asmdg in the variables _oraasm_manage_diskgroups_attr_name and _oraasm_manage_diskgroups_attr_value. For a disk group without attributes they contained the string None, which was passed to oracle_asmdg as attribute (#491). The variables are removed, the role passes attributes of asm_diskgroups directly to oracle_asmdg, a disk group without attributes gets no attributes.

Note
No action is needed for inventories with asm_diskgroups, attributes works as before.
Attributes of a disk group
asm_diskgroups:
  - diskgroup: data
    state: present
    attributes:
      - {name: compatible.asm, value: 19.0.0.0.0}
      - {name: compatible.rdbms, value: 19.0.0.0.0}
    disk:
      - {device: /dev/sdb, asmlabel: data01}

1.15. Autostart of databases with one systemd unit per database and listener

The autostart with autostartup_service: true (Single Instance without Grid Infrastructure / Oracle Restart) uses new systemd units (#265). The solution of 4.x is still available with autostartup_service_mode: legacy.

The service oracle-rdbms.service of 4.x had some problems:

  • systemd killed a database after 90 seconds, when shutdown immediate took longer (default timeout, KillMode=control-group).

  • A database without ~/.profile_<ORACLE_SID> was not shut down, but killed by systemd.

  • A database started outside of systemd (e.g. with sqlplus in a ssh session) was killed with the session during a shutdown of the server.

  • Remote file systems (e.g. NFS) could be unmounted before the database was stopped.

  • A failed start of a database was not reported, the service was active.

4.x (autostartup_service_mode: legacy) 5.x (autostartup_service_mode: units, default)

Units

oracle-rdbms.service for all databases in /etc/oratab, created by oraswdb_install

oracle-db@<ORACLE_SID>.service per database from oracle_databases and oracle-listener@<LISTENER_NAME>.service per listener from listener_installed (or listener_name of oracle_databases), grouped by oracle-databases.target and oracle-listeners.target, created by oradb_manage_db

Start/stop script

/usr/local/bin/manage_oracle_rdbms_procs.sh, listener from ~/.profile_<ORACLE_SID>

/usr/local/bin/oracle-systemd, environment in /etc/sysconfig/oracledb/db-<ORACLE_SID> and /etc/sysconfig/oracledb/listener-<LISTENER_NAME>

Timeouts

systemd default (90 seconds), then the database is killed

autostartup_service_start_timeout / autostartup_service_stop_timeout (default 15min), systemd never kills a database (KillMode=process)

Databases started outside of systemd

killed during a shutdown of the server

attached to their unit by oracle-adopt.timer (autostartup_service_adopt_interval, systemd API AttachProcessesToUnit) and at the start of the unit

Dependencies

autostartup_service_dependency (default network.target) in Requires= and After=

autostartup_service_dependency in Requires= and After= of all units, After=network-online.target remote-fs.target local-fs.target is always set

Start of a single database

not possible

systemctl start oracle-db@<ORACLE_SID>

Crash of a database

not detected, the service stays active

The unit monitors PMON. After a crash (no Instance shutdown complete / Instance shutdown (abort) complete in the alert log) systemd restarts the database after autostartup_service_restart_sec (default 2min), see Restart of a database after a crash. A shutdown in sqlplus is not restarted, the unit is inactive.

ORACLE_HOME of a database

from /etc/oratab

from /etc/oratab (start), from the running instance (stop)

The migration is done by the next run of oradb_manage_db (playbook manage_db.yml). The databases and listeners are not restarted: the new units are started and attach the running databases and listeners. oracle-rdbms.service is disabled. Its unit file is removed, when the service is inactive, otherwise in the first run after the next reboot. An active service without unit file would be stopped without its stop script during a shutdown.

1.15.1. Restart of a database after a crash

This is new behavior compared to 4.x: oracle-rdbms.service did not detect a crash, a crashed database stayed down until it was started manually.

The unit oracle-db@<ORACLE_SID>.service monitors PMON of the database every 10 seconds. When PMON is gone, the last entry in the alert log decides:

Last entry in the alert log Unit

Instance shutdown complete or Instance shutdown (abort) complete (shutdown in sqlplus, also shutdown abort)

inactive, no restart. The unit is started again by oracle-adopt.timer, when the database is started in sqlplus.

other entry, e.g. Instance terminated by …​ (crash)

autostartup_service_restart: true (default): restart after autostartup_service_restart_sec (default 2min), max. 10 starts of the unit within 1 hour. autostartup_service_restart: false: failed, no restart.

autostartup_service_restart can be set for all databases or per database in oracle_databases. A change is used by the next run of oradb_manage_db without a restart of the database.

Restart after a crash only for DB1
autostartup_service_restart: false

oracle_databases:
  - oracle_db_name: DB1
    autostartup_service_restart: true
    ...
Important
Check the restart, when a database must not start automatically after a crash, e.g. because a crash is analyzed before the restart or another tool restarts the database.
Important
Set autostartup_service_mode: legacy in the inventory before the upgrade to 5.x to keep the solution of 4.x. oraswdb_install creates oracle-rdbms.service as before and oradb_manage_db does not create the new units.
Keep the autostart of 4.x
autostartup_service: true
autostartup_service_mode: legacy

A switch from units back to legacy removes the new units in the next run of oradb_manage_db (the databases are not stopped). Active units are disabled and their unit files are removed in the first run after the next reboot. Run oraswdb_install (playbook swdb.yml) to create oracle-rdbms.service again.

Note
dbora for init (EL6) is not changed. Restart the databases with systemctl restart oracle-db@<ORACLE_SID> instead of sqlplus, then the database is always in its unit.

1.16. Autostart of databases is enabled by default

The default of autostartup_service is true since 5.0.0 (4.x: false). A Single Instance without Grid Infrastructure / Oracle Restart gets the autostart of the databases and listeners with the next run of oraswdb_install and oradb_manage_db, when autostartup_service is not set in the inventory. The solution is selected with autostartup_service_mode, see Autostart of databases with one systemd unit per database and listener.

autostartup_service is not used with Grid Infrastructure / Oracle Restart, the databases are started by the Clusterware.

The databases are written with the flag Y into /etc/oratab (as before with autostartup_service: true).

Keep the behavior of 4.x without autostart
autostartup_service: false

1.17. init_parameters for one instance: sid and dbca

init_parameters in oracle_databases and oracle_pdbs support the new attribute sid (#677). It is the SID clause of ALTER SYSTEM SET, e.g. for local_listener per RAC instance. Without sid or with sid: '*' the parameter is set for all instances as before.

dbca has no SID clause for initialization parameters. A parameter for one instance would be set for all instances during the creation of the database. Therefore sid other than * together with dbca: true is not allowed, the assert of oracle_databases in orasw_meta fails. Entries with sid and without dbca are not passed to dbca (default dbca: false for these entries), they are set by oradb_manage_parameters after the creation of the database.

Note
sid is case-sensitive like the SID clause of ALTER SYSTEM, use the instance name exactly as in V$INSTANCE.INSTANCE_NAME.
Not allowed
oracle_databases:
  - oracle_db_name: orcl
    init_parameters:
      - {name: local_listener, value: 'rac1-vip:1521', scope: both, state: present, sid: orcl1, dbca: true}
local_listener per instance
oracle_databases:
  - oracle_db_name: orcl
    init_parameters:
      - {name: local_listener, value: 'rac1-vip:1521', scope: both, state: present, sid: orcl1}
      - {name: local_listener, value: 'rac2-vip:1521', scope: both, state: present, sid: orcl2}

1.18. oradb_rman: control_file_record_keep_time in init_parameters

control_file_record_keep_time defines, how many days the controlfile keeps the records of backups. Without RMAN catalog, RMAN knows only the backups in the controlfile: a value smaller than the recovery window makes backups unusable for RMAN, although they exist.

As of 5.0.0 oradb_rman asserts for every database with rman_jobs:

  • control_file_record_keep_time is defined in init_parameters (state: present, sid: '*'), and

  • the value is >= <n> for the retention policy RECOVERY WINDOW OF <n> DAYS (rman_retention_policy of the database or oradb_rman_retention_policy_default).

The playbook stops with a failed assert, when the parameter is missing or too small.

Example for RECOVERY WINDOW OF 14 DAYS
oracle_databases:
  - oracle_db_name: DB1
    rman_retention_policy: 'RECOVERY WINDOW OF 14 DAYS'
    init_parameters:
      - {name: control_file_record_keep_time, value: '30', scope: both, state: present}
    rman_jobs:
      - {name: parameter}

The assert can be disabled in emergency cases:

oradb_rman_assert_control_file_record_keep_time: false

1.19. oradb_rman: logfiles of rman_backup.sh

The logging of rman_backup.sh and of the RMAN cron jobs was rebuilt. Before 5.0.0 the output was written into two growing files per job, which mixed the output of parallel executions and were only cleaned up by an external logrotate (e.g. orahost_logrotate).

Table 1. Logfiles before and as of 5.0.0
Before 5.0.0 As of 5.0.0

Cron output

<oradb_rman_cron_logdir>/rman_<oracle_db_name>_<job>.log (default /var/log/oracle/rman/log), appended

/dev/null: rman_backup.sh writes all output into its own logfile

Logfile of rman_backup.sh

<RMAN_LOGDIR>/<ORACLE_SID>_<action>.log, output of RMAN only, appended

<RMAN_LOGDIR>/<db_unique_name>_<action>_<YYYYmmdd>_<HHMMSS>_<pid>.log, one file per execution with the output of the script, sqlplus and RMAN

Last execution

-

Symlink <RMAN_LOGDIR>/<db_unique_name>_<action>.log to the logfile of the last execution

Cleanup

-

Logfiles older than oradb_rman_log_retention days (default 30, RMAN_LOG_RETENTION in rman_backup.cfg) are removed by rman_backup.sh

Parallel executions of the same action

executed, output mixed in one logfile

The 2nd execution stops with exit code 30 (lock file <RMAN_LOGDIR>/.<db_unique_name>_<action>.lock)

Changes in detail:

  • oradb_rman_cron_logdir and rman_cron_logdir are removed. The directory /var/log/oracle/rman/log is no longer created or written.

  • The prefix of the logfile is the db_unique_name instead of the ORACLE_SID. The name changes for every database with db_unique_name != ORACLE_SID, e.g. Data Guard or RAC.

  • The 1st execution renames an existing logfile <RMAN_LOGDIR>/<db_unique_name>_<action>.log of older versions to *.log.old and creates the symlink with this name.

  • Manual executions write the same logfiles as the cron jobs.

Who is affected:

  • Monitoring of the logfiles (e.g. checkmk logwatch, own scripts), which reads /var/log/oracle/rman/log/* or <RMAN_LOGDIR>/<ORACLE_SID>_<action>.log.

  • Monitoring of the exit code (e.g. mk-job): exit code 30 for a 2nd execution of a running action is new.

  • logrotate rules for the RMAN logfiles, e.g. the default of orahost_logrotate: rman_backup.sh removes old logfiles itself, logrotate must not rotate the logfiles with timestamp (logrotate refuses to rotate the symlink).

Required actions:

  1. Remove oradb_rman_cron_logdir and rman_cron_logdir from the inventory.

  2. Change the monitoring to the symlink <RMAN_LOGDIR>/<db_unique_name>_<action>.log or to the logfiles with timestamp.

  3. Remove logrotate rules for RMAN_LOGDIR and set oradb_rman_log_retention instead.

  4. Remove the old logfiles after the upgrade, when they are not needed anymore:

    rm -r /var/log/oracle/rman/log
    rm <RMAN_LOGDIR>/*.log.old

1.20. listener_installed with Grid Infrastructure / Oracle Restart: listener in its home

With Grid Infrastructure or Oracle Restart oradb_manage_db registers every listener of listener_installed with srvctl add/modify listener (module oracle_crs_listener). As of 5.0.0 the listener resource uses the ORACLE_HOME of the entry:

Table 2. Before and as of 5.0.0
Entry in listener_installed Before 5.0.0 As of 5.0.0

home: <db_homes_config key>

listener.ora in the ORACLE_HOME of home, but a new listener resource was added in the GI home (-oraclehome %CRS_HOME%)

listener.ora and the listener resource (srvctl …​ -oraclehome) in the ORACLE_HOME of home

use_gi_home: true

GI home from oracle_home_gi

GI home from crs_home in /etc/oracle/olr.loc (there is only one Grid Infrastructure / Oracle Restart per host)

Changes in detail:

  • A listener with home is registered with the ORACLE_HOME of home. An existing listener resource in the GI home is modified with srvctl modify listener -oraclehome <ORACLE_HOME of home>. A running listener is stopped before and started after the change, i.e. it runs from the new ORACLE_HOME.

  • A listener with use_gi_home: true stays in the GI / Oracle Restart home.

Who is affected:

  • Inventories with Grid Infrastructure / Oracle Restart and home in listener_installed, where the listener runs from the GI home, e.g. LISTENER with home of the database.

Required actions:

  1. Use use_gi_home: true without home for a listener, which must stay in the GI / Oracle Restart home.

  2. Check the ORACLE_HOME of the listener resources before the upgrade. oradb_manage_db restarts a moved listener, run it in a maintenance window:

    srvctl config listener -listener <listener_name>
Example
listener_installed:
  # listener in the GI / Oracle Restart home (crs_home from /etc/oracle/olr.loc)
  - listener_name: LISTENER
    use_gi_home: true
    state: present
  # additional listener in the ORACLE_HOME of the database, registered with srvctl in this home
  - listener_name: LISTENER2
    home: db19-gi-ee
    state: present

1.21. local_listener in init_parameters of Single Instance databases

As of 5.0.0 the assert of oracle_databases in orasw_meta requires local_listener in init_parameters for every Single Instance database (with and without Oracle Restart) with state: present. RAC and RAC One Node databases are not checked, Clusterware sets local_listener.

Why:

DBCA sets local_listener to an alias, e.g. LISTENER_<DB_NAME>, and writes the alias into tnsnames.ora of the ORACLE_HOME (with Read-Only Oracle Homes in $ORACLE_BASE/homes/<oracle_home_name>/network/admin). After a switch of the ORACLE_HOME (Out-of-Place Patching, see out_of_place_patching.adoc) the alias is missing in tnsnames.ora of the new ORACLE_HOME. The instance cannot resolve local_listener and does not register its services at the listener (ORA-12514 for clients). An EZConnect address in local_listener needs no entry in tnsnames.ora.

Who is affected:

  • Inventories with a Single Instance database in oracle_databases without local_listener in init_parameters. The playbooks fail in the assert of orasw_meta:

    local_listener is missing in init_parameters of <oracle_db_name>, ...

Required actions:

  1. Add local_listener with EZConnect (//<host>:<port>) to init_parameters of the database. localhost and the port of the listener avoid an extra entry in tnsnames.ora.

  2. Add one address per listener, if the instance must register at more than one listener, e.g. '(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1523)))'.

  3. Existing databases: oradb_manage_parameters changes local_listener with scope: both, the instance registers at the listener without restart. The entries of DBCA in tnsnames.ora are not removed by ansible-oracle.

Note
localhost requires a listener on the loopback address. A listener with the hostname in HOST listens on all addresses of the host, unless IP=FIRST is set in listener.ora. Use the hostname or the address of the listener otherwise, e.g. //{{ ansible_hostname }}:1521.
Example
oracle_databases:
  - oracle_db_name: DB1
    oracle_db_type: SI
    listener_port: 1521
    init_parameters:
      - {name: local_listener, value: '//localhost:1521', scope: both, state: present}

1.22. oracle_databases: is_container and omf are true by default

As of 5.0.0 the attributes is_container and omf of oracle_databases are true by default. They need not to be defined in oracle_databases anymore.

Why:

26ai supports only Container Databases and ansible-oracle uses Oracle Managed Files for tablespaces by default. The defaults simplify the inventory.

Who is affected:

  • Inventories with a 19c database in oracle_databases without is_container. Before 5.0.0 a non-CDB was created, now a CDB is created. The tasks for PDBs (oradb_manage_pdb), the timezone upgrade (oradb_tzupgrade) and the switch of the ORACLE_HOME treat the database as CDB.

  • Inventories with a database in oracle_databases without omf. Before 5.0.0 dbca was called with -useOMF false, now with -useOMF true. omf is used during the creation of a database with dbca only.

Required actions:

  1. Set is_container: false for a 19c non-CDB.

  2. Use OMF for new databases. OMF is strongly recommended in ansible-oracle.

  3. Without OMF: set omf: false and use an own DBCA template without OMF (dbca_templatename). The DBCA templates of ansible-oracle are made for OMF. Databases without OMF are not tested anymore.

Example
oracle_databases:
  - oracle_db_name: DB1
    home: db19-si-ee
    is_container: false   # non-CDB, 19c only
    # not recommended and not tested:
    # omf: false          # dbca -useOMF false
    # dbca_templatename: <own DBCA template without OMF>

1.23. oracle_databases: db_create_file_dest and db_recovery_file_dest in init_parameters

As of 5.0.0 the assert of oracle_databases in orasw_meta requires db_create_file_dest and db_recovery_file_dest in init_parameters for every database with state: present and omf: true (default).

Why:

The parameters are needed in different situations, e.g. for dbca, standby databases (RMAN duplicate) and the directories of a new database. They are defined directly in init_parameters instead of deriving them from other variables. dbca uses the values for the datafile and recovery area destination. The global defaults oracle_dbf_dir_fs and oracle_reco_dir_fs are only used with omf: false (only storage_type: FS, own DBCA template, not tested).

Who is affected:

  • Inventories with a database in oracle_databases without db_create_file_dest or db_recovery_file_dest in init_parameters. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: omf: true (default) needs db_create_file_dest and db_recovery_file_dest in init_parameters, ...

Required actions:

  1. Add db_create_file_dest and db_recovery_file_dest to init_parameters. Use the values of datafile_dest and recoveryfile_dest, if they were defined.

  2. Existing databases: use the current values of the database (show parameter db_create_file_dest, show parameter db_recovery_file_dest), otherwise oradb_manage_parameters changes them.

  3. Define db_recovery_file_dest_size before db_recovery_file_dest in init_parameters for existing databases without a recovery area (ORA-19802).

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: db_create_file_dest, value: '/u02/oradata', scope: both, state: present}
      - {name: db_recovery_file_dest_size, value: '10G', scope: both, state: present, dbca: false}
      - {name: db_recovery_file_dest, value: '/u02/fra', scope: both, state: present}

1.24. oracle_databases: datafile_dest and recoveryfile_dest are removed

As of 5.0.0 the attributes datafile_dest and recoveryfile_dest of oracle_databases are removed. They are redundant to db_create_file_dest and db_recovery_file_dest in init_parameters. datafile_dest in oracle_pdbs is not affected.

Who is affected:

  • Inventories with datafile_dest or recoveryfile_dest in oracle_databases. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: datafile_dest and recoveryfile_dest in oracle_databases are removed. ...

Required actions:

  1. Move the values to db_create_file_dest and db_recovery_file_dest in init_parameters.

  2. Remove datafile_dest and recoveryfile_dest from oracle_databases.

  3. Databases with omf: false: the destinations are oracle_dbf_dir_fs and oracle_reco_dir_fs (global per host).

Before
oracle_databases:
  - oracle_db_name: DB1
    is_container: true
    datafile_dest: /u02/oradata
    recoveryfile_dest: /u02/fra
    init_parameters:
      - {name: db_create_file_dest, value: '/u02/oradata', scope: both, state: present}
After
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: db_create_file_dest, value: '/u02/oradata', scope: both, state: present}
      - {name: db_recovery_file_dest, value: '/u02/fra', scope: both, state: present}

1.25. oracle_databases: omf false not allowed with ASM, oracle_dbf_dir_asm and oracle_reco_dir_asm removed

As of 5.0.0 the assert of oracle_databases in orasw_meta fails for a database with storage_type: ASM and omf: false. ASM without OMF makes no sense in practice. The variables oracle_dbf_dir_asm and oracle_reco_dir_asm are removed, the disk groups are taken from db_create_file_dest and db_recovery_file_dest in init_parameters.

oradb_rman: the templates parameter_rac and parameter_fra_disk use db_create_file_dest from init_parameters for the snapshot controlfile instead of oracle_dbf_dir_asm. Before, parameter_fra_disk created a wrong name with two ` (`+DATA).

Who is affected:

  • Inventories with a database with storage_type: ASM and omf: false. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: omf: false is not allowed with storage_type: ASM. ...
  • Inventories with oracle_dbf_dir_asm or oracle_reco_dir_asm. The variables have no effect anymore.

Required actions:

  1. Remove omf: false from ASM databases and add db_create_file_dest and db_recovery_file_dest to init_parameters.

  2. Remove oracle_dbf_dir_asm and oracle_reco_dir_asm from the inventory.

Example
oracle_databases:
  - oracle_db_name: DB1
    storage_type: ASM
    init_parameters:
      - {name: db_create_file_dest, value: '+DATA', scope: both, state: present}
      - {name: db_recovery_file_dest_size, value: '10G', scope: both, state: present, dbca: false}
      - {name: db_recovery_file_dest, value: '+FRA', scope: both, state: present}

1.26. oracle_databases: max_pdbs in init_parameters of Container Databases

As of 5.0.0 the assert of oracle_databases in orasw_meta fails for a Container Database (is_container: true, default as of 5.0.0) with state: present without max_pdbs in init_parameters (without sid or sid: '*'). max_pdbs defines explicitly how many PDBs are allowed by the license. The value must be an integer:

Edition (db_homes_config) Maximum max_pdbs Remark

SE2 (SE)

3

EE

1024

More than 3 PDBs require a license for the Multitenant Option!

Important
Enterprise Edition without a license for the Multitenant Option allows only 3 PDBs, like Standard Edition 2. ansible-oracle has no switch for licensed options yet, so the assert allows up to 1024 PDBs for EE. Check your license before you set max_pdbs greater than 3.

Who is affected:

  • Inventories with a Container Database without max_pdbs in init_parameters, or with a value greater than 3 (SE2) or 1024 (EE). This includes 19c databases without is_container, which are Container Databases as of 5.0.0. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: is_container: true (default) needs max_pdbs in init_parameters with an integer value <= <limit> for edition <edition> ...

Required actions:

  1. Add max_pdbs with the number of PDBs allowed by your license to init_parameters. max_pdbs is set by dbca for a new database and changed by oradb_manage_parameters for an existing database.

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: max_pdbs, value: '3', scope: both, state: present}

1.27. oracle_databases: control_management_pack_access in init_parameters of Enterprise Edition databases

As of 5.0.0 the assert of oracle_databases in orasw_meta fails for a database of an Enterprise Edition home (edition: EE in db_homes_config) with state: present without control_management_pack_access in init_parameters (without sid or sid: '*'). control_management_pack_access defines explicitly which Management Packs are licensed. Without the parameter, the Oracle default for Enterprise Edition is DIAGNOSTIC+TUNING, which needs a license for the Diagnostics and Tuning Pack.

Value License

NONE

no Management Pack

DIAGNOSTIC

Diagnostics Pack

DIAGNOSTIC+TUNING

Diagnostics Pack and Tuning Pack

The parameter is optional for Standard Edition 2. If it is defined, the value must be NONE, the Management Packs are not available for Standard Edition 2.

Important
Set the value according to your license. The Management Packs are options of the Enterprise Edition. ansible-oracle has no switches for licensed options yet. Check if further settings are needed for your licensed options.

Who is affected:

  • Inventories with a database of an Enterprise Edition home without control_management_pack_access in init_parameters, or with another value than NONE, DIAGNOSTIC or DIAGNOSTIC+TUNING.

  • Inventories with a database of a Standard Edition 2 home with control_management_pack_access other than NONE. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: control_management_pack_access in init_parameters must be one of NONE, DIAGNOSTIC or DIAGNOSTIC+TUNING and is mandatory for Enterprise Edition ...

Required actions:

  1. Add control_management_pack_access with the value allowed by your license to init_parameters. For an existing database, oradb_manage_parameters changes the parameter to the value from the inventory.

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: control_management_pack_access, value: 'NONE', scope: both, state: present}

1.28. oracle_databases: sga_target and pga_aggregate_target in init_parameters

As of 5.0.0 the assert of oracle_databases in orasw_meta fails for a database with state: present without sga_target or pga_aggregate_target in init_parameters. These are the essential memory parameters of an Oracle database and must be defined explicitly. Entries with sid are accepted (instance specific values for RAC).

oracle_db_mem_totalmb (totalMemory in dbca) is not supported anymore, the assert fails if it is set. Before, the assert required either oracle_db_mem_totalmb or sga_target in init_parameters.

Who is affected:

  • Inventories with a database without sga_target or pga_aggregate_target in init_parameters, or with oracle_db_mem_totalmb. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: sga_target and pga_aggregate_target are essential memory parameters and must be defined in init_parameters ...

Required actions:

  1. Add sga_target and pga_aggregate_target to init_parameters.

  2. Remove oracle_db_mem_totalmb.

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: sga_target, value: '2G', scope: spfile, state: present}
      - {name: pga_aggregate_target, value: '512M', scope: both, state: present}

1.29. oracle_databases: no memory_target and memory_max_target with Hugepages

As of 5.0.0 the assert of oracle_databases in orasw_meta fails for a database with state: present with memory_target or memory_max_target in init_parameters (also with sid) if Hugepages are configured (configure_hugepages: true, default of orahost). Automatic Memory Management (AMM) prevents the usage of Hugepages. Entries with state: absent are allowed to remove the parameters from the spfile.

Who is affected:

  • Inventories with a database with memory_target or memory_max_target in init_parameters and without configure_hugepages: false. The playbooks fail in the assert of orasw_meta:

    <oracle_db_name>: memory_target and memory_max_target are not allowed in init_parameters with Hugepages ...

Required actions:

  1. Use sga_target and pga_aggregate_target (Automatic Shared Memory Management) instead of AMM.

  2. Set state: absent for memory_target and memory_max_target to remove them from the spfile of existing databases, or set configure_hugepages: false if Hugepages are not used.

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: memory_target, value: '0', scope: spfile, state: absent}
      - {name: memory_max_target, value: '0', scope: spfile, state: absent}
      - {name: sga_target, value: '2G', scope: spfile, state: present}
      - {name: pga_aggregate_target, value: '512M', scope: both, state: present}

1.30. Role oraswahf_install replaced by ahf

The role oraswahf_install was an empty placeholder for the installation of Oracle Autonomous Health Framework (AHF) - it had no tasks and no variables. It is removed and replaced by the new role ahf, which installs, upgrades and configures AHF. The central web interface AHF Fleet Insights is installed with the new role ahf_fleet_insights.

Note
AHF is not installed by oraswahf_install, existing installations are not affected. Only playbooks, which reference the role, fail with the role 'oraswahf_install' was not found.
Before: own playbook
- hosts: dbfs
  become: true
  roles:
    - opitzconsulting.ansible_oracle.oraswahf_install
After: own playbook or playbooks/ahf.yml
- hosts: dbfs
  become: true
  roles:
    - opitzconsulting.ansible_oracle.ahf

The variables and the decisions for an installation are described in Oracle Autonomous Health Framework (AHF).

1.31. oracle_databases: processes in init_parameters

As of 5.0.0 the assert of oracle_databases in orahost_meta fails for a database with state: present without processes in init_parameters, or with a value which is not an integer > 0. Entries with sid are accepted (instance specific values for RAC).

processes is needed to calculate kernel.sem in orahost_meta and by further asserts. Before, a database without processes failed with an undefined variable (oracle_databases_processes_max), or kernel.sem was calculated without the databases lacking processes.

Note
In practice the breaking change is usually irrelevant: the default of processes is too small in most cases anyway and should be set explicitly.

The assert is in orahost_meta, because orahost_meta runs before the asserts of orasw_meta and the role orahost uses only orahost_meta. It is disabled together with the other asserts of oracle_databases by orasw_meta_assert_oracle_databases: false.

Who is affected:

  • Inventories with a database without processes in init_parameters. The playbooks fail in the assert of orahost_meta:

    <oracle_db_name>: processes must be defined in init_parameters with an integer value > 0 ...

Required actions:

  1. Add processes to init_parameters. For an existing database, use the current value (show parameter processes) to avoid a change of the spfile and a restart.

Example
oracle_databases:
  - oracle_db_name: DB1
    init_parameters:
      - {name: processes, value: '640', scope: spfile, state: present}

1.32. Default of oracle_install_version_gi is 26ai

As of 5.0.0 the default of oracle_install_version_gi is 26.1.0.0 (26ai) instead of 19.3.0.0 (19c). New installations should use the current release, an older release as default makes no sense anymore.

Note
In practice the breaking change is usually irrelevant: oracle_install_version_gi must match the installation media of Grid Infrastructure (oracle_sw_image_gi, oracle_install_image_gi) and should be set in every inventory with Grid Infrastructure or Oracle Restart.

oracle_install_version_gi is only used with oracle_install_option_gi: HA_CONFIG or CRS_CONFIG. Inventories without Grid Infrastructure or Oracle Restart are not affected.

Who is affected:

  • Inventories with oracle_install_option_gi: HA_CONFIG or CRS_CONFIG without oracle_install_version_gi. The variable selects the response file, the task file and the installation media of oraswgi_install, and the key in gi_patches.

  • The default of oracle_home_gi contains the version: {{ oracle_base }}/{{ oracle_install_version_gi }}/grid (Oracle Restart) and /u01/app/{{ oracle_install_version_gi }}/grid (Grid Infrastructure). Without oracle_install_version_gi and without oracle_home_gi in inventory, oracle_home_gi changes from …​/19.3.0.0/grid to …​/26.1.0.0/grid.

    Warning
    For an installed Oracle Restart, a different oracle_home_gi starts the switch into a new home (Out-of-Place Patching, switch_home.yml of oraswgi_install). Set oracle_install_version_gi (and oracle_home_gi) before the first run with 5.0.0.

Required actions:

  1. Set oracle_install_version_gi to the base release of the installation media (19c: 19.3.0.0, 26ai: 26.1.0.0).

  2. Recommended: set oracle_home_gi as well, to be independent from the default.

Example: Oracle Restart 19c
oracle_install_option_gi: HA_CONFIG
oracle_install_version_gi: 19.3.0.0
oracle_home_gi: /u01/app/oracle/19.3.0.0/grid   # the former default

2. Adding new breaking changes

Every changelog fragment with a breaking_changes entry needs an entry in this guide:

  1. Add a row to the overview table of the next major version. Create the version section, if it does not exist.

  2. Describe the change in an own subsection: what changed, why, who is affected, before/after and the required action with an example.

  3. Reference this guide in the changelog fragment (see doc/guides/breaking_changes.adoc).

14 - Setup of Oracle Restart and Grid Infrastructure

1. Setup of Oracle Restart and Grid Infrastructure

This guide describes the setup of the software of Grid Infrastructure with ansible-oracle in 2 independent parts:

  • Oracle Restart: Grid Infrastructure for a Standalone Server, Single Instance databases with ASM on one host.

  • Grid Infrastructure (cluster): Grid Infrastructure for a Cluster, the prerequisite for RAC and RAC One Node.

Both parts are complete on their own. Some variables are described in both parts on purpose, because their meaning and their defaults differ between Oracle Restart and Grid Infrastructure. Read only the part you need.

Note
Terms (Oracle Restart, Grid Infrastructure, RAC): see Featurelist.
Not tested in this guide means: not covered by the molecule scenarios. Untested combinations should work, as long as they are not explicitly excluded and are supported by Oracle.
Both Oracle Restart and Grid Infrastructure use the software of Grid Infrastructure (oracle_home_gi). The variable oracle_install_option_gi decides what is configured.
Table 1. Oracle Restart and Grid Infrastructure at a glance
Oracle Restart Grid Infrastructure (cluster)

oracle_install_option_gi

HA_CONFIG

CRS_CONFIG

State in ansible-oracle

Oracle Restart 26ai is supported, tested with databases 19c and 26ai (molecule scenario has-ol9).

Grid Infrastructure 26ai is supported, tested with 2 nodes and a RAC database 26ai (molecule scenario rac).
Grid Infrastructure 19c, RAC databases 19c and RAC One Node are not tested.

Hosts

every host is configured on its own

all nodes of the cluster are configured together
(inventory group orasw_meta_cluster_hostgroup)

Default of oracle_home_gi

{{ oracle_base }}/{{ oracle_install_version_gi }}/grid

/u01/app/{{ oracle_install_version_gi }}/grid

Default of grid_base

oracle_base

/u01/app/grid/base

oracle_home_gi and grid_base

oracle_home_gi must be below grid_base

oracle_home_gi must not be below grid_base

Storage for ASM

local disks

shared disks, visible on all nodes

Network

no special requirements

public and private network, SCAN, VIP for each node

SSH user equivalence

not needed

configured by orahost_ssh for the grid user (and oracle_user)

1.1. Playbooks and roles

The same playbooks are used for Oracle Restart and Grid Infrastructure:

Order Playbook Content for Oracle Restart / Grid Infrastructure

1

opitzconsulting.ansible_oracle.os

orahost: users, groups, kernel parameters, packages, firewall.
orahost_storage: partitions and device persistence (asmlib, udev) of the ASM disks.
orahost_ssh: SSH user equivalence between the nodes (Grid Infrastructure only).

2

opitzconsulting.ansible_oracle.swgi

oraswgi_install: installation and configuration of Grid Infrastructure (gridSetup.sh, root.sh, ConfigTools).
oraswgi_manage_patches: patches for oracle_home_gi.
oraasm_manage_diskgroups: all ASM disk groups of asm_diskgroups except oracle_asm_init_dg.

3

opitzconsulting.ansible_oracle.swdb

Installation of the RDBMS software (db_homes_installed). The version of a database home must not be newer than Grid Infrastructure.

4

opitzconsulting.ansible_oracle.manage_db

Listener, databases and PDBs.

The playbooks use the variable hostgroup (default: all) as target hosts.

1.2. Common basics for both parts

1.2.1. Software of Grid Infrastructure

Variable Default Description

oracle_install_version_gi

26.1.0.0

Base release of the software, e.g. 26.1.0.0 or 19.3.0.0. It selects the response file template (grid-install.rsp.<version>.j2), the task file of oraswgi_install, the default installation media in oracle_sw_image_gi, the key in gi_patches and the default of oracle_home_gi. It is the base release, not the Release Update: a Golden Image with 19.33 is still 19.3.0.0, 23.26.3 is 26.1.0.0. Always set it in the inventory, it must match the installation media.

oracle_sw_image_gi

list of the base release images

List of installation media with filename and version. The entry with version: oracle_install_version_gi is unpacked into oracle_home_gi, when oracle_install_image_gi is not defined.
No need to change it: the default contains all supported releases.

oracle_install_image_gi

not defined

Filename of the installation media. When it is defined, it is used instead of the filename from oracle_sw_image_gi, e.g. for a Golden Image (gi_goldimage_26_03.zip).
Recommended: always install a Golden Image with the Release Update from Oracle and use a new oracle_home_gi for every Release Update (Out-of-Place Patching, see below).

oracle_sw_copy, is_sw_source_local, oracle_stage_remote

Source of the installation media (copied into oracle_stage or used directly from oracle_stage_remote). See Install and Patch Settings.

oracle_home_gi

derived, see below

ORACLE_HOME of Grid Infrastructure. The default depends on oracle_install_option_gi: oracle_home_gi_so for Oracle Restart, oracle_home_gi_cl for Grid Infrastructure. Set oracle_home_gi directly when the path should not contain the base release, e.g. for Golden Images with a Release Update.

Important
The default of oracle_install_version_gi is 26.1.0.0 (26ai) as of 5.0.0 (19c before). Set oracle_install_version_gi in every inventory with Oracle Restart or Grid Infrastructure, especially for existing 19c installations without oracle_home_gi in the inventory: the default of oracle_home_gi contains the version. See Breaking Changes (Default of oracle_install_version_gi is 26ai).

1.2.2. Passwords

Variable Default Description

default_gipass

''

Default password for SYS (SYSASM) and ASMSNMP of ASM. Mandatory, as long as sysasmpassword and asmmonitorpassword are not defined.

sysasmpassword

{{ default_gipass }}

Password of SYS in ASM (sysasmPassword in the response file).

asmmonitorpassword

{{ default_gipass }}

Password of ASMSNMP (asmsnmpPassword in the response file).

1.2.3. Patching during the installation

Variable Default Description

apply_patches_gi

true

Apply the patches from gi_patches to oracle_home_gi.

patch_before_rootsh

true

true: the Release Update (opatchauto patch with state: present) is applied by gridSetup.sh -applyRU before root.sh.
Only used together with apply_patches_gi: true.

gi_patches

{}

Patches for Grid Infrastructure per base release (opatchauto, opatch). See the example in role oraswgi_meta and Install and Patch Settings.

Tip
Recommended is a Golden Image with the Release Update from Oracle in oracle_install_image_gi instead of patching the base release. Then set apply_patches_gi: false and patch_before_rootsh: false. A new Release Update is installed as new Golden Image into a new oracle_home_gi (Out-of-Place Patching), not with opatchauto into the running home.

1.2.4. ASM disk groups

asm_diskgroups defines all disk groups, for Oracle Restart and for Grid Infrastructure.

Example asm_diskgroups
asm_diskgroups:
  - diskgroup: crs                   # oracle_asm_init_dg => created by gridSetup.sh
    state: present
    properties:
      - {redundancy: external, ausize: 4}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
    disk:
      - {device: /dev/sdc, asmlabel: crs01}
  - diskgroup: data                  # created by oraasm_manage_diskgroups
    state: present
    properties:
      - {redundancy: external}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
      - {name: au_size, value: 4M}
    disk:
      - {device: /dev/sdd, asmlabel: data01}
Table 2. Attributes of an entry in asm_diskgroups
Attribute Description

diskgroup

Name of the disk group (lower case in the inventory, ASM uses upper case).

state

present or absent.

properties

List with 1 dictionary: redundancy (external, normal, high) and ausize (in MB).
ausize in properties is used only for oracle_asm_init_dg (response file of gridSetup.sh). Use the attribute au_size for all other disk groups.

attributes

ASM attributes of the disk group (name, value), e.g. compatible.asm, compatible.rdbms, au_size.

disk

List of disks:

* device: device of the disk (e.g. /dev/sdc). * asmlabel: label of the disk for asmlib, udev and asmfd (see device_persistence). * failure_group (optional): name of the failure group. Disks with the same name are in the same failure group. Without failure_group, ASM creates a failure group for every disk. For oracle_asm_init_dg set failure_group for all disks or for none. * quorum (optional): true for a quorum failure group (voting files only). Must be the same for all disks of a failure group.

oracle_asm_init_dg (default: crs) is the 1st disk group. It must be an entry of asm_diskgroups. It is created during the configuration of Grid Infrastructure (gridSetup.sh, root.sh) and contains the SPFile of ASM, with Grid Infrastructure additionally the OCR and the voting files. All other disk groups are created later by oraasm_manage_diskgroups.

1.2.5. Device persistence of the ASM disks

device_persistence defines how the ASM disks are made persistent. It controls 3 things together with oracle_asm_disk_string:

  1. the preparation of the disks by orahost_storage or oraswgi_install,

  2. the path of each disk in the response file of gridSetup.sh (diskList of oracle_asm_init_dg),

  3. the ASM discovery string (asm_diskstring, diskString in the response file).

Table 3. device_persistence
Value Preparation Path of a disk Discovery string

'' (default)

nothing, the devices must already exist with owner _grid_install_user and group asmadmin_group.

device

oracle_asm_disk_string

asmlib

orahost_storage: oracleasm createdisk <asmlabel> <device><partition_suffix>

oracle_asm_disk_string without trailing * + asmlabel (upper case), e.g. ORCL:DATA01

oracle_asm_disk_string (default: ORCL:*)

udev

orahost_storage: udev rules with a symlink <oracle_asm_disk_string without leading /dev/><asmlabel> per disk

oracle_asm_disk_string + asmlabel ⇒ oracle_asm_disk_string must end with / (no *), e.g. /dev/oracle/

oracle_asm_disk_string

asmfd

oraswgi_install: installation and configuration of the ASM Filter Driver, asmcmd afd_label <asmlabel> <device>1

AFD:<ASMLABEL>

oracle_asmfd_disk_string (mandatory for asmfd)

Variable Default Description

oracle_asm_disk_string

ORCL:*

ASM discovery string (asm_diskstring). The default fits asmlib only. Set it for all other values of device_persistence, e.g. /dev/oracleasm/* for ''.

oracle_asmfd_disk_string

not defined

ASM discovery string with asmfd, e.g. AFD:*.

partition_devices

true

Create 1 partition on every disk of asm_diskgroups with parted (GPT). asmlib uses <device><partition_suffix>, asmfd uses <device>1. Set false when the disks must not be partitioned (e.g. device_persistence: '' with existing devices).

Warning
Only device_persistence: '' is tested by the molecule scenarios of ansible-oracle. asmlib, udev and asmfd are not tested with Oracle Linux 9 and 26ai.

2. Oracle Restart

Oracle Restart is Grid Infrastructure for a Standalone Server (oracle_install_option_gi: HA_CONFIG). It manages ASM, listeners and Single Instance databases on one host.

Oracle Restart 26ai is supported and tested with databases 19c and 26ai (molecule scenario has-ol9). Every host is configured on its own; there is no dependency between hosts.

2.1. Requirements

  • Oracle Linux / RHEL 8 or 9, see Featurelist.

  • Local disks for ASM (at least for oracle_asm_init_dg).

  • oracle_install_version_gi must be the same or newer than the version of all database homes.

2.2. Example inventory

host_vars/<host>/restart.yml
oracle_install_option_gi: HA_CONFIG
oracle_install_version_gi: 26.1.0.0
oracle_install_image_gi: gi_goldimage_26_03.zip   # Golden Image with RU 23.26.3
oracle_home_gi: /u01/app/oracle/product/26/restart_26.03

apply_patches_gi: false        # the Golden Image contains the Release Update
patch_before_rootsh: false

role_separation: false         # GI software owned by oracle_user
default_gipass: "{{ vault_default_gipass }}"

device_persistence: ''
partition_devices: false
oracle_asm_disk_string: /dev/oracleasm/*
oracle_asm_init_dg: data

asm_diskgroups:
  - diskgroup: data
    state: present
    properties:
      - {redundancy: external, ausize: 4}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
    disk:
      - {device: /dev/oracleasm/data01, asmlabel: data01}
  - diskgroup: fra
    state: present
    properties:
      - {redundancy: external}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
      - {name: au_size, value: 4M}
    disk:
      - {device: /dev/oracleasm/fra01, asmlabel: fra01}

2.3. Installation type

Variable Value Description

oracle_install_option_gi

HA_CONFIG

Configure Oracle Restart. An empty string (default) means: no Grid Infrastructure software on the host, databases use file systems.

With HA_CONFIG:

  • orasw_meta_primary_node is always true: every host runs all steps itself.

  • orasw_meta_cluster_hostgroup, oracle_scan, oracle_vip and all network variables of Grid Infrastructure are not used.

2.4. ORACLE_HOME and ORACLE_BASE

Variable Default for Oracle Restart Description

oracle_base

/u01/app/oracle

ORACLE_BASE of the databases.

grid_base

{{ oracle_base }}

ORACLE_BASE of Oracle Restart. For Oracle Restart it is the same as oracle_base by default.

oracle_home_gi_so

{{ oracle_base }}/{{ oracle_install_version_gi }}/grid

Default for oracle_home_gi with Oracle Restart (so = stand alone).

oracle_home_gi

{{ oracle_home_gi_so }}

ORACLE_HOME of Oracle Restart. It must be below grid_base. Recommended: an own path per Release Update, e.g. /u01/app/oracle/product/26/restart_26.03. That is the prerequisite for the switch into a new home, see below.

oracle_inventory_loc

/u01/app/oraInventory

Central inventory.

Important
Oracle Restart: oracle_home_gi must be a subdirectory of grid_base, e.g. grid_base: /u01/app/oracle and oracle_home_gi: /u01/app/oracle/product/26/restart_26.03.
This is the opposite of Grid Infrastructure (cluster), where oracle_home_gi must not be below grid_base. Keep this in mind, when an inventory is changed from Oracle Restart to Grid Infrastructure or vice versa.

2.5. Users and groups

Variable Default Description

role_separation

false

false: Oracle Restart is installed and owned by oracle_user.
true: Oracle Restart is installed and owned by grid_user.

oracle_user

oracle

Owner of the database software.

grid_user

grid

Owner of Oracle Restart with role_separation: true.

asmadmin_group, asmdba_group, asmoper_group

asmadmin, asmdba, asmoper

OS groups OSASM, OSDBA and OSOPER for ASM (OSASM, OSDBA, OSOPER in the response file). The ASM disks must be owned by the install user and asmadmin_group.

The install user of Oracle Restart is derived from both variables: _grid_install_user is grid_user with role_separation: true, otherwise oracle_user. It is the owner of oracle_home_gi, of grid_base and of the ASM disks.

2.6. Installation steps

oraswgi_install executes the following steps. Steps 5 to 8 are only executed when /etc/oracle/olr.loc does not exist (Oracle Restart is not configured yet).

  1. Unpack the installation media into oracle_home_gi, when the home is not registered in the central inventory.

  2. Install cvuqdisk (only with device_persistence: asmfd).

  3. Create the response file <oracle_rsp_stage>/grid-<oracle_cluster_name>.rsp.

  4. Run cluvfy (skipped with gi_ignoreprereq: true).

  5. With device_persistence: asmfd: configure the ASM Filter Driver and label the disks.

  6. gridSetup.sh -silent -responseFile …​ (with -applyRU, when apply_patches_gi and patch_before_rootsh are true).

  7. orainstRoot.sh and root.sh. root.sh configures Oracle Restart and creates oracle_asm_init_dg.

  8. gridSetup.sh -executeConfigTools (run_configtoolallcommand: true): listener LISTENER in oracle_home_gi, ASM post configuration, CVU.

  9. CRS flag (CRS=TRUE) of oracle_home_gi in the central inventory.

  10. Patches (oraswgi_manage_patches) and all other disk groups (oraasm_manage_diskgroups).

2.7. Prerequisite checks and ConfigTools

Variable Default Description

gi_ignoreprereq

false

true: cluvfy is not executed and gridSetup.sh is started with -ignorePrereq. Failures of the Cluster Verification Utility (CVU) in -executeConfigTools are ignored as well. Failures of all other configuration assistants (NETCA, ASMCA) still stop the installation. runInstaller -updateNodeList is started with -ignoreSysPrereqs (e.g. for missing swap space).

run_configtoolallcommand

true

Run gridSetup.sh -executeConfigTools after root.sh.

2.8. Listener and databases with Oracle Restart

Databases on Oracle Restart are created by oradb_manage_db (playbook manage_db) with storage_type: ASM.

  • The listener in oracle_home_gi is defined in listener_installed with use_gi_home: true (no home). The ORACLE_HOME of Oracle Restart is read from /etc/oracle/olr.loc.

  • A listener in a database home is defined with home and is registered in Oracle Restart with srvctl.

  • The ORACLE_HOME of a database must not be newer than oracle_home_gi.

  • db_create_file_dest and db_recovery_file_dest in init_parameters point to the disk groups (e.g. +DATA, +FRA).

listener_installed:
  - listener_name: LISTENER
    use_gi_home: true
    state: present

oracle_databases:
  - home: db19-gi-ee
    oracle_db_name: DBHAS
    oracle_db_type: SI
    storage_type: ASM
    init_parameters:
      - {name: db_create_file_dest, value: '+DATA', scope: both, state: present}
      - {name: db_recovery_file_dest, value: '+FRA', scope: both, state: present}
      ...

2.9. Out-of-Place Patching of Oracle Restart

With 26ai the home of Oracle Restart can be switched into a new oracle_home_gi (new Release Update), see Out-of-Place Patching. This is the recommended way for Release Updates: install the Golden Image of the new Release Update with oracle_install_image_gi into a new oracle_home_gi (e.g. /u01/app/oracle/product/26/restart_26.04), the switch is done by oraswgi_install. Obsolete homes can be removed with oraswgi_install_remove_obsolete_homes: true.

3. Grid Infrastructure (cluster)

Grid Infrastructure for a Cluster (oracle_install_option_gi: CRS_CONFIG) is the prerequisite for RAC and RAC One Node.

Important
Grid Infrastructure 26ai is supported: tested with 2 nodes on Oracle Linux 9 and a RAC database 26ai (molecule scenario rac, see Test environment: molecule scenario rac).
Grid Infrastructure 19c, RAC databases 19c and RAC One Node are not tested.

All nodes of a cluster are configured together in one play. Most steps run on the 1st node only; gridSetup.sh copies the software to the other nodes with SSH.

3.1. Requirements

  • All nodes in one inventory group (orasw_meta_cluster_hostgroup), all nodes in the same play.

  • Shared disks for ASM, visible with the same path and the same owner on all nodes.

  • 2 networks with the same interface names on all nodes:

    • public network: IP of the node, VIP of every node, SCAN,

    • private network: interconnect (and ASM network with Flex ASM).

  • Name resolution of the nodes, the VIPs and the SCAN on all nodes (DNS or /etc/hosts). The hostname of a node must resolve to its public IP only.

  • sshd on all nodes and SSH logins allowed for the grid user (and oracle_user).

  • Time synchronization (chrony). Without it, the Cluster Time Synchronization Service (CTSS) runs in active mode.

3.2. Inventory structure

The cluster is defined by an inventory group. orasw_meta_cluster_hostgroup contains the name of this group. The order of the hosts in the group is important: the 1st host is the primary node.

hosts.yml
all:
  children:
    rac:                  # orasw_meta_cluster_hostgroup
      hosts:
        rac-1:            # primary node: gridSetup.sh, 1st root.sh
        rac-2:
group_vars/rac/gi.yml
oracle_install_option_gi: CRS_CONFIG
oracle_install_version_gi: 26.1.0.0
oracle_install_image_gi: gi_goldimage_26_03.zip
oracle_home_gi: /u01/app/gridinfra/26.03

orasw_meta_cluster_hostgroup: rac
oracle_cluster_name: cluster-rac
oracle_gi_cluster_type: STANDARD

oracle_scan: rac-scan.example.com
oracle_scan_port: 1521
oracle_gi_nic_pub: eth0
oracle_gi_nic_priv: eth1

role_separation: true          # grid user for GI, oracle user for the databases
default_gipass: "{{ vault_default_gipass }}"

apply_patches_gi: false
patch_before_rootsh: false

device_persistence: udev      # not tested, see 'Device persistence of the ASM disks'
partition_devices: true
oracle_asm_disk_string: /dev/oracle/
oracle_asm_init_dg: crs

asm_diskgroups:
  - diskgroup: crs
    state: present
    properties:
      - {redundancy: normal, ausize: 4}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
    disk:
      - {device: /dev/sdc, asmlabel: crs01, failure_group: fg1}
      - {device: /dev/sdd, asmlabel: crs02, failure_group: fg2}
      - {device: /dev/sde, asmlabel: crs03, failure_group: fg3}
  - diskgroup: data
    state: present
    properties:
      - {redundancy: external}
    attributes:
      - {name: compatible.rdbms, value: 19.0.0.0.0}
      - {name: compatible.asm, value: 23.0.0.0.0}
      - {name: au_size, value: 4M}
    disk:
      - {device: /dev/sdf, asmlabel: data01}
host_vars/rac-1/gi.yml (rac-2 accordingly)
oracle_node_vip: rac-1-vip.example.com

3.3. Installation type and cluster

Variable Default Description

oracle_install_option_gi

''

CRS_CONFIG: configure Grid Infrastructure for a Cluster.

orasw_meta_cluster_hostgroup

''

Name of the inventory group with all nodes of the cluster. Mandatory for CRS_CONFIG; the group must exist in the inventory (assert in orahost_meta).

orasw_meta_primary_node

derived

true for the 1st host of groups[orasw_meta_cluster_hostgroup], false for all other nodes. Do not set it in the inventory.

oracle_cluster_name

{{ orasw_meta_cluster_hostgroup }}

Name of the cluster (clusterName in the response file). Oracle allows at most 15 characters: letters, digits and -, starting with a letter. Set it explicitly when the group name contains _ or is too long. It is also part of the name of the response file (grid-<oracle_cluster_name>.rsp).

oracle_gi_cluster_type

STANDARD

Type of the cluster: STANDARD or STANDALONE (assert in oraswgi_install). Only STANDARD is tested.

oracle_asm_storage_option

FLEX_ASM_STORAGE (12.2+)

Storage option of ASM in the response file. It changes the usage of the private interface in networkInterfaceList, see Networks.

3.3.1. Primary node and the other nodes

orasw_meta_primary_node decides which node executes a step:

Step Primary node Other nodes

Users, groups, kernel parameters, packages (orahost)

x

x

Partitions of the ASM disks (partition_devices), asmlib createdisk

x

udev rules (device_persistence: udev)

x

x

SSH user equivalence (orahost_ssh)

x

x

Unpack the installation media into oracle_home_gi

x

Install cvuqdisk (copied from the primary node via the Ansible controller)

x

x

Response file, cluvfy, gridSetup.sh (copies the home to all nodes)

x

orainstRoot.sh

x

x

root.sh: 1st on the primary node, then on the other nodes one after the other (throttle: 1)

x

x

gridSetup.sh -executeConfigTools

x

CRS flag of oracle_home_gi in the central inventory (runInstaller -updateNodeList -local)

x

x

Disk groups (oraasm_manage_diskgroups)

x

Important
Run the playbooks always for all nodes of the cluster. Many steps on the other nodes use facts or results of the primary node (hostvars).

3.4. ORACLE_HOME and ORACLE_BASE

Variable Default for Grid Infrastructure Description

oracle_home_gi_cl

/u01/app/{{ oracle_install_version_gi }}/grid

Default for oracle_home_gi with Grid Infrastructure (cl = cluster).

oracle_home_gi

{{ oracle_home_gi_cl }}

ORACLE_HOME of Grid Infrastructure. Same path on all nodes. It must not be below grid_base (and not below oracle_base): root.sh changes the owner of the home and its parent directories to root.

grid_base

/u01/app/grid/base

ORACLE_BASE of Grid Infrastructure. It must differ from oracle_base with role_separation: true (assert in orasw_meta).

oracle_base

/u01/app/oracle

ORACLE_BASE of the databases.

Important
Grid Infrastructure (cluster): oracle_home_gi must not be a subdirectory of grid_base, e.g. grid_base: /u01/app/grid/base and oracle_home_gi: /u01/app/gridinfra/26.03.
This is the opposite of Oracle Restart, where oracle_home_gi must be below grid_base. The default oracle_home_gi_cl (/u01/app/<version>/grid) is outside of the default grid_base.

3.5. Users and groups

The variables are the same as for Oracle Restart (role_separation, grid_user, oracle_user, asmadmin_group, …​). For Grid Infrastructure role_separation: true is recommended (and tested):

  • grid_user owns oracle_home_gi, grid_base and the ASM disks,

  • oracle_user owns the database homes.

The install user _grid_install_user is grid_user with role_separation: true, otherwise oracle_user.

3.6. Networks

3.6.1. Public and private network

Variable Default Description

oracle_gi_nic_pub

eth0

Interface of the public network.

oracle_gi_nic_priv

eth1

Interface of the private network (interconnect).

Both interfaces must exist with an IPv4 address on all nodes and have the same name on all nodes. The response file is created on the primary node from its facts (ansible_<interface>.ipv4.network):

networkInterfaceList=<oracle_gi_nic_pub>:<public network>:1,<oracle_gi_nic_priv>:<private network>:5

The interface type of the private network depends on oracle_asm_storage_option:

  • FLEX_ASM_STORAGE (default for 12.2+): 5 = interconnect and ASM network,

  • LOCAL_ASM_STORAGE: 2 = interconnect only.

Grid Infrastructure uses Highly Available IP addresses (HAIP, 169.254.x.x) on the private interface. The private network must allow these addresses between the nodes (no filter on the subnet of the private network).

Note
configure_interconnect and oracle_ic_net of orahost write ifcfg files of the network scripts. They are not usable with Oracle Linux 9. Configure the private network outside of ansible-oracle.

3.6.2. SCAN

Variable Default Description

oracle_scan

not defined

Name of the Single Client Access Name (scanName). Mandatory for CRS_CONFIG (assert in oraswgi_install). scanType is always LOCAL_SCAN.

oracle_scan_port

1521

Port of the SCAN listener. orahost opens it in firewalld together with the listener ports (firewall_open_listener_ports, default true).

The SCAN should resolve to 3 IPs in DNS. A SCAN with 1 IP in /etc/hosts works, but the Cluster Verification Utility reports PRVG-1101 (see Prerequisite checks and ConfigTools for the cluster).

3.6.3. VIPs of the nodes

The response file contains every node of orasw_meta_cluster_hostgroup with its VIP:

clusterNodes=<ansible_fqdn of node 1>:<VIP of node 1>,<ansible_fqdn of node 2>:<VIP of node 2>
Variable Default Description

oracle_node_vip (host variable)

not defined

Name of the VIP of this node, e.g. rac-1-vip.example.com. Recommended: set it in host_vars for every node.

oracle_vip

-vip

Suffix for the VIP, when oracle_node_vip is not defined: <ansible_hostname><oracle_vip>[.<ansible_domain>], e.g. rac-1-vip.example.com.

3.6.4. Name resolution

Without DNS, orahost can write the entries into /etc/hosts:

Variable Default Description

configure_etc_hosts

false

true: add <etc_hosts_ip> <ansible_hostname> <ansible_fqdn> of the node and all etc_hosts_entries to /etc/hosts.

etc_hosts_ip

ansible_default_ipv4.address

IP of the node in /etc/hosts. Must be the public IP.

etc_hosts_entries

[]

Additional entries, e.g. other nodes, VIPs and the SCAN (fqdn, ip, aliases).

configure_etc_hosts: true
etc_hosts_entries:
  - {fqdn: rac-1.example.com, ip: 192.168.56.11}
  - {fqdn: rac-2.example.com, ip: 192.168.56.12}
  - {fqdn: rac-1-vip.example.com, ip: 192.168.56.21}
  - {fqdn: rac-2-vip.example.com, ip: 192.168.56.22}
  - {fqdn: rac-scan.example.com, ip: 192.168.56.30}
Important
The hostname must resolve to exactly 1 IP (the public IP) on all nodes. A 2nd entry with the private IP stops gridSetup.sh with PRVH-1541 / PRVG-13632.

The Grid Naming Service (GNS) is not supported: configureGNS is always false in the response file.

3.7. SSH user equivalence

gridSetup.sh copies oracle_home_gi from the primary node to the other nodes with SSH. orahost_ssh (playbook os) configures the user equivalence automatically for CRS_CONFIG for oracle_user and, with role_separation: true, for grid_user:

  • creates a key pair per user and node (orahost_ssh_key_type, default rsa, orahost_ssh_key_size, default 4096),

  • adds the public keys of all nodes into authorized_keys on all nodes,

  • adds the host keys (ssh-keyscan -t <orahost_ssh_hostkeytypes> for ansible_hostname) into known_hosts on all nodes.

configure_ssh is not needed for Grid Infrastructure.

sshd must run on all nodes and allow logins of these users. Typical failures: /run/nologin exists, AllowUsers in sshd_config. gridSetup.sh stops with PRVF-4098 : User equivalence not found for node.

3.8. Shared storage for ASM

The ASM disks must be shared between all nodes:

  • Every device of asm_diskgroups must exist on every node with the same path. With device_persistence: '' the owner (_grid_install_user) and the group (asmadmin_group) must be set before gridSetup.sh.

  • Partitions (partition_devices) and asmlib labels are created on the primary node only.

  • oracle_asm_init_dg contains the OCR and the voting files. The number of voting files depends on the redundancy: external 1, normal 3, high 5 disks (failure groups). With normal and 2 storage systems use a quorum failure group (quorum: true) for the 3rd voting file.

3.9. Prerequisite checks and ConfigTools for the cluster

Variable Default Description

gi_ignoreprereq

false

true: no cluvfy before the installation, gridSetup.sh -ignorePrereq, failures of the post check with CVU in -executeConfigTools are ignored (e.g. PRVG-1101 for a SCAN without DNS), runInstaller -updateNodeList -ignoreSysPrereqs. Failures of NETCA or ASMCA still stop the installation.

run_configtoolallcommand

true

Run gridSetup.sh -executeConfigTools on the primary node after root.sh on all nodes.

Tip
Start with gi_ignoreprereq: false. Use true only for known and accepted failures of cluvfy, e.g. in test environments.

3.10. RAC databases

A RAC database needs 2 things on top of Grid Infrastructure: an ORACLE_HOME on all nodes (playbook swdb) and the database itself (playbook manage_db).

3.10.1. ORACLE_HOME of a RAC database

The database home is defined in db_homes_config and installed with db_homes_installed, as for Single Instance databases. oraswdb_install decides from oracle_databases how the home is installed: when a database with oracle_db_type: RAC or RACONENODE uses the home, it is installed as cluster home.

Step Primary node Other nodes

Unpack the installation media into the ORACLE_HOME

x

runInstaller with clusterNodes = all hosts of orasw_meta_cluster_hostgroup (copies the home to all nodes)

x

root.sh of the database home (other nodes only after runInstaller on the primary node)

x

x

  • The version of the database home must not be newer than Grid Infrastructure, e.g. a database home with 23.26.3 needs Grid Infrastructure with 23.26.3 or newer.

  • oracle_databases must be defined for swdb, not only for manage_db: without a RAC database in oracle_databases, the home is installed as Single Instance home on every node.

  • All databases of a home must have the same oracle_db_type (assert in orasw_meta).

  • Read-Only Oracle Homes (readonly_home: true with oracle_home_name) are supported for RAC as well.

group_vars/rac/db.yml: database home
db_homes_config:
  db26-rac-ee:
    version: 26.1.0.0
    oracle_home: /u01/app/oracle/product/26/db_26.03
    imagename: db_home_26.03.zip      # Golden Image with RU 23.26.3
    edition: EE
    readonly_home: true
    oracle_home_name: db2603racee

db_homes_installed:
  - home: db26-rac-ee
    state: present
    apply_patches: false

3.10.2. Database

oradb_manage_db creates the database with dbca on the primary node:

  • nodelist in the response file of dbca contains all hosts of orasw_meta_cluster_hostgroup.

  • The instances are named <oracle_db_name><n> (e.g. RACCDB1 on the 1st node, RACCDB2 on the 2nd node).

  • dbca registers the database in Grid Infrastructure (srvctl config database), the database is administrator managed.

  • /etc/oratab gets an entry <oracle_db_name>:<ORACLE_HOME>:N on all nodes (start and stop by Clusterware, not by dbstart).

Attribute in oracle_databases Value Description

oracle_db_type

RAC

RAC database on all nodes of orasw_meta_cluster_hostgroup. (RACONENODE is not tested.)

storage_type

ASM

The datafiles are in the disk group of db_create_file_dest.

init_parameters

local_listener is not needed: Clusterware sets local_listener (VIP of the node) and remote_listener (SCAN). The assert for local_listener applies to Single Instance databases only.
db_create_file_dest, db_recovery_file_dest (with dbca: false for db_recovery_file_dest_size), processes, sga_target, pga_aggregate_target and control_management_pack_access are mandatory as for every database.

Variable Default Description

oradb_manage_db_dbca_ignore_prereq_failure

{{ gi_ignoreprereq }}

true: dbca -ignorePrereqFailure. dbca executes its prerequisite checks for RAC databases (e.g. SCAN, runlevel), but failures do not stop the creation of the database. The default follows gi_ignoreprereq.

group_vars/rac/db.yml: database
oracle_databases:
  - home: db26-rac-ee
    oracle_db_name: RACCDB
    oracle_db_type: RAC
    storage_type: ASM
    oracle_database_type: MULTIPURPOSE
    redolog_size: 50M
    redolog_groups: 2
    listener_port: 1521
    archivelog: false
    flashback: false
    force_logging: false
    state: present
    init_parameters:
      - {name: max_pdbs, value: '3', scope: both, state: present}
      - {name: control_management_pack_access, value: 'NONE', scope: both, state: present}
      - {name: db_create_file_dest, value: '+DATA', scope: both, state: present}
      - {name: db_recovery_file_dest_size, value: '8G', scope: both, state: present, dbca: false}
      - {name: db_recovery_file_dest, value: '+FRA', scope: both, state: present}
      - {name: pga_aggregate_target, value: '256M', scope: both, state: present}
      - {name: processes, value: '320', scope: spfile, state: present}
      - {name: sga_target, value: '2560M', scope: spfile, state: present}

Sizing hints from the tests with 26ai:

  • dbca checks the free space of the disk group of the Fast Recovery Area before the creation (DBT-06604, about 13 GB for a new Container Database), independent from db_recovery_file_dest_size.

  • sga_target: 1536M was too small: datapatch during dbca failed with ORA-04031 in the java pool and the instance hung. 2560M works.

3.11. Limitations

  • Out-of-Place Patching of Grid Infrastructure 26ai (switch into a new oracle_home_gi) is supported, root.sh runs rolling, see Out-of-Place Patching. A switch back into a former home of Grid Infrastructure is not implemented (assert in oraswgi_install). The switch of a RAC database into a new ORACLE_HOME is not supported.

  • oraswgi_install_remove_obsolete_homes is supported for Oracle Restart only.

  • RAC databases are tested with 26ai only (2 nodes, administrator managed, Container Database without PDB).

  • RAC One Node is not tested.

  • Grid Infrastructure 19c is not tested.

  • Grid Infrastructure (cluster) on hosts with FIPS (ansible_fips) is not supported: oracle_tmp_stage is not /tmp, _JAVA_OPTIONS (-Djava.io.tmpdir) is passed to root.sh and java prints Picked up _JAVA_OPTIONS: …​. rootcrs.pl parses this line as output of cluutil and stops with CLSRSC-732. Oracle Restart is not affected.

3.12. Test environment: molecule scenario rac

The molecule scenario extensions/molecule/rac installs Grid Infrastructure 26ai on 2 containers with Oracle Linux 9 and creates the RAC database RACCDB (26ai, instances RACCDB1 and RACCDB2). It is a test environment, not a template for production:

  • ASM disks are loop devices on sparse files (device_persistence: '').

  • SCAN with 1 IP and VIPs in /etc/hosts ⇒ gi_ignoreprereq: true.

  • Container specific workarounds (init.ohasd with cgroup v2, /run/nologin, entries of Docker in /etc/hosts).

  • converge.yml starts only with at least 12 GB usable memory on the Docker host (MemAvailable + memory of running containers of the scenario), override with MOLECULE_RAC_MIN_FREE_MB=<MB>.

Inventory: extensions/molecule/shared_config/inventory/group_vars/rac (gi.yml, db.yml) and host_vars/rac-*.yml.
verify.yml checks the cluster stack, SCAN, VIPs, ASM, voting files, OCR, disk groups and the open instances of the RAC databases.
verify.yml uses the running home of Grid Infrastructure (crs_home in /etc/oracle/olr.loc).
side_effect.yml: Out-of-Place Patching of Grid Infrastructure (gold image 23.26.3 ⇒ 23.26.4, root.sh rolling), PDB ORCLPDB1, tnsnames.ora, sqlnet.ora, wallet and the module tests (module_tests.yml) on the 1st node.

15 - RMAN Backups with oradb_rman

This guide describes the configuration of RMAN backups with the role oradb_rman and the backup script rman_backup.sh.

1. Overview

oradb_rman is executed by the playbook opitzconsulting.ansible_oracle.manage_db (last role). It configures for every database in oracle_databases with rman_jobs:

Table 1. Files created by oradb_rman
File Description

$ORACLE_BASE/bin/rman_backup.sh

Backup script, executes an RMAN script

<RMAN_SCRIPTDIR>/<job>.rman

RMAN script of a job, from the template <job>.rman.j2

$ORACLE_BASE/admin/<db_unique_name>/rman/rman_backup.cfg

Configuration of rman_backup.sh for the database (mode 0600)

/etc/cron.d/oracle_rman_ansible

Crontab with one entry per job (oradb_rman_cronfile)

<oradb_rman_tns_admin>/tnsnames.ora, sqlnet.ora

Connection to the RMAN catalog (rman_tnsalias)

<oradb_rman_wallet_loc>

Wallet with the credential of the RMAN catalog (rman_wallet: true)

The crontab contains no passwords and no directories:

10 01 * * 0 oracle /u01/app/oracle/bin/rman_backup.sh -a online_level0 -s DB1 > /dev/null 2>&1

All other settings are read by rman_backup.sh from rman_backup.cfg. The output is written by rman_backup.sh to a logfile per execution, see Logfiles.

2. Inventory

2.1. rman_jobs

rman_jobs is a list in an entry of oracle_databases. Every job executes the RMAN script <name>.rman.

Table 2. Attributes of rman_jobs
Attribute Mandatory Description

name

yes

Name of the job = name of the template <name>.rman.j2 and of the RMAN script <name>.rman

state

no

present (default) or absent (removes the crontab entry)

minute, hour, day, weekday

for cron

Schedule of the crontab entry. A job without schedule gets no crontab entry (e.g. parameter).

disabled

no

true: the crontab entry is commented out

immediate

no

true: the job is executed during the playbook (without catalog), e.g. for parameter. service and role are used as in the crontab.

service

no

Backup only on the node where the service is running (Grid Infrastructure / Oracle Restart), rman_backup.sh --service. Not together with role.

role

no

primary or standby: backup only when the database has this role (Data Guard), rman_backup.sh --role. Not together with service.

Important
service and role are mutually exclusive in a job, an assert stops the role when both are defined. Both select the member or node which executes the backup, a combination makes no sense. For Data Guard use role.

2.2. Attributes of oracle_databases

Table 3. RMAN attributes of an entry in oracle_databases
Attribute Description

rman_jobs

List of jobs, see above

rman_retention_policy

CONFIGURE RETENTION POLICY in the template parameter, default: oradb_rman_retention_policy_default

rman_channel_disk

CONFIGURE CHANNEL DEVICE TYPE DISK, default: oradb_rman_channel_disk_default

rman_controlfile_autobackup_disk

CONFIGURE CONTROLFILE AUTOBACKUP FORMAT, default: oradb_rman_controlfile_autobackup_disk_default

rman_script_dir

Directory of the RMAN scripts of the database, default: oradb_rman_script_dir

rman_log_dir

Directory of the RMAN logfiles of the database, default: oradb_rman_log_dir

rman_tnsalias

TNS alias of the RMAN catalog (entry in oracle_tnsnames_config)

rman_user

Owner of the RMAN catalog, password from dbpasswords[<rman_tnsalias>][<rman_user>]

rman_wallet

true: the password is stored in a wallet (oradb_rman_wallet_loc), the connect is /@<rman_tnsalias>

oracle_db_unique_name

Directory of rman_backup.cfg: $ORACLE_BASE/admin/<db_unique_name>/rman, default: oracle_db_name

rman_password in oracle_databases is deprecated. Use dbpasswords instead.

2.3. control_file_record_keep_time

Best practice: control_file_record_keep_time is defined in init_parameters of every database with rman_jobs. The controlfile keeps the records of the backups for this number of days. Without catalog, RMAN knows only the backups in the controlfile.

oradb_rman asserts:

  • control_file_record_keep_time is defined in init_parameters (state: present, sid: '*'), and

  • the value is >= <n> for the retention policy RECOVERY WINDOW OF <n> DAYS.

init_parameters:
  - {name: control_file_record_keep_time, value: '30', scope: both, state: present}

The assert can be disabled with oradb_rman_assert_control_file_record_keep_time: false.

2.4. Global variables

The variables of the role are described in the README of oradb_rman.

Table 4. Important variables
Variable Default Description

oradb_rman_script_dir

$ORACLE_BASE/rman

RMAN scripts (RMAN_SCRIPTDIR)

oradb_rman_log_dir

$ORACLE_BASE/rman/log

Logfiles of rman_backup.sh (RMAN_LOGDIR)

oradb_rman_log_retention

30

Days to keep the logfiles (RMAN_LOG_RETENTION), 0: keep all

oradb_rman_cronfile

oracle_rman_ansible

File in /etc/cron.d

oradb_rman_tns_admin

$ORACLE_BASE/rman/network/admin

TNS_ADMIN for the catalog (RMAN_TNS_ADMIN)

oradb_rman_cron_mkjob

false

Prefix mk-job for checkmk

Important
The template parameter uses rman_device_type_disk (e.g. 'PARALLELISM 1 BACKUP TYPE TO COMPRESSED BACKUPSET'), the templates *_fra_disk use rman_fra_backupdir. The role has no default for these variables, define them in the inventory when the templates are used.

2.5. RMAN templates

Table 5. Templates of oradb_rman
Name Description

parameter

RMAN configuration: retention policy, controlfile autobackup, channel

parameter_standby

Like parameter without CONFIGURE RETENTION POLICY (not configurable on a standby database), use it with role: standby

online_level0

Online incremental level 0 backup, archive logs, delete obsolete <rman_retention_policy>

online_level1

Online incremental level 1 backup, archive logs, delete obsolete

offline_level0

Offline level 0 backup: the database is restarted in MOUNT, opened afterwards

archivelog

Backup of the archive logs, deletes the archive logs backed up 1 time

*_fra_disk, parameter_rac

Variants for backups of the FRA to disk and for RAC. The snapshot controlfile is created in db_create_file_dest from init_parameters

Own templates <name>.rman.j2 are found by Ansible in the templates directory of the playbook (Ansible search path), the job uses the same name.

2.6. Example

Database with RMAN catalog and Data Guard
oracle_databases:
  - oracle_db_name: DB1
    oracle_db_unique_name: DB1_SITE1
    ...
    rman_retention_policy: 'RECOVERY WINDOW OF 14 DAYS'
    rman_tnsalias: RMANCAT
    rman_user: rman_db1
    rman_jobs:
      - {name: parameter, role: primary, immediate: true}
      - {name: parameter_standby, role: standby, immediate: true}
      - {name: online_level0, role: standby, weekday: "0", hour: "01", minute: "10", day: "*"}
      - {name: online_level1, role: standby, weekday: "1-6", hour: "01", minute: "10", day: "*"}
      - {name: archivelog, weekday: "*", hour: "*", minute: "30", day: "*"}

rman_device_type_disk: 'PARALLELISM 1 BACKUP TYPE TO COMPRESSED BACKUPSET'

dbpasswords:
  RMANCAT:
    rman_db1: <password>

3. rman_backup.sh

3.1. Usage

rman_backup.sh -a <action> [-s <ORACLE_SID|DB_NAME>] [--admin <Directory>]
               [-r <Directory>] [-l <Directory>] [-c <CATALOGCONNECT>] [-t <TARGETCONNECT>]
               [--service <Service>] [--role <PRIMARY|STANDBY>] [--nocatalog]
rman_backup.sh -h

-a is the only mandatory parameter. All other settings are read from the command line, the environment or the configuration file.

Interactive usage as oracle
export ORACLE_SID=DB1
$ORACLE_BASE/bin/rman_backup.sh -a archivelog
$ORACLE_BASE/bin/rman_backup.sh -a online_level0 --nocatalog

3.2. Order of the settings

The first value wins:

  1. parameter on the command line

  2. environment variable

  3. configuration file <ORACLE_ADMIN>/rman/rman_backup.cfg

  4. default of rman_backup.sh

Table 6. Parameters, environment variables and defaults
Parameter Environment / rman_backup.cfg Default Description

-a/--action

-

-

RMAN script <RMAN_SCRIPTDIR>/<action>.rman

-s/--ORACLE_SID

ORACLE_SID

-

ORACLE_SID, DB_NAME in RAC environments (the instance is determined with srvctl)

--admin

ORACLE_ADMIN

see below

Directory of rman/rman_backup.cfg

-r/--rmanscriptdir

RMAN_SCRIPTDIR

<ORACLE_ADMIN>/rman

RMAN scripts

-l/--logdir

RMAN_LOGDIR

<RMAN_SCRIPTDIR>/log

Directory of the logfiles, see Logfiles

-

RMAN_LOG_RETENTION

30

Remove logfiles older than <n> days, 0: keep all

-c/--catalogconnect

RMAN_CATALOGCONNECT

no catalog

connect catalog <value>

--nocatalog

-

-

No catalog, even when RMAN_CATALOGCONNECT is set

-t/--targetconnect

RMAN_TARGETCONNECT

/

connect target <value>

--service

RMAN_SERVICE

-

Backup only when the service runs on the current node (GI / Oracle Restart)

--role

RMAN_ROLE

-

PRIMARY or STANDBY (= PHYSICAL STANDBY). Use either --service or --role.

-

RMAN_TNS_ADMIN

-

TNS_ADMIN for RMAN

The old environment variables RMANSCRIPTDIR, RMANLOGDIR, CATALOGCONNECT, TARGETCONNECT and RMANTNS_ADMIN are still supported.

Warning
Parameters are visible in the process list. Use rman_backup.cfg for the password of the catalog, not -c.

3.3. ORACLE_ADMIN

rman_backup.sh determines ORACLE_ADMIN in the following order:

  1. --admin or the environment variable ORACLE_ADMIN

  2. $ORACLE_BASE/admin/<db_unique_name>: db_unique_name is read from the running instance (v$parameter, sqlplus / as sysdba, works in NOMOUNT)

  3. $ORACLE_BASE/admin/<ORACLE_SID>, when the instance is not running

The db_unique_name makes the configuration unique on every member of a Data Guard configuration: the ORACLE_SID is identical on all members, the db_unique_name is different.

3.4. rman_backup.cfg

The configuration file contains lines with KEY=VALUE. It is read line by line and not sourced: shell code is not executed.

# Managed by Ansible
RMAN_SCRIPTDIR=/u01/app/oracle/rman
RMAN_LOGDIR=/u01/app/oracle/rman/log
RMAN_LOG_RETENTION=30
RMAN_TNS_ADMIN=/u01/app/oracle/rman/network/admin
RMAN_CATALOGCONNECT=rman_db1/<password>@RMANCAT
  • Allowed keys: RMAN_SCRIPTDIR, RMAN_LOGDIR, RMAN_LOG_RETENTION, RMAN_TARGETCONNECT, RMAN_CATALOGCONNECT, RMAN_SERVICE, RMAN_ROLE, RMAN_TNS_ADMIN. Unknown keys and invalid lines are ignored with a warning.

  • One pair of quotes around the value is removed.

  • Empty lines and lines starting with # are ignored.

  • The file could contain the password of the catalog: mode 0600. rman_backup.sh warns, when the file is readable by other users.

  • oradb_rman creates the file. Manual changes are overwritten by the next execution of the role.

RMAN_SERVICE and RMAN_ROLE are set per job in the crontab (--service, --role) and not in the file, because the jobs of a database could use different values.

3.5. Catalog

rman_backup.sh checks the connection to the catalog before the backup. The backup runs without catalog, when the catalog is not reachable.

3.6. Logfiles

rman_backup.sh writes all output of the script, sqlplus and RMAN to stdout and to a logfile per execution. Manual executions are logged the same way as the cron jobs, the crontab redirects the output to /dev/null.

<RMAN_LOGDIR>/<db_unique_name>_<action>_<YYYYmmdd>_<HHMMSS>_<pid>.log
<RMAN_LOGDIR>/<db_unique_name>_<action>.log -> logfile of the last execution
  • Every execution has its own logfile: parallel executions do not mix their output.

  • The symlink points to the logfile of the last execution of the action (for monitoring and tail).

  • Messages before the logfile is opened (e.g. reading rman_backup.cfg) are written to the logfile afterwards.

  • Logfiles of the database and action older than RMAN_LOG_RETENTION days are removed.

  • Errors before the logfile is opened (e.g. ORACLE_HOME of the ORACLE_SID not found) and the major actions are logged in syslog (tag rman_backup.sh).

  • <db_unique_name> is the name of the directory ORACLE_ADMIN, when the instance is not running.

3.7. Parallel executions

Only one execution of an action is allowed for a database (lock file <RMAN_LOGDIR>/.<db_unique_name>_<action>.lock, flock). A 2nd execution of the same action stops immediately with exit code 30, e.g. an hourly archivelog while the last execution is still running. Different actions of a database run in parallel, e.g. archivelog during online_level0.

3.8. Exit codes

Table 7. Exit codes of rman_backup.sh
Code Description

0

RMAN successful, or backup skipped (--service not running on this node, --role does not match)

1

Wrong parameters, service not existing

10, 11, 12

ORACLE_HOME of the ORACLE_SID not found (/etc/oratab, with Grid Infrastructure / Oracle Restart the database resource ora.<ORACLE_SID>.db from crsctl), ORACLE_HOME or ORACLE_BASE not existing

13

Database role could not be determined (--role, instance not mounted)

14

rman_backup.cfg not readable

21, 22, 23

Log directory, RMAN script or logfile not existing

30

The action is running already for the database

99

RMAN failed

4. Data Guard

All members of a Data Guard configuration get the same rman_jobs. role decides, on which member a job executes the backup:

  • role: standby: backups of the database on the physical standby, the primary is not loaded

  • role: primary: backups on the primary only

  • without role: the job runs on every member

The CONFIGURE commands are stored in the controlfile of each member and must be executed on all members:

  • parameter with role: primary

  • parameter_standby with role: standby: without CONFIGURE RETENTION POLICY, the retention policy is not configurable on a standby database

online_level0 uses rman_retention_policy in delete obsolete. Therefore it works on a standby database, whose controlfile (e.g. from RMAN DUPLICATE) does not contain the retention policy, and on a new primary after a switchover.

After a switchover the backups follow the role automatically, the inventory is not changed.

Example: CONFIGURE on all members, backups on the primary only
oracle_databases:
  - oracle_db_name: DB1
    rman_jobs:
      # executed during the playbook: parameter on the primary, parameter_standby on the standby
      - {name: parameter, role: primary, immediate: true}
      - {name: parameter_standby, role: standby, immediate: true}
      # role primary: crontab entry on every member, rman_backup.sh --role primary skips it on the standby
      - {name: online_level0, role: primary, weekday: "0", hour: "01", minute: "10", day: "*"}
      - {name: archivelog, role: primary, weekday: "*", hour: "*", minute: "30", day: "*"}

All members get the same crontab entries. rman_backup.sh checks database_role in v$database at every execution and skips the backup with exit code 0, when the role does not match. Valid values of role are primary and standby (checked by an assert).

Important
Use an RMAN catalog for Data Guard. Backups of a physical standby database can be used for the primary database (and vice versa), the catalog makes the backups known to all members.
Note
archivelog deletes all archive logs that were backed up once. The ARCHIVELOG DELETION POLICY (e.g. APPLIED ON ALL STANDBY) is configured by oradb_standby with the mandatory attribute archivelog_deletion_policy in db_standby_config, see Standby Databases - Archivelog deletion policy, to prevent the deletion of archive logs which are not yet applied on the standby database.

5. Migration from older versions

  • The crontab entries contained the directories and the catalog connect (-r, -l, -c) or the environment variables RMANLOGDIR, RMANSCRIPTDIR and RMANTNS_ADMIN in /etc/cron.d. oradb_rman removes the environment variables and recreates the crontab entries. The values are in rman_backup.cfg now.

  • Existing calls of rman_backup.sh with all parameters still work.

  • The logfiles are written to RMAN_LOGDIR per execution. An existing logfile <RMAN_LOGDIR>/<name>_<action>.log of older versions is renamed to *.log.old by the first execution. The cron output in /var/log/oracle/rman/log (oradb_rman_cron_logdir) is no longer written, the directory can be removed.

  • Before this change, the attributes service, rman_retention_policy, rman_channel_disk, rman_controlfile_autobackup_disk, rman_log_dir, rman_script_dir and the catalog connect of a database were ignored by oradb_rman (bug). They are used now: check the values in the inventory before the update.

16 - Data Guard Guide

This guide installs an Oracle AI Database 26ai with Data Guard on two Oracle Linux 9 VMs with ansible-oracle:

  • dataguard-dbfs1: primary database DB1_SITE1 (container database DB1 with the PDB ORCLPDB)

  • dataguard-dbfs2: physical standby database DB1_SITE2, managed by the Data Guard Broker

The 1st VM exports its installation media with NFS. AutoUpgrade downloads the gold image on the 1st VM only, the 2nd VM mounts the installation media with NFS.

The 1st VM exports a 2nd NFS share for the RMAN backups, the 2nd VM mounts it permanently. A weekly level 0 backup and an hourly archivelog backup run on the current primary database.

The guide is based on the Beginners Guide. Steps that are identical are referenced and not repeated.

See Standby Databases for the details of the Data Guard implementation in ansible-oracle.

1. Overview

1.1. Architecture overview

Architecture

1.2. What are we going to do?

  1. Prepare the Ansible controller: Ansible in Ubuntu on WSL2 on Windows.

  2. Create two Oracle Linux 9 VMs on Hyper-V.

  3. Configure the inventory with the IP addresses and login user of the VMs.

  4. Start the playbook playbooks/do_all.yml. It executes all needed playbooks:

    1. opitzconsulting.ansible_oracle.os: prepare both VMs

    2. playbooks/nfs_server.yml: NFS server on dataguard-dbfs1, export of the installation media /u01/sw and of the RMAN backups /u01/app/oracle/backup, permanent mount of the RMAN backups on dataguard-dbfs2

    3. playbooks/autoupgrade.yml: install AutoUpgrade on dataguard-dbfs1 and download a gold image from My Oracle Support

    4. opitzconsulting.ansible_oracle.single_instance_fs on both VMs:

      1. install the ORACLE_HOME on dataguard-dbfs1 from /u01/sw and on dataguard-dbfs2 from the NFS share,

      2. create the primary database DB1_SITE1 with dbca,

      3. create the physical standby database DB1_SITE2 with RMAN DUPLICATE …​ FROM ACTIVE DATABASE,

      4. create the Data Guard Broker configuration DG1,

      5. configure the RMAN backups with cron (role oradb_rman).

1.3. Defaults

Table 1. Defaults used in this guide
Value Description

~/git/ansible-oracle

git repository on the Ansible controller

~/git/ansible-oracle/example/dataguard/ansible

Working directory for all Ansible commands

dataguard-dbfs1

Name of the 1st VM in the inventory: initial primary, NFS server

192.168.66.166

IP address of the 1st VM (ansible_host)

dataguard-dbfs2

Name of the 2nd VM in the inventory: initial physical standby, NFS client

192.168.66.167

IP address of the 2nd VM (ansible_host)

ol

Login user on the VMs (ansible_user)

/u01/sw

Installation media: target of the download on dataguard-dbfs1, NFS mount point on dataguard-dbfs2

/u01/app/oracle/backup

RMAN backups: directory on dataguard-dbfs1, NFS mount point on dataguard-dbfs2

/u01/stage

Staging directory on the VMs (oracle_stage)

2. Requirements

2.1. Oracle Linux 9 VMs

Two VMs with the requirements of the Beginners Guide are needed.

In addition:

  • Both VMs reach each other on the fixed IP addresses: SSH is not needed between the VMs, but the listener port 1521 (Data Guard) and NFS from dataguard-dbfs2 to dataguard-dbfs1. The playbooks open the ports in firewalld.

  • Internet access for My Oracle Support is needed on dataguard-dbfs1 only.

2.2. Create the VMs with Hyper-V and Packer

Build the Oracle Linux 9 template as described in Create the VM with Hyper-V and Packer of the Beginners Guide.

Important
All steps in this chapter are executed in PowerShell on Windows, not in WSL.
Create the VMs from the template
cd OL9
.\create-vm.ps1 -NewVMName dataguard-dbfs1 -Hostname dataguard-dbfs1 -ExternalSwitch intern_66 -FixedIP 192.168.66.166 -CPUs 2 -MemoryGB 8
.\create-vm.ps1 -NewVMName dataguard-dbfs2 -Hostname dataguard-dbfs2 -ExternalSwitch intern_66 -FixedIP 192.168.66.167 -CPUs 2 -MemoryGB 8
Important
The template has a disk with 80 GB, but the guide needs 81 GB free space in the volume group ol.
Increase the disk of both VMs, e.g. to 130 GB, before you start the installation.

2.3. Ansible controller and My Oracle Support

The requirements are identical to the Beginners Guide:

3. Setup Ansible controller

Follow Setup Ansible controller of the Beginners Guide, but install the collections in the working directory of this guide.

Install collections
cd ~/git/ansible-oracle/example/dataguard/ansible
ansible-galaxy collection install -r requirements.yml
Important
Data Guard needs ansible-oracle 5.0.0 or newer (role oradb_standby).
Use the development entry in requirements.yml to install the collection from the git repository, as long as 5.0.0 is not released.

4. Configure inventory

4.1. IP addresses and login user

The VMs are configured in inventory/hosts.yml. Change ansible_host and ansible_user for your VMs.

inventory/hosts.yml
---
all:
  children:
    # both database servers of the Data Guard configuration DG1
    dbfs:
      hosts:
        dataguard-dbfs1:
          # IP address or DNS name of the 1st Oracle Linux 9 VM (initial primary)
          ansible_host: 192.168.66.166
          # Login user on the VM with sudo privileges (SSH key login)
          ansible_user: ol
        dataguard-dbfs2:
          # IP address or DNS name of the 2nd Oracle Linux 9 VM (initial physical standby)
          ansible_host: 192.168.66.167
          ansible_user: ol

    # NFS server for the installation media (playbooks/nfs_server.yml)
    # AutoUpgrade downloads the gold image on this host only.
    nfs_server:
      hosts:
        dataguard-dbfs1:

    # Database servers which mount the installation media from nfs_server
    nfs_client:
      hosts:
        dataguard-dbfs2:

ansible_host is used for the connect identifiers of the databases (group_vars/dbfs/tnsnames.yml), for local_listener (group_vars/dbfs/database_DB1.yml) and for the NFS export. No DNS is needed between the VMs.

Note
The host names dataguard-dbfs1 and dataguard-dbfs2 are used in db_standby_config (group_vars/dbfs/standby.yml), in group_vars/dbfs/tnsnames.yml and as directories in host_vars. Change all of them, when you rename a host.

4.2. SSH key, connection test, credentials and passwords

Follow the Beginners Guide for both VMs:

4.3. Data Guard configuration

Table 2. Inventory files for Data Guard
File Content

group_vars/dbfs/standby.yml

db_standby_config: configuration DG1 with the members DB1_SITE1 (initial_role: primary) and DB1_SITE2 (initial_role: physical_standby), archivelog_deletion_policy: APPLIED ON ALL STANDBY (mandatory)

group_vars/dbfs/tnsnames.yml

Connect identifiers DB1_SITE1 and DB1_SITE2 with the IP addresses of the VMs

group_vars/dbfs/database_DB1.yml

Database DB1 with standby_config: DG1 for both members

host_vars/<host>/databases.yml

db1_unique_name: db_unique_name of the member on the host

The database is defined once for both members. Only the db_unique_name differs, it is set per host with db1_unique_name.

Table 3. Data Guard specific settings of the database in group_vars/dbfs/database_DB1.yml
Setting Description

oracle_db_unique_name: "{{ db1_unique_name }}"

DB1_SITE1 or DB1_SITE2

standby_config: DG1

Assignment to the Data Guard configuration

listener_name: LISTENER

Static listener entry <db_unique_name>_DGMGRL for RMAN DUPLICATE and the broker

archivelog, force_logging, flashback

true

local_listener

//<ansible_host>:1521

db_create_file_dest, db_create_online_log_dest_1

Oracle Managed Files (OMF) are mandatory

dg_broker_config_file1/2

/u01/app/oracle/dbs/dr1<db_unique_name>.dat: outside of the read-only ORACLE_HOME

dg_broker_start

TRUE, below dg_broker_config_file1/2

standby_file_management

AUTO

4.4. Installation media with NFS

AutoUpgrade is installed and executed on dataguard-dbfs1 only (group nfs_server). The download of the gold image works as in the Beginners Guide, see AutoUpgrade on the VM.

playbooks/nfs_server.yml configures dataguard-dbfs1 as NFS server:

  • package nfs-utils, service nfs-server

  • directory /u01/sw (owner oracle), target of the AutoUpgrade download

  • export of /u01/sw read only for the IP addresses of the group nfs_client (/etc/exports.d/oracle-sw.exports)

  • services nfs, rpc-bind and mountd in firewalld, when firewalld is running

dataguard-dbfs2 (group nfs_client) mounts the share during the installation of the ORACLE_HOME (role oraswdb_install) and unmounts it afterwards.

Table 4. NFS settings in inventory/group_vars/all/software_src.yml
Variable Value Description

install_from_nfs

false, true in host_vars/dataguard-dbfs2/software_src.yml

Mount the NFS share during the installation

nfs_server_sw

ansible_host of the 1st host of the group nfs_server

NFS server

nfs_server_sw_path

/u01/sw

Exported directory on the NFS server

oracle_stage_remote

/u01/sw

Source of the installation media: mount point on the NFS client, local directory on the NFS server

oracle_sw_copy

false

The ORACLE_HOME is extracted directly from oracle_stage_remote

The mount point is identical to the directory on the NFS server. Therefore the symlink db_home_26_latest.zip of the gold image works on both hosts.

Tip
Use the same settings for any database server, which mounts its installation media from an existing NFS server: install_from_nfs: true, nfs_server_sw, nfs_server_sw_path and oracle_stage_remote.

4.5. RMAN backups with NFS

playbooks/nfs_server.yml configures the NFS share for the RMAN backups as well:

  • dataguard-dbfs1: directory /u01/app/oracle/backup and one subdirectory per oracle_db_name (DB1), owner oracle, mode 0750 (RMAN does not create directories)

  • dataguard-dbfs1: export of /u01/app/oracle/backup read write for the group nfs_client (/etc/exports.d/oracle-backup.exports)

  • dataguard-dbfs2: permanent mount (/etc/fstab) on the same path

Both members write their backups into the same directory. Backups of the primary can be used on the standby and vice versa.

Table 5. Backup settings in inventory/group_vars/all/backup.yml
Variable Value Description

nfs_server_backup

ansible_host of the 1st host of the group nfs_server

NFS server

nfs_server_backup_path

/u01/app/oracle/backup

Exported directory and mount point

nfs_client_backup_opts

rw,bg,hard,tcp,timeo=600,rsize=1048576,wsize=1048576

Mount options on the NFS client

rman_device_type_disk

PARALLELISM 1 BACKUP TYPE TO COMPRESSED BACKUPSET

CONFIGURE DEVICE TYPE DISK

The RMAN settings are part of oracle_database_db1 in group_vars/dbfs/database_DB1.yml:

RMAN settings of DB1
  rman_retention_policy: 'RECOVERY WINDOW OF 7 DAYS'
  rman_channel_disk: "format '/u01/app/oracle/backup/%d/%d_%T_%U'"
  rman_controlfile_autobackup_disk: "'/u01/app/oracle/backup/%d/%d_{{ db1_unique_name }}_%F'"
  rman_jobs:
    - {name: parameter, role: primary, immediate: true}
    - {name: parameter_standby, role: standby, immediate: true}
    - {name: online_level0, role: primary, weekday: "0", hour: "01", minute: "10", day: "*"}
    - {name: archivelog, role: primary, weekday: "*", hour: "*", minute: "30", day: "*"}
Table 6. RMAN jobs
Job Schedule Executed on

parameter

during the playbook

current primary: CONFIGURE …​ in the controlfile

parameter_standby

during the playbook

current standby: CONFIGURE …​ without the retention policy (not configurable on a standby)

online_level0

Sunday 01:10

current primary, delete obsolete with the retention policy of the inventory

archivelog

hourly at minute 30

current primary

Both members get the same crontab entries. rman_backup.sh --role primary skips the backup on the physical standby (exit code 0). After a switchover the backups run on the new primary, the inventory is not changed.

%F contains the DBID, which is identical on all members. Therefore the format of the controlfile autobackup contains the db_unique_name.

control_file_record_keep_time (30) is greater than the recovery window (7 days), see RMAN Guide.

Logfiles of the backups
ls -l /u01/app/oracle/rman/log

oradb_standby configures ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY on both members (archivelog_deletion_policy in group_vars/dbfs/standby.yml): archivelog deletes only archive logs, which are applied on the standby database. BACKED UP …​ is not used: without catalog the standby database does not know the backups of the primary and its archive logs would never be eligible for deletion.

Note
The example uses no RMAN catalog. The backups of a member are only known in its own controlfile.
Warning
The share is mounted with hard: RMAN hangs on dataguard-dbfs2, while dataguard-dbfs1 is not available. Use a separate NFS server for production systems.

5. Install databases with Ansible

Start installation
cd ~/git/ansible-oracle/example/dataguard/ansible
ansible-playbook playbooks/do_all.yml
Note
The execution takes approx. 60 minutes plus the download of the gold image.

single_instance_fs is executed for both VMs in one play. oradb_standby needs the facts of the primary database on dataguard-dbfs1 to create the standby database on dataguard-dbfs2.

Table 7. Configuration of the databases
Parameter dataguard-dbfs1 dataguard-dbfs2

ORACLE_BASE

/u01/app/oracle

ORACLE_HOME

/u01/app/oracle/product/db26_si_ee (read-only)

ORACLE_SID

DB1

db_unique_name

DB1_SITE1

DB1_SITE2

initial role

primary

physical standby

Created by

dbca

RMAN DUPLICATE …​ FROM ACTIVE DATABASE

PDB

ORCLPDB (open on the primary only)

db_create_file_dest

/u02/oradata

db_recovery_file_dest

/u02/fra

Listener

LISTENER, port 1521

Data Guard Broker

configuration DG1, MaxPerformance, LogXptMode ASYNC

Archivelog deletion policy

APPLIED ON ALL STANDBY

The systemd unit oracle-db@DB1.service starts the database in MOUNT and opens a primary database only. The physical standby database is not opened read only (no Active Data Guard).

6. How to work with the Data Guard configuration

Login into the 1st VM and set the environment
ssh ol@192.168.66.166
sudo -iu oracle
ocenv
DB1
Show the broker configuration
dgmgrl / "show configuration"
Example
Configuration - DG1

  Protection Mode: MaxPerformance
  Members:
  DB1_SITE1 - Primary database
    DB1_SITE2 - Physical standby database

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS
Note
The broker needs approx. 1 minute after the creation until the status is SUCCESS.
Switchover to DB1_SITE2 (password of SYS)
dgmgrl sys@DB1_SITE1
DGMGRL> validate database DB1_SITE2;
DGMGRL> switchover to DB1_SITE2;
Important
The inventory is not changed after a switchover. initial_role is used for the creation of the databases only, ansible-oracle does not switch back. See Role transitions outside of ansible-oracle.