, , , Paramétrer NGINX en SSL sur votre IBMi

Une fois que vous avez installer NGINX , vous voudrez sans doute le sécuriser

Voici comment le sécuriser

Pour notre exemple

nous générerons nous même les clés de cryptographie

Nous allons générer 2 fichiers qui vont contenir vos clés
un fichier .pem et un fichier .key
par convention on les appellera cert
on choisira un certificat de type x509 valable un année

pour cela on va utiliser OPENSSL, vous devrez donc vérifier que le produit est installé

sous qsh voici la commande à passer

openssl req -newkey rsa:2048 -nodes -keyout /home/nginx/cert.key -x509 -days 365 -out /home/nginx/cert.pem

Après avoir répondu aux questions vous obtenez vos 2 certificats

Maintenant vous allez devoir indiquer à nginx qu’il est protégé par ces clés

Vous allez devoir modifier le fichier de configuration de nginx dans notre cas celui par défaut
bien sur vous sauvegardez avant l’ancien /QOpenSys/etc/nginx/nginx.conf

Une fois que vous avez fait votre modification vous devez arrêter le serveur nginx et le redémarrer

nginx -c /QOpenSys/etc/nginx/nginx.conf -s stop
puis
nginx -c /QOpenSys/etc/nginx/nginx.conf

Pour tester vous allez indiquer https://votreserveur:1880
dans notre cas le port est 1880.

Vous allez recevoir une alerte de sécurité , c’est normal votre certificat de n’a pas de racine connue
Vous répondez une fois ou de manière définitive et vous obtenez votre première page et vous voyez le https:

, , Utiliser un USER SPACE en SQL

Depuis la TR6 de la V7R4, vous pouvez manipuler entièrement les objets de type *USRSPC
Rappel depuis la TR4 vous pouviez les lire

voici la liste des commandes à utiliser avec un exemple

Création, procédure, CREATE_USER_SPACE

CALL QSYS2.CREATE_USER_SPACE(USER_SPACE => ‘MONUSRSPC’,
USER_SPACE_LIBRARY => ‘GDATA’,
SIZE => 100,
PUBLIC_AUTHORITY => ‘*CHANGE’);


Ecriture, procédure, CHANGE_USER_SPACE ou CHANGE_USER_SPACE_BINARY

CALL QSYS2.CHANGE_USER_SPACE(USER_SPACE => ‘MONUSRSPC’,
USER_SPACE_LIBRARY => ‘GDATA’,
DATA => ‘Tester c »est douter , corriger c »est adbiquer’,
START_POSITION => 1);

Lecture, fonction table, USER_SPACE

SELECT Substr(DATA , 1 , 100) FROM TABLE(QSYS2.USER_SPACE(
USER_SPACE => ‘MONUSRSPC’,
USER_SPACE_LIBRARY => ‘GDATA’));

il existe d’autres services SQL , pour manipuler les USER SPACE

QSYS2.USER_SPACE_INFO pour avoir les informations du user space

SELECT * FROM TABLE(QSYS2.USER_SPACE_INFO(
USER_SPACE => ‘USRSPC1’,
USER_SPACE_LIBRARY => ‘GDATA’));

mais aussi pour modifier


QSYS2.CHANGE_USER_SPACE_ATTRIBUTES()

vous pouvez facilement intégrer ces requêtes dans du RPGLE par exemple

On utilise de plus en plus java dans les travaux de votre IBMi et on a parfois besoin d’avoir des informations d’exécution

Voici comment connaitre la version de la JVM d’un travail ?

2 solutions simples

==> WRKJWMJOB

Puis 5 puis 1

Ou en utilisant sql services par la vue QSYS2.JVM_INFO

exemple :

select JOB_NAME, JAVA_HOME from qsys2.JVM_INFO

plus d’informations ici

https://www.ibm.com/docs/en/i/7.3?topic=usage-java-system-properties

, Liste de valeurs dans une commande

Vous voulez utiliser dans une commande pour un paramètre une liste de valeur paramétrable

sur la commande de votre paramétre vous avez un paramètre CHOICEPGM pour indiquer votre programme

Ce programme peut être en Rpg ou en CL ou autres, il devra juste respecter un certaine syntaxe sur le paramètre à renvoyer

Voici un petit exemple d’une commande qui permet de passer une bibliothèque de *TEST à *PROD et alternativement

La commande

                           
              CMD        PROMPT('Change Type Lib')                       
 /*  NOM DE FICHIER                                                  */  
              PARM       KWD(BIBLIO) TYPE(*CHAR) LEN(10) MIN(1) +        
                           CHOICE(*PGM) CHOICEPGM(*LIBL/CHGLIBTYP4) +    
                           PROMPT('Nom de la bib')                       

le programme de liste

PGM PARM(&PARM1 &PARM2)                                              
DCL VAR(&PARM1) TYPE(*CHAR) LEN(21)                                  
DCL VAR(&PARM2) TYPE(*CHAR) LEN(2000)                                
  /*--------------------------------------------------------------*/ 
  /* Programme  CHOICEPGM                                         */ 
  /*                                                              */ 
  /* PARM1 :     PREMIER PARAMETRE                                */ 
  /*        1   A   10   NOM DE COMMANDE                          */ 
  /*       11   A   20   NOM DU PARAMETRE DE LA COMMANDE          */ 
  /*       21   A   21   TYPE  (P) LISTE  DE VALEURS    ? ou F4   */ 
  /*                           (C) TEXTE  D'INVITE      F11       */ 
  /*                                                              */ 
  /* PARM2 :     DEUXIEME PARAMETRE                               */ 
  /*   ------>si liste                                            */ 
  /*        1   A    2   NOMBRE DE PARAMETRES TRANSMIS EN BINAIRE */ 
  /*        3   A    4   TAILLE DE LA PREMIERE VALEUR  EN BINAIRE */ 
  /*        5   A    N   PREMIERE VALEUR                          */ 
  /*      N+1   A    N+2 TAILLE DE LA DEUXIEME VALEUR  EN BINAIRE */ 
  /*      N+3   A    M   DEUXIEME VALEUR                          */ 
   /*        ETC...   2000                                         */ 
   /*                                                              */ 
   /*   ------>si texte                                            */ 
   /*        1   A   30   TEXTE A AFFICHER                         */ 
   /*       31   A 2000   INUTILISER                               */ 
   /*                                                              */ 
   /****************************************************************/ 
   /*                                                              */ 
   /*  Déclaration des zones de travail                            */ 
   /*                                                              */ 
   dclf qadspobj                                                      
   DCL        VAR(&FINFICHIER) TYPE(*LGL)                             
   DCL VAR(&BIN) TYPE(*CHAR) LEN(2)                                   
   DCL VAR(&DEC) TYPE(*DEC) LEN(5 0)                                  
   DCL VAR(&SIGN) TYPE(*CHAR) LEN(1)                                  
   DCL VAR(&pos ) TYPE(*dec ) LEN(5)                                  
   DCL VAR(&nbr ) TYPE(*dec ) LEN(10 0)                               
    /*                                                              */
    /* Initialisation de la zone paramètre à retourner              */
   /* Attention elle peut contenir n'importe quoi a l'arrivée      */ 
   /*                                                              */ 
   CHGVAR  VAR(&PARM2) VALUE(' ')                                     
   /*                                                              */ 
   /* Test de la demande  P = liste de paramètres                  */ 
   /*                                                              */ 
               IF         COND(%SST(&PARM1 21 1) *EQ 'P') THEN(DO)    
   /*--------------------------------------------------------------*/ 
   /*                                                              */ 
   /* 1er cas pos 21 = P demande d'une liste de valeurs            */ 
   /*                                                              */ 
   /*--------------------------------------------------------------*/ 
   /*                                                              */ 
   /* Formatage de la variable à retourner                         */ 
   /* Position 1 à 2 en binaire nombre de paramètres de retours    */ 
   /* conversion longueur du paramètre en binaire                  */ 
   /* Position 3  à  4 en binaire longueur de la première valeur   */ 
   /* Position 5  à 14 premiére Valeur à afficher                  */ 
   /* Position 15 à 16 en binaire longueur de la deuxième valeur   */ 

    /* Position 17 à 26 Deuxième Valeur à afficher                  */   
    /* etc ...                                                      */   
    /* xxxxyyyyyyyyyxxyyyyyyyyyxxyyyyyyyyy                          */   
    /* Génération du fichier des bibliothèques  GAIA*               */   
    /*--------------------------------------------------------------*/   
              DSPOBJD    OBJ(QSYS/GAIA*) OBJTYPE(*LIB) OUTPUT(*OUTFILE) +
                           OUTFILE(QTEMP/WADSPOBJ)                       
              OVRDBF     FILE(QADSPOBJ) TOFILE(QTEMP/WADSPOBJ) +         
                           LVLCHK(*NO) SHARE(*YES)                       
              RTVMBRD    FILE(QTEMP/WADSPOBJ) NBRCURRCD(&NBR)            
    /* ecriture nombre de postes */                                      
              chgvar &pos 1                                              
                CHGVAR     VAR(%bin(&BIN)) VALUE(&nbr)                   
                CHGVAR     VAR(%SST(&PARM2 &pos 2)) VALUE(&BIN)          
              chgvar &pos 3                                              
                CHGVAR     VAR(%bin(&BIN)) VALUE(10)                     
   /* Traitement des postes      */                                      
              DOUNTIL    COND((&FINFICHIER) *OR &POS > 1980)             
              rcvf 
             monmsg cpf0864 exec(leave)                              
  /* Ecriture de la longueur du poste */                             
                CHGVAR     VAR(%SST(&PARM2  &pos  2)) VALUE(&BIN)    
             chgvar &pos (&pos + 2)                                  
  /* ecriture du poste                */                             
                CHGVAR     VAR(%SST(&PARM2  &pos  10)) VALUE(&odobnm)
             chgvar &pos (&pos + 10)                                 
             enddo                                                   
             dltovr     FILE(QADSPOBJ)                               
             ENDDO                                                   
 /*--------------------------------------------------------------*/  
 /*                                                              */  
 /* 2ème cas pos 21 = C demande d'un texte                       */  
 /*                                                              */  
 /*--------------------------------------------------------------*/  
             IF         COND(%SST(&PARM1 21 1) *EQ 'C') THEN(DO)     
             CHGVAR     VAR(&PARM2) VALUE('Un nom valide de +        
                          Bibliothèque')                             
             ENDDO                                                   
 ENDPGM 

Le programme de traitement

PGM PARM(&LIB)                                            
/*-----------------------------------------------------*/ 
/* Programme de traitement de la commande              */ 
/* récupération du type et changement                  */ 
/* *PROD  *TEST                                        */ 
/*-----------------------------------------------------*/ 
DCL &LIB *CHAR 10                                         
DCL &TYPE *CHAR 10                                        
             RTVLIBD    LIB(&LIB) TYPE(&TYPE)             
IF COND(&TYPE = '*TEST') THEN(DO)                         
             CHGLIB     LIB(&LIB) TYPE(*PROD)             
ENDDO                                                     
ELSE DO                                                   
             CHGLIB     LIB(&LIB) TYPE(*TEST)             
ENDDO                                                     
ENDPGM   

C’est un exemple simple et facilement transposable, on aurait pu utiliser les vues SQL … pour obtenir la liste de bibliothèque

, , Utilisations des indexs

l’utilisation des index est devenu un enjeux majeur de la bas de données ,

On me pose réguliérement la question sur l’utilisation de ceux ci, je vais essayer de vous éclairer

En DDS/LF RPGLE (par exemple)

Vous connaissez tous cette solution on déclare le fichier LF ou index et on le lis quand on regarde la description de l’objet
vous avez la date de dernière utilisation , bien sur cette information est disponible dans la vue qsys2.object_statistics

En SQL/INDEX SQLRPGLE (par exemple)


Dans ce cas la vous allez lire la table par SELECT et c’est l’optimiseur qui va déterminer que vous avez besoin du chemin d’accès ou pas
attention : ce n’est pas parce que l’index existe qu’il sera utilisé , mais il sera avisé

Pour faire le ménage dans les indexs inutilisés ?

Il vous faudra donc croiser ses éléments avant de faire vos suppressions

rappel

Il y a des procédures dans systools qui permettent d’ajouter des indexs par rapport à des critères ,
c’est la procédure SYSTOOLS.ACT_ON_INDEX_ADVICE
et pour supprimer vous avez la procédures SYSTOOLS.REMOVE_INDEXES (ne supprimera que les index ayants pour nom %RADIX_INDEX% et %EVI_INDEX%
ca peut être brutal de les utiliser comme ca .
Par contre vous pouvez extraire les sources de ces procédures pour voir ce qu’elles font et vous en inspirer …

Vous avez des informations très intéressantes sur le sujet sur le lkdn de Christian Griere (merci à lui)

lien sur les index

https://www.linkedin.com/pulse/vos-tables-db2-i-sont-elles-trop-index%C3%A9es-christian-griere/

autres liens

https://www.linkedin.com/in/christian-griere-6a3828a/recent-activity/posts/

Avec la TR6 de la V7R4 (TR disponible à partir du 24 mai 2022)


Vous pouvez désormais compresser en standard avec l’algorithme *ZLIB dans les commandes SAV*


Bien que un peu vieux, (la première version date de 1995) c’est un algo très fiable et efficace


Pour en savoir plus sur ZLIB https://fr.wikipedia.org/wiki/Zlib

Remarque :

Jusque la on pouvait l’installer en open-source

Envoyer un message avec réponse

Il peut être intéressant d’envoyer un message avec une réponse, pour des traitements occasionnels par exemple .

Voici 2 exemples de code qui font la même chose

exemple en RPG

**free
// Message simple avec réponse
// texte 55 max
dcl-s w_reponse char(1) ;
dsply ‘Voulez-vous continuer ?’ ‘QSYSOPR’ w_reponse;
// traitement de la réponse
if w_reponse = ‘O’;
dsply ‘Oui avec plaisir !’ ‘QSYSOPR’;
else ;
dsply ‘Certainement pas !’ ‘QSYSOPR’ ;
endif ;
*inlr = *on ;

exemple en répondant O

Exemple en CLP

pgm
/* Message simple avec réponse / / */
dcl &w_reponse *char 10
SNDUSRMSG MSG(‘Voulez-vous continuer ?’) +
TOMSGQ(QSYSOPR) MSGRPY(&W_REPONSE)
if cond(&w_reponse = ‘O’) then(do)
SNDUSRMSG MSG(‘Oui avec plaisir !’) msgtype(info) TOMSGQ(QSYSOPR)

enddo

else do

SNDUSRMSG MSG(‘Certainement pas !’) msgtype(info) TOMSGQ(QSYSOPR)
enddo
endpgm

exemple en répondant O

Rappel

C’est n’est pas la meilleur manière pour traiter de l’information, mais ca peut rendre service

, Réorganiser un très gros fichier

Voici la problématique qui nous est arrivée :

Suite à un gros coup de ménage sur une petite machine saturée avec un énorme fichier de 12 millions d’enregistrement plus 30 millions d’enregistrements supprimés et 4 index dessus

Le réflexe naturel, a été le bon vieux RGZPFM qui devait nous réorganiser tout ça … eh bien non. On n’a jamais réussi à le lancer jusqu’au bout.

Voici comment nous avons fait pour nous en sortir. Nous avons utilisé une des possibilités du CPYF qui élimine les enregistrements supprimés.

En gros voici les opérations à réaliser


1) Copie vers un fichier de sauvegarde de votre fichier
2) Suppression des index pour éviter de saturer le système en reconstruisant tous les index en même temps
3) Mise à blanc du fichier initial
4) Recopie des données depuis votre sauvegarde
5) Remise des index initiaux

Vous pouvez retrouver un exemple de source qui fait ça ici https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Remarque :


Dans cet outil l’astuce utilisée pour la suppression des index ne marche que sur les LF et pas sur les index (le plus simple dans ce cas la est d’extraire le source , droper l’index et rejouer votre script SQL)
On doit pouvoir également faire une copie par insert into … select
Il existe certainement d’autres alternatives …

, Génération CSV à partir de SQL

Il existe plusieurs méthodes pour faire du CSV, la solution la plus connue est la commande CPYTOIMPF.

Les procédures SQL peuvent offrir une alternative intéressante dans certains cas, voici un exemple.

Cette exemple utilise « Dynamic Compound Statement » avec la procédure QSYS2.IFS_WRITE_UTF8

quauoopt est le fichier des options PDM

BEGIN
— Génération fichier + entête
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME =>’/tmp/qauoopt.csv’,
LINE => ‘Option;Commande’,
OVERWRITE => ‘REPLACE’,
END_OF_LINE => ‘NONE’);
— Boucle de traitement des lignes
FOR SELECT option concat ‘;’ concat command as TEXTE From QGPL.QUAUOOPT DO
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME => ‘/tmp/qauoopt.csv’,
LINE => TEXTE);
END FOR;
END

Remarque :

Cette solution ne peut pas s’appliquer partout , en effet un « ; » dans une zone pourrait poser un problème à la lecture.
A l’inverse vous pouvez ajouter des instructions comme dans la fichiers XLS par exemple.

Il y a plusieurs procédures qui vous permettent de gérer l’IFS, et ca nous simplifie la tache.

pour retrouver les informations sur SQL services, https://www.ibm.com/support/pages/ibm-i-services-sql

, Extraire simplement vos références Web Services <-> Programme (de service)

Nous développons de plus en plus de web services grâce à IWS (Integrated Web Services).

L’implémentation de ces services peut être un programme, un programme de service, ou SQL. Par ailleurs, les deux dernières solutions nous permettent d’avoir des services avec plusieurs opérations (vocabulaire SOAP) ou routes (vocabulaire REST). Quoiqu’il en soit, plusieurs actions possibles au travers d’un unique service.

La question se pose désormais à plus grande échelle des impacts sur la maintenance des programmes et programmes de service sous-jacents à des services web !

Concrètement : je modifie un programme (de service) : comment savoir s’il est exposé en tant que service web ?

Scripts fournis

IWS est fourni avec des scripts, dans le répertoire /QIBM/ProdData/OS/WebServices/bin

Ces scripts permettent d’automatiser toutes les actions possibles, autrement disponibles via l’interface d’administration :

Nous vous recommandons leur usage pour déployer vos services par exemple, de façon automatique avec vos outils.

Ces scripts sont également capables d’extraire des informations des serveurs et services !

Extraction

Pour le principe ici, un script shell qui utilise ces fonctionnalités pour lister tous les services et récupérer tous les programmes (de service) référencés ! Le résultat est stocké en BD. C’est perfectible, mais démontre le fonctionnement :

#
# Extraire les références croisées web service <-> programme/programme de service
#

# Paramètres : bibliothèque de création du fichier 

export PATH=$PATH:/QIBM/ProdData/OS/WebServices/bin

# créer le fichier BD pour stocker les liens
db2 "create or replace table $1.wsxref (server varchar(10), service varchar(128), program varchar(128)) on replace delete rows"

# Lister les serveurs sans tenir compte de l'état (started/stopped)
servers=$(listWebServicesServers.sh | sed 's/(.*//' )
# Pour chaque serveur listé
for server in $servers ; do
  # Retourver les services sans tenir compte de l'état
  services=$(listWebServices.sh -server $server | sed 's/(.*//')
  # Pour chaque service
  for service in $services ; do
#   # Retrouver les propriétés. Ne garder que l'implémentation
	program=$(getWebServiceProperties.sh -server $server -service $service | grep -i 'Program object path:' | sed 's/Program object path://')
	echo $server $service $program
    db2 "insert into $1.wsxref values('$server', '$service', trim('$program'))"
  done
done

Le code est disponible ici : https://github.com/FrenchIBMi/webservices/blob/master/wsxref.sh

Une fois le script lancé (prévoyez un café le temps de l’exécution) :

Et voilà !

Vous pouvez l’appeler dans QSH, le planifier (commande QSH …), le mettre dans un CL. Et surtout intégrer le résultat dans vos outils d’analyse !