相关文章推荐

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.

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
  • Check if AHF is already installed and get its current version
  • Uninstall 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_archive_version: "{{ (ahf_archive_name | regex_replace('v|.zip', '_')).split('_')[2] }}" 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', '<')

Check if AHF is already installed and get its current version

The stat module gets information about a file, just like stat Unix command. The result of this state module is registered in variable stat_ahf .

- name: Check if AHF is already installed stat: path: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl" register: stat_ahf

Since the information about a potential tfactl binary has been retrieved in variable stat_ahf , we can now get the installed version of AHF if the tfactl binary exists. The result is stored in variable ahf_current_version .

- name: Get current version of already installed AHF shell: "set -o pipefail && {{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl version | cut -d' ' -f3" register: ahf_current_version when: stat_ahf.stat.exists

The version built-in test exists in Ansible to compare version numbers. In this case, if the installed AHF version is greater or equal to the version about to be deployed, then the play will stop, thanks to module meta , using parameter end_play .

- name: End execution if AHF is already installed with this version or higher meta: end_play when: - stat_ahf.stat.exists - ahf_current_version.stdout is version(ahf_archive_version, '>=')

Uninstall an eventual existing installation of AHF

Uninstalling 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: Uninstall existing ahf installation shell: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl uninstall -silent" when: stat_ahf.stat.exists

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 also pretty straightforward by running a simple command . Once again, the shell module turns out to be useful :

- 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: Check if AHF is already installed stat: path: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl" register: stat_ahf - name: Get current version of already installed AHF shell: "set -o pipefail && {{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl version | cut -d' ' -f3" register: ahf_current_version when: stat_ahf.stat.exists - name: End execution if AHF is already installed with this version or higher meta: end_play when: - stat_ahf.stat.exists - ahf_current_version.stdout is version(ahf_archive_version, '>=') - name: Uninstall existing ahf installation shell: "{{ ahf_install_dir }}/ahf/oracle.ahf/bin/tfactl uninstall -silent" when: stat_ahf.stat.exists - 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 }}" 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 : Check if AHF is already installed] ************************************************************* ok: [flora-server] TASK [ora_inst_ahf : Get current version of already installed AHF] ************************************************** changed: [flora-server] TASK [ora_inst_ahf : Uninstall existing ahf installation] *********************************************************** changed: [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.

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 🙂

Liked by 1 person

Reply

Good work!

I learnt something new about Ansible. I had not considered using ‘meta end_play’ when making a script idempotent. I usually waste a lot of time and code putting in unneeded when clauses on subsequent tasks. This will be a great time-saver for me.

I share your frustration with products changing the way they run between releases. This makes Ansible scripts fragile.

Your method of extracting the product version is much succinct than what I came up with. I do in three (3) steps what you did in two (2).

Like

Reply

Thank you! I am glad it can help!
And by the way, after using “meta: end_play”, I realized it ends the whole play, for all the managed nodes. I think using “meta: end_host” would be even more useful in this case.

Liked by 1 person

Reply

I think that using “meta: end_host” in a role would introduce a subtle bug. But this would be useful for a playbook with multiple hosts.
My understanding of the purpose of roles is that a playbook can use multiple ones. The order of roles in a playbook would produce different results. For example, playbook consisting of roles for database server followed by ora_inst_ahf would work fine, whereas the opposite order would fail if the AHF was installed with the latest version and the database server was not configured correctly.

Like

I did not think about using this role in a playbook made up of different roles, yet. But if I do this, then I’ll definitely end up with an incomplete installation, depending on the order. Thank you for the hint!

Liked by 1 person

Hi Jose!
Thank you for your comment.
This playbook file is stored in the same directory as the role directory “ora_inst_ahf”. If you store it elsewhere, then you’ll get an error message when you run the playbook, similar to:

ERROR! the role ‘ora_inst_ahf’ was not found in /home/[your_user]/roles:/home/[your_user]/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:/home/[your_user]

Like

Reply

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

 
推荐文章