Contexte et introduction

J’avais pour projet de monitorer le nombre d’appels pour les différents web services de l’ESB. Pour ce, nous avons donc décider d’utiliser la solution de monitoring « Grafana » pour avoir une représentation graphique de nos données. Par des graphiques, nous pourrons facilement savoir si il y a un nombre d’appels anormal. Pour répondre à notre besoin nous avons besoin d’insérer des données dans Influxdb depuis une base de données Oracle.

Nous allons récupérer des données depuis une base de données Oracle avec la librairie « Cx_Oracle » en python. Exécuter un script depuis Telegraf à un certain intervalle. Insérer les données dans notre base de données Influxdb. La base InfluxDB est accessible par Grafana…

Schéma

Voici un schéma illustrant comment fonctionnera notre système pour récupérer les données depuis notre base Oracle et les insérer dans notre base Influxdb.
Voici un schéma illustrant comment fonctionnera notre système pour récupérer les données depuis notre base Oracle et les insérer dans notre base Influxdb.

Cx_Oracle

Cx_Oracle est une librairie python qui nous permet de récupérer des données depuis une base de données Oracle. Etant donné que nous pourrions avoir plusieurs besoins concernant des données à récupérer depuis une base Oracle, nous avons fait un script générique qui lit un fichier de configuration dans lequel nous mettrons notre chaîne de connexion, les identifiants à utiliser et la requête SQL à lancer.

Comment Telegraf récupère-t-il les données ?

Lorsque Telegraf exécute le script Python, il lit ce qu’écrit le script sur la console, donc si j’utilise la fonction print() de Python, Telegraf va traiter ce qui en ressortira.

Les tags et les fields

Lorsque vous récupérer des données depuis une requête SQL lancé sur votre base de données Oracle, vous allez obtenir le résultat avec différentes colonnes, voici un exemple.

serviceoperationconsumernb_totalnb_koavgminmax

Voici les différentes colonnes tiré du résultat de notre requête. Nous avons 8 colonnes. Les colonnes « service », « operation » et « consumer » sont des tags, car celle-ci nous permettrons de filtrer les différentes valeurs qu’on a dans les colonnes « nb_total » à « max ». Dans Telegraf, dans l’input exec, nous allons déclarer toute les valeurs comme tag même si nous n’utiliserons en vrai que les trois premiers.

InfluxDB Line Protocol

InfluxDB Line Protocol est un format de données que va envoyer Telegraf à Influxdb qui pourra lire ces dites données et les insérées dans la base de données configurée. Mon script Python que j’ai développé nous permet de récupérer les données depuis notre base Oracle et de les retranscrire dans ce format lisible par InfluxDB et Telegraf.

Voici un exemple d’une ligne formatée dans au format InfluxDB :

exec_mesure,service=abc,operation=def,consumer=ghi nb_total=16,nb_ko=0,avg=10.6875,min=3,max=107 {timestamp_unix_format}

Configuration de l’input « exec » dans Telegraf

Pour que Telegraf puisse exécuter le script, il faut tout d’abord éditer notre fichier de configuration Telegraf, normalement dans « /etc/telegraf ». Vous devez configurer un input dans la configuration, de ce fait il sera capable de récupérer les données renseignées.

[[inputs.exec]]
  commands = ["oracle4telegraf.py"]
 
  interval = "300s"
  timeout = "50s"
 
  data_format = "influx"
  tagexclude = ["host"]
 
  tag_keys = [
    "service",
    "operation",
    "consumer",
    "nb_total",
    "nb_ko",
    "avg",
    "min",
    "max"
  ]

On exclue le tag « host » qui est par défaut activé, celui-ci ne nous ai pas utile pour notre cas, mais si vous souhaitez filtrer les différents serveurs qui sont la source pour alimenter votre base de données Influxdb, vous pouvez supprimer la ligne « tagexclude = [« host »] ».

Configuration d’Oracle4Telegraf

La configuration est simple et complète, un fichier de configuration pour le même script est requis pour chaque récupération de données depuis une base de données Oracle différente, dans le cas où seul la requête ne change, on peut tout de même utiliser le même fichier de configuration. On peut forcer l’utilisation d’un fichier SQL via l’argument optionnel « -f <fichier.sql> » au script.

{
    "connection": {
        "username": "*************",
        "password": "*************",
        "tns": "*********************"
    },
    "request": {
        "sqlfile": "/oracle4telegraf/fichier.sql"
    },
    "telegraf": {
        "measurement_name": "exec_nom_de_la_mesure",
        "timestamp" : 8,
        "tags" : [
            {
                "name" : "service",
                "column": 0
            },
            {
                "name" : "operation",
                "column": 1
            },
            {
                "name" : "consumer",
                "column": 2
            }
        ],
        "fields" : [
            {
                "name" : "nb_total",
                "column": 3
            },
            {
                "name" : "nb_ko",
                "column": 4
            },
            {
                "name" : "avg",
                "column": 5
            },
            {
                "name" : "min",
                "column": 6
            },
            {
                "name" : "max",
                "column": 7
            }
        ]
    }
}

Script Oracle4Telegraf

# Oracle4Telegraf - Getting data from a Oracle Database and inserting it into influxdb.
# Author : Alexy DA CRUZ (dacruzalexy@gmail.com)

# BSD 3-Clause License
# Copyright (c) 2018, Alexy DA CRUZ All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
# Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import cx_Oracle
import urllib3
import sys
import calendar, time
import argparse
import json

parser = argparse.ArgumentParser()
parser.add_argument("configfile")
parser.add_argument("-f", "--forcesqlfile", help="Contourne le fichier de configuration pour le fichier SQL qui permettra de requêter la base de données Oracle.", type=str)
args = parser.parse_args()

class Oracle4Telegraf:
    def __init__(self, config):
        self.username = config["connection"]["username"]
        self.password = config["connection"]["password"]
        self.tns = config["connection"]["tns"]

        self.measurement = config["telegraf"]["measurement_name"]
        self.tags = config["telegraf"]["tags"]
        self.fields = config["telegraf"]["fields"]
        self.timestamp = int(config["telegraf"]["timestamp"])

        self.connection = cx_Oracle.connect(self.username, self.password, self.tns)

    # Time format for telegraf to insert into influxdb.
    def ConvertToUnixTime(self, value):
        return calendar.timegm(time.strptime(str(value), '%Y-%m-%d %H:%M:%S.%f')) * 1000000000

    # Convert "none" into 0 to handle empty result from the Oracle database.
    def TurnIntoZero(self, value):
        try:
            return int(value)
        except TypeError:
            return 0

    # Generate a influxline for each line in the Oracle database 
    def GenerateInfluxLine(self, data):
        for result in data:
            row = self.measurement + ","
            i = 0

            for tag in self.tags:
                row += tag["name"] + "=" + str(result[tag["column"]])
                if(i is not len(self.tags) - 1):
                    row += ","
                i += 1

            row += " "
            i = 0

            for field in self.fields:
                row += field["name"] + "=" + str(result[field["column"]])
                if(i is not len(self.fields) - 1):
                    row += ","
                i += 1

            row += " " + str(self.ConvertToUnixTime(str(result[self.timestamp])))

            print(row)

    # Oracle connection
    def OracleConnect(self, sql_request):
        cursor = self.connection.cursor()
        cursor.execute(sql_request)

        self.GenerateInfluxLine(cursor.fetchall())

if __name__ == "__main__":
    config_filename = args.configfile
    sql_filename = args.forcesqlfile

    if sql_filename is None:
        try:
            with open(config_filename, encoding='utf-8') as json_file:
                data = json_file.read()

                config = json.loads(data)

                sql_request = open(config["request"]["sqlfile"]).read()

                metrics = Oracle4Telegraf(config)
                metrics.OracleConnect(sql_request)
        except FileNotFoundError:
             print("[ERROR] This configuration file doesn't exists.")
    else:
        try:
            with open(config_filename, encoding='utf-8') as json_file:
                data = json_file.read()

                config = json.loads(data)

                sql_request = open(sql_filename).read()

                metrics = Oracle4Telegraf(config)
                metrics.OracleConnect(sql_request)
        except FileNotFoundError:
            print("[ERROR] This configuration file doesn't exists.")

Le script est sous licence BSD 3-Clause.


Alexy DA CRUZ

Administrateur systèmes depuis maintenant plus d'un an. Passionné par le développement, j'écris des articles sur mon portfolio.

0 commentaire

Laisser un commentaire