, Utilisez NFS sur votre IBMi

NFS est un protocole de partage de fichier issu du monde Unix, SMB est celui de windows c’est celui qui est utilisé dans NETSERVER.

https://fr.wikipedia.org/wiki/Network_File_System

Nous allons voir comment l’utiliser NFS sur l’IBMi qui peut être client et serveur par exemple pour partager un fichier d’installation ou de paramétrage.

Sur le serveur

Vous devez démarrer le serveur.

STRNFSSVR SERVER(*ALL)

Vous devez créer le répertoire à exporter

CRTDIR (‘/SHARE_NFS’)

Vous devez monter l’export

Paramétrage dans exports vous avez un fichier

EDTF STMF(‘/etc/exports’)
/SHARE_NFS/URANUS -ro

pour exporter

EXPORTFS

Si tout se passe bien vous aurez ce message :

Demande d’exportation exécutée.
1 postes exportés, 0 postes non exportés.

Vous pouvez être obligé de rajouter des droits sur votre partage :

CHGNFSEXP OPTIONS(‘-I -O RW=,ANON=0’) DIR(‘/SHARE_NFS/URANUS’)

Votre répertoire est maintenant exporté.

Sur le client

Vous devez démarrer le même serveur.

STRNFSSVR SERVER(*ALL)

Vous devez créer un répertoire pour le montage

MKDIR DIR(‘/MNT/NEPTUNE’)

Vous devez faire le montage

MOUNT TYPE(*NFS) MFS(‘NEPTUNE:/SHARE_NFS/URANUS’)
MNTOVRDIR(‘/MNT/NEPTUNE’)

Si tout va bien vous aurez ce message :


Système de fichiers monté.

Vous pouvez contrôler par :

DSPMFSINF OBJ(‘/MNT/NEPTUNE’)

Objet . . . . . . . . . . . . : /MNT/NEPTUNE

Type de système de fichiers . : Syst. de fichiers réseau (NFS)

Taille de bloc . . . . . . . . : 32768
Nombre total de blocs . . . . : 23303175
Blocs libres . . . . . . . . . : 5182808
Nombre maximal de liens à des
objets . . . . . . . . . . . : 32767
Nombre maximal de liens à un
répertoire . . . . . . . . . : 1000000
Longueur maximale d’un
composant de nom de chemin . : 255
Longueur maximale de nom de
chemin . . . . . . . . . . . : Pas de maximum

Pour accéder au fichier par exemple :

wrklnk (‘/mnt/NEPTUNE/*’)

Répertoire . . . . : /mnt/NEPTUNE

Vous pourrez voir votre fichier par 5

Remarque :

Vous pouvez l’utiliser que en serveur ou en client avec un système distant sous Linux le plus souvent.

C’est un protocole très connu par les administrateurs Unix.

Pour échanger entre IBMi, vous pouvez également utiliser QFileSvr.400

, , Nommer un groupe d’activation pour des programmes RPGLE

Vous voulez nommer votre groupe d’activation pour toute une application
donc sans indiquer d’option dans le source qui seraient prioritaires par rapport à votre commande de compile

On va parler ici des BIND c’est l’opération que fait une commande pour compiler le module et l’assembler pour en faire un programme

Pour les sources RPGLE

C’est simple vous avez un paramètre

CRTBNDRPG PGM(GDATA/AAACTGRP)
SRCFILE(GDATA/QRPGLESRC)
SRCMBR(AAACTGRP)
DFTACTGRP(NO) ACTGRP(GAIA) <======== c’est ici

Pour les SQLRPGLE

Vous n’avez pas le paramètre ACTGRP dans la commande CRTSQLRPGI

il faut donc passer par les options de compile c’est le paramètre COMPILEOPT

CRTSQLRPGI OBJ(GDATA/AAACTGRP2)
SRCFILE(GDATA/QRPGLESRC)
SRCMBR(AAACTGRP2)
OBJTYPE(PGM) REPLACE(NO)
COMPILEOPT(‘DFTACTGRP(*NO) ACTGRP(GAIA)’) <====== comme ceci

On est obligé de mettre les 2 paramètres même si DFTACTGRP(*NO) dans le source ????

Attention, il n’y a pas de contrôle de syntaxe sur le paramètre

Remarque

Bien sur mes informations concernent les binds, pour l’assemblage de modules l’option est dans la commande CRTPGM directement

, , , Connaitre la bibliothèque du programme en cours

Vous voulez connaitre la bibliothèque d’un programme en cours d’exécution, pour ajouter cette bibliothèque par exemple, pour contextualiser un exit programme, un watcher, un trigger ou pour limiter un environnement prod, versus dev.
Le tout, sans harcoder une bibliothèque qui figera votre code et vos environnements.

Voici 2 exemples

En RPGLE

dcl-ds *N PSDS ;                  
  bibli_du_pgm CHAR(10) POS(81);  
  nom_du_pgm CHAR(10) POS(1);     
 End-ds ;                          
dcl-s present ind ;
// on tente d'ajouter la bibliothèque
 exec sql                                                                  
 call qcmdexc('Addlible ' concat :bibli_du_pgm concat ' *FIRST') ;         
if sqlcode = 0 ;
  present = *on ;
endif ;
// votre traitement ici
// on enlève si on a ajouté 
if present = *on ;
 exec sql                                                                  
 call qcmdexc('Rmvlible ' concat :bibli_du_pgm ) ;         
endif ;

En CLLE

PGM                                                    
            DCL        VAR(&DATA) TYPE(*CHAR) LEN(80)  
            DCL        VAR(&LIB) TYPE(*CHAR) LEN(10)   
            DCL        VAR(&PGM) TYPE(*CHAR) LEN(10)   
            DCL        VAR(&TEMOIN) TYPE(*LGL)
 /* Paramétrage de l'appel */                          
            CHGVAR     VAR(%BIN(&DATA  1 4)) VALUE(80) 
            CHGVAR     VAR(%BIN(&DATA  5 4)) VALUE(80) 
            CHGVAR     VAR(%BIN(&DATA  9 4)) VALUE( 0) 
            CHGVAR     VAR(%BIN(&DATA 13 4)) VALUE( 0) 
 /* Appel de la procédure */                           
            CALLPRC    PRC('_MATPGMNM') PARM(&DATA)    
 /* Extraction des informations  */                    
            chgvar &pgm %SST(&DATA 51 10)              
            chgvar &lib %SST(&DATA 19 10) 
/* ajout de la bibliothèque */
ADDLIBLE &LIB *FIRST
monmsg cpf2103 exec(do)
chgvar &temoin '1'
enddo 
/* Votre traitement ici */
/* on enlève si on a ajouté */
if cond(*not &temoin) then(do)
RMVLIBLE &LIB
enddo          
ENDPGM          

Remarque :

On a mis également le programme en cours dans les exemples

On a mis le code pour enlever la bibliothèque après le traitement, uniquement si c’est notre programme qui l’a ajouté.


En RPGLE si vous avez un fichier vous devrez déclarer votre fichier en USROPN et ouvrir le fichier par un OPEN, après avoir ajouté la bibliothèque

, CPF2225 sur CHGUSRPRF

Il est possible que vous receviez ce message sur un changement de profil ou sur une suppression (DLTUSRPRF ou CHGUSRPRF)

Ca signifie qu’un autre utilisateur verrouille l’AUT (Authorized User Table) par une autre commande liée à la sécurité.

Cette objet s’appelle QSYUPTBL de la bibliothèque QSYS et il est de Type *AUT.

le premier reflexe serait de faire :

==>

WRKOBJLCK OBJ(QSYS/QSYUPTBL)
OBJTYPE(*AUT)

Mais le type *AUT n’est pas supporté dans la commande (sniff !)

Vous devrez donc passer la commande suivante

==>CALL QTNDSPLS (‘QSYS/QSYUPTBL’ *AUT)

qui vous indiquera qui tient cette table :

Exemple

‘STATUS OF ALL JOBS FOR THE ABOVE SPACE LOCATION:        
                                                         
158965/PLB/QPADEV0001                         LSRD  HELD  

Vous devrez déterminer si c’est normal , sinon il faudra arrêter le travail fautif

Pour en savoir plus :

https://www.ibm.com/support/pages/cpf2225-received-running-security-commands

Remarque :

Vous n’avez pas intérêt à faire tourner 2 process de gestion de profil en même temps

Gestion de l’IDENTITY d’une table

Que se passe-t-il si on définit soi-même une zone IDENTITY lors de la mise à jour d’une table ?

Ce n’est évidemment pas la meilleure des idées qu’on puisse avoir, mais parfois dans l’urgence d’une correction de données …

  • Commençons par créer une table de tests avec une zone identité de type bigint :
CREATE TABLE NK.IDENT
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (
  START WITH 1  INCREMENT BY 1
  NO MINVALUE  NO MAXVALUE
  NO CYCLE NO ORDER),
 NOM_SQL_ZONE_CHAR FOR COLUMN ZONECHAR CHAR(20) NOT NULL DEFAULT '',
 CONSTRAINT IDENT_PK PRIMARY KEY( ID)
)
RCDFMT RIDENT  ;
RENAME TABLE NK.IDENT TO TESTS_IDENTITY
 FOR SYSTEM NAME IDENT;
  • Les zones qui nous intéressent dans la vue syscolumns de QSYS2  ressemblent à ça :
select column_name,
       is_identity,
       identity_generation,
       identity_minimum,
       identity_maximum 
  from qsys2.syscolumns
 where system_table_name ='IDENT'
   and system_table_schema ='NK'
order by ordinal_position;
  • Alimentation de la table avec quelques enregistrements
 insert into nk.ident (Zonechar) values ('Insert Zone2 #1');
 insert into nk.ident (Zonechar) values ('Insert Zone2 #2');
 insert into nk.ident (Zonechar) values ('Insert Zone2 #3');
 insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #4');
 insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #5');
 insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #6');

On peut ignorer ID ou le renseigner en DEFAULT, la table est alimentée :

select * from nk.ident;
  • Que se passe-t-il si je définis moi-même ID lors d’un insert ?

Si l’identity est déjà occupée par un enregistrement : SQL n’accepte pas l’instruction, il fait ce qu’on lui a demandé !

insert into nk.ident (ID, Zonechar) values (1, 'Insert KO');
  • Si je fais des insertions de données dans IDENT en définissant moi-même des ID libres :
insert into nk.ident
 select id+6, trim(zonechar) || ' Cpy' from nk.ident;

Tout semble s’être bien passé :

select * from nk.ident;

Si j’interroge SQL sur la dernière valeur IDENTITY qu’il a géré :

values (IDENTITY_VAL_LOCAL());

Tout semble encore correct.

Mais si je refais une insertion de données en laissant à nouveau SQL gérer l’identity :

insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #7');

On consulte la log du travail comme le message d’erreur nous invite :

select message_second_level_text 
  from table(qsys2.joblog_info('111778/QUSER/QZDASOINIT')) 
 where message_id = 'CPF5009';

Deux enregistrements sont trouvés :

&N Cause . . . . . :   L’opération d’écriture ou de mise à jour dans le membre numéro 1 (enregistrement numéro 0, format RIDENT) pour le membre IDENT du fichier IDENT, se trouvant dans NK, n’a pas abouti.Le membre numéro 1 (enregistrement numéro 1, format RIDENT) a la même clé d’enregistrement que le membre numéro 1 (enregistrement numéro 0, format RIDENT). Si ce numéro d’enregistrement est 0, la clé d’enregistrement en double a été créée lors d’une opération d’écriture.

&N Que faire . . . : Modifiez les clés en double, de sorte que chaque enregistrement ait une clé unique. Renouvelez la demande.

&N Cause . . . . . :   L’opération d’écriture ou de mise à jour dans le membre numéro 1 (enregistrement numéro 0, format RIDENT) pour le membre IDENT du fichier IDENT, se trouvant dans NK, n’a pas abouti. Le membre numéro 1 (enregistrement numéro 7, format RIDENT) a la même clé d’enregistrement que le membre numéro 1 (enregistrement numéro 0, format RIDENT). Si ce numéro d’enregistrement est 0, la clé d’enregistrement en double a été créée lors d’une opération d’écriture.

&N Que faire . . . : Modifiez les clés en double, de sorte que chaque enregistrement ait une clé unique. Renouvelez la demande.

Le premier message est relatif à la tentative d’insertion « insert into nk.ident (ID, Zonechar) values (1, ‘Insert KO’); » tentée plus haut et pour laquelle l’erreur était attendue.

Le second message est relatif à «insert into nk.ident (ID, Zonechar) values (DEFAULT, ‘Insert Zone2 #7’); »

SQL a tenté d’utiliser la valeur suivante de la dernière identity qu’il a lui-même géré, mais a échoué car la valeur IDENT.ID=7 existe déjà.

Si on retente l’insertion qui vient juste d’échouer :

insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #7');

Elle échoue de la même façon, sauf que cette fois SQL a tenté d’utiliser l’ID = 8 :

&N Cause . . . . . :   L’opération d’écriture ou de mise à jour dans le membre numéro 1 (enregistrement numéro 0, format RIDENT) pour le membre IDENT du fichier IDENT, se trouvant dans NK, n’a pas abouti. Le membre numéro 1 (enregistrement numéro 8, format RIDENT) a la même clé d’enregistrement que le membre numéro 1 (enregistrement numéro 0, format RIDENT). Si ce numéro d’enregistrement est 0, la clé d’enregistrement en double a été créée lors d’une opération d’écriture.

&N Que faire . . . : Modifiez les clés en double, de sorte que chaque enregistrement ait une clé unique. Renouvelez la demande.

  • Comment corriger la situation ?

La solution pour se sortir de là si on a fait 3000 insertions ne va pas être de tenter 3000 insertions bidons pour que la table ait son compteur interne gérant l’identity à jour (d’ailleurs, si quelqu’un sait où il se cache je suis preneur).

On récupère la dernière ID utilisée dans la table :

select max(ID) from nk.ident ;

Et on ajoute 1 pour mettre à jour la table :

alter table nk.ident
alter column id restart with 13;

L’instruction précédente :

insert into nk.ident (ID, Zonechar) values (DEFAULT, 'Insert Zone2 #7'); 

se passe bien maintenant et la numérotation de IDENT.ID a bien repris normalement :

 select * from nk.ident;

Pour se prémunir de tout ceci, il suffit de vérifier la nature de la clé primaire d’une table avant de commencer à y insérer des enregistrements.

Sur DB2 l’usage d’IDENTITY dans une table SQL n’est pas très répandu, il est donc nécessaire de comprendre la structure d’une table avant de l’utiliser. L’IDENTITY se révèle alors pratique tant qu’on laisse le système la gérer. On peut bien sûr, dans des cas exceptionnels, la gérer soi-même si on fait attention…

, , Fichier SQLPRE de QTEMP

Vous avez des sources SQLRPGLE qui sont différents des tailles par défaut de 100

Vous pouvez avoir ce message à la compile RNF0733
C’est le fichier, QTEMP/QSQLPRE de pré-compilation qui est trop court QTEMP/QSQLPRE
Ce fichier est utilisé dans les commandes CRTBNDRPG , CRTRPGMOD, ou CRTSQLRPGI

Vous avez une variable d’environnement QIBM_RPG_PPSRCFILE_LENGTH qui permet de changer la valeur par défaut qui est de 112.
Elle doit avoir la longueur de votre donnée + 12
Exemple
SRCDTA = 140
Vous devrez indiquer 152

Pour ajouter la variable :

ADDENVVAR ENVVAR(QIBM_RPG_PPSRCFILE_LENGTH.)
VALUE(152)
LEVEL(*SYS)

*SYS pour l’ajouter à tout le système

Pour ne savoir plus ici

https://www.ibm.com/support/pages/node/6857461

, , Protéger APPEL / SYSTEME

Vous voulez protéger vos sessions 5250 de la possibilité de faire un Appel systéme

Vous devez mettre en place un programme d’exit (8 possibles)

QIBM_QWT_SYSREQPGMS

Vous devez ensuite indiquer sur chaque profil les programmes à utiliser

Schéma ci dessous

L’utilisateur quand il appuiera sur APP SYST le programme PGM1 sera appelé

Programme Exit ici le 1 , nom du programme APPSYS



**free
     //  programme QIBM_QWT_SYSREQPGMS contrôle d'accès à la touche
     //  ATTN REQUEST
     // l'utilisateur à ce programme de contrôle son profil il s'exécute
     //     et il n'a pas le droit
      ctl-opt
      DFTACTGRP(*NO) ;
  Dcl-Pi *N;
    Reponse            int(10);
  //                            1 ok
  //                            0 ko
    data               Char(128);
  End-Pi;
  //
    Reponse = 0;
    *inlr = *on ;
GDATA_QRPGLESRC_APPSYS.TXT
Affichage de GDATA_QRPGLESRC_APPSYS.TXT en cours...

Ce programme est simple , il interdit s’il est appelé

Pour ajouter ce programme :

ADDEXITPGM EXITPNT(QIBM_QWT_SYSREQPGMS)

FORMAT(SREQ0100)
PGMNBR(1)
PGM(Votrelib/APPSYS)
REPLACE(NO)

Dans ==>WRKREGINF pour contrôle

Programme de mise à jour des profils qui devront être concernés par le contrôle

**free
//     Programme exit pour protéger appel système
//     exit PGM   QIBM_QWT_SYSREQPGMS
//     Pour que ce programme ce déclenche il faut que vous
//     l'indiquiez au niveau du profil
//     8 programmes possibles ici le 1 correspond au PGMNBR(1)
//     vous devez utiliser l'API  QWTSETPX
ctl-opt
  DFTACTGRP(*NO) ;
// paramètre recu le profil à protéger
Dcl-Pi *N;
  P_user             char(10);
End-Pi;
// prototypage de l'API de mise à jour
Dcl-PR QWTSETPX ExtPgm( 'QWTSETPX');
  nbrent  int(10)     ;
  flags   char(32)    ;
  format  char(8)    ;
  user    char(10)   ;
  erreur  char(32)   ;
End-PR;
// Variables de travail
dcl-s wnbrent  int(10)     ;
dcl-s wflags   char(32)    ;
dcl-s wformat  char(8)    ;
dcl-s werreur  char(32)    ;
// constantes figuratives
dcl-s inact    char(04)  inz(x'00000000') ;
dcl-s actif    char(04)  inz(x'00000001') ;
// Appel du programme
  wformat = 'SREQ0100' ;
  wnbrent = x'00000004' ;
  %subst(wflags :1 : 4) = actif       ;   <<<<< ici
  %subst(wflags :5 : 4) = inact       ;
  %subst(wflags :9 : 4) = inact       ;
  %subst(wflags :13 : 4) = inact       ;
  %subst(wflags :17 : 4) = inact       ;
  %subst(wflags :21 : 4) = inact       ;
  %subst(wflags :25 : 4) = inact       ;
  %subst(wflags :29 : 4) = inact       ;
  QWTSETPX(wnbrent:wflags:wformat:p_user:werreur) ;
  *inlr = *on ;

Programme pour voir les programmes du profil

**free
//
// Lecture des informations sur les profils pour appel système
// sur exit pgm  QIBM_QWT_SYSREQPGMS
// Rappel 8 possibilités qui correspondent au PGMNBR de l'exit PGM
//
ctl-opt
  DFTACTGRP(*NO) ;
// paramétre le profil
Dcl-Pi *N;
  P_user             char(10);
End-Pi;
// API de lecture des postes
Dcl-PR QWTRTVPX ExtPgm( 'QWTRTVPX');
  rcvvar  char(40)     ;
  rcvlen  char(4)    ;
  format  char(8)    ;
  user    char(10)   ;
  erreur  char(32)   ;
End-PR;
// déclaration des variables de travail
dcl-s wrcvlen  char(4) inz(x'00000028') ;
dcl-s wrcvvar char(40) inz(' ')  ;
dcl-s wformat  char(8)    ;
dcl-s werreur  char(32)    ;
dcl-s wflags   char(32)    ;
dcl-s wnbpos   int(10)     ;
// constantes figuratives
dcl-s inact    char(04)  inz(x'00000000') ;
dcl-s actif    char(04)  inz(x'00000001') ;
dcl-s msg      char(50)  inz(' ') ;
dcl-s i        int(10)  inz(0) ;
// Appel de l'API
  wformat = 'SREQ0100' ;
  QWTRTVPX(wrcvvar:wrcvlen:wformat:p_user:werreur) ;
  wnbpos  = 32;  // 8 * 4
// extraction des informations pour les 8 programmes
  wflags = %subst(wrcvvar : 9 : 32) ;
  for i = 1 by 4 to wnbpos   ;
    if  %subst(wflags : i : 4) =  actif ;
      msg = %trim(msg) + '*ON'                    ;
    else ;
      msg = %trim(msg) + '*OFF'                  ;
    endif;
  endfor ;
  // affichage du résulat
  dsply msg  ;
  *inlr = *on ;

Remarque :

L’utilisateur ne reçoit aucun message , mais rien ne se passe

Attention, il ne faut pas le mettre sur tous les profils, mais uniquement ceux qui le nécessitent.

Par exemple une fenêtre bloquante de ressaisie de mot de passe pour une option sensible.

Vous pouvez faire la même chose pour le programme ATTN …

, , , Utilisez les journaux Système

IBM fourni un certain nombre de journaux systèmes que vous pouvez Analyser, la plus part sont dans QUSRSYS et d’autres sont dans QSYS.

Un petit lien ici pour avoir une liste

https://www.ibm.com/docs/fr/i/7.5?topic=journals-working-supplied

Premier exemple, Analyse de l’ajustement des pools mémoires

Mise en œuvre

Valeur système QPFRADJ doit être à 3 ou 2

Vous devez créer le récepteur et le journal

CRTJRNRCV JRNRCV(QUSRSYS/QPFRADJ)
CRTJRN JRN(QSYS/QPFRADJ) JRNRCV(QUSRSYS/QPFRADJ)


Analyse par les fichiers supports ( c’est des fichiers modèles qui sont dans QSYS )
CRTDUPOBJ OBJ(QAWCTPJE) FROMLIB(QSYS) OBJTYPE(*FILE) TOLIB(Votrebib) NEWOBJ(QPFRADJTP)

DSPJRN JRN(QSYS/QPFRADJ) ENTTYP(TP) OUTPUT(*OUTFILE) OUTFILE(Votrebib/QPFRADJTP)

pour analyser le suivi ici du pour des travaux interactifs

SELECT TPPNAM, TPFLG1, TPCSIZ, TPCRES, TPCACT, TPDFLT, TPNFLT,
TPWI, TPAW, TPCJOB, TPAJOB, TPNSIZ, TPNACT
FROM Votrebib/QPFRADJTP
WHERE TPPNAM = ‘*INTERACT’
order by TPDATE, TPTIME

Deuxième exemple, voir les ports filtrés sur votre partition

Analyse par services SQL

create table votrebib.analyse as(
WITH Log_Port AS (
SELECT CAST(ENTRY_DATA AS VARCHAR(1000)) AS entry
FROM TABLE (
QSYS2.DISPLAY_JOURNAL(‘QUSRSYS’, ‘QIPFILTER’, JOURNAL_ENTRY_TYPES => ‘TF’)
)
)
SELECT SUBSTR(entry, 1, 10) AS line,
SUBSTR(entry, 29, 15) AS AdrSrcIp,
SUBSTR(entry, 44, 5) AS SrcPort,
SUBSTR(entry, 49, 15) AS AdrDestIp,
SUBSTR(entry, 64, 5) AS DestPort
FROM Log_Port
) WITH DATA;

Remarques

Certains journaux sont en standard , d’autres devront être démarrés
Si vous n’analysez pas ne les démarrer pas
Pensez à faire le ménage dans les récepteurs si vous les démarrez

Merci à Sylvain pour ca suggestion

Mise en place d’une documentation Sphinx sur IBM i

Introduction

Introduction

Cet article à pour but de présenter la possibilité d’exploiter diverses solutions libres et gratuites très répandues dans le monde OpenSource. L’objectif est de découvrir Sphinx, un outil de génération de documentation sous forme de site web. Il est notamment utilisé pour réaliser les documentations suivantes :

  • La référence de l’OpenSource sur IBM i, IBM i OSS Docs – ici
  • La documentation de Sphinx – ici
  • Le Kernel Linux – ici
  • Le langage de programmation Python – ici
  • Le logiciel de conception 3D Blender – ici
  • Le moteur de jeu OpenSource Godot – ici

Nous allons donc voir en quelques étapes simples comment publier et maintenir votre documentation directement sur votre IBM i.

Prérequis

  • PASE (IBM Portable Application Solutions Environment for i)
  • Environnement OpenSource sur l’IBM i
  • Python sur votre IBM i (Pour rappel, les modules OpenSource comme python sont compilés et mis à disposition par IBM spécialement pour l’IBM i, il n’y a pas plus de risque à les utiliser qu’à utiliser des logiciels sous licence)

Etape 1 – Mise en place de l’environnement

Cette section est réalisée via qsh, qp2term ou ssh.

Le projet sera structuré comme suit :

/home/demosphinx sera le répertoire du projet, il contiendra les sources (doc/) et l’environnement de travail python (env/).
/www/demosphinx sera le répertoire du serveur Apache permettant de publier localement la documentation

Création des répertoires :

$ mkdir /home/demosphinx/
$ mkdir /home/demosphinx/doc
$ mkdir /home/demosphinx/env

Une fois l’arborescence créée, on installe ou met à jour Python 3.9 :

$ yum install python39

On crée ensuite un environnement virtuel pour python afin d’éviter d’être impacté par des changements de versions et pour isoler notre environnement de travail :

// Création de l'environnement virtuel Python
$ python3.9 -m venv --system-site-packages /home/demosphinx/env

// Entrer dans l'environnement virtuel
$ source /home/demosphinx/env/bin/activate

// Mise à jour du gestionnaire de paquets python (pip)
(env) $ pip install --upgrade pip
// Installation et mise à jour de Sphinx
(env) $ pip install --upgrade sphinx
// Installation du thème ReadTheDocs (Facultatif)
(env) $ pip install sphinx-rtd-theme

D’autres modules peuvent être intéressant, comme myst-parser (qui permet notamment d’utiliser du MarkDown pour rédiger sa documentation) et sphinx-jinja (qui permet l’usage de variables dans les pages). Il existe également une grande quantité de thèmes natifs présentés ici et des thèmes tiers présentés ici.

Etape 2 – Création du projet

Cette section est réalisée via qsh, qp2term ou ssh.

L’environnement en place on peut très simplement créer notre documentation.

// Entrer dans l'environnement virtuel
$ source /home/demosphinx/env/bin/activate

// Positionnement dans le répertoire du projet
$ cd /home/demosphinx/doc

// Génération du projet
$ sphinx-quickstart

On arrive ensuite sur un assistant qui va nous guider pour saisir les informations de base nécessaires à la documentation (qui seront toujours modifiables dans les sources de la documentation) : Le nom du projet, ceux des auteurs, la version, la langue de la documentation, etc… Voici un exemple :

Bienvenue dans le kit de démarrage rapide de Sphinx 7.3.7.
Veuillez saisir des valeurs pour les paramètres suivants (tapez Entrée pour accepter la valeur par défaut, lorsque celle-ci est indiquée entre crochets).

Chemin racine sélectionné : .
Vous avez deux options pour l'emplacement du répertoire de construction de la sortie de Sphinx.
Soit vous utilisez un répertoire "_build" dans le chemin racine, soit vous séparez les répertoires "source" et "build" dans le chemin racine.
> Séparer les répertoires source et de sortie (y/n) [n]: y

Le nom du projet apparaîtra à plusieurs endroits dans la documentation construite.
> Nom du projet: SphinxOnIBMi
> Nom(s) de(s) l'auteur(s): Gaia Mini Systèmes
> Version du projet []: v0

Si les documents doivent être rédigés dans une langue autre que l'anglais, vous pouvez sélectionner une langue ici grâce à son id entifiant. Sphinx utilisera ensuite cette langue pour traduire les textes que lui-même génère.

Pour une liste des identifiants supportés, voir
https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.
> Langue du projet [en]: fr

Fichier en cours de création /home/demosphinx/doc/source/conf.py.
Fichier en cours de création /home/demosphinx/doc/source/index.rst.
Fichier en cours de création /home/demosphinx/doc/Makefile.
Fichier en cours de création /home/demosphinx/doc/make.bat.

Terminé : la structure initiale a été créée.

Vous devez maintenant compléter votre fichier principal /home/demosphinx/source/index.rst et créer d'autres fichiers sources de documentation. Utilisez le Makefile pour construire la documentation comme ceci :
   make builder
où « builder » est l'un des constructeurs disponibles, tel que html, latex, ou linkcheck.

Le projet de documentation est maintenant généré !

Les différentes pages sont à ajouter dans le répertoire source/ :

  • index.rst (ou index.md si le module MarkDown est installé) – Il s’agit du point d’entrée de la documentation.
  • conf.py contient les informations de création de la documentation comme le thème, la langue, les différents format interprétés, etc…

Voici un exemple de fichier de configuration :

# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = 'SphinxOnIBMi'
copyright = '2024, Gaia'
author = 'Gaia'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = []

templates_path = ['_templates']
exclude_patterns = []

language = 'fr'

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = 'alabaster'
html_static_path = ['_static']

Les différents fichiers sources peuvent être édités directement sur l’IBM i via VSCode, RDi…

Etape 3 – Création de la page Apache via HTTPAdmin

Cette section est réalisée via HTTPAdmin.

Avant de générer la documentation en tant que telle, créons un petit serveur Apache basique (on pourrait tout à fait utiliser une instance nginx). Pour se faire nous allons passer par HTTPAdmin afin d’exploiter l’assistant de configuration pour créer le serveur :

L’instance Apache est maintenant créée :

Pour éviter les problèmes de CCSID, il est préférable de supprimer au préalable le fichier index.html qui sera regénéré par Sphinx.

Etape 4 – Génération de la documentation

Cette section est réalisée via qsh, qp2term ou ssh.

Revenons sur notre environnement PASE pour générer la documentation, pour cela, une simple commande suffit :

// Entrer dans l'environnement virtuel
$ source /home/demosphinx/env/bin/activate

// Génération de la documentation vers le répertoire de l'instance Apache
$ sphinx-build -b html /home/demosphinx/doc/source /www/demosphinx/htdocs -E

Sphinx v7.4.7 en cours d'exécution chargement des traductions [fr]... fait
construction en cours [mo] : cibles périmées pour les fichiers po 0
Écriture...
construction [html] : cibles périmées pour les fichiers sources 1
mise à jour de l'environnement : [nouvelle configuration] 1 ajouté(s), 0 modifié(s), 0 supprimé(s)
lecture des sources... [100%] index

Recherche des fichiers périmés... aucun résultat trouvé

Environnement de sérialisation... fait
vérification de la cohérence... fait
documents en préparation... fait
copie des ressources...
Copie des fichiers statiques... fait
copie des fichiers complémentaires... fait
copie des ressources: fait
Écriture... [100%] index

génération des index... genindex fait
Écriture des pages additionnelles... search fait
Export de l'index de recherche en French (code: fr)... fait
Export de l'inventaire des objets... fait
La compilation a réussi.

Les pages HTML sont dans /www/demosphinx/htdocs.

Voici les fichiers produits par la génération, directement dans notre instance Apache :

On voit entre autre le script Java Script, searchindex.js, un moteur de recherche intégré directement dans la documentation, l’un des gros points forts de Sphinx.

Démarrage de l’instance et résultat

On peut démarrer notre instance via HTTPAdmin :

Ou via 5250 et la commande :

STRTCPSVR SERVER(*HTTP) HTTPSVR(DEMOSPHINX)

Voici le résultat avant d’avoir rédigé la documentation :


Il ne reste plus qu’à remplir la documentation et à jouer avec les différentes possibilités de Sphinx et de ses modules.

Pour plus de détails

Documentation Sphinx :https://www.sphinx-doc.org
Documentation Build Sphinx : https://www.sphinx-doc.org/en/master/man/sphinx-build.html
Documentation ReadTheDocs :https://docs.readthedocs.io
, , Trigger sur insert

Vous voulez créer un trigger qui vous indique la création d’un enregistrement dans un fichier par exemple pour superviser, dans notre exemple on enverra un email , il est conseillé de faire un fichier de paramétrage

En CLLE soit le programme Alerte_msg


             PGM        PARM(&BUFFER &BUFLEN)             
/* Paramètres */                                          
             DCL        VAR(&BUFFER) TYPE(*CHAR) LEN(200) 
             DCL        VAR(&BUFLEN) TYPE(*CHAR) LEN(4)   
/* Variables de travail */                                
             DCL        VAR(&USR) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&JOB) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&NBR) TYPE(*CHAR) LEN(06)
             DCL        VAR(&EMAIL) TYPE(*CHAR) LEN(50)
             DCL        VAR(&SUJET) TYPE(*CHAR) LEN(100)
             DCL        VAR(&NOTES) TYPE(*CHAR) LEN(200)
             RTVJOBA    JOB(&JOB) USER(&USR) NBR(&NBR)    
             CHGVAR     VAR(&EMAIL) VALUE('votre@mail.fr')
             CHGVAR     VAR(&SUJET) VALUE('Enregistrement crée')
             CHGVAR     VAR(&NOTES) VALUE('Job :' +
                          *BCAT &NBR *TCAT '/' *TCAT &USR *TCAT '/' +
                          *TCAT &USR)                                    
 SNDSMTPEMM RCP((&EMAIL)) SUBJECT(&SUJET) NOTE(&NOTES) +
             CONTENT(*HTML)            
  MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERREUR))
goto fin                                                           
/* Gestion des erreurs  */                                         
erreur:                                                            
             SNDUSRMSG  MSG('Envoi email impossible pour msgint ' +
                          *bcat &job *bcat &usr *bcat &nbr ) +     
                          MSGTYPE(*INFO)                           
fin:                                                               
ENDPGM 

Pour attacher votre programme et enregistrer votre trigger

 ADDPFTRG   FILE(&LIB/REP_VALID) TRGTIME(*AFTER)     
              TRGEVENT(*INSERT) PGM(&LIB/ALERT_MSG) 
              TRGLIB(&LIB) 

&lib sera le nom de votre bibliothèque

En SQL ca créera un programme CEE et ca l’associera au trigger

CREATE OR REPLACE TRIGGER ALERTE_MSG                                   
 AFTER  INSERT ON REP_VALID                                            
 REFERENCING NEW AS N                                                  
 FOR EACH ROW                                                          
 MODE DB2ROW                                                           
-- email destinataire                                                  
 BEGIN                                                                 
DECLARE W_EMAIL CHAR(50);                                              
DECLARE W_SUJET CHAR(100);                                             
DECLARE W_NOTES CHAR(200);                                             
DECLARE EXIT HANDLER FOR SQLSTATE '38501'                              
 RESIGNAL SQLSTATE '38501' SET MESSAGE_TEXT = 'ENVOI MAIL IMPOSSIBLE.';
SET W_NOTES = 'Job : ' concat trim(N.REPNBR)                  
concat '/' concat trim(N.REPUSER) concat '/' concat trim(N.REPJOB) ;
SET W_EMAIL = 'votre@email.fr' ;                 
SET W_SUJET = 'Enregistrement crée' ;   
CALL QCMDEXC('SNDSMTPEMM RCP((''' concat trim(w_email) concat           
''')) SUBJECT(''' concat trim(replace(w_sujet , '''', '"'))             
concat ''') NOTE('''                                                    
concat trim(replace(W_NOTES , '''' , '"')) concat''') CONTENT(*HTML)') ;
END;  

Remarques :

Dans les 2 cas si l’utilisateur n’est pas inscrit à la liste de distribution votre email ne sera pas envoyé
c’est plus simple de gérer l’erreur en CLP.
Si vous devez accéder aux données du buffer ca sera plus rapide et plus simple en SQL ici n.zone

C’est des triggers après , puisque l’information doit être écrite dans tous les cas .