=====Oracle System Umgebung für die Parametrisierung von Ansible Automatisierungen lokal hinterlegen=====
**Aufgabe:**
In einer Oracle Umgebung mit zig Server soll auf jeden Server lokal die Oracle System Umgebung hinterlegt werden.
Mit diesen Informationen können dann später Oracle Aufgaben wie das Patchen der DB Umgebung in Ansible parametrisiert werden.
**Lösung:**
**Verwendung von Local/Custum Ansible Facts**
Diese Oracle Parameter wie Oracle Home etc. könnten als Host_vars auf dem Tower Server hinterlegt werden, müssten aber dann auch von Hand gepflegt oder mit Dynamischen Inventory Plugins etc. erzeugt werden.
Hier ist es einfacher lokal auf den Zielrechner diese Information als "Local Fact" zu hinterlegen.
Zumal sich diese auch dynamisch während der Laufzeit erzeugen lassen.
----
==== Verbreitung ====
Das Asible Fact Verzeichnis als root anlegen:
#als root
mkdir -p /etc/ansible/facts.d/
Alle *.fact Dateien werden aus diesem Verzeichnis eingelesen.
Die *.fact Dateien können im ini. oder im Json Format hinterlegt werden.
Ist die Datei ausführbar, kann auch dynamisch ein Ergebnis in Json Format zurückgegeben werden.
Darauf achten das der Ansible Service-User die Datei auch lesen und ausführen können muss!
=== Test der Grundfunktion ===
Facts als im ini Format:
vi /etc/ansible/facts.d/oracle.fact
[BASE]
ORACLE_BASE=/app/oracle
[HOME]
ORACLE_HOME=/app/oracle/19
[SID]
ORACLE_SID=gpi
Erster Test mit:
ansible gpidb01 -m setup -a "filter=ansible_local"
..
"ansible_facts": {
"ansible_local": {
"oracle": {
"BASE": {
"oracle_base": "/app/oracle"
},
"HOME": {
"oracle_home": "/app/oracle/19"
},
"SID": {
"oracle_sid": "gpi"
..
===YAML Format===
Wie aber nun im YAML Format?
Problem : " "oracle": "error loading fact - please check content"
Wird wohl so nicht unterstützt? Scheint so zu sein.
=== Json Format testen===
/etc/ansible/facts.d/oracle.fact
{
"base": "/app/oracle",
"homes": [{
"sid": ["GPI", "PROD"],
"home": "/app/oracle/19c",
"owner": "oracle",
"version": "19"
},
{
"sid": [],
"home": "/app/oracle/12c",
"owner": "oracle",
"version": "12"
}
]
}
ansible gpidb01 -m setup -a "filter=ansible_local"
gpidb01 | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"oracle": {
"base": "/app/oracle",
"homes": [
{
"home": "/app/oracle/19c",
"owner": "oracle",
"sid": [
"GPI",
"PROD"
],
"version": "19"
},
{
"home": "/app/oracle/12c",
"owner": "oracle",
"sid": [],
"version": "12"
}
]
}
},
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
----
==== Dynamisch erzeugen ====
Beim Dynamischen Erzeugen ist die Herausforderung das der Ansible Service User (der die Ansible Jobs ausführt) auch die Rechte benötigt um die Meta Daten aus dem OS zu lesen!
D.h. bei Bedarf muss zusätzlich noch über einen Sudo Komanndo Aufruf auf den Oracle User umgeschaltet werden, da sonst nicht alle Informationen erreichbar sind!
Datei rechte auf der Fact Datei:
chmod 777 oracle.fact
Das erste Skript mit etwas dynamischen Inhalt:
#!/bin/sh
time_stamp=`date`
cat <
Nun kann in Folge ein Bash Skript geschrieben werden das Json erzeugt.
Oder auch nicht .. Fehler : "oracle": "error loading fact - please check content" zeigt das irgendein Fehler passiert ist ...
Glück hat wer auf der Maschine schon ein Programm für json wie "jo" in der Schell hat => siehe https://jpmens.net/2016/03/05/a-shell-command-to-create-json-jo/ , ansonsten eben Json zu Fuß erzeugen.
----
==== Wie wird nun aber auf diese Facts in Ansible zugegriffen?====
Ausgeben:
---
- name: List Oracle Parameter
hosts: gpidb01
pre_tasks:
- set_fact:
ORACLE_HOME: "{{ ansible_local.oracle.homes[0].home }}"
- debug:
var: ORACLE_HOME
tasks:
- debug:
msg:
- "Oracle Base is {{ ansible_local.oracle.base }}"
- "First Oracle Home is {{ ansible_local.oracle.homes[0] }}"
- "First Oracle Home path is {{ ansible_local.oracle.homes[0].home }}"
- "Global Oracle Home for this run is set to {{ ORACLE_HOME }}"
- name : get Size of the oracle Folder
command: "du -sk {{ ORACLE_HOME }}"
become: yes
become_user: oracle
register: oracle_folder_size
- set_fact:
ora_size : "{{ oracle_folder_size.stdout.split()[0] }}"
- debug:
msg: "{{ ora_size }}"
ansible-playbook getOracleHome.yml
TASK [debug] ******************************************************************************************************************************************
ok: [gpidb01] => {
"msg": [
"Oracle Base is /app/oracle",
"First Oracle Home is {u'owner': u'oracle', u'home': u'/app/oracle/19c', u'version': u'19', u'sid': [u'GPI', u'PROD']}",
"First Oracle Home path is /app/oracle/19c",
"Global Oracle Home for this run is set to /app/oracle/19c"
]
}
TASK [get Size of the oracle Folder]
********************
changed: [gpidb01]
TASK [set_fact]
******************
ok: [gpidb01]
TASK [debug]
*******************
ok: [gpidb01] => {
"msg": "10290988"
}
----
==== Playbook um das Fact Skript dann am Ende auch auf alle Maschinen auszurollen ====
Nachdem alle Komponenten klar sind, kann auf den anderen Maschinen das per Ansible automatisch erstellt werden.
Mit den folgenden Playbook Beispiel wird dann das Skript um den lokalen Json Output zu erstellen verteilt:
---
- name: Copy the Create Local Facts Script to the hosts
hosts: all
become: yes
become_user: root
vars:
fact_dir: "/etc/ansible/facts.d"
fact_filename: "oracle.fact"
tasks:
- name: create fact directory if not exits
ansible.builtin.file:
path: "{{ fact_dir }}"
state: directory
- name: Copy Check Script
ansible.builtin.template:
src: templates/setLocalDBFacts.sh.j2
dest: "{{ fact_dir }}/{{ fact_filename }}
owner: root
group: root
mode: '0777'
Das Fact Script liegt dabei unter "templates/setLocalDBFacts.sh.j2".
Am Ende reicht aber diese Demo nicht aus, da ja der Ansible User nicht genug Rechte hat, daher wird nun über ein "oracle.fact" nur ein weiters Schell Script mit Sudo aufgerufen um das eigentliche Schell Skript als Oracle User laufen lassen zu können.
=== Optimierte Lösung mit zwei Skripts ===
---
- name: Copy the Create Local Facts Script to the hosts
hosts: all
become: yes
become_user: root
vars:
fact_dir: "/etc/ansible/facts.d"
fact_filename: "oracle.fact"
fact_script: "get_oracle_facts.sh"
tasks:
- name: create fact directory if not exits
ansible.builtin.file:
path: "{{ fact_dir }}"
state: directory
- name: Copy Check Script
ansible.builtin.template:
src: templates/setLocalDBFacts.sh.j2
dest: "{{ fact_dir }}/{{ fact_script }}"
owner: root
group: root
mode: '0777'
- name: copy Fact Script oracle.fact.j2
ansible.builtin.template:
src: templates/oracle.fact.j2
dest: "{{ fact_dir }}/{{ fact_filename }}"
owner: root
group: root
mode: '0777'
Das oracle.fact Skript:
#!/bin/sh
sudo -u oracle /etc/ansible/facts.d/get_oracle_facts.sh
Das eigentliche Skript um die Umgebung zu analysieren, optimiert für Datenbank und Weblogic Umgebungen:
#!/bin/bash
#
# get the local configuration of all oracle products on this server
#
# Version: gpi 02.2021
#
# ###############################################
# guess oracle base
if [ -d "/opt/oracle" ]; then
ORACLE_BASE="/opt/oracle"
elif [ -d "/u01/app/oracle" ]; then
ORACLE_BASE="/u01/app/oracle"
elif [ -d "/var/oracle" ]; then
ORACLE_BASE="/var/oracle"
elif [ -d "/u00/app/oracle" ]; then
ORACLE_BASE="/u00/app/oracle"
elif [ -d "/app/oracle" ]; then
ORACLE_BASE="/app/oracle"
else
ORACLE_BASE="~"
fi
export ORACLE_BASE
##############################################
if [ -f "/etc/oraInst.loc" ]; then
# Read the Oracle Inventory if exists
. /etc/oraInst.loc
ORACLE_INVENTORY_HOME=${inventory_loc}
elif [ -f "/var/opt/oracle/oraInst.loc" ]; then
# Read the Oracle Inventory if exists
. /var/opt/oracle/oraInst.loc
ORACLE_INVENTORY_HOME=${inventory_loc}
else
if [ -d "${ORACLE_BASE}/oraInventory" ]; then
ORACLE_INVENTORY_HOME=${ORACLE_BASE}/oraInventory
fi
fi
export ORACLE_INVENTORY_HOME
#######################################################
# preare json
FACT_STRING={\"base\":\"${ORACLE_BASE}\",\"inst_loc\":\"${ORACLE_INVENTORY_HOME}\",\"homes\":[
########################################################
## check for oracle homes
## search over inventory
##
if [ -d "${ORACLE_INVENTORY_HOME}/ContentsXML" ]; then
# analyse the inventory
ORAINVENTORY_LIST=`grep "HOME NAME" ${ORACLE_INVENTORY_HOME}/ContentsXML/inventory.xml | awk '{ print($2 "#" $3);}' | sed 's/"//g'`
#echo ${ORAINVENTORY_LIST}
ORA_HOME_COUNTER=0
for ORA_HOME_STRING in $ORAINVENTORY_LIST
do
ORA_PROD_TYPE=MW
#echo ${ORA_HOME_STRING}
## subtract the string
LOC_POS=`expr ${ORA_HOME_STRING} : '.*LOC'`
let "LOC_POS=${LOC_POS}+1"
##printLine "LOC POS" "$LOC_POS"
LOC_PATH=${ORA_HOME_STRING:$LOC_POS}
##printLine "PATH" ${LOC_PATH}
let "LOC_POS=${LOC_POS}-10"
##printLine "LOC POS" "$LOC_POS"
LOC_NAME=${ORA_HOME_STRING:5:$LOC_POS}
##printLine "LOC" ${LOC_NAME}
##printLine
if [[ "${ORA_HOME_COUNTER}" -gt 0 ]]; then
FACT_STRING=${FACT_STRING},
fi
FACT_STRING=${FACT_STRING}{\"home\":\"${LOC_PATH}\",\"sid\":[
if [ -d "${LOC_PATH}/dbs" ]; then
ORADB_LIST=`ls ${LOC_PATH}/dbs | grep -e spfile | sed 's/spfile//g' | sed 's/.ora//g' `
ORA_SID_COUNTER=0
ORA_PROD_TYPE=DB
if [ "${ORADB_LIST}" != "" ]; then
for ORAHOMESID in $ORADB_LIST
do
if [[ "${ORA_SID_COUNTER}" -gt 0 ]]; then
FACT_STRING=${FACT_STRING},
fi
FACT_STRING=${FACT_STRING}\"${ORAHOMESID}\"
#debug
#echo DATABASE_ENV ${ORA_SID_COUNTER}] ${LOC_PATH} ${ORAHOMESID}
let "ORA_SID_COUNTER=${ORA_SID_COUNTER}+1"
done
fi
else
ORA_PROD_TYPE=MW
fi
if [ -d "${LOC_PATH}/user_projects" ];
then
ORA_PROD_TYPE=MW
fi
FACT_STRING=${FACT_STRING}],
if [[ "${ORA_PROD_TYPE}" == "DB" ]]; then
if [ -f "${LOC_PATH}/bin/oraversion" ]; then
ORACLE_BASE_VER=`${LOC_PATH}/bin/oraversion -majorVersion`
ORACLE_VERSION=`${LOC_PATH}/bin/oraversion -compositeVersion`
else
# get ver
if [ -f "${LOC_PATH}/OPatch/opatch" ]; then
ORACLE_VERSION=`${LOC_PATH}/OPatch/opatch lsinventory -oh ${LOC_PATH} | awk '/^Oracle Database/ {print $NF}'`
ORACLE_BASE_VER=${ORACLE_VERSION:0:2}
else
# get ver middleware
ORACLE_BASE_VER=0.0
ORACLE_VERSION=0.0
fi
fi
else
if [ -f "${LOC_PATH}/bin/oraversion" ]; then
ORACLE_BASE_VER=`${LOC_PATH}/bin/oraversion -majorVersion`
ORACLE_VERSION=`${LOC_PATH}/bin/oraversion -compositeVersion`
else
if [ -f "${LOC_PATH}/wlserver/server/lib/build-versions.properties" ]; then
ORACLE_VERSION=`cat ${LOC_PATH}/wlserver/server/lib/build-versions.properties | grep version.fmwgenerictoken `
# normal version.fmwgenerictoken=12.2.1.4.0-190725.1822.0231
ORACLE_VERSION=$(echo $ORACLE_VERSION | cut -d'=' -f 2)
ORACLE_VERSION=${ORACLE_VERSION:0:8}
ORACLE_BASE_VER=${ORACLE_VERSION:0:2}
else
# get ver middleware
ORACLE_BASE_VER=0.0
ORACLE_VERSION=0.0
fi
fi
fi
FACT_STRING=${FACT_STRING}\"base_version\":\"${ORACLE_BASE_VER}\",
FACT_STRING=${FACT_STRING}\"version\":\"${ORACLE_VERSION}\",
# prod typ
FACT_STRING=${FACT_STRING}\"type\":\"${ORA_PROD_TYPE}\"
#close Oracle Home
FACT_STRING=${FACT_STRING}}
let "ORA_HOME_COUNTER=${ORA_HOME_COUNTER}+1"
done
#else
# echo "No ${ORACLE_INVENTORY_HOME}/ContentsXML/inventory.xml found"
fi
time_stamp=`date`
#######################################################
# preare json
FACT_STRING=${FACT_STRING}],\"time\":\"${time_stamp}\"}
#######################################################
echo ${FACT_STRING}
----
====Quellen =====
* Json testen mit https://jsonlint.com/
* https://serverascode.com/2015/01/27/ansible-custom-facts.html
* https://www.middlewareinventory.com/blog/ansible-facts-list-how-to-use-ansible-facts/