You are not logged in.

Announcement

 Téléchargez la dernière version stable de GLPI      -     Et vous, que pouvez vous faire pour le projet GLPI ? :  Contribuer
 Download last stable version of GLPI                      -     What can you do for GLPI ? :  Contribute

#1 2020-11-02 17:27:40

Toundrik
Member
Registered: 2020-09-02
Posts: 2

CLI - insertion d'entités et cache

Bonjour,

Je viens vers vous après de nombreuses heures de tests et de recherches sur le forum. Je développe actuellement un plugin pour des besoins internes spécifiques et l'une des fonctionnalités doit permettre le chargement d'une liste d'entités fournies dans un fichier texte (environ 40000 entrées de type extraction COBOL). J'arrive à lire ce fichier et à insérer l'ensemble des entités via des requêtes paramétrées (buildInsert, prepare, bindParam, execute et free_result).
Les entités sont bien reliées hiérarchiquement via le completename, les entities_id et le level jusqu'à l'entité racine. (j'ai même fait des essais en insérant ancestors_cache et sons_cache via les fonctions getAncestorsOf et getSonsOf)
Le problème vient (à mon avis), du rafraichissement du "cache", à savoir les fichiers présents dans glpi/files/_cache . En effet, si je me connecte en tant que glpi (Super-Admin R) immédiatement après l'exécution du plugin via front/cron.php, je ne vois que l'entité racine dans le menu Entité. Par contre, j'ai bien accès à toute l'arborescence dans le menu de sélection d'entité pour mon utilisateur et je peux changer l'entité affectée.
Si d'aventure je supprime les fichiers de glpi/files/_cache, la connexion s'éternise jusqu'au timeout PHP (configuré sur 5 mns) et j'observe la lente reconstruction du fichier de cache.
Enfin, si je limite mon fichier à une dizaine de lignes, le cache se reconstruit et les entités sont visibles dans le menu Entité.

Si je comprends bien, il faudrait que je puisse lancer le rafraichissement du cache dans mon plugin immédiatement après avoir inséré les données. En effet, le script sera exécuté de nuit et j'aurai largement le temps de tout reconstruire. Malgré mes recherches, je ne trouve pas de méthode ou de fonction qui réaliserait cette tâche. Est-ce possible ? Ou alors faut-il utiliser une autre méthode pour l'insertion d'entités et qui permettrait de mettre à jour le cache pour chaque insertion ?

Pour info, mon poste de développement est sous Windows 10, PHP 7.4.12, OPCache et APCu activés, MariaDB 10.5 et GLPI 9.4.6

Offline

#2 2020-11-03 12:59:09

Toundrik
Member
Registered: 2020-09-02
Posts: 2

Re: CLI - insertion d'entités et cache

Pour info, voici la partie de mon hook.php :

/**
 * hook d'installation
 * 
 * @return boolean
 */
function plugin_monplugin_install() {
   CronTask::Register(
      'PluginMonpluginStructures', 
      'MajStructures', 
      WEEK_TIMESTAMP, 
      [
         'mode' => CronTask::MODE_EXTERNAL,
         'comment'   => "Mise à jour hebdomadaire des entités"
      ]
   );

   return true;
}

et la classe Structures qui charge le fichier de 40000 lignes :


<?php
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
   define("REPERTOIRE_STRUCT", "C:\\nginx-1.19.4\\html\\glpi\\files\\_plugins\\monplugin\\");
}
else {
   define("REPERTOIRE_STRUCT", "/usr/share/nginx/html/glpi/files/_plugins/monplugin/");
}

class PluginMonpluginStructures extends commonDBTM
{
   static function cronInfo($name)
   {
      switch ($name) {
         case 'MajStructures' :
            $retour = [
               "description"  => "Mise à jour du référentiel des structures"
            ];
            break;
         default :
            $retour = [];
      }

      return $retour;
   }

   /**
    * cron GLPI : mise à jour des structures
    */
   static function cronMajStructures($task = null)
   {
      global $DB;

      // ouverture du fichier STRUCTURES
      // TODO récupérer la date du fichier
      $nomfichier = REPERTOIRE_STRUCT . 'STRUCTURES';
      $fhStructures = fopen($nomfichier, 'r');
      $structures = [];
      $inactives = [];
      $libelles = [];
      $doublons = [];
      $nb_update = 0;
      $nb_insert = 0;

      if ($fhStructures) {
         // TODO lire le nombre de lignes dans l'entête
         $entete = fgets($fhStructures);

         // l'index 0 de structures contient la racine 
         $structures[] = [
            "numua"        => "0000000000",
            "uaparent"     => "0000000000",
            "entities_id"  => -1,
            "level"        => 1,
            "name"         => "Racine",
            "completename" => "Racine",            
         ];

         /**
          * 1ère passe
          * 
          * lecture du fichier ligne par ligne
          * enregistrement des structures actives dans 'structures'
          * enregistrement des strucutres inactives dans 'inactives'
          */
         while ($ligne = fgets($fhStructures)) {
            $adresse= substr($ligne, 256, 190);
            $date_validite = substr($ligne, 10, 8);
            $etat = substr($ligne, 69, 1);
            $libcrt1 = trim(substr($ligne, 203, 15));
            $libcrt2 = trim(substr($ligne, 218, 15));
            $name = trim($libcrt1 . " " . $libcrt2); // si $libcrt2 est vide
            $uaparent1 = substr($ligne, 98, 10);
            $uaparent2 = substr($ligne,108, 10);
            $uaparent3 = substr($ligne,118, 10);

            // $uaparent correspond à la structure hiérarchique directe
            if ($uaparent3 !== "0000000000") {
               $uaparent = $uaparent3;
            }
            else if ($uaparent2 !== "0000000000") {
               $uaparent = $uaparent2;
            }
            else {
               $uaparent = $uaparent1;
            }

            $lu = [
                  "numua"        => substr($ligne, 0, 10),
                  "uaparent"     => $uaparent,
                  "codest"        => trim(substr($ligne, 18, 10)),
                  "name"         => $name,
                  "completename" => $name,
                  "phonenumber"  => substr($ligne, 522, 10),
                  "email"        => strtolower(trim(substr($ligne, 552, 58))),
                  "address"      => trim(substr($adresse, 0, 114)),
                  "town"         => trim(substr($adresse, 114, 38))
            ];

            if ($date_validite === "99999999" && $etat === "C") {
                  $structures[] = $lu;
            }
            else {
                  $inactives[] = $lu;
            }
         }

         // correspondance index 'structures' et 'numua'
         $indexua  = array_column($structures, "numua");
         // correspondance 'numua' et index 'structures'
         $uaindex  = array_flip($indexua);

         /**
          * 2de passe
          * 
          * reconstruction de la hiérarchie des libellés
          */
         foreach($structures as $index => $s) {
            $structures[$index]['completename'] = self::creerLibHierarc($s,$structures,$indexua);
         }

         /**
          * 3ème passe
          * 
          * ajout préfixe Racine ,index du parent et niveau hiérarchique
          */
         foreach ($structures as $index => $s) {
            $structures[$index]["completename"] = "Racine > " . $s["completename"];
            $structures[$index]["entities_id"] = $uaindex[$s["uaparent"]];
            $structures[$index]['level'] = count(explode('>', $structures[$index]['completename']));  
         }

         $libelles = array_column($structures, "completename");
         $doublons = array_diff_assoc($libelles, array_unique($libelles));
         
         /**
          * 4ème passe
          * 
          * suffixe des doublons
          */
         foreach (array_keys($doublons) as $clef) {
            $suffixe = " " . $structures[$clef]["codest"];
            if ($clef) {
               $structures[$clef]["completename"] .= $suffixe;
               $structures[$clef]["name"] .= $suffixe;
            }
         }  

         /**
          * 5ème passe TODO
          * 
          * écriture en base de données
          */

         // nombres de structures à insérer
         $nb_structures = count($structures);
         
         // renommage de l'entité racine
         $DB->update(
            'glpi_entities',
            [
               'name'   => 'Racine',
               'completename' => 'Racine'
            ],
            [
               'id'     => 0
            ]
         );

         // requêtes paramétrées
         $insert_query = $DB->buildInsert(
            'glpi_entities', 
               [
                  'id'           => new Queryparam(),
                  'name'         => new Queryparam(),
                  'entities_id'  => new Queryparam(),
                  'completename' => new Queryparam(),
                  'address'      => new Queryparam(),
                  'town'         => new Queryparam(),
                  'phonenumber'  => new Queryparam(),
                  'email'        => new Queryparam(),
                  'level'        => new Queryparam(),
                  'ldap_dn'      => new Queryparam(),
                  'tag'          => new Queryparam(),
                  'date_mod'     => new QueryParam(),
                  'date_creation'=> new QueryParam(),
               ]
         );

         $update_query = $DB->buildUpdate(
            'glpi_entities', 
               [
                  'name'         => new Queryparam(),
                  'entities_id'  => new Queryparam(),
                  'completename' => new Queryparam(),
                  'address'      => new Queryparam(),
                  'town'         => new Queryparam(),
                  'phonenumber'  => new Queryparam(),
                  'email'        => new Queryparam(),
                  'level'        => new Queryparam(),
                  'ldap_dn'      => new Queryparam(),
                  'date_mod'     => new QueryParam(),
               ],
               [
                  'tag'          => new QueryParam()
               ]
         );

         $insert_stmt = $DB->prepare($insert_query);
         $update_stmt = $DB->prepare($update_query);
         $select_stmt = $DB->prepare("SELECT count(*) FROM glpi_entities WHERE tag=?");
         $date = new DateTime();
         $date_du_jour = $date->format("Y-m-d H:i:s");

         for ($c = 1 ; $c < $nb_structures ; $c++) {
            // requête sur le numéro UA, pour savoir si la structure est déjà présente
            $select_stmt->bind_param('s', $structures[$c]['numua']);
            $select_stmt->execute();
            $select_stmt->bind_result($existe); // le count est placé dans $existe
            $select_stmt->fetch(); // on récupère le résultat
            $select_stmt->free_result(); // on libère la mémoire

            // si la structure existe alors on UPDATE sinon on INSERT
            if ($existe > 0) {
               $update_stmt->bind_param(
                  'sisssssisss',
                  $structures[$c]['name'],
                  $structures[$c]['entities_id'],
                  $structures[$c]['completename'],
                  $structures[$c]['address'],
                  $structures[$c]['town'],
                  $structures[$c]['phonenumber'],
                  $structures[$c]['email'],
                  $structures[$c]['level'],
                  $structures[$c]['codest'],
                  $date_du_jour,
                  $structures[$c]['numua']
               );
               
               $update_stmt->execute();
               $update_stmt->free_result(); // on libère la mémoire
               $nb_update++;
            }
            else {
               $insert_stmt->bind_param(
                  'isisssssissss',
                  $c,
                  $structures[$c]['name'],
                  $structures[$c]['entities_id'],
                  $structures[$c]['completename'],
                  $structures[$c]['address'],
                  $structures[$c]['town'],
                  $structures[$c]['phonenumber'],
                  $structures[$c]['email'],
                  $structures[$c]['level'],
                  $structures[$c]['codest'],
                  $structures[$c]['numua'],
                  $date_du_jour,
                  $date_du_jour
               );

               $insert_stmt->execute();
               $insert_stmt->free_result(); // on libère la mémoire
               $nb_insert++;
            }
         }
         

         /**
          * 6ème passe
          *
          * reconstruction des ancestors_cache et sons_cache
          */
         /*$caches_query = $DB->buildUpdate(
            'glpi_entities', 
               [
                  'ancestors_cache' => new Queryparam(),
                  'sons_cache'      => new QueryParam(),
               ],
               [
                  'id' => new QueryParam()
               ]
         );
         $caches_stmt = $DB->prepare($caches_query);
         
         for ($c = 1 ; $c < $nb_structures ; $c++) {
            $ancestors_cache = json_encode(getAncestorsOf('glpi_entities', $c));
            $sons_cache = json_encode(getSonsOf('glpi_entities', $c));
            $caches_stmt->bind_param(
               'ssi',
               $ancestors_cache,
               $sons_cache,
               $c
            );
            $caches_stmt->execute();
            $caches_stmt->free_result();
         }*/
      }

      fclose($fhStructures);

      // ajoute le nombre de structures à la tâche
      $task->addVolume($nb_structures);

      // ajoute une entrée dans le journal
      $task->log("Lecture de " . $nb_structures . " structures.");
      $task->log($nb_insert . " nouvelles structures.");
      $task->log($nb_update . " structures mises à jour.");

      return 1;
   }

   /**
    * crée la chaîne hiérarchique du 'completname' pour insertion dans la
    * table 'entities' de GLPI par appels récursifs
    *
    * @param Array $ligne une structure
    * @param Array &$structures référence vers les structures (lecture seule)
    * @param Array &$indexua référence vers le tableau de correspondance 
    *    position dans $structures -> numéro d'UA
    *
    * @return String La chaîne hiérarchique représentant la structure
    */
   private static function creerLibHierarc($ligne, &$structures, &$indexua): String
   {
      $libelle = $ligne["name"];
      $uaparent = $ligne["uaparent"];
      if ($uaparent !== "0000000000") {
         $ligneParente = $structures[array_search($uaparent, $indexua)];
         $libelle = self::creerLibHierarc($ligneParente, $structures, $indexua) 
               . " > " . $libelle;
      }

      return $libelle;
   }
}

Comme dit précédemment, le code fonctionne et l'insertion s'effectue sans soucis. Mais les entités n'apparaissent pas lors d'une reconnexion, sauf après plusieurs reconnexion où quelques entités apparaissent mais jamais la totalité des 40000 lignes.

Offline

Board footer

Powered by FluxBB