=====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/