ES-GLPI – Kibana pour GLPI en 15 minutes!

Bonsoir à tous!

J’ai récemment eu a migrer ma plateforme GLPI sur un nouvel environnement, et par extension le serveur que j’utilisais pour Kibana, ES etc…

Voir les articles précédents sur l’utilisation de Kibana et Elasticsearch avec GLPI :
http://desaille.fr/kibana-glpi-dashboard-v2/
http://desaille.fr/elk-glpi-raspberrypi-tv/

Une fois la nouvelle plateforme GLPI en prod, je me suis attaqué au re-paramétrage du bouzin. J’ai relu les notes que j’avais prises et j’ai quand même trouvé ça un peu chiant à mettre en place.

Les articles sur Kibana et GLPI sont les plus consultés du site, toute proportions gardées, y’a pas non plus grand monde qui passe par ici, mais ça représente quand même 20% des pages sur les 6 derniers mois, je me suis donc dit qu’il pourrait être pas mal d’automatiser et surtout de simplifier un un peu tout ça.

J’ai donc fait un petit script qui permet entre autre de traiter automatiquement les points suivants :

  • Paramétrage de Logstash basé sur des fichiers de configurations qui embarquent directement les requètes SQL, plus besoins de passer par la création de vues etc…
  • Création automatique des index et des mappings dans Kibana, on peut directement passer à la visualisations des données!
  • Synchronisation automatique des données entre la base MySQL de GLPI et ElasticSearch (Malheureusement pas une vrai synchro, on flush la base à intervalle régulier, mais c’est pas très gourmand, et on peut planifier ça quand on veut)

En gros, il suffit d’avoir l’adresse du serveur MySQL ainsi qu’un login avec des droits de lecture pour que tout fonctionne. Faites vous plaisir, ça serait con de pas tester…

J’ai tout posé sur Github à cette adresse : https://github.com/desaille/es-glpi

Les prérequis sont les suivants :

  • ElasticSearch 2.x
  • Plugin delete-by-query for ElasticSearch
  • Logstash 2.x
  • Plugin JDBC for Logstash
  • JDBC Driver for MySQL

La procédure ci dessous, a été faite pour un conteneur LXC Ubuntu 14.04 à blanc mais devrait être ok sur n’importe quelle Ubuntu 14.04.

# On ajoute la clef et le dépôt pour récupérer Kibana via APT
wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb http://packages.elastic.co/kibana/4.4/debian stable main" | sudo tee -a /etc/apt/sources.list
apt-get update
# On installe les prérequis et Kibana
apt-get -y install curl openjdk-7-jre-headless kibana git && cd /tmp
# FIN

Et : (Obligé de faire en deux temps, sinon APT coupe la suite de commandes)

# On télécharge les sources (Elasticsearch, Logstash et le driver JDBC pour MySQL) 
wget https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.2.0/elasticsearch-2.2.0.deb
wget https://download.elastic.co/logstash/logstash/packages/debian/logstash_2.2.2-1_all.deb
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.38.zip
# On installe ES et Logstash
dpkg -i elasticsearch-2.2.0.deb
dpkg -i logstash_2.2.2-1_all.deb
# On extrait et on intègre le driver JDBC
unzip mysql-connector-java-5.1.38.zip
cd mysql-connector-java-5.1.3*
mkdir -p /opt/logstash/jdbc-drivers
cd mysql-connector-java-5.1.*
mv mysql-connector-java-5.1.3* /opt/logstash/jdbc-drivers/mysql-connector-java.jar
# On lance les services et on paramètre le démarrage automatique
service elasticsearch start
service kibana start
update-rc.d elasticsearch defaults 95 10
update-rc.d kibana defaults 95 10
# On installe les plugins
/usr/share/elasticsearch/bin/plugin install delete-by-query
/opt/logstash/bin/plugin install logstash-input-jdbc
# On télécharge les sources du module de synchro
cd /opt
git clone https://github.com/desaille/es-glpi.git
cd es-glpi
# Fin

Un copié-collé des deux blocs ci dessus sur une machine fraîchement installée et tout devrait être ok !

Plus qu’a paramétrer les fichiers suivants :  conf/logstash/glpi_tasks.conf et glpi_tickets.conf

input {
    jdbc {
        jdbc_connection_string => "jdbc:mysql://xx.xx.xx.xx:3306/glpi"
        jdbc_user => "user"
        jdbc_password => "password"
        jdbc_driver_library => "/opt/logstash/jdbc-drivers/mysql-connector-java.jar"
        jdbc_driver_class => "com.mysql.jdbc.Driver"
        statement_filepath => "/opt/es-glpi/conf/sql/glpi_tasks.sql"
    }
}
output {
    elasticsearch {
        hosts => "localhost:9200"
        index => "glpi_tasks"
  }
}

Renseigner l’adresse du serveur MySQL ainsi que le login et mot de passe pour accéder à la base.
Penser également à vérifier la version de Kibana installée et a bien le renseigner dans le fichier conf/bin/config.sh 

Le script est paramétré par défaut sur la version 4.4.1
Si tout est ok on peut executer config.sh , une fois le paramétrage terminé le script en appelle un second (sync.sh ) pour lancer la synchronisation entre la base et ElasticSearch.

Et si tout c’est bien passé :

es-glpi ok

Ensuite, plus qu’a vous rendre sur l’url de votre serveur sur le port 5601 et a vous faire plaisir en faisant parler les données!

Par exemple si on veut obtenir un camembert avec la somme du temps total logué dans les tâches par technicien, il suffit de faire :

Visualize > Pie Chart > New Search puis on sélectionne l’index que l’on souhaite. Tickets ou Tâches. On sélectionne ensuite dans le panel de gauche l’agrégation que l’on souhaite (count, sum, unique count) et le bucket, ici « split slices » par « terms » et on sélectionne « techniciens » !

kibanapie

On peut ensuite ajouter un sub-bucket, pareil avec « terms », par exemple une sous agrégation par entité.

kibanapie2

Je m’en sert aussi pour extraire des temps, très pratique. Si on fait une visualisation « data table » récupérer le temps passé moyen par entité est un jeu d’enfant, et même le temps moyen par entité, par technicien. Le tout est bien sur exportable directement en csv !

Amusez vous bien.

7 commentaires

  1. Bonsoir ,
    Merci pour votre site une petite mine d’information .
    Je me suis dis que j’allais tenter l’installation de ELK pour un dashbord GLPI mais lorsque je vais pour lancer le script config.sh je me retrouve avec le message d’erreur suivant
    config.sh: 38: config.sh: Bad substitution
    Sachant que je suis bien en #!/bin/bash & non en #!/bin/sh

    Merci pour vos lumières .
    Bonne soirée

  2. Bonjour a tous,

    @Vincent, Merci beaucoup pour ces articles concernant GLPI, qui sont extrêmement intéressants.

    Je cherche à visualiser la variation du nombre de ticket sur une période donnée. Quelqu’un aurait-il réussi ?

    Merci 🙂
    Mickael

  3. Bonjour,
    Merci pour ce plugin, très sympa 😉
    J’ai un petit soucis, je n’arrive pas à faire l’import du fichier « Visualizations.json ».

    Voici le message d’erreur que j’obtiens :
    « Saved Objects: The file could not be processed. »

    Auriez-vous une idée ? C’est avec un kibana 4.5.1
    Merci

  4. j’ai réussi finalement , je poste la réponse ;o)

    Voici la nouvelle requete SQl qui ne prends que les tickets de moins d’un mois. la synchro est bcp plus rapide et surtout ne fait plus effondrer GLPi pendant une minute le temps de la synchro : avec ajout des groupes d’attribution pour d’autres stats :

    SELECT
    G.id,
    CASE T.groups_id
    WHEN 84 THEN ‘DRI_Systeme’
    WHEN 86 THEN ‘DRI_BackOffice’
    WHEN 91 THEN ‘Telephonie’
    WHEN 93 THEN ‘CDS_Front_Office’
    WHEN 94 THEN ‘CDS_Sites’
    WHEN 117 THEN ‘DRI_Reseau’
    WHEN 118 THEN ‘DRI_BaseDeDonnes’
    WHEN 119 THEN ‘MED_Support’
    WHEN 120 THEN ‘DRi_Applicatif’
    WHEN 121 THEN ‘DRI_Superviseur’
    WHEN 122 THEN ‘DRI_Administratif’
    WHEN 123 THEN ‘CDS_Reservation’
    WHEN 124 THEN ‘DRi_Securite’
    WHEN 125 THEN ‘CDS_BackOffice’
    WHEN 126 THEN ‘CDS_Systeme’
    WHEN 127 THEN ‘CDS_Superviseur’
    WHEN 129 THEN ‘CPU’
    WHEN 130 THEN ‘IREC’
    WHEN 131 THEN ‘VIP’
    WHEN 132 THEN ‘VVIP’
    WHEN 133 THEN ‘CDS’
    WHEN 134 THEN ‘DRI’
    WHEN 135 THEN ‘MED’
    END as « groups_id »,

    E.name as « Entité »,
    (select Ur.users_id from glpi_tickets_users Ur where Ur.type = 1 and Ur.tickets_id = G.id limit 1) as « DID »,
    (select Ur.users_id from glpi_tickets_users Ur where Ur.type = 2 and Ur.tickets_id = G.id limit 1) as « TID »,
    (select concat(concat(Ur2.realname,’ ‘),Ur2.firstname) from glpi_users Ur2 where DID = Ur2.id) AS « Demandeur »,
    (select concat(concat(Ur2.realname,’ ‘),Ur2.firstname) from glpi_users Ur2 where TID = Ur2.id) AS « Technicien »,
    R.name as « Source »,
    CASE G.type
    WHEN 1 THEN ‘Incident’
    ELSE ‘Demande’
    END as « Type »,
    CASE G.status
    WHEN 1 THEN ‘Nouveau’
    WHEN 2 THEN ‘En Cours’
    WHEN 3 THEN ‘En Cours (Attribué)’
    WHEN 4 THEN ‘En Attente’
    WHEN 5 THEN ‘Resolu’
    WHEN 6 THEN ‘Clos’
    ELSE NULL
    END as « Status »,
    C.name as « Catégorie »,
    G.name as « Objet »,
    G.date,
    G.closedate,
    G.actiontime/3600 as « actiontime »,
    G.waiting_duration/3600 as « temps attente »,
    G.close_delay_stat/3600 as « temps cloture »,
    G.solve_delay_stat/3600 as « temps resolution »,
    G.takeintoaccount_delay_stat/3600 as « temp prise en compte »
    FROM
    glpi_tickets G left outer join
    glpi_groups_tickets T on G.id = T.tickets_id left outer join
    glpi_itilcategories C on G.itilcategories_id = C.id left outer join
    glpi_entities E on G.entities_id = E.id left outer join
    glpi_users U1 on G.users_id_recipient = U1.id left outer join
    glpi_users U2 on G.users_id_lastupdater = U2.id left outer join
    glpi_requesttypes R on G.requesttypes_id = R.id
    WHERE
    G.is_deleted = 0
    AND G.date BETWEEN DATE_ADD(Now(), Interval -1 MONTH)
    AND Now()
    —————————————————–

    et voici le River correspondant ;

    ————————
    {
    « properties »: {
    « @timestamp »: {
    « type »: « date »,
    « format »: « strict_date_optional_time||epoch_millis »
    },
    « @version »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « catégorie »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « did »: {
    « type »: « long »
    },
    « demandeur »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « entité »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « objet »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « source »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « status »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « groups_id »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « tid »: {
    « type »: « long »
    },
    « technicien »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « type »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « actiontime »: {
    « type »: « double »
    },
    « closedate »: {
    « type »: « date »,
    « format »: « strict_date_optional_time||epoch_millis »
    },
    « date »: {
    « type »: « date »,
    « format »: « strict_date_optional_time||epoch_millis »
    },
    « id »: {
    « type »: « long »
    },
    « temp prise en compte »: {
    « type »: « double »
    },
    « temps attente »: {
    « type »: « long »
    },
    « temps cloture »: {
    « type »: « long »
    },
    « temps resolution »: {
    « type »: « double »
    }
    }
    }

    ————————————————————————

  5. Bonjour, j’ai voulu recréer un index uniquement pour les nouveaux tickets.
    La synchro que vous avez fournit fonctionne tres bien mais la vu est trop longue à raffraichir, j’ai donc décidé de refaire une vue plus légere pour récupérer le status « 1 » des tickets et m’afficher un compteur des nouveau tickets. hors apres toutes les modifications présentes apportées, mon kibana voit l’index glpi_newtickets, mais me sort une erreur SHARD ou reste bloqué sur SEARCH dans le mode discovery !! Auriez-vous une idée de ce que j’ai manqué ! je débute en linux et SQl , sans parler du Java ;oD J’avance tnat bien que mal ! toute aide serait la bienvenue !!! ;o) Merci

    Alexis.

    #!/bin/bash

    #########################################################
    # ES-GLPI – Config Shell Script #
    # desaille.fr #
    # #
    # Full Path of es-glpi #
    # If you change this directory, you have to update it #
    # in the two es-glpi logstash config files #
    # => conf/logstash/*.conf #
    DIR= »/opt/es-glpi » #
    # #
    # Params for dynamic configuration #
    INDEX=(glpi_newtickets) #
    # #
    # ElasticSearch Host #
    ES=localhost:9200 #
    # #
    # Kibana Version #
    KIB= »4.4.1″ #
    # #
    # Colors #
    G= »\033[32m » #
    W= »\033[0m » #
    Y= »\033[33m » #
    R= »\033[31m » #
    # #
    #########################################################

    for v in ${INDEX[@]}
    do
    echo -e « ${Y}#### Put index [${v}] in ElasticSearch (delete if already exists):${W} »
    /usr/bin/curl -XDELETE  »${ES}’/’${v} » &>/dev/null
    /usr/bin/curl -XPUT  »${ES}’/’${v} » &>${DIR}/logs/curl.log
    if [ $? == 0 ]
    then
    echo -e « ${G}==> OK${W} »
    else
    echo -e « ${R}==> NOK – Check ${DIR}/logs/curl.log for more informations${W} »
    fi
    echo -e « ${Y}#### Puts [${v}] mappings in ElasticSearch:${W} »
    /usr/bin/curl -XPUT  »${ES}’/’${v}’/’${v}’/_mapping’ -d @${DIR}/conf/mappings/${v}.json &>${DIR}/logs/curl.log
    if [ $? == 0 ]
    then
    echo -e « ${G}==> OK${W} »
    else
    echo -e « ${R}==> NOK – Check ${DIR}/logs/curl.log for more informations${W} »
    fi
    echo -e « ${Y}#### Puts [${v}] indices to Kibana config:${W} »
    /usr/bin/curl -XPUT  »${ES}’/.kibana/index-pattern/’${v} » -d ‘{« title » : « ${v} », « timeFieldName »: « date »}’ &>${DIR}/logs/curl.log
    if [ $? == 0 ]
    then
    echo -e « ${G}==> OK${W} »
    else
    echo -e « ${R}==> NOK – Check ${DIR}/logs/curl.log for more informations${W} »
    fi
    done

    ——————————————

    #!/bin/bash

    #########################################################
    # ES-GLPI – Sync Shell Script #
    # desaille.fr #
    # #
    # Full Path of es-glpi #
    # If you change this directory, you have to update it #
    # in the two es-glpi logstash config files #
    # (conf/logstash/*.conf) #
    DIR= »/opt/es-glpi » #
    # #
    # Params for dynamic configuration #
    INDEX=(glpi_newtickets) #
    # #
    # ElasticSearch Host #
    ES=localhost:9200 #
    # #
    # Colors #
    G= »\033[32m » #
    W= »\033[0m » #
    Y= »\033[33m » #
    R= »\033[31m » #
    # #
    #########################################################

    for v in ${INDEX[@]}
    do
    echo -e « ${Y}#### Delete docs from index [${v}] in ElasticSearch:${W} »
    /usr/bin/curl -XDELETE ‘http://’${ES}’/’${v}’/_query’ -d’ { « query » : { « match_all »: {} } }’ &>${DIR}/logs/curl.log
    if [ $? == 0 ]
    then
    echo -e « ${G}==> OK${W} »
    else
    echo -e « ${R}==> NOK – Check ${DIR}/logs/curl.log for more informations${W} »
    fi
    echo -e « ${Y}#### Sync [${v}] data from MySQL:${W} »
    /opt/logstash/bin/logstash agent –config ${DIR}/conf/logstash/${v}.conf &>${DIR}/logs/logstash.log
    if [ $? == 0 ]
    then
    echo -e « ${G}==> OK${W} »
    else
    echo -e « ${R}==> NOK – Check ${DIR}/logs/logstash.log for more informations${W} »
    fi
    done

    ———————————————————

    conf/LOGSTAH/glpi_newtickets.conf

    input {
    jdbc {
    jdbc_connection_string => « jdbc:mysql://xxxx:3306/glpidb »
    jdbc_user => « xxx »
    jdbc_password => « xxx »
    jdbc_driver_library => « /opt/logstash/jdbc-drivers/mysql-connector-java.jar »
    jdbc_driver_class => « com.mysql.jdbc.Driver »
    statement_filepath => « /opt/es-glpi/conf/sql/glpi_newtickets.sql »
    }
    }
    output {
    elasticsearch {
    hosts => « localhost:9200 »
    index => « glpi_newtickets »
    }
    }

    ——————————-

    /conf/mappings/glpi_newtickets.conf

    {
    « properties »: {
    « source »: {
    « type »: « string »,
    « index »: « not_analyzed »
    },
    « id »: {
    « type »: « long »
    }
    }
    }

    —————————–
    /conf/sql/glpi_newtickets.sql

    SELECT
    id,status
    FROM
    glpi_tickets

    WHERE
    status=’1′

  6. Bonjour, un grand merci pour ces explications nettes et précises ! je me régale avec le dashboard ! j’ai juste un souci sur la convertion des temps de prise en compte ! Les resultats sont en 0.xxx ! Je dois bien prendre le résultat le multiplier par 1000 et le diviser par 60 pour avoir le temps en minutes de prise en compte ??

    EX : 0.069 = 0.069 * 1000 = 69 ……… 69 / 60 = 1.15 minutes. ou je me plante littéralement ?
    Merci pour cette précision ! Bonne journée et bonne continuation !

    Time temp prise en compte
    May 17th 2016, 18:15:07.000 0
    May 17th 2016, 17:34:36.000 0.069
    May 17th 2016, 16:54:30.000 0.026
    May 17th 2016, 16:23:42.000 0.029
    May 17th 2016, 15:49:01.000 0.097
    May 17th 2016, 15:46:02.000 0.035

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.