Fenêtre sans le mot clé WINDOW

Vous connaissez tous le mot clé WINDOW dans les DDS qui vous permet d’afficher une fenêtre.

Cette solution a certaine contraintes comme les réaffichages par exemple.

Voici une alternative qui permet d’afficher une fenêtre sans utiliser ce mot clé

Le code l’écran

     A*%%TS  SD  20231003  225454  PLB         REL-V7R4M0  5770-WDS
     A* EXEMPLE DE FENETRE SANS WINDOW
     A*
     A*%%EC
     A                                      DSPSIZ(24 80 *DS3)
     A                                      USRDSPMGT
     A                                      INDARA
     A          R FMT01
     A*%%TS  SD  20231003  225454  PLB         REL-V7R4M0  5770-WDS
     A                                      CA03(03)
     A                                      CA12(12)
     A                                      SLNO(01)
     A                                      CLRL(*NO)
     A            TITRE         25A  O 15 45DSPATR(HI)
     A                                      DSPATR(RI)
     A                                 16 45' '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)
     A            TEXTE01       21A  O 16 47
     A                                 16 69' '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)
     A                                 17 45' '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)
     A            TEXTE02       21A  O 17 47
     A                                 17 69' '
     A                                      DSPATR(RI)
     A                                      DSPATR(HI)
     A                                 18 45' '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)
     A            TEXTE03       21A  O 18 47
     A                                 18 69' '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)
     A                                 19 45' F2=Valider       F3=Fin '
     A                                      DSPATR(HI)
     A                                      DSPATR(RI)

Le programme RPGLE

**free
// Affiche une fenêtre sans window
// renvoi OK si vous appuyez sur <ENTER>
// renvoi KO si vous appuyez sur F3 ou F12
// call fenetre ('Titre' 'texte01' 'texte02' 'texte03' ' ')
dcl-f fenetre  workstn  ;
  dcl-pi *N ;
   Titre   char(25);   // Titre fenêtre
   Texte01 char(21);   // texte première ligne
   Texte02 char(21);   // texte deuxiéme ligne
   Texte03 char(21);   // texte troisième ligne
   Retour  char(2);    // code retours
  end-pi ;
if %parms = 0 ;
  dsply ('Ce programme nécessite 6 paramètres') ;
else ;
  // Affichage format
  Exfmt FMT01 ;
  if *in03 or *in12 ;
    Retour = 'KO' ;
  else ;
    Retour = 'OK' ;
  endif ;
endif;
// Fin de programme
*inlr = *on ;

Exemple :

call fenetre (‘Titre’ ‘texte01’ ‘texte02’ ‘texte03’ ‘ ‘)


, , , Effacer la log de votre travail

Vous faites le l’administration, le plus souvent en 5250 sous l’écran de commandes IBMI
==> call QCMD

Vous voulez effacer la log que vous voyez par la commande DSPJOBLOG.

La première solution consiste à vous déconnecter, du coup la log est effacée ou transformée en spool.

Cette méthode efface tout le contexte mis en place, liste de bibliothèques , variables d’environnement, objets dans QTEMP que vous avez peut être mis beaucoup de temps à construire dans vos tests.

L’idée c’est d’avoir une solution moins radicale qui permette de n’effacer que la log de votre travail, c’est pourquoi vous devriez avoir dans vos tools un outil comme celui la

Une commande qu’on a appelé astucieusement CLRLOG que vous pouvez trouver ici

https://github.com/Plberthoin/PLB/tree/master/CLRLOG

Comme le code est simple on le remet ici

On utilise la commande RMVMSG qui n’est utilisable que dans un programme CLLE

Le programme en CLLE

 PGM /*------------------------------------*/        
 /* Supprime les messages de votre log     */        
 /* Ne supprime pas les messages *PGMQ     */        
 /*----------------------------------------*/        
              RMVMSG     PGMQ(*ALLINACT) CLEAR(*ALL) 
              RMVMSG     PGMQ(*EXT) CLEAR(*ALL)      
 ENDPGM                                              

La commande

 /* Supprime les messages de votre log     */ 
CMD        PROMPT('Clearer la log de votre job') 

Remarque :

Cette commande n’efface pas les messages de type *PGMQ

, Différence touche CAXX et touche CFXX

Vous avez encore des développements en 5250 avec des DSPF et vous vous posez la question : dois je utiliser une touche CA ou CF ?

Nous allons essayer de vous apporter quelques éclaircissements sur les usages et les bonnes pratiques

Première chose, vous pouvez utiliser une touche de fonction de 2 manières

CA03(03)
ou
CA03 uniquement

Avec CA03(03), vous testerez *IN03 dans vos programmes RPGLE et &IN03 en CLLE.
Avec CA03, vous testerez *INKC dans vos programmes RPGLE et c’est plus compliqué à gérer en CLLE.

Jusque la on utilisait les indicateurs *inka … pour économiser les indicateurs de 01 à 24 (99 disponibles),
sur les nouveaux développement on utilise moins d’indicateurs et plus de fonctions intégrées comme %EOF(). Il est donc conseillé d’utiliser les indicateurs *IN01 à *IN24 ; de plus vous pouvez les renommer pour rendre plus lisible dans les programmes !

Pour ce faire, vous devez déclarer votre écran en INDARA pour indiquer que les indicateurs sont séparés du flux des données.

A* Niveau fichier dans le dds :
A INDARA

Dans le source du programme RPG :
dcl-f
MONECR WORKSTN
indds(DS_Ind)
;
Dans les déclaration :

Dcl-DS DS_Ind len(99);
// Touches de fonctions
Sortie ind pos(3);
Liste ind pos(4);
Reaffichage ind pos(5);
Creer ind pos(6);
Valider ind pos(10);
Annuler ind pos(12);

End-DS;

Conseil

Comme vous devez vous baser sur la NORME AUA , vous pouvez faire un include avec ces touches de fonction.

dans le code vous pouvez indiquer :


IF SORTIE ;

Comment déclarer les touches dans vos DDS ?
Vous pouvez les déclarer

  • au niveau du fichier, votre touche sera utilisable dans tous les formats.
    C’est le cas par exemple de CA03 pour sortir.
  • au niveau du format, on ne peut pas redéclarer une touche déjà décrite au niveau fichier

Exemple :


A* niveau fichier
A CA03(03)
A R PREMIER
A
A R DEUXIEME
A* niveau du format
A CA12(12)

vous pourrez utiliser :
CA03 sur le format PREMIER
CA03 et CA12 sur le DEUXIEME

Plus vous serez précis dans vos déclarations, plus vos programmes serons clairs et précis.
Toutes les touches déclarées dans les formats doivent être traitées dans le code.
Si vous utilisez une touche dans un programme et qu’elle n’est pas traitée … ça peut poser problème, du genre mise à jour sans lecture de l’écran.

Pourquoi CA et pas CF !?

Pour vous expliquer nous avons choisi de faire ce petit exemple (écran + programme RGPLE)

le programme CACF

**free                                
Ctl-Opt DFTACTGRP(*NO) ;              
Dcl-f CACF      WORKSTN ;             
dou *in07 or *in08                  ; 
  exfmt fmt01                       ; 
  dsply zone                        ; 
enddo                               ; 
*inlr = *on                         ; 

Et l’écran CACF

     A*%%TS  SD  20230914  164942  PLB         REL-V7R4M0  5770-WDS             
     A*%%EC                                                                     
     A                                      DSPSIZ(24 80 *DS3)                  
     A          R FMT01                                                         
     A*%%TS  SD  20230914  164942  PLB         REL-V7R4M0  5770-WDS             
     A                                      CA07(07)                            
     A                                      CF08(08)                            
     A                                  4 10'Test pour comprendre la différence-
     A                                       entre une touche CA et CF'         
     A                                      DSPATR(HI)                          
     A                                  9 10'Saisir une valeur ici '            
     A                                  9 33':'                                 
     A            ZONE           1A  B  9 35VALUES('1' '2')                     
     A                                  9 38'Veuillez saisir 1 ou 2'            
     A                                      DSPATR(HI)                          
     A                                 20 10'CA07==> Sortir sans lecture'       
     A                                 21 10'CF08==> Sortir avec lecture'       

Où il est possible d’utiliser les touches de fonction 07 déclarée CA et 08 déclarée CF.

f

Premier test

Vous tapez directement F7 ou F8, le comportement est le même si votre buffer n’a pas été modifié, il n’y a pas de lecture.
Pour déterminer si le buffer est modifié le système se sert d’un octet attribut (dans le format ou dans la zone) dans le quel on un MDT qui sera à *ON.
Si le buffer ou la zone à été modifiée. vous devrez forcer ce tag dans le cas ou contrôlez des données.

C’est le mot clé DSPATR(MDT) dans les DDS qui permet ça, vous pouvez le conditionner par un indicateur.

Deuxième test

Vous saisissez 9 dans l’écran.
F7 sort directement et tient pas compte de la saisie, c’est ce qui convient pour les touches F3 ou F12.
F8 déclenche une lecture avant de sortir et comme on a fait le choix de contrôler la valeur saisie à 1 ou 2, on est bloqué jusqu’à ce qu’on ait renseigné la zone à la bonne valeur,
C’est ce qui convient pour les touches F6 ou F10.
On imagine bien que si vous avez 10 zones avec contrôles, ça poser problèmes quel que soit le contrôle

Rappel sur l’ordre à faire sur vos contrôles

1) Contrôle de valeurs, gérer par VALUES() ou CHECK() dans le DDS, aucun code à traiter côté programme.

2) Contrôles de dépendance exemple borne début < borne de fin (toutes les informations de l’écran sont suffisantes pour le contrôle).

3) Contrôles nécessitants une ressource externe, le plus souvent en base de données (saisie d’un numéro de client à contrôler).

La règle si la valeur saisie n’est pas bonne ce n’est pas la peine de faire le contrôle suivant !

Conseil :
Soyez précis dans vos déclarations dans le niveau format ou tous les format au fichier.
et n’utilisez les touches CF que quand vous devrez traiter les données de l’écran.

Merci à Nicolas pour son aide !

, Tables avec informations de création et modification

Comment gérer simplement les informations de modifications sur les enregistrements
utilisateur + date de création ou de modification

Vous connaissez la méthode applicative par sql ou update

Vous connaissez la méthode par trigger after, vous modifiez les zones dans le buffer après avec les informations en cours.

Voici un solution ou vous n’avez rien à faire, vous laissez faire SQL

Soit la table suivante des applications :

CREATE TABLE APPLICATION1 FOR SYSTEM NAME APPLICAT1 (
DESCRIPTION_APPLICATION FOR COLUMN DES_APP CHAR(50) NOT NULL WITH DEFAULT,
APPLICATION_CODE FOR COLUMN APP_COD CHAR(8) NOT NULL WITH DEFAULT,

/* Informations de mise à jour */
APPAUSRCHG VARCHAR(18) GENERATED ALWAYS AS (USER),
APPATMPCHG TIMESTAMP FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL,
APPAUSRCRT CHAR(18) NOT NULL DEFAULT USER IMPLICITLY HIDDEN,
APPATMPCRT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP IMPLICITLY HIDDEN
)
Explications :

GENERATED ALWAYS AS (USER), indique que la zone est systématiquement forcée avec l’utilisateur en cours
FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP indique qu’a chaque modification, vous aurez la date en cours
DEFAULT USER indique valeur par défaut utilisateur en cours
DEFAULT CURRENT_TIMESTAMP indique valeur par défaut date en cours
HIDDEN indique que la zone n’apparaitra pas dans un select *

Exemple d’insertion

INSERT INTO GDATA.APPLICATION1 (
DESCRIPTION_APPLICATION,APPLICATION_CODE ) VALUES ( ‘TEST’, ‘TEST’ )

Exemple de mise à jour

UPDATE GDATA.APPLICATION1
SET
DESCRIPTION_APPLICATION = ‘TEST2’,
APPLICATION_CODE = ‘test2’
WHERE DESCRIPTION_APPLICATION = ‘TEST’

Visualisation du résultat

Sauf zones cachées


SELECT * ROM GDATA.APPLICATION1

Avec les zones cachés

SELECT DESCRIPTION_APPLICATION, APPLICATION_CODE, APPAUSRCHG,
APPATMPCHG, APPAUSRCRT, APPATMPCRT FROM GDATA.APPLICATION1

Remarques :
Les zones user devront être sur 18 en varchar
Vous devez utiliser le format explicite pour la création
Voila , Simple et efficace
Merci à Patrick pour son exemple de code

Ajouter de l’aide à vos DSPF

Vous voulez ajouter de l’aide à vos écrans DSPF ?

Nous allons vous présenter la méthode que nous utilisons en interne.

Nous avons choisi, comme format, les aides en UIM (panel de groupe).

Pour mettre en place de l’aide sur vos écrans vous devez faire 2 choses :

Mettre en place les mots clés dans le source de votre DSPF

D’abord au niveau fichier (au début du source) :

A                                      HLPTITLE('AIDE GPARAME')   
A                                      HLPPNLGRP(GPARAME GPARAME) 
A                                      ALTHELP(CA01)              
A                                      HELP                       
  • HLPTITLE indique le titre de votre fenêtre d’aide
  • HLPPNLGRP indique le nom du panel de groupe (conseil : mettre le même nom)
  • HELP pour pouvoir utiliser la touche de fonction Aide
  • ALTHELP pour indiquer que l’aide pourra être invoqué par F1, ce qui est le standard AUA

Ensuite au niveau du format (après la carte qui contient le R du format correspondant) :

A          H                           HLPPNLGRP('GPARAME/GLOG GPARAME')
A                                      HLPARA(*FLD GLOG)                
  • HLPPNLGRP indique le panneau d’aide et le groupe
  • HLPARA aire pour laquelle ce panneau sera affiché, *FLD pour indiquer une zone

https://www.ibm.com/docs/en/i/7.5?topic=80-hlppnlgrp-help-panel-group-keyword-display-files

Créer votre panneau d’aide

Vous devez coder un source en UIM, qui correspond aux déclarations que vous avez indiqué dans votre DSPF :

                                       
:PNLGRP.                                        
:HELP NAME = 'GPARAME' .AIDE (GPARAME).         
:XH3. AIDE GPARAME.                             
:P.                                             
Ce programme vous permet de (ici descriptif général du programme)                
:EHELP.                                         
:HELP NAME = 'GPARAME/GLOG' .AIDE (GLOG).       
:XH3. AIDE GLOG.                                
:P.                                             
Cette zone (ici descriptif de la zone)                                 
:EHELP.  
...
:EPNLGRP.                                        

C’est relativement simple à faire, vous pouvez utiliser tout le langage UIM.

Pour en savoir plus sur UIM : https://www.ibm.com/docs/en/i/7.4?topic=interfaces-user-interface-manager

Vous devez compiler votre panel de groupe :

==> CRTPNLGRP PNLGRP(lib/pannel) SRCFILE(libsrc/QPNLSRC)

Voici un exemple de ce que cala donne en faisant <F1> sur une Zone ici numéro de série

Pour vous aider, nous proposons un outil qui vous permet de générer les mots clés nécessaires pour le DSPF et le squelette de votre panel de groupe. Vous pouvez trouver l’outil ICI il s’appelle GENUIMDSP :

https://github.com/Plberthoin/PLB/tree/master/GENUIMDSP

Vous devez indiquer le fichier écran , le source avec les mots clés ici QDDSSRC, le squelette du panel ici dan QPNLSRC.

Remarque :

L’outil n’est pas parfait mais, il vous aide bien, si vous voulez contribuer, n’hésitez pas !

L’ajout des mots clés dans le DSPF ne change pas la commande de compile, ni les niveaux des formats dans lesquels vous ajoutez des aides.

La liaison est dynamique et si l’aide n’est pas trouvée, il n’y a pas de plantage donc pas de régression applicative, vous aurez « juste » le message suivant :

Ça peut être intéressant quand vous faites de la maintenance d’ajouter des aides, et avec notre méthode c’est rapide, simple et efficace.

Merci à Julien et Yoann pour leur aide.

IA et développement IBMI

Vous avez tous entendu parler de l’IA et de Chat GPT ou Bard (Google).

Nous avons décidé de faire quelques tests.

Tout n’est pas parfait mais le résultat est parfois bluffant.

Sur Chat GPT, ici https://chat.openai.com/

Nous avons fait des tests sur du CLP

Exemple :

Résultat

Nous avons fait des tests du RPG

Exemple

Résultat

C’est plus décevant il génére en colonné mais sur les demandes simples ce n’est pas mal du tout

Je ne sais pas pourquoi, il met **FREE au début ??

Du coup nous lui avons également demandé de traduire du code RPGLE colonné vers FREE
sur les carte c ce n’est pas mal

Et Sur les DDS

Voici la réponse

Sur BARD ici https://bard.google.com/u/1/

Il ne connait visiblement pas l’Ibmi, il n’y a pas de résultat probant ?

Conclusion:

Tout n’est pas parfait, mais ça progresse et les logiciels apprennent rapidement.
Le conseil, il faut être précis, n’hésitez pas à le conseiller

A suivre …

, , , Exemple de panel liste

C’est une solution à base d’UIM qui permet de gérer des sous fichiers sans écran DDS, en utilisant un panel de groupe et des API pour le manipuler.

Toutes les commandes WRKXXX de votre IBMi sont codées avec cette solution.

Nous allons vous présenter un exemple pour les PF par exemple vous trouverez le code sur github ici https://github.com/Plberthoin/PLB/tree/master/WRKPF

Cet article est inspiré à l’origine d’un article de mcpressonline, j’ai remis le RPG à jour

Vous devrez avoir un PNLGRP avec la structure suivante, pour indiquer les listes

:PNLGRP.

:CLASS.
:ECLASS.

:VAR.

:VARRCD.

:LISTDEF.

:KEYL.
:KEYI.
:EKEYL.

:PANEL.
.
.
:LIST.

:LISTACT.
:LISTCOL.
:LISTVIEW.
.
.
.
:ELIST.
.
:CMDLINE.
.
:EPANEL.
.
.
.
:HELP.
.
.
.
:EHELP.

:EPNLGRP.

Vous aurez un programme ici en RPG qui va utiliser les API suivantes :

Api Utilisation

QUIOPNDA Ouverture du panel création du Handle
QUIPUTV Renseignement variable
QUIADDLE Ecriture d’un poste dans la liste
QUIDSPP Affichage du panel
QUIDLTL Suppression du contenu de la liste
QUICLOA Fermeture du panel

Rappel, vous pouvez chercher les APIs disponibles sur votre IBMi avec API-FINDER disponible

ici https://www.ibm.com/docs/en/i/7.5?topic=interfaces-api-finder

Conclusion

C’est une solution standard qui ne nécessite pas de DSPF, mais qui nécessite de connaitre un peu le langage UIM.
Avec un squelette de PNL et de programme on peut créer des outils WRKXXX rapidement, idéal pour les outils d’administration par exemple

, , , Se connecter à un serveur SSH exécuté sous Windows à partir d’un IBM i (Comment obtenir la log pour débuguer les problèmes éventuels)

Se connecter à un serveur SSH exécuté sous Windows à partir d’un IBM i (Comment obtenir la log pour débuguer les problèmes éventuels)

Mise en place d’OpenSSH Server sur Windows

Pour mettre en place OpenSSH Server sur Windows, la méthode « standard » consiste à passer par les Paramètres > Applications et fonctionnalités > fonctionnalités facultatives :

Il est recommandé de redémarrer Windows une fois la fonctionnalité ajoutée.

Il suffit ensuite de démarrer le serveur via le gestionnaire de Services Windows :

Il est également souhaitable de configurer le démarrage automatique du serveur :

Remarque

Il est également possible d’installer OpenSSH sur Windows via d’autres sources (GitHub par exemple) ce qui permet, entre autres, de choisir plus facilement sa version d’OpenSSH, voir section Détail.

Création d’un jeu de clefs SSH via ssh-keygen

Pour plus de détails sur la création de clefs, vous pouvez vous référer à l’article de Guillaume Gestion des clefs SSH.

Il est également possible d’utiliser PuttyGen, outil venant avec le client Putty pour générer le jeu de clefs de manière graphique (https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).

Dans cet article je vais tout réaliser sur l’IBM i via QSH :

$ ssh-keygen -t ecdsa -f ~/.ssh/ssh_key

Mise en place de la clef privée et configuration côté IBM i (client)

On a généré la clef privée dans le répertoire .ssh de l’utilisateur, donc elle est déjà bien en place. Il suffit donc de créer un fichier config dans le répertoire .ssh de l’utilisateur afin de simplifier nos commandes pour la suite.
Voici un exemple de fichier config :

[~/.ssh/config]

Host windows
    Hostname sshd_server.lan
    User jl
    IdentityFile ~/.ssh/key
    StrictHostKeyChecking accept-new
HostNom de la configuration, utilisé à la place des différentes informations à la connexion
HostnameAdresse ou nom du serveur à atteindre
UserNom de l’utilisateur
IdentityFileChemin vers la clef privée
StrictHostKeyChecking accept-newPermet d’ajouter automatiquement la signature du serveur distant au known_hosts

Mise en place de la clef privée et configuration côté Windows (serveur)

Il faut transférer la clef ssh_key.pub vers Windows et l’ajouter soit au fichier %UserProfile%.ssh\authorized_keys pour un utilisateur lambda, soit au fichier C:\ProgramData\ssh\administrators_authorized_keys pour un utilisateur ayant des droits d’administrateur local.

Attention à ce niveau, les droits des fichiers sont un peu particulières, il faut comme toujours avec le SSH réduire au maximum les utilisateurs ayant accès au fichier et, particularité de Windows, ajouter le droit de lecture au profil de service local Système :

Activation du fichier de log – Configuration sshd_config

Afin de pouvoir analyser d’éventuels problèmes ou simplement vouloir observer un peu plus en détail les différentes étapes de la mise en relation d’un flux ssh il est possible d’activer la log du serveur.

Par défaut celle-ci est redirigée vers les journaux d’évènements Windows et est seulement en « info ».
On les retrouver via l’Observateur d’événements Windows :

Le mieux à mon avis est de repasser par un système plus standard, soit un vrai fichier de logs.

Pour ce faire, il faut aller modifier le fichier de configuration du serveur SSH, généralement il se trouve ici :

C:\ProgramData\ssh\sshd_config
ou
%ProgramData%\ssh\sshd_config

Il faut rechercher les lignes suivantes :

[sshd_config]

# Logging
#SyslogFacility AUTH
#LogLevel INFO

Les décommenter et indiquer les valeurs suivantes :

[sshd_config]

# Logging
SyslogFacility LOCAL0
LogLevel Debug3

Une fois la configuration modifiée et le serveur redémarré, il suffit de retenter une connexion puis d’aller consulter le fichier de log :

C:\ProgramData\ssh\logs\sshd.log
ou
%ProgramData%\ssh\logs\sshd.log

Remarque

Les problèmes courants se passent généralement autour des lignes liées au fichier authorized_keys ou administrators_authorized_keys, problèmes de droits ou
chemin du fichier utilisé…

Test de SSH IBM i vers Windows

On peut maintenant tester le tout via QSH ou CALL QP2TERM.
Grâce au fichier config la commande est simple :
(l’option -T permet de désactiver l’allocation d’un pseudo terminal)

$ ssh -T windows
Microsoft Windows [version 10.0.19045.3208]
(c) Microsoft Corporation. Tous droits r

Il est maintenant possible d’exécuter des commandes Shell Windows à partir de cette connexion.

Si on voulait obtenir les mêmes niveaux de log côté client (IBM i) que l’on a activé côté Windows, on pourrait utiliser la commande suivante :

$ ssh -T -vvv windows
OpenSSH_8.0p1, OpenSSL 1.1.1t  7 Feb 2023                                            
debug1: Reading configuration data /home/jl/.ssh/config                              
debug1: /home/jl/.ssh/config line 1: Applying options for *                          
debug1: /home/jl/.ssh/config line 4: Applying options for laptop                     
debug1: Reading configuration data /QOpenSys/QIBM/ProdData/SC1/OpenSSH/etc/ssh_config
...
Microsoft Windows [version 10.0.19045.3208]
(c) Microsoft Corporation. Tous droits r

Pour plus de détails

OpenSSH.com : https://www.openssh.com/
OpenSSH Server sous Windows – Document Microsoft : https://learn.microsoft.com/fr-fr/windows-server/administration/openssh/openssh_overview
OpenSSH – GitHub : https://github.com/PowerShell/Win32-OpenSSH/releases
Gestion des logs SHELL dans un CLLE

Gestion des logs SHELL dans un CLLE

Pierre-Henry avait déjà abordé la problématique de récupération des logs SHELL par la soumission de la commande QSH.

Un très bon article, que je vous conseille, si vous ne l’avez pas encore lu.

https://www.gaia.fr/recuperer-les-logs-dune-commande-shell-qsh/

Cependant pour des raisons d’organisation des traitements, on peut ne pas souhaiter débrancher la commande QSH, en la soumettant, du reste de notre programme.
Dans ce cas l’appel à QSH se fait dans le CLLE. Le log SHELL ne se retrouve pas dans les spools du job.
On peut néanmoins les récupérer par l’intermédiaire de variable d’environnements.

Les variables d’environnement et l’exécution de script SHELL

Je profite de cet article pour rappeler l’usage de quelques variables d’environnement pour l’exécution d’un script SHELL dans un CLLE. Ce ne sont que des exemples, il y en a beaucoup d’autres de possible !

  • QIBM_MULTI_THREADED

    Cette variable d’environnement est essentielle pour le fonctionnement des scripts SHELL. Il faut être multi-threadé pour que la commande s’exécute.
    Par exemple, dans l’image ci-dessous, j’ai lancé via la commande SHELL, un script SFTP.
    En dernière ligne nous trouvons le job lançant la commande shell, et au dessus 3 jobs de type BCI, « Batch immediate jobs », qui sont activés pour l’exécution d’un job multi-threadé.

ADDENVVAR ENVVAR(QIBM_MULTI_THREADED) VALUE(Y)

  • QIBM_QSH_CMD_ESCAPE_MSG

    L’exécution de la commande QSH dans un programme entraine un message QSH0005, QSH0006 ou QSH0007. Le message QSH0005 contient un code statut, allant de 0 à 255. 0 pour une exécution complète, les autres codes pour renvoyer un avertissement ou une erreur.

    Par défaut, ces messages sont envoyés avec un type d’achèvement complétion (*COMP), circulez, y’a rien à voir ! Seule une lecture du log du job, pour trouver le statut du message QSH0005, permettra de dire si l’exécution de la commande QSH s’est bien passée.

    En ajoutant la variable d’environnement avec la valeur Y, le type d’achèvement dépendra du statut du message QSH0005.
    • Si le statut est 0 : message en complétion
    • Autre valeur du statut, message en *ESCAPE. Ca veut dire qu’il faudra monitorer les messages QSH dans votre CL et gérer ces erreurs, sinon votre programme plantera.

      Message sans la variable d’environnement

Message avec la variable d’environnement

ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y)

  • QIBM_QSH_CMD_OUPUT

Cette variable définit le mode de sortie lors de l’exécution du script SHELL.
NONE : exécution du script en mode silencieux
STDOUT : affiche le terminal C avec le déroulé du script. En interactif, possibilité de répondre à une question en cours de script.
FILE : génération d’un fichier log dans l’IFS. Si le fichier existe déjà, il sera remplacé.
FILEAPPEND : génération d’un fichier dans l’IFS en ajout. Le fichier sera créé, s’il n’existe pas.

Attention : Pour les options « File » et « Fileappend », si le chemin IFS n’existe pas ou s’il n’est pas atteignable, problème de droit par exemple, le terminal C s’affichera.

ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(‘FILE=/Monrepertoire/MonFichierlog’)

Attention : Entre le « = « et le début du chemin pour le fichier log, il n’y a pas d’espace.

Dans votre programme, vous pouvez générer un nom de fichier log en dynamique, avant de générer votre variable d’environnement. Pour cela, il faudra que votre variable contienne ‘FILE=’ suivi du nom de votre fichier log, et passer la variable dans le paramètre VALUE de la commande.

Récupération du log

La variable d’environnement QIBM_QSH_CMD_ESCAPE_MSG permet de dissocier les fins OK des fins KO de QSH.
Mais sans la moindre information sur ce qui s’est passé.
Par la récupération du log dans l’IFS, vous pourrez interroger par SQL le fichier et gérer les problèmes. Au moins les cas les plus fréquents, et laisser les cas rares en gestion humaine.
La génération d’une log, peut aussi avoir de l’intérêt en cas de création de script en dynamique dans votre programme, pour garder une trace de ce qui a été exécuté… Pensez à la maintenance, et aux recherches en cas d’anomalie…


Prenons un exemple :
Un asynchrone qui scrute, via SFTP dans un répertoire réseau, la présence de fichier xml.
Télécharger ces fichiers dans l’IFS et laisser un traitement d’intégration dans l’ERP.
Ce traitement se lance à intervalle régulier, temporisation de 15 secondes.
Il peut ne pas y avoir de fichier à récupérer, répertoire distant vide.
C’est un cas classique d’interface asynchrone entre un logiciel externe à l’IBM i et la partie legacy de l’applicatif.

Dans cette exemple, je fais le choix d’effectuer directement un mget /repertoiredistant/*.xml, sans passer par un listing du répertoire suivi d’une lecture de ce listing pour charger fichier par fichier.
Je récupère directement tous les fichiers xml présents.
Problème, s’il n’y a aucun fichier xml dans le répertoire, le script SFTP renvoie une erreur, via le message QSH0005 qui a un statut 1. Pour moi ce n’est pas une erreur. C’est ce qu’on pourrait appeler un faux positif !

Pour déterminer si le message d’échappement reçu est une « vrai » erreur, ou l’absence de fichier à récupérer, je dois pouvoir récupérer la log.

Via la variable d’environnement, QIBM_QSH_CMD_OUPUT, en ‘FILE=’, je génère mes logs dans l’IFS, sans mode verbose sur le SFTP.

Quand tout se passe bien j’ai le log ci-dessous :

Le fichier contient la liste des commandes passées. Toutes les commandes de mon script se retrouvent dans la log.

Quand le mget ne trouve aucun fichier à ramener, j’ai le log :

À la suite du mget, je reçois un message d’erreur, pour fichier not found. Le script n’est pas allé plus loin, la commande « exit » n’a pas été passée.

Autre exemple, j’ai généré un problème de connexion, voici le log que j’ai reçu :

Aucune commande SFTP n’a été passée, normal, la connexion a été interrompu pendant la phase d’authentification.
Ce log n’est bien entendue qu’un exemple parmi beaucoup de problèmes de connexion.

Lecture du fichier par SQL

Je dispose de log dans l’IFS, je peux donc par SQL lire ces fichiers via la fonction table IFS_READ.

Voici ce que ça donne pour les 3 logs :

Le log ne disposant pas de code erreur ou de statut de fin, si je veux exploiter ces fichiers, je dois utiliser des recherches textuelles dans le champ LINE. Ce n’est pas l’ideal, mais faute de mieux…

Je peux par exemple rechercher « exit », pour savoir si mon script est allé au bout :

Je peux au contraire rechercher le problème de connexion interrompue :

Je peux aussi rechercher le « not found » pour détecter la fausse anomalie :

Dans notre programme, suite à l’exécution du script SHELL, plusieurs possibilités :

  • Avec la variable d’environnement QIBM_QSH_CMD_ESCAPE_MSG, monitorer les messages QSH0000 et donc en cas de problème lire les fichiers log. Dans ce cas la chaîne « exit » n’a pas besoin d’être testée, dans ce cas le message de retour est en complétion.
  • Sans la variable d’environnement QIBM_QSH_CMD_ESCAPE_MSG. Dans ce cas, je dois lire le log systématiquement et si je trouve « exit » ou « not found », la fin est normale, sinon il y a une erreur.

Les fichiers log, surtout sans mode verbose, comportent très peu de ligne, les SQL utilisés même s’ils sont « gourmands » par l’utilisation de like sur un fichier IFS, restent rapide. Mais attention à ne pas utiliser sur un fichier IFS contenant un log en cumul sur un mois !

L’idée est de gérer en automatique les retours les plus simples qui ne nécessitent pas d’intervention humaine.
Encore une fois, ce n’est pas l’idéal, mais si on peut alléger les alertes pour les équipes de maintenance, surtout pour un traitement lancé plusieurs fois par minute, il ne faut pas hésiter.

En cas de récupération des logs dans l’IFS, si vous générez un fichier distinct par appel, n’oubliez pas la base : durée de rétention des fichiers, script d’épuration des log obsolètes. Par SHELL, vous avez les outils pour gérer votre stratégie facilement.
Ca évitera un IFS qui enfle. Pour rappel, la volumétrie de données n’est pas la seule responsable des temps de sauvegarde / restauration de l’IFS, le nombre de fichiers aussi. Il vaut mieux un seul fichier de 1 Mo que 1000 fichiers de 1 ko

, , Traiter les membres d’un fichier

Vous avez parfois besoin de traiter tous les membres d’un fichier, par exemple pour analyser des sources ou des logs

Vous avez principalement 3 manières de le faire :

1) La manière historique par les fichiers modèles

C’est des fichiers qui sont dans qsys qu’on duplique et remplie le plus souvent avec le paramètre OUTFILE() de différentes commandes

dans un programme CLLE

DCLF FILE(QSYS/QAFDMBRL)

DSPFD FILE(LIB/file) TYPE(MBRLIST) OUTPUT(OUTFILE) OUTFILE(QTEMP/WAFDMBRL)
OUTMBR(*FIRST *ADD)

OVRDBF FILE(QAFDMBRL) TOFILE(QTEMP/WAFDMBRL)

RCVF

Votre traitement ici
DLTOVR FILE(QAFDMBRL)

2) En utilisant les services SQL

QSYS2.SYSPARTITIONSTAT

En sélectionnant la bibliothèque et le fichier, vous pouvez soit utiliser le résultat dans un programme CLLE.
Ou directement dans un select avec la fonctions SQL QCMDEXC

3) Sans générer de fichier

Cette méthode est moins connue, mais elle permet de traiter tous les membres d’un fichier sans générer de fichier intermédiaire

Vous allez avoir un programme CLLE, qui aura cette logique

Vous allez récupérer le premier membre
RTVMBRD FILE(LIB/FIL) MBR(*FIRSTMBR) RTNMBR(&MBR)
MONMSG MSGID(CPF0000) EXEC(do)
ENDDO

Vous allez ensuite boucler sur les suivants
RTVMBRD FILE(LIB/&FIL) MBR(&MBR *NEXT) RTNMBR(&MBR)
MONMSG MSGID(CPF3049) EXEC(leave)

Vous allez ainsi lire tous vos membres de votre fichier.

Vous pouvez également utiliser cette méthode pour traiter des membres de transfert qui arriveraient dans votre fichier.

Conclusion :

Vous avez 3 solutions pour les traiter les membres d’un fichier, à vous de choisir la méthode la plus adapter à votre traitement