Sunday, May 12, 2019

TNS entry addition using ansible

Dear Readers,

In continuation to the previous blogpost narrated here. I have put out a blog on adding a new tns entry as you see below...

I will try to upload the code to git.

Ansible notes:

A) Read the note on this below link for order of precedence of the variable

link: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

background: Variable declaration, it is possible we define host_vars & group_vars to define host and group specific variables, but here for this example, I am going to

keep the variable declaration very simple (do it in inventory file and ensure the same variable isnt used any where else with higher precedence).

B) Ansible config search order [from actual ansible.cfg file]

# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first

C) I used ANSIBLE_GATHERING='explicit' to avoid fact collection each time for my tests.

TNS addition:

1) Setting up ansible config, ansible inventory files.

2) Find out the tnsnames.ora file path corresponding to the db

3) Verify that the tnsnames.ora file exists already and it doesnt have the TNS entry we are about to add

4) Create a directory under $OH as follows
$OH/network/admin/TNS_Ansible

5) Whenever we add a TNS entry create a temp file tnsadmin.ora with the TNS entry under the above temp tns directory

6) Set TNS_ADMIN directory to the new ansible temp tns directory created and test weather the tnsping works fine.

7) If it does work, append this entry directly from this temporary TNS file to the original file and test weather the tnsping still works.

8) Archive the temporary tnsadmin.ora under the TNS_Asnible directory with a date directory, so it can be used for troubleshooting purpose later.


========

Inputs for this excercise: server name and DB name inventory file.

Step 1) Setting up ansible config, ansible inventory files

It is better to keep an ansible config file on your local home where you have RW permisson, so you can control your ansible environment.

My ansible home is: /home/oracle/dba/ansi-inventory
By default we get /etc/ansible/ansible.cfg with ansible install, you can copy/edit this file on your ansible home directory if you wish.
My ansible.cfg file has the following setup... [i dont have this "#" symbol line, this is for readability I put it here]

##########################################################
[defaults]
inventory=./inventory_file.txt
forks=3
log_path=/home/oracle/dba/ansi-inventory/ansible.log
##########################################################

Each of the parameter has a meaning to it, specifically the forks tells how many nodes in parallel we need to act up on. In my case if I have 6 nodes, 3 nodes will be

acted up on parallel at a time.

Ansible inventory, to start with I kept a very simple inventory with the needed variable.

##########################
cat inventory_file_2.txt
[servers]
node1 tns_db="db1" tns_alias="db_new"
##########################

Here node1 is the node name and tns_db is the variable name, I should ensure I dont have any other places where this tns_db is declared. Also the tns_alias is the new

tns entries alias I am going to make.

Now we have ansible inventory and ansible config file prepared. Now let us draft a play book to act on this nodes for the variable declared.

Step 2) Find out the tnsnames.ora file path corresponding to the db.

##begin from below
- hosts: "{{ node_list }}"

  tasks:
    - name: Discover DB existance in oratab
      shell:
        cat /etc/oratab|grep -iw {{ tns_db }}|wc -l
      register: var_dbexist

    - name: Fail if the db doesnt exist in oratab
      fail:
        msg: "ANSI-ORAERR: Whoops! DB doesnt exist in oratab"
      when: var_dbexist.stdout|int == 0

    - name: Discover DB Home
      shell:
        echo `cat /etc/oratab|grep -iw {{ tns_db }}|awk -F ":" '{print $2}'`/network/admin/tnsnames.ora
      register: var_tnsfilepath
##end here


Step 3) Verify that the tnsnames.ora file exists already and it doesnt have the TNS entry we are about to add

##begin from below
    - name: Verify tnsnames.ora file exists on the TNS_ADMIN path
      stat:
        path: "{{ var_tnsfilepath.stdout }}"
      register: var_tnsfileexist

    - name: Fail if tnsnames.ora file isnt found as expected
      fail:
        msg: "ANSI-STATERR: Whoops! File tnsnames.ora doesnt exist"
      when: var_tnsfileexist.stat.exists == False

    - name: Try do a tnsping for the entry we are about to add
      shell:
        export ORACLE_SID={{ tns_db }};export ORAENV_ASK=NO;. oraenv;
        tnsping {{ tns_alias }}
      register: var_tnsping
      ignore_errors: True

    - name: Verify the tnsping result to see if the TNS entry is already there to avoid making duplicate entries.
      fail:
        msg: "ANSI-ORAERR: Whoops! tns entry is already there"
      when: '"TNS-03505" not in var_tnsping.stdout'

    - debug: var=var_tnsping.stdout

##end here

Step 4) Create a directory under $OH as follows
$OH/network/admin/TNS_Ansible

##begin from below
    - name: Temp tns directory path
      shell:
        echo `cat /etc/oratab|grep -iw {{ tns_db }}|awk -F ":" '{print $2}'`/network/admin/tns_ansible
      register: var_temptnspath

    - name: Create the temp tns admin directory
      command: mkdir {{ var_temptnspath.stdout }}
      args:
        creates: "{{ var_temptnspath.stdout }}"

    - name: Modify the permisson of the directory
      file:
        path: "{{ var_temptnspath.stdout }}"
        owner: oracle
        group: dba
        mode: 0744

##end here

5) Whenever we add a TNS entry create a temp file tnsadmin.ora with the TNS entry under the above temp tns directory

##begin from below
    - name: Temp tnsfile path
      shell:
        echo {{ var_temptnspath.stdout }}/tnsnames.ora
      register: var_temptnsfilepath

    - name: Add the tns entry
      lineinfile:
        path: "{{ var_temptnsfilepath.stdout }}"
        create: yes
        owner: oracle
        group: dba
        mode: 0644
        line: '{{ item }}'
      with_items:
        - 'DB_NEW ='
        - ' (DESCRIPTION ='
        - ' (ADDRESS = (PROTOCOL = TCP)(HOST = node1)(PORT = 1521))'
        - ' (CONNECT_DATA ='
        - ' (SERVER = DEDICATED)'
        - ' (SERVICE_NAME = DB1)'
        - '  )'
        - ' )'

##end here

6) Set TNS_ADMIN directory to the new ansible temp tns directory created and test weather the tnsping works fine.

##begin from below
    - name: Tnsping to verify if the new tns entry is pinging fine
      shell:
        export ORACLE_SID={{ tns_db }};export ORAENV_ASK=NO;. oraenv;export TNS_ADMIN={{ var_temptnspath.stdout }}
        tnsping {{ tns_alias }}
      register: var_tnsping
      ignore_errors: True

    - name: Verify the tnsping result to see if the TNS entry works ok
      fail:
        msg: "ANSI-ORAERR: Whoops! tnsping fails for {{ tns_alias }}"
      when: '"TNS-" in var_tnsping.stdout or var_tnsping.rc > 0'

    - debug: var=var_tnsping.stdout

##end here

7) If it does work, append this entry directly from this temporary TNS file to the original file and test weather the tnsping still works.

##begin from below
    - name: Backup existing tnsnames.ora file
      shell:
        cp {{ var_tnsfilepath.stdout }} {{ var_temptnspath.stdout }}/tnsnames.ora_savorig_`date +%Y%m%d%H%M%S`

    - name: Append a new line before the tns entry is added
      shell:
        echo "" >> {{ var_tnsfilepath.stdout }}

    - name: Append the new TNS entry to the original tnsnames.ora file and do test TNS ping
      shell:
        cat {{ var_temptnsfilepath.stdout }} >> {{ var_tnsfilepath.stdout }};
        export ORACLE_SID={{ tns_db }};export ORAENV_ASK=NO;. oraenv;
        tnsping {{ tns_alias }}
      register: var_tnsping
      ignore_errors: True

    - name: Final verifcation of the tnsping result to see if the TNS entry works ok
      fail:
        msg: "ANSI-ORAERR: Whoops! tnsping fails for {{ tns_alias }}"
      when: '"TNS-" in var_tnsping.stdout or var_tnsping.rc > 0'

##end here

step 8) Archive the temporary tnsadmin.ora under the TNS_Asnible directory with a date directory, so it can be used for troubleshooting purpose later.

##begin from below
    - name: Archive the temp ansible tns file for later troubleshooting purpose
      shell:
        mv {{ var_temptnsfilepath.stdout }} {{ var_temptnsfilepath.stdout }}_ansitemp_`date +%Y%m%d%H%M%S`

##end here

command to execute the above playbook:
ansible-playbook tnsadd.yml  -i ./inventory_file_2.txt --extra-vars="node_list=servers" -u oracle -v

result expected:

PLAY RECAP ***************************************************************************************************************
node1                     : ok=16   changed=10   unreachable=0    failed=0



Example execution:

[root@node1 ansi-inventory]# ansible-playbook tnsadd.yml  -i ./inventory_file_2.txt --extra-vars="node_list=servers" -u oracle

PLAY [servers] ***********************************************************************************************************

TASK [Discover DB existance in oratab] ***********************************************************************************
changed: [node1]

TASK [Fail if the db doesnt exist in oratab] *****************************************************************************
skipping: [node1]

TASK [Discover DB Home] **************************************************************************************************
changed: [node1]

TASK [Verify tnsnames.ora file exists on the TNS_ADMIN path] *************************************************************
ok: [node1]

TASK [Fail if tnsnames.ora file isnt found as expected] ******************************************************************
skipping: [node1]

TASK [Try do a tnsping for the entry we are about to add] ****************************************************************
fatal: [node1]: FAILED! => {"changed": true, "cmd": "export ORACLE_SID=db1;export ORAENV_ASK=NO;. oraenv; tnsping db_new", "delta": "0:00:00.267024", "end": "2019-05-

12 18:08:02.918267", "failed": true, "msg": "non-zero return code", "rc": 1, "start": "2019-05-12 18:08:02.651243", "stderr": "", "stderr_lines": [], "stdout": "The

Oracle base for ORACLE_HOME=/opt/app/oracle/product/11.2.0/db_1 is /opt/app/oracle\n\nTNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 12-MAY-2019

18:08:02\n\nCopyright (c) 1997, 2009, Oracle.  All rights reserved.\n\nUsed parameter files:\n/opt/app/oracle/product/11.2.0/db_1/network/admin/sqlnet.ora\n\nTNS-

03505: Failed to resolve name", "stdout_lines": ["The Oracle base for ORACLE_HOME=/opt/app/oracle/product/11.2.0/db_1 is /opt/app/oracle", "", "TNS Ping Utility for

Linux: Version 11.2.0.1.0 - Production on 12-MAY-2019 18:08:02", "", "Copyright (c) 1997, 2009, Oracle.  All rights reserved.", "", "Used parameter files:",

"/opt/app/oracle/product/11.2.0/db_1/network/admin/sqlnet.ora", "", "TNS-03505: Failed to resolve name"]}
...ignoring

TASK [Verify the tnsping result to see if the TNS entry is already there to avoid making duplicate entries.] *************
skipping: [node1]

TASK [debug] *************************************************************************************************************
ok: [node1] => {
    "var_tnsping.stdout": "The Oracle base for ORACLE_HOME=/opt/app/oracle/product/11.2.0/db_1 is /opt/app/oracle\n\nTNS Ping Utility for Linux: Version 11.2.0.1.0 -

Production on 12-MAY-2019 18:08:02\n\nCopyright (c) 1997, 2009, Oracle.  All rights reserved.\n\nUsed parameter files:

\n/opt/app/oracle/product/11.2.0/db_1/network/admin/sqlnet.ora\n\nTNS-03505: Failed to resolve name"
}

TASK [Temp tns directory path] *******************************************************************************************
changed: [node1]

TASK [Create the temp tns admin directory] *******************************************************************************
ok: [node1]

TASK [Modify the permisson of the directory] *****************************************************************************
ok: [node1]

TASK [Temp tnsfile path] *************************************************************************************************
changed: [node1]

TASK [Add the tns entry] *************************************************************************************************
changed: [node1] => (item=DB_NEW =)
changed: [node1] => (item= (DESCRIPTION =)
changed: [node1] => (item= (ADDRESS = (PROTOCOL = TCP)(HOST = node1)(PORT = 1521)))
changed: [node1] => (item= (CONNECT_DATA =)
changed: [node1] => (item= (SERVER = DEDICATED))
changed: [node1] => (item= (SERVICE_NAME = db1))
changed: [node1] => (item=  ))
changed: [node1] => (item= ))

TASK [Tnsping to verify if the new tns entry is pinging fine] ************************************************************
changed: [node1]

TASK [Verify the tnsping result to see if the TNS entry works ok] ********************************************************
skipping: [node1]

TASK [debug] *************************************************************************************************************
ok: [node1] => {
    "var_tnsping.stdout": "The Oracle base for ORACLE_HOME=/opt/app/oracle/product/11.2.0/db_1 is /opt/app/oracle"
}

TASK [Backup existing tnsnames.ora file] *********************************************************************************
changed: [node1]

TASK [Append a new line before the tns entry is added] *******************************************************************
changed: [node1]

TASK [Append the new TNS entry to the original tnsnames.ora file and do test TNS ping] ***********************************
changed: [node1]

TASK [Final verifcation of the tnsping result to see if the TNS entry works ok] ******************************************
skipping: [node1]

TASK [Archive the temp ansible tns file for later troubleshooting purpose] ***********************************************
changed: [node1]

PLAY RECAP ***************************************************************************************************************
node1                     : ok=16   changed=11   unreachable=0    failed=0



Thanks for the visit. Happy learning!


No comments:

Post a Comment

Flashback data archive steps

 Objective: Ways to track DML changes in a table Detailed objective: We should be able to track the changes in the table at any point in tim...