Install Oracle Autonomous Health Framework with Ansible : a beginner’s approach

I recently took the plunge to learn and use Ansible. I read many interesting blog posts on how Ansible can simplify DBA life from a lot of people in the community. But I felt I needed a simple use case to finally get started and become familiar with it.

How is Ansible relevant to my DBA job?

To overly simplify the concept of Ansible, let’s say it allows to SSH from any machine (control node) to a group of servers (managed nodes) to run the same commands, as long as Ansible is installed on the control node. The magic part is that you don’t have to deploy any agent on the the managed nodes. I will not cover the functioning of Ansible in this blog post, the official documentation is a must-read.

I recently had to deploy Autonomous Health Framework on a group of new servers : finally I got the simple use-case I was waiting for.

How can I start in a simple but proper way?

There are A LOT of interesting concepts to learn about Ansible, from the basic first steps of how to install it, to more complex functionality it offers.

Let’s define some key concepts :

Modules : The units of code Ansible executes. Each module has a particular use.
Tasks : The units of action in Ansible.

https://docs.ansible.com/ansible/latest/network/getting_started/basic_concepts.html

OK that’s great … But is there a way to reuse this list of tasks, and define some variables, for example ? Yes it is possible using roles :

Roles let you automatically load related vars_files, tasks, handlers, and other Ansible artifacts based on a known file structure. Once you group your content in roles, you can easily reuse them and share them with other users.

https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

Fortunately, there is a command to create a role, ansible-galaxy . It generates the directory structure for a new role :

# ansible-galaxy init ora_inst_ahf
- Role ora_inst_ahf was created successfully

The result is a directory tree compliant with Ansible role standards :

tree ora_inst_ahf/
ora_inst_ahf/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

In this blog post, I will only focus on 2 files : the commands I want to perform on all servers will go in file tasks/main.yml, and the related variables will go in defaults/main.yml

Here is the official description of those 2 files :

tasks/main.yml – the main list of tasks that the role executes.

defaults/main.yml – default variables for the role […]. These variables have the lowest priority of any variables available, and can be easily overridden by any other variable, including inventory variables.

Source : https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

What is the installation scenario for Autonomous Health Framework ?

Now that we’ve got the basic concepts of Ansible, let’s list the necessary steps to install Autonomous Health Framework (AHF) :

  • Check if the server has a compatible OS distribution and version
  • Clean up an eventual existing installation of AHF
  • Transfer the up-to-date AHF archive on the server
  • Unzip it
  • Run the AHF installer
  • Clean up the installer once AHF is installed
  • Clean up the README file once AHF is installed
  • Run orachk (This is not mandatory, but it will validate the installation has been performed correctly
  • Show the orachk results

How to go from this list to a list of Ansible tasks ?

All these steps will be translated into a list of Ansible tasks, each of these tasks consisting in running the relevant module. There is a huge amount of modules available, and almost all the steps listed above can be executed by a module.

Before jumping in the creation of tasks, useful variables can be defined in file defaults/main.yml so they can be modified easily if something changes :

---
# defaults file for ora_inst_ahf

# variables

os_distrib: RedHat # Actually, not only RH, but also OEL, SuSE, Solaris ...
ahf_archive_repo: /tmp
ahf_archive_name: AHF-LINUX_v20.2.3.zip # Downloadable on MOS note 30166242
ahf_install_dir: /u01/app/oracle/local

With these variables and a clear idea of the installation procedure, it’s time to translate every step in Ansible tasks.

Check if the server has a compatible OS distribution and version

The setup module gets useful pieces of information (known as “facts”) from remote hosts. Parameter filter is used to get only what is necessary : the info related to the OS distribution.

  - name: Check OS distribution
    setup:
      filter: distribution
    failed_when: ansible_distribution != os_distrib   # Var. "os_distrib" defined in defaults/main.yml

The use of directive failed_when means that this tasks will fail if the condition is met. So if the OS distribution is not equal to variable os_distrib, the task will fail. And all other tasks will not be executed.

According to AHF documentation, it supports Linux RedHat 4 minimum, so it is also necessary to check the OS version :

  - name: Check OS version
    setup:
      filter: distribution
    failed_when: ansible_distribution_version is version('4', '<')

Clean up an eventual existing installation of AHF

The file module is extremely powerful and offers a lot of possibilities to handle any file (or directory) on the managed node. In this example, a directory path is provided and a state is defined : in this case, it means this directory should be absent. So if the directory does not exist, it’s OK, and if the directory does exist, it will be deleted.

  - name: Clean up existing ahf installation
    file:
      path: "{{ ahf_install_dir }}/ahf"
      state: absent

Transfer the up-to-date AHF archive on the server + Unzip it

The unzip module is very interesting, because it copies an archive from the control node to the managed node, and then unzips it. It avoids running 2 different steps.

It is necessary to specify the source of the zip file (in this case, located in the control node), a destination for the unzipped directory, and the permission (mode) of the resulting directory.

  - name: Unzip ahf archive
    unarchive:
      src: "{{ ahf_archive_repo }}/{{ ahf_archive_name }}"
      dest: "{{ ahf_install_dir }}"
      mode: 0755

Run the AHF installer

Installing AHF is pretty straightforward by running a simple command. And there is not (yet?) a dedicated module to install AHF 😉 … This is where shell module comes in handy : it will execute any shell command passed in argument.

Never the less, it should not be used to execute an action for which a module already exists, because most of the time, using the dedicated module would be more convenient and secure.

  - name: Run ahf installer
    shell: "{{ ahf_install_dir }}/ahf_setup -ahf_loc {{ ahf_install_dir }}/ahf"

Clean up the installer once AHF is installed

Once AHF is installed, the installer can be deleted. File module is used once again to remove this file.

  - name: Clean up ahf installer
    file:
      path: "{{ ahf_install_dir }}/ahf_setup"
      state: absent

Clean up the README file once AHF is installed

Same here, once AHF is installed, README file can be deleted. File module is used once again to remove this file.

  - name: Clean up ahf README.txt
    file:
      path: "{{ ahf_install_dir }}/README.txt"
      state: absent

Run orachk

Here is the most tricky part of this role. We could simply run orachk using the shell module : I tried with AHF version 20.2 and it worked. But then I downloaded the latest version 20.3, re-ran this role and I got a timeout ! In fact, the latest orachk now prompts 2 questions in the command line, and expects interactive inputs :

This computer is for [S]ingle instance database or part of a [C]luster to run RAC database [S|C] [C]: S

orachk did not find the inventory location on flora-server from environment. Does flora-server have Oracle software installed [y/n][n]? n

This can be solved with module expect. To use it, provide the expected strings (or part of them, or matching regex) and the strings to respond with :

  - name: Run orachk with option nordbms
    expect:
      command: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/orachk -nordbms"
      responses:
        'This computer is for ': "S"
        'orachk did not find the inventory location on ': "n"
      timeout: null
    register: orachk_output

The output of the orachk is in variable orachk_output using the keyword register.

Show orachk results

And finally, the debug module will simply print the content of variable orachk_output :

  - name: Show orachk result
    debug:
      msg: "{{ orachk_output.stdout_lines }}"

How to actually install AHF using Ansible now ?

Now the complete tasks/main.yml file looks like this :

---
# tasks file for ora_inst_ahf


  - name: Check OS distribution
    setup:
      filter: distribution
    failed_when: ansible_distribution != os_distrib


  - name: Check OS version
    setup:
      filter: distribution
    failed_when: ansible_distribution_version is version('4', '<')


  - name: Clean up existing ahf installation
    file:
      path: "{{ ahf_install_dir }}/ahf"
      state: absent


  - name: Unzip ahf archive
    unarchive:
      src: "{{ ahf_archive_repo }}/{{ ahf_archive_name }}"
      dest: "{{ ahf_install_dir }}"
      mode: 0755


  - name: Create ahf installation directory
    file:
      path: "{{ ahf_install_dir }}/ahf"
      state: directory
      mode: 0755


  - name: Run ahf installer
    shell: "{{ ahf_install_dir }}/ahf_setup -ahf_loc {{ ahf_install_dir }}/ahf"


  - name: Clean up ahf installer
    file:
      path: "{{ ahf_install_dir }}/ahf_setup"
      state: absent


  - name: Clean up ahf README.txt
    file:
      path: "{{ ahf_install_dir }}/README.txt"
      state: absent


  - name: Run orachk with option nordbms
    expect:
      command: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/orachk -nordbms"
      responses:
        'This computer is for ': "S"
        'orachk did not find the inventory location on ': "n"
      timeout: null
    register: orachk_output


  - name: Show orachk result
    debug:
      msg: "{{ orachk_output.stdout_lines }}"

To use it, let’s create a very simple playbook ora_inst_ahf_playbook.yml :

- hosts: all
  remote_user: oracle
  roles:
    - ora_inst_ahf

The keyword remote_user means we want to run all the tasks in this playbook as user oracle on the managed node. When running this playbook, the parameter -k ask for oracle user password in the command line :

ansible-playbook ora_inst_ahf_playbook.yml -i inventory_file.yml -k
SSH password: 

PLAY [flora-server] *************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************
ok: [flora-server]

TASK [ora_inst_ahf : Check OS distribution] *************************************************************************
ok: [flora-server]

TASK [ora_inst_ahf : Check OS version] ******************************************************************************
ok: [flora-server]

TASK [ora_inst_ahf : Clean up existing ahf installation] ************************************************************
ok: [flora-server]

TASK [ora_inst_ahf : Unzip ahf archive] *****************************************************************************
changed: [flora-server]

TASK [ora_inst_ahf : Create ahf installation directory] *************************************************************
changed: [flora-server]

TASK [ora_inst_ahf : Run ahf installer] *****************************************************************************
ok: [flora-server]

TASK [ora_inst_ahf : Clean up ahf installer] ************************************************************************
changed: [flora-server]

TASK [ora_inst_ahf : Clean up ahf README.txt] ***********************************************************************
changed: [flora-server]

TASK [ora_inst_ahf : Run orachk with option nordbms] ****************************************************************
changed: [flora-server]

TASK [ora_inst_ahf : Show orachk result] ****************************************************************************
ok: [flora-server] => {
    "msg": [
        " ",
        "This computer is for [S]ingle instance database or part of a [C]luster to run RAC database [S|C] [C]: 
orachk did not find the inventory location on flora-server from environment. 
Does flora-server have Oracle software installed [y/n][n]? ",
        "Checking Status of Oracle Software Stack - Clusterware, ASM, RDBMS",
        "",
        ".  ",
        ".  .  .  .  .  .  .  .  ",
        "-------------------------------------------------------------------------------------------",
        "                                                 Oracle Stack Status                          ",
        "-------------------------------------------------------------------------------------------",
        "  Host Name       CRS Installed       ASM HOME  RDBMS Installed    CRS UP    ASM UP  RDBMS UP    DB Instance Name",
        "-------------------------------------------------------------------------------------------",
        [...]
        "UPLOAD [if required] - /u01/app/oracle/local/ahf/oracle.ahf/data/flora-server/orachk/orachk_flora-server_110220_184819.zip"
    ]
}

PLAY RECAP *********************************************************************************************
flora-server                  : ok=11   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

With no manual action, the Autonomous Health Framework can now be quickly and easily deployed on a group of servers, using one command.

So what now ?

When thinking about how to “Ansible” a process, it really helps me to first split all the required steps into small actions, to have a clear idea of what I want to achieve. Then, for each action, I look for the most relevant module, if any. Then, I (try to) read carefully the module documentation, to make sure I understand it (and sometimes it is a lot of work). But every day I feel more comfortable writing Ansible roles like this one, and automate more DBA tasks.

2 thoughts on “Install Oracle Autonomous Health Framework with Ansible : a beginner’s approach

  1. Douglas

    Your solution seems to be dangerous in that it always removes the current installation of AHF, even though the installed version matches the new one. In that way, the solution is not idempotent.

    As a design decision, why do you choose removal of the AHF directory rather than using the ‘tfactl uninstall’ command?

    Like

    Reply
    1. florab Post author

      Hi Douglas!
      Thank you very much for your valuable feedback. I agree with both suggestions : checking the version before attempting any installation is a way more logical (and indeed idempotent) method. And using ‘tfactl uninstall’ command is the right way to uninstall.
      I’ll modify my role accordingly. Thanks again 🙂

      Like

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s