I. Introduction

DFSORT est un outil crucial dans les traitements z/OS : celui-ci a été amélioré depuis des dizaines d'années pour offrir un nombre très large de fonctionnalités. Il peut travailler avec des VSAM ou des QSAM, et si les fonctionnalités ne sont pas présentes dans le produit de base, des PTFs (« add-ons » ou « plug-ins » dans le vocabulaire IBM Mainframe) et des Exits (« hooks » sur certaines routines pour appliquer ses propres traitements au lieu de ceux prévus) permettent de s'adapter aux besoins exacts.

Les tris internes liés aux compilateurs sont certes efficaces, mais nécessitent de recompiler ou relinker les programmes lorsque de nouvelles versions sont disponibles. De plus, si l'on souhaite changer certaines informations ou trier différemment, il faut recompiler. Pour toutes ces raisons, l'externalisation du tri par rapport au traitement applicatif a été retenue. Dans le contexte batch, c'est une solution simple à mettre en place, et très flexible.

Ce tutoriel a été rédigé environ 1 an et demi après mes premiers pas sur z/OS, il s'agit donc d'un tutoriel pour débutants : les méthodes employées sont très simples et diversifiées pour avoir le plus d'exemples concrets disponibles. L'exemple présenté peut être réalisé de plusieurs autres manières, mais pour apprendre il vaut mieux répéter/écrire plusieurs fois la même chose.

II. Description

Dans ce tutoriel, on expliquera quelques commandes et fonctionnalités du DFSORT telles que : le filtre, le tri, la réécriture de valeurs, la comparaison avec des dates, la suppression de doublons et la jointure de 2 datasets (les « fichiers » au sens Mainframe).

On abordera également quelques programmes communs : IEBGENER et ICEGENER pour la copie de données vers des datasets et le changement de format d'enregistrement (passage de FB, Fixe Blocked, à VB, Variable Blocked, car les datasets sont de taille et formats limités).

L'analyse des logs et des STC (Started Task, équivalents des services ou daemons sur Windows et UNIX) se fera avec le programme SDSF, dont une version avec plus de fonctionnalités existe : ISFAFD.

III. DFSORT

L'outil DFSORT est disponible sous différents formats : fonction pour du développement, commande de script ou bien programme à appeler. On se concentrera sur le programme « SORT » à appeler en JCL.

La documentation officielle est très chargée, mais complète. Ce tutoriel a été rédigé pour faciliter l'utilisation de base, mais il ne faut pas hésiter à aller chercher dans la documentation ou l'Infocentre IBM (publib.boulder.ibm.com, pic.dhe.ibm.com…) pour plus de détails !

DFSORT/MVS Publications

III-A. Processing/Pipeline de traitement

Avant toute utilisation de SORT, il est important de connaître l'ordre dans lequel celui-ci va effectuer les instructions que nous allons lui passer. En effet, à chaque étape (step) utilisant SORT, on va effectuer un traitement (tri ou copie) entouré par plusieurs pré-traitements (sauts de N enregistrements, filtre, et réorganisation) et plusieurs post-traitements (réorganisation, et de multiples ordres).

Le schéma fourni par IBM est très complet et permet de voir la progression dans le pipeline :

http://pic.dhe.ibm.com/infocenter/zos/v1r12/index.jsp?topic=%2Fcom.ibm.zos.r12.icea100%2Fice1ca5031.htm

Image non disponible

On se concentrera sur les étapes simples pour faciliter l'approche :

  1. SORTIN : c'est le nom de la carte DD d'entrée, on va donc insérer les données dans le pipeline avec elle.
  2. SKIPREC : instruction en SYSIN, c'est une instruction indiquant combien d'enregistrements on va dépasser avant de commencer le traitement (utile si on a des commentaires avant les données).
  3. INCLUDE/OMIT : on indique ici un critère pour filtrer les données, soit on inclut en fonction d'un critère, soit on omet en fonction d'un critère.
  4. STOPAFT : on indique le nombre maximum d'enregistrements que l'on veut traiter, « STOP AFTer » tout simplement.
  5. INREC : si l'on souhaite réorganiser les colonnes « avant » le traitement, cela permet de placer certaines valeurs sur certaines colonnes.
  6. SORT/SUM ou COPY : c'est le traitement principal ! Soit on trie (ordre ascendant/descendant en fonction de certaines colonnes), soit on copie. Dans tous les cas, seuls les enregistrements qui arrivent à cette étape seront traités et apparaîtront sur la sortie prévue.
  7. OUTREC : similaire à INREC mais « après » traitement, on peut réorganiser les colonnes, faire des calculs, changer certaines valeurs par d'autres…
  8. [SORTOUT] : si on n'ajoute pas d'instruction OUTFIL, alors les enregistrements qui sont arrivés jusque-là sont recopiés en SORTOUT et le step est terminé.
  9. [OUTFIL] : si une ou plusieurs instructions OUTFIL sont insérées, elles démarrent à partir de là. On ne présentera pas les clauses OUTFIL, car on ne les abordera quasiment pas. Ce qu'il faut retenir : un autre pipeline de traitements est géré par OUTFIL, on peut donc effectuer des traitements supplémentaires avec.

III-B. Sauter des enregistrements (SKIPREC)

On peut sauter N enregistrements grâce à la clause SKIPREC. Cela est très utile si le dataset est démarré par des commentaires.

Exemple :

 
Sélectionnez
  1.  SORT FIELDS=(1,20,CH,A) 
  2.  OPTION SIZE=50000,SKIPREC=5,EQUALS,DYNALLOC 

Ici, on va sauter les 5 premiers enregistrements avant de continuer.

III-C. Maximum d'enregistrements (STOPAFT)

On spécifie ici le maximum d'enregistrements que l'on veut traiter.

Exemple :

 
Sélectionnez
  1. OPTION STOPAFT=100 

Ici, on va s'arrêter après 100 enregistrements lus.

Si on le combine avec le SKIPREC, il faut repenser au pipeline : on va d'abord ignorer les N premiers enregistrements, on va ensuite filtrer avec INCLUDE/OMIT, puis compter les enregistrements restants jusqu'à atteindre le maximum.

Exemple:

Avec en entrée des enregistrements de 1 à 10, et cette ligne :

 
Sélectionnez
  1. OPTION COPY,SKIPREC=4,STOPAFT=3 

On aura en sortie les enregistrements 5 à 7 qui seront traités : les 4 premiers sont ignorés, puis on prend le 5, le 6 et le 7 (3 enregistrements sont passés), puis on s'arrête.

Cette option est utile lorsque l'on veut tester sur de très gros volumes : on prend un échantillon.

III-D. Filtrer les enregistrements (INCLUDE/OMIT)

Avant de trier, on peut vouloir ne sélectionner que certaines lignes, ou effectuer un traitement sur certaines valeurs précises. Dans ce cas, on peut préalablement filtrer les lignes qui seront traitées avec les instructions INCLUDE et OMIT.

Si le but du step est « uniquement » de filtrer certaines valeurs, au lieu d'indiquer un traitement de tri, on va indiquer que l'on souhaite seulement copier les enregistrements répondant aux critères de sélection.

Exemple :

 
Sélectionnez
  1. INCLUDE COND=(1,80,SS,EQ,C'ABC') 
  2. SORT FIELDS=COPY 

Ici, on va copier les enregistrements (sans trier) où l'on arrive à trouver la chaîne « ABC » depuis la colonne 1 sur 80 octets.

Le mot INCLUDE peut être remplacé par OMIT pour inverser l'inclusion :

 
Sélectionnez
  1. OMIT COND=(1,80,SS,EQ,C'ABC') 

Ici, on va éviter tous les enregistrements contenant la chaîne « ABC » depuis la colonne 1 sur 80 caractères.

Il est possible d'effectuer plusieurs conditions avec des « AND » et « OR » :

 
Sélectionnez
  1. OMIT COND=((1,3,CH,EQ,C'LOL'),AND,(4,3,CH,EQ,C'MDR')) 

Ici, on va retirer les enregistrements contenant simultanément « LOL » depuis la colonne 1 sur 3 caractères et « MDR » depuis la colonne 4 sur 3 caractères.

Il est possible d'interpréter les champs de différentes manières :

CH (CHaracter) signifie que l'on interprète le champ comme une chaîne de caractères, il faut donc la comparer avec une autre chaîne déclarée avec C'…'. La comparaison se fait strictement dans les bornes indiquées.

SS (SubString) signifie que l'on cherche une chaîne de caractères dans les bornes indiquées. Il s'agit bien d'une recherche de sous-chaîne, on peut donc indiquer l'ensemble de l'enregistrement pour rechercher une chaîne dedans.

ZD (Zone Decimal) signifie que l'on a des nombres inscrits sous formes de caractères (« 42 » par exemple). On peut les comparer avec d'autres valeurs : « (31,6,ZD,GT,2500) » on va comparer les 6 caractères avec la valeur 2500.

Y2T, Y4T… sont des formats de dates. On peut ainsi comparer ces valeurs avec différentes dates autour du jour courant : « (23,5,Y2T,EQ,Y'DATE3') » ici la date lue est sur 5 caractères, est au format Y2T (14001 : 1er jour de l'an 2014) et est comparée à la date du jour au même format (Y'DATE3' donne la date du jour dans le format « DATE3 »). Il est possible de comparer avec le lendemain (Y'DATE3'+1) ou la veille (Y'DATE3'-1) sur 30 jours. Beaucoup d'autres formats de dates existent.

On peut interpréter les champs de nombreuses façons, mais on a présenté les 4 méthodes simples pour des travaux de débutant. L'ensemble des formats se trouve ici : DFSORT Data Formats

En plus de l'égalité (EQ), on peut tester l'inégalité (NE). Dans le cas des nombres ou des dates on peut tester les habituels « plus grand que » (GT), « plus grand ou égal » (GE), « plus petit que » (LT), « plus petit ou égal » (LE).

INCLUDE Control Statement

OMIT Control Statement

III-E. Trier les enregistrements (SORT)

On a vu comment copier sans modifier les enregistrements, en utilisant le mot « COPY », mais on peut vouloir trier en fonction de plusieurs critères. Grâce aux formats vus, SORT va comparer et trier en fonction de ces critères.

Il s'agit du « traitement principal » au centre du pipeline. Il y a donc des modifications possibles à faire avant et après le tri.

Exemple :

 
Sélectionnez
  1. SORT FIELDS=(1,80,CH,A) 

On trie de façon ascendante (A final), en fonction des caractères (CH) dans le champ démarrant en colonne 1 sur 80 caractères. Pour trier de façon descendante, il suffit de remplacer le ‘A' par un ‘D'.

Il est possible de trier sur plusieurs critères (en cas de clés primaires en plusieurs exemplaires) :

 
Sélectionnez
  1. SORT FIELDS=(5,5,ZD,A,12,6,PD,D,21,3,PD,A,35,7,ZD,A) 

La clé primaire sera la première déclarée (5,5,ZD,A : tri de façon ascendante sur un ID numérique sur 5 posits depuis la colonne 5), et on avance vers les clés d'importances moindres.

SORT Control Statement

III-F. Supprimer les doublons (SUM)

La suppression des doublons intervient dans le cadre d'un tri. Les doublons sont déduits en fonction des clés ! Si tous les champs sont à prendre en compte, il faut donc expliciter chaque champ ou l'ensemble de l'enregistrement.

SUM fonctionne en temps normal comme additionneur pour les champs en multiples exemplaires, mais en explicitant le fait que l'on ne veut PAS additionner, celui-ci va retirer les doublons et « ne pas » effectuer d'addition entre les entrées en double. SUM est donc une instruction à double usage.

Exemple :

 
Sélectionnez
  1. SORT FIELDS=(1,80,CH,A) 
  2. SUM FIELDS=NONE 

Ici, on va trier de façon ascendante sur les 80 premières colonnes, et supprimer tous les enregistrements en double.

SUM Control Statement

III-G. Modifier les valeurs ou les colonnes (INREC, OUTREC, OUTFIL)

La modification de valeurs ou de colonnes intervient dans les phases avant et après traitement. En effet, si l'on souhaite reformater les données ou n'utiliser que certaines d'entre elles, on peut en extraire une partie et ignorer les autres.

Attention : si on modifie les champs « avant » traitement, il faudra penser à trier en fonction des nouvelles colonnes !

Il existe plusieurs syntaxes possibles pour décrire les champs et les placements. Jusqu'à maintenant, on a seulement décrit les champs dans le format :
colonne du 1er caractère,longueur à prendre en compte

Mais il est possible de décrire d'une autre façon le format que l'on souhaite obtenir :
colonne de sortie où démarrer:colonne du 1er caractère en entrée à copier,longueur

Exemple :

 
Sélectionnez
  1. INREC FIELDS=(1:8,7, 
  2.                9:26,7, 
  3.                17:35,8, 
  4.                26:44,4, 
  5.                31:51,6, 
  6.                38:62,6) 

« 1:8,7 » On va copier sur la 1re colonne de sortie 7 caractères démarrant à la colonne 8, etc.
Ce qui donnerait en entrée :

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18
x x x x x x x x A B C D E F G x x x


Et en sortie :

01 02 03 04 05 06 07
A B C D E F G

On peut également ajouter des espaces au lieu de laisser des ‘0' binaires. En effet, si on souhaite transférer des datasets vers des systèmes ouverts (UNIX, Linux, Windows), il vaut mieux ne pas laisser de caractères signifiant la fin d'une chaîne, surtout quand il s'agit de commandes ou de balises qui seront interprétées. La syntaxe est simplement le nombre d'espaces voulus suivi d'un ‘X'.

Exemple :

 
Sélectionnez
  1. OUTREC BUILD=(1,43,167X) 

On va copier les 43 premiers caractères, et ajouter 167 espaces.

Dans ces 2 exemples, on a par ailleurs réorganisé le fichier « avant » traitement (INREC) et « après » traitement (OUTREC).

Dans les traitements additionnels, on trouve l'opérande OUTFIL. L'un des usages possibles est de reformater la sortie prévue. OUTFIL OUTREC ou OUTFIL BUILD permettent cela :

 
Sélectionnez
  1. OUTFIL OUTREC=(12,5,21,8,29,12,41,9,60,120,86X) 

Ici, on retrouve un ordre très similaire aux exemples précédents. OUTFIL permet beaucoup plus que cela. En effet, il permet d'envoyer différents traitements vers plusieurs datasets. Il est également possible de sortir vers un dataset les enregistrements retirés via les différents filtres.

On peut aussi appliquer des conditions et transformer en fonction de certaines valeurs :

 
Sélectionnez
  1. INREC IFTHEN=(WHEN=(45,2,CH,EQ,C'OK'), 
  2.    BUILD=(1,44,45:C'<FONT COLOR="GREEN">OK</FONT>',200X)), 
  3.    IFTHEN=(WHEN=NONE, 
  4.      BUILD=(1,44,45:C'<FONT COLOR="RED">KO</FONT>',202X)) 
  5. OUTREC BUILD=(C'<TR><TD>', 
  6.               1,7,C'</TD><TD>', 
  7.               9,44,C'</TD><TD>', 
  8.               45,100) 

Si on trouve un « OK » en colonne 45, alors on va le remplacer par un « OK » encadré de balises HTML en vert, sinon, dans tous les autres cas on affiche un « KO » en rouge avec du HTML.

INREC Control Statement

OUTREC Control Statement

OUTFIL Control Statements

III-H. Les Jointures

L'un des points fort de DFSORT est qu'il possède de multiples PTFs aux fonctionnalités variées, l'une d'elle est la gestion des jointures entre deux fichiers (PTFs UK51706 et UK51707 (SORTUGPG)). La jointure de DFSORT est similaire à ce que l'on retrouve dans les bases de données : il s'agit de sélectionner des enregistrements qui ont des valeurs en commun. Un exemple simple : un fichier contient le nom des clients et un ID, l'autre fichier contient les différents livrets et l'ID associé à chacun, la jointure va permettre de créer en sortie un fichier avec le nom des clients associés aux livrets.

Les précédentes instructions permettaient d'effectuer de multiples opérations internes au fichier, mais il arrive que l'on souhaite lier plusieurs fichiers et en extraire plusieurs valeurs : les enregistrements ayant un lien dans deux fichiers, les enregistrements n'apparaissant que dans un seul fichier, la somme totale de valeurs d'une clé dans les fichiers…
Pour cela, on va utiliser les jointures « internes » (inner join), et les jointures « externes » plus permissives (outer join, full outer join).

III-H-1. Processing/Pipeline jointures

Comme pour le tri classique, DFSORT a un pipeline précis d'opérations à respecter. Voici le schéma fait par IBM pour décrire l'ordre des opérations :

http://pic.dhe.ibm.com/infocenter/zos/v1r12/index.jsp?topic=%2Fcom.ibm.zos.r12.icea100%2Fjoinkeys.htm

Image non disponible

Les principales étapes qui nous intéressent pour ce tutoriel sont :

  1. SORTJNF1/SORTJNF2 : c'est le nom de la carte DD d'entrée, on va donc insérer les données dans le pipeline avec elle. Le nom des cartes DD peut être modifié si on souhaite effectuer des opérations complexes. F1 correspond à la carte SORTJNF1, et F2 à SORTJNF2.
  2. Un premier pipeline similaire à celui du SORT est effectué sur chacun des datasets. Ce pipeline est distinct pour chaque dataset. Cette étape est optionnelle et est à indiquer en DD JNF1CNTL/JNF2CNTL si on souhaite la personnaliser (un SORT ou un COPY sera effectué en fonction de la SYSIN qui contient l'ordre de JOIN). On retrouvera donc les opérations :
    1. SKIPREC : le nombre d'enregistrements en début de dataset à ignorer ;
    2. INCLUDE/OMIT : critère(s) pour filtrer les données ;
    3. STOPAFT : le nombre maximum d'enregistrements que l'on veut traiter ;
    4. INREC : réorganisation des colonnes « avant » le traitement ;
    5. SORT/SUM ou COPY : par défaut, un tri est effectué sur chaque fichier, mais si on indique à DFSORT que le dataset est déjà trié (en ajoutant « SORTED »), alors un simple COPY sera effectué.
  3. JOINKEYS & JOIN : Les fichiers sont récupérés par l'Exit E15 dans les 2 pipelines, et sont joints en respectant les ordres donnés en SYSIN. On peut utiliser la commande REFORMAT pour apporter d'autres modifications.
  4. Un second pipeline de fin est positionné afin de faire un post-traitement si nécessaire. Ces opérations sont à indiquer en SYSIN :
    1. INCLUDE/OMIT : critère(s) pour filtrer les données ;
    2. STOPAFT : le nombre maximum d'enregistrements que l'on veut traiter ;
    3. INREC : réorganisation des colonnes « avant » le traitement ;
    4. SORT/SUM ou COPY : un SORT ou un COPY avant sortie est nécessaire. En effet, on a lié en fonction de plusieurs clés, mais on peut vouloir trier sur d'autres critères en sortie (on a lié sur un ID, mais on va trier sur le nom) ;
    5. OUTREC : réorganisation des colonnes « après » le traitement ;
    6. [SORTOUT] : si on n'ajoute pas d'instruction OUTFIL, recopie en SORTOUT des enregistrements, et step terminé ;
    7. [OUTFIL] : si une ou plusieurs instructions OUTFIL sont insérées, les ordres sont effectués.

III-H-2. JOINKEYS statement

Avant de faire une jointure, on va d'abord déclarer les champs qui serviront de clé pour la jointure sur chacun des datasets. Plusieurs options sont disponibles pour l'appel des datasets, on utilisera les noms et options de nommage par défaut (FILE=F1/F2 avec SORTJNF1/SORTJNF2).


Le choix du ou des champs de jointure est important (paramètre « FIELDS »). Il fonctionne exactement comme le SORT, c'est-à-dire qu'on lui indique un ou des champs sous forme ascendante ou descendante.
On a donc deux JOINKEYS à indiquer pour joindre les datasets.

Exemple :

 
Sélectionnez
  1. JOINKEYS FILE=F1,FIELDS=(26,8,A) 
  2. JOINKEYS FILE=F2,FIELDS=(17,8,A) 

Ici, la clé de jointure se trouve sur 8 caractères, elle démarre en position 26 dans le dataset donné en SORTJNF1, et elle commence en position 17 dans le dataset associé à SORTJNF2.

Si on indique que le dataset est déjà trié sur cette clé (paramètre « SORTED »), alors DFSORT ne fera pas de vérification, mais il s'arrêtera dès que le dataset comportera une erreur de tri.
En plus du paramètre « SORTED », on peut forcer en ajoutant « NOSEQCK » : aucune erreur ne sera remontée. « NOSEQCK » est ignoré si « SORTED » n'est pas indiqué.

Exemples :

 
Sélectionnez
  1. JOINKEYS FILE=F1,FIELDS=(26,8,A),SORTED 
  2. JOINKEYS FILE=F1,FIELDS=(26,8,A),SORTED,NOSEQCK 

Les ordres INCLUDE/OMIT placés après le JOINKEYS seront exécutés sur le dataset lié, mais il est plutôt recommandé de le placer en DD JNF1CNTL/JNF2CNTL en cas d'expression complexe :

 
Sélectionnez
  1. JOINKEYS FILE=F1,FIELDS=(26,8,A),INCLUDE=ALL 
  2. JOINKEYS FILE=F2,FIELDS=(17,8,A),INCLUDE=ALL 

JOINKEYS Statements

III-H-3. REFORMAT statement

Les deux datasets vont être joints, mais il est important de déclarer comment ceux-ci vont être écrits en sortie. C'est le rôle de REFORMAT. De plus, il permet d'écrire dans certains cas si l'enregistrement actuellement généré a été trouvé dans F1, F2 ou les deux.
Le caractère ‘ ?' devient :

  • ‘B' si l'enregistrement est présent dans les deux datasets (inner join) ;
  • ‘1' s'il est présent dans F1 uniquement (left join)
  • ‘2' s'il est présent dans F2 uniquement (right join).

Exemple :

 
Sélectionnez
  1. REFORMAT FIELDS=(F2:1,43,?,F1:45,156) 

Ici, on va copier les 43 premiers caractères provenant de F2 (F2:1,43), on insère le caractère permettant de déclarer où se trouvait l'enregistrement (‘?'), puis on insère 156 caractères provenant de la 45e colonne de F1 (F1:45,156).

Le format d'enregistrement en sortie va dépendre de la déclaration du JOIN. Si on choisit de faire une jointure externe sur un des deux datasets (left/right outer join), alors celui-ci sera utilisé comme référence (LRECL sera le même, et RECFM sera variable ou fixe). Si on choisit d'inclure tous les enregistrements (full outer join), alors le format de sortie sera variable.

On peut d'ailleurs remplir les espaces en trop avec certains caractères si le REFORMAT est plus petit :

 
Sélectionnez
  1. REFORMAT FIELDS=(F2:1,43,?,F2:45,156),FILL=C'X' 

Ici, on remplit de caractères ‘X' l'espace restant.

REFORMAT Statement

III-H-4. JOIN statement

Le mot clé JOIN sert uniquement à spécifier le type de jointure externe voulue. En effet, si l'on souhaite une jointure interne (inner join), il suffit de ne pas l'indiquer.

JOIN Statement

III-H-4-a. Jointures Internes

Comme dit précédemment, la jointure interne s'effectue très facilement : on indique les JOINKEYS, et DFSORT sortira exclusivement les enregistrements présents dans les 2 datasets avec la clé donnée.

Concrètement :

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
ABC,200F
DEF,337 142,200F GHU,400F
GHU,856 856,400F  

III-H-4-b. Jointures Externes

Il existe plusieurs types de jointures externes. Celles-ci permettent d'inclure les enregistrements n'ayant d'existence que dans un des deux datasets (la clé est présente dans un dataset, mais pas dans l'autre).

La jointure permettant d'inclure « tous » les enregistrements est appelée dans les bases de données « full outer join », en DFSORT on la codera ainsi :

« JOIN UNPAIRED,F1,F2 » ou plus simplement « JOIN UNPAIRED »

Exemple :

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
   ,800F
DEF,337 142,200F ABC,200F
GHU,856 856,400F DEF    
GHU,400F



Les jointures externes avec prise en compte d'un seul des deux datasets comme source sont appelées « left outer join » ou « right outer join » en SQL en fonction de la table servant de référence. Voici deux exemples dans le cas du DFSORT sur les « JOIN UNPAIRED » :

Exemple 1 : JOIN UNPAIRED,F1

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F1
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
ABC,200F
DEF,337 142,200F DEF   
GHU,856 856,400F GHU,400F

Le dataset en F1 a été désigné comme référence. Il a donc été intégralement inclus, les informations manquantes ont été remplacées par des espaces comme demandé : « 101,800F » n'a aucune référence en F1 donc il n'est pas recopié. À l'inverse, « DEF,337 » est dans F1 mais pas dans F2, il est recopié.

Exemple 2 : JOIN UNPAIRED,F2

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F2
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
   ,800F
DEF,337 142,200F ABC,200F
GHU,856 856,400F GHU,400F

Le dataset en F2 a été désigné comme référence. Il a donc été intégralement inclus, les informations manquantes ont été remplacées par des espaces comme demandé : « 101,800F » est en F2 donc il est recopié. À l'inverse, « DEF,337 » est dans F1 mais pas dans F2, donc il n'est pas recopié.

Il est possible de demander « uniquement » les enregistrements orphelins : « JOIN UNPAIRED,F1,F2,ONLY »

Exemple :

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F1,F2,ONLY
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
   ,800F
DEF,337 142,200F DEF   
GHU,856 856,400F  

Il s'agit des enregistrements orphelins de F1 et F2. On a donc extrait les données qui n'apparaîtront pas lors d'une jointure interne.

On peut demander les enregistrements orphelins de l'un des deux datasets, uniquement :

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F1,ONLY
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
DEF   
DEF,337 142,200F  
GHU,856 856,400F  

Ici, seul l'enregistrement orphelin de F1 est affiché.

A l'inverse, sur F2 :

SORTJNF1 SORTJNF2 SYSIN SORTOUT
ABC,142 101,800F JOINKEYS FILE=F1,FIELDS=(5,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
JOIN UNPAIRED,F2,ONLY
REFORMAT FIELDS=(F1:1,3,F2:4,5),FILL=C' '
   ,800F
DEF,337 142,200F  
GHU,856 856,400F  

C'est celui de F2 qui est affiché.

Un JCL est fourni en exemple (test_full_outer_join.jcl), celui-ci écrit dans la SYSOUT du job (dans SDSF, taper ‘?' devant le nom du job qui a tourné, puis mettre un ‘s' devant SORTOUT pour visualiser uniquement les résultats). Ce JCL est un exemple à modifier pour tester les divers cas.

IV. Exemple d'utilisation

Un exemple complet de JCL faisant plusieurs appels à DFSORT est également fourni (Etat_STC.jcl).
Pour cette fonctionnalité, ce JCL n'est pas le plus optimal. Un autre JCL d'exemple est fourni pour l'exercice (Etat_STC_SYSOUT.jcl), celui-ci n'envoie pas de mail et ne fait que supprimer les doublons et afficher en SYSOUT le résultat.

IV-A. Explication de l'exemple

Le but de l'exemple est de regarder les STC qui fonctionnent actuellement, rechercher une erreur précise dans la log système durant une tranche horaire précise et récurrente. La log système z/OS est comparable à la syslog UNIX : chaque service émet des messages grâce à la commande « WTO » (Write To Operator), et chacun de ces messages est daté. L'ensemble est lisible en tapant « LOG » dans SDSF. Les logs sont ensuite archivées selon les règles en place sur le système. Certains « WTO » nécessitent une réponse de la part des opérateurs, et certains logiciels peuvent répondre automatiquement à leur place.

Dans notre exemple, si l'erreur est trouvée, on va déclarer cette STC comme invalide dans le mail. On peut imaginer d'autres fonctionnements ou utilités à l'ensemble des outils présentés, mais il ne faut pas oublier quelques contraintes importantes qui seront décrites dans le chapitre suivant.

Voici les étapes de l'exemple :

  • Step 1 : Extraction des STC qui fonctionnent actuellement (SDSF ou ISFAFD) ;
  • Step 2 : Filtre des lignes inutiles, et réorganisation des colonnes (SORT) ;
  • Step 3 : Extraction de la log système entre 4h59 et 5h30 du matin (SDSF ou ISFAFD) ;
  • Step 4 : Transformation du format d'enregistrement (IEBGENER) ;
  • Step 5 : Extraction des messages d'erreur du jour même (SORT) ;
  • Step 6 : Jointure entre les deux datasets sur le nom des STC (SORT) ;
  • Step 7 : Ajout des balises HTML autour des résultats (SORT) ;
  • Step 8-11 : Multiples steps pour copier les en-têtes SMTP, mail et HTML (ICEGENER et SORT) ;
  • Step 12 : Envoi du mail par copie dans la file JES2/SMTP (ICEGENER) ;

IV-B. Flowchart associé à l'exemple

Avec n'importe quel batch, il est nécessaire de réaliser un « flowchart » (organigramme de programmation) préalable expliquant les différents traitements souhaités. On ne détaillera pas la réalisation de flowchart, cependant, une des méthodes possibles consiste à :

  • réaliser un premier organigramme de ce que l'on souhaite, par exemple : extraction logs -> reformatage -> envoi mail ;
  • rentrer dans chaque élément et voir ce que les programmes peuvent réaliser afin de dessiner un deuxième organigramme contenant chaque step/programme ;
  • optimiser ce deuxième organigramme en décidant du nombre de fichiers à utiliser dans l'intégralité du traitement.

Voici le flowchart dans sa version finale :

Image non disponible

IV-C. Contraintes liées à l'exemple

Plusieurs contraintes sont à respecter pour faire fonctionner ce JCL :

  • serveur SMTP actif et disponible (la STC doit être visible en SDSF DA, elle est liée à USS/OMVS), et connaître la/les files autorisées à récupérer des mails pour les envoyer ;
  • la tâche à observer doit émettre ses alertes et messages d'erreurs dans la log système (SDSF LOG) via un WTO ou autre ;
  • la log système ne doit pas être archivée pendant ou après le moment où l'on cherche l'erreur… ou alors la STC doit démarrer « après » que la log ait tournée, afin que l'on puisse revenir analyser les heures qui nous intéressent dans SDSF en tapant « log » ;
  • la STC en exemple doit être active pour être affichée dans le mail ! Il arrive que des services/serveurs soient actifs mais qu'ils ne délivrent pas leur service, et dans ce cas des erreurs sont remontées dans la log système. Si le service que vous souhaitez surveiller n'existe pas en DA (« Display Active », l'équivalent de la commande « top » sur UNIX), il vous faudra rechercher en LOG (la log système, similaire au « dmesg » et au service « syslog » d'UNIX) et faire d'autres filtres et JOIN avec le DFSORT ;
  • Des PTFs précis sont nécessaires pour certaines fonctionnalités présentées : le JOIN et les formats de dates sont pris en charge par les PTFs UK51706 et UK51707 (SORTUGPG). La documentation de ces PTFs est disponible ici : User Guide for DFSORT PTFs UK51706 and UK51707 (SORTUGPG).

V. Le JCL et les Steps

On va maintenant expliquer chaque step (chaque étape est constituée d'un programme, donc un step est comparable à une ligne d'un script shell, ou à une commande entre des pipes), le contenu des cartes DD (l'équivalent des entrées-sorties sous formes de devices), et chaque paramètre en INSTREAM (lorsque l'on insère du texte dans une carte DD).

V-A. JOB et Set

La carte JOB contient les informations pour lancer le JCL : le nom du JOB, le nom de la personne/groupe à prévenir du code de fin, la classe dans laquelle exécuter le JOB, la classe de gestion des messages, les messages à retenir, la taille maximale allouable au JOB et aux steps le constituant, ainsi que de nombreux autres paramètres optionnels…

Dans notre exemple, on alloue 32 Mo à l'ensemble du JOB (afin de ne pas s'attribuer trop de mémoire, cette valeur peut être modifiée si l'exécution nécessite plus d'espace), on notifie la personne qui soumet le JCL (&SYSUID est une variable JCL), on affichera également en sortie le JCL que l'on a soumis (utile pour déboguer). Les détails sur les « JOB statements » se trouvent ici dans la documentation IBM : JOB Statement.

Les 3 SET sont des définitions de variables JCL. On définit FILE1, FILE2 et FILE3 pour faciliter l'écriture et la logique des steps suivants. Attention, ces variables ne sont pas valorisées dans tous les cas ! On verra qu'en « INSTREAM » sur une des extractions, on recopie le nom complet d'un des datasets pour cette raison.

V-B. Step 1 : SDSFDA (PGM=SDSF)

La première étape consiste à extraire depuis SDSF les STC actuellement en fonctionnement. Pour cela, deux programmes existent : SDSF et ISFAFD. ISFAFD est une version permettant d'effectuer plus de choses que SDSF, dans notre cas, SDSF suffit amplement. SDSF est le nom du programme de gestion des JOBs et STC de z/OS, celui-ci affiche précisément les sorties et rapports concernant les tâches. Il est également disponible sous forme de programme imprimant dans un dataset chaque écran qui s'affiche lorsque l'on tape une commande suivie de la touche ENTER.

On va se servir de cette fonctionnalité pour demander à SDSF de nous afficher les tâches ayant un préfixe qui nous intéresse (« PROG » dans notre cas) :

 
Sélectionnez
  1. O= 
  2. PRE NOTHING 
  3. DA 
  4. ARR REAL A OWNER 
  5. ARR ECPU-TIME A REAL 
  6. PRE PROG* 

La ligne 1 sert à retirer le filtre « owner » (on affiche les JOBs de tout le monde).

La ligne 2 sert à appliquer un filtre où seuls les JOBs commençant par « NOTHING » seront affichés (cela va nous servir à garder une liste vide).

La ligne 3 nous affiche les tâches actives (Display Active), avec les filtres appliqués (donc aucune tâche ne sera affichée).

La ligne 4 sert à afficher la colonne REAL après la colonne OWNER (ainsi on affichera quelques informations utiles). REAL est la mémoire totale consommée en nombre de pages (« frames ») de 4096Ko. Si REAL affiche 42, alors le job consomme 42 pages, soit 42 * 4096Ko de mémoire. En cas de grosse consommation (plus de 1000 pages), un T est affiché pour indiquer « Thousands of frames » : 42T signifiera 42000 pages, soit 42000 * 4096 Ko de mémoire.

La ligne 5 déplace la colonne ECPU-Time après la colonne REAL. ECPU-Time indique le temps total CPU consommé depuis le lancement de la tâche, en comptant les appels systèmes et instructions de changement de contexte (quand le CPU stocke l'état du processus en cours pour travailler sur un autre, et qu'il recharge l'état plus tard). C'est une valeur intéressante pour la consommation réelle.

La ligne 6 demande de filtrer les préfixes des JOBs avec « PROG* », c'est-à-dire que l'on va afficher tous les JOBs et STC commençant par « PROG ». C'est ici que l'on peut indiquer le préfixe qui nous intéresse, et seules ces tâches seront affichées.

Avec cette méthode, le programme SDSF va générer en sortie une capture du terminal pour chaque commande. On s'est assuré avec cet ordre de commandes de n'afficher aucune ligne « avant » d'avoir correctement filtré et aligné les colonnes. En effet, la ST (STatus log) n'affiche pas les mêmes colonnes que la DA ! On filtre donc préalablement pour n'afficher aucune tâche, on réorganise les colonnes, puis on affiche les tâches pour les voir apparaître avec les informations souhaitées une seule fois.

V-C. Step 2 : CATGREP (PGM=SORT)

Au deuxième step, on va récupérer un dataset contenant des captures d'écran du terminal recopiées les unes après les autres. Il faut donc extraire les quelques lignes intéressantes. Avec les commandes insérées précédemment, on sait que l'on a affiché uniquement les STC en rapport avec notre problème, on peut donc extraire les lignes contenant le préfixe recherché.

On va donc appeler DFSORT en lui demandant d'inclure uniquement les lignes contenant le préfixe qui nous intéresse. On va également arranger l'affichage pour retirer les décorations et l'interface de SDSF.
En SYSIN on insérera donc :

 
Sélectionnez
  1. SORT FIELDS=(1,7,CH,A) 
  2. INCLUDE COND=(1,80,SS,EQ,C'PROG') 
  3. INREC FIELDS=(1:8,7, 
  4.               9:26,7, 
  5.               17:35,8, 
  6.               26:44,4, 
  7.               31:51,6, 
  8.               38:62,6) 
  9. OUTREC BUILD=(1,43,167X) 

La ligne 1 est la déclaration du traitement principal : un tri des enregistrements en ordre croissant en fonction des 7 premiers caractères.

La ligne 2 donne la condition pour que les enregistrements soient pris en compte à la lecture : il faut que la ligne contienne la sous-chaîne « PROG » à partir de la colonne 1 sur 80 caractères. Ce filtre empêchera que d'autres lignes soient traitées.

Les lignes 3-8 expliquent comment réorganiser les lignes filtrées « avant » traitement. On va placer depuis la colonne 1 les 7 caractères démarrant en colonne 8, et faire d'autres déplacements. Le traitement sur la ligne 1 va donc lire la clé que l'on vient de déplacer.

La ligne 9 va recopier en sortie les 43 premiers caractères de chaque enregistrement précédemment traité, et y ajouter 167 espaces. Pourquoi ajouter ces espaces ? Pour se simplifier l'ensemble des steps suivants, on va travailler immédiatement sur une longueur d'enregistrement (LRECL) de 200.

V-D. Step 3 : SDSFLOG (PGM=SDSF)

Cet autre step de requête à SDSF va maintenant analyser la log système (LOG) et en extraire tous les messages entre 00:59 et 02:00. À vous de choisir une tranche horaire où votre STC peut envoyer des messages. Pour extraire ces données, on utilise le système de printer interne à SDSF : il faut indiquer en dur le dataset que l'on souhaite utiliser, comme les ordres sont passés en INSTREAM dans ISFIN, on ne peut pas les variabiliser avec JES2. Il faut donc bien réfléchir au nom utilisé.

ISFIN
Sélectionnez
  1. LOG 
  2. PRINT ODSN 'MY.FILE1' * MOD 
  3. PT 00.59.00 02.00.00 
  4. PRINT CLOSE 

La ligne 1 va demander l'affichage de la LOG dans SDSF.

La ligne 2 va ouvrir le dataset « MY.FILE1 », s'il n'existe pas il est créé.

La ligne 3 va demander à imprimer (PrinT) tous les messages de 00h59 à 02h00 du matin.

La ligne 4 va fermer l'imprimante et libérer le dataset en lecture.

La commande PRINT de SDSF génère des datasets en RECFM VB (taille variable d'enregistrements), il va donc falloir par la suite les extraire. Cependant, il faut garder en mémoire qu'il s'agit de logs donc que les styles d'écriture vont varier en fonction des logiciels et des éditeurs. Le format VB est donc nécessaire et réellement appliqué.

V-E. Step 4 : VBTOFB (PGM=IEBGENER)

Pour transformer la sortie PRINT de SDSF du format VB vers le FB (Fixed Blocked, qui est utilisé par les autres programmes), on va appeler IEBGENER pour dupliquer le dataset en copiant chaque champ.

Comme une log z/OS risque d'être particulièrement chargée, on va allouer 5 cylindres au moins :

 
Sélectionnez
  1. //SYSUT2   DD DSN=&FILE3, 
  2. //            DCB=(RECFM=FB,LRECL=240,BLKSIZE=0), 
  3. //            SPACE=(CYL,(5,2),RLSE), 
  4. //            DISP=(NEW,PASS,DELETE) 

L'ordre en SYSIN sera de copier le champ principal composé de 240 caractères depuis la première colonne. Le format du RECORD FIELD est le suivant : (longueur du champ, colonne en entrée, conversion, colonne de sortie où écrire)

 
Sélectionnez
  1. GENERATE MAXFLDS=1 
  2. RECORD FIELD=(240,1,,1) 

V-F. Step 5 : GREPLOG (PGM=SORT)

Maintenant que l'on travaille sur un format FB, on va filtrer les lignes de log pour ne prendre que celles du jour actuel et celles contenant l'erreur que nous recherchons. On profite également de cette instruction pour réorganiser les colonnes et supprimer ce qui n'est pas utile.

 
Sélectionnez
  1. SORT FIELDS=COPY 
  2. INCLUDE COND=(23,5,Y2T,EQ,Y'DATE3',AND, 
  3.               1,240,SS,EQ,C'ERREUR404') 
  4. OUTFIL OUTREC=(12,5, 
  5.                21,8, 
  6.                29,12, 
  7.                41,9, 
  8.                60,120, 
  9.                86X) 

La ligne 1 signifie que l'on va copier les enregistrements sans les trier.

Les lignes 2 et 3 sont deux conditions à respecter simultanément. On va extraire à partir de la colonne 25 sur 5 caractères une date au format [année sur 2 posits][jour de l'année] (par exemple le 1er février 2014 sera codé 14032 car c'est le 32e jour de l'année), et on va le comparer à la date du jour générée dans le même format. La 2e condition à respecter est la présence de l'erreur dans l'enregistrement. Si l'enregistrement contient la date du jour à un endroit précis et l'erreur recherchée, alors on l'affiche.

Les lignes 4-9 réorganisent en sortie les enregistrements et y ajoutent des espaces. Cette réorganisation peut varier selon l'éditeur de votre logiciel.

On a maintenant un dataset contenant quelques lignes avec l'erreur qui nous intéresse, mais comme cela provient de SDSF, on a également la date et le nom de la STC ayant produit cette erreur.

V-G. Step 6 : JOINLOGS (PGM=SORT)

Le step 2 a récupéré les STC actuellement en fonctionnement, et le step 5 a permis d'extraire les erreurs du jour, et les noms des STC associés à ces erreurs. On va donc pouvoir associer ces deux informations grâce au nom des STC, et générer un rapport complet sur les tâches. DFSORT a la capacité de joindre les informations selon une ou plusieurs clés définies sur des colonnes.

F1 est la LOG de SDSF, et F2 est le contenu du DA.
On retrouve donc en SYSIN :

 
Sélectionnez
  1. JOINKEYS FILE=F1,FIELDS=(26,8,A),INCLUDE=ALL 
  2. JOINKEYS FILE=F2,FIELDS=(17,8,A),INCLUDE=ALL 
  3. JOIN UNPAIRED,F2 
  4. REFORMAT FIELDS=(F2:1,43,?,F2:45,156),FILL=C' ' 
  5. SORT FIELDS=COPY 
  6. OUTFIL FNAMES=SORTOUT,INCLUDE=(44,1,SS,EQ,C'1,2,B'), 
  7.   IFTHEN=(WHEN=(44,1,CH,EQ,C'B'), 
  8.    BUILD=(1,43,44:C' KO',154X)), 
  9.    IFTHEN=(WHEN=NONE, 
  10.      BUILD=(1,43,44:C' OK',154X)) 

Les lignes 1 et 2 servent à indiquer la position des champs contenant les identificateurs des STC de chaque dataset lié à un JOINKEYS. Ces identificateurs sont les clés pour la jointure.

La ligne 3 est une jointure externe où l'on va conserver les enregistrements orphelins de F2. On aura donc une jointure qui affichera toutes les STC actives, et reportera les erreurs liées aux STC actives.

La ligne 4 décrit les enregistrements de sortie, on remarque le ‘?' qui sert à déterminer si l'enregistrement était uniquement dans l'un des deux datasets, ou s'il a bien une correspondance dans F1 et F2 (dans ce cas c'est qu'une erreur a été trouvée pour cette STC). On ajoute des espaces s'il en manque.

La ligne 5 est une copie d'enregistrement, aucun tri effectué.

Les lignes 6-10 servent à tester en sortie si le caractère ‘?' a bien été remplacé par une des 3 valeurs possibles, puis il affiche un « KO » si l'enregistrement avait une correspondance en jointure (STC active et erreur trouvée) ou un « OK » si aucune correspondance trouvée (STC active sans erreur trouvée).

On a donc maintenant un dataset en sortie qui indique clairement quelles STC ont produit l'erreur ou non, il ne reste plus qu'à le mettre en forme si on souhaite faire un tableau ou du HTML, puis le transférer vers un terminal utilisateur (PC, smartphone…).

Les steps suivants sont donc dédiés à l'ajout de HTML, puis de texte pour envoyer un mail par la file JES2 et la STC SMTP de USS/OMVS (Unix System Services/Open MVS). D'autres méthodes d'envoi de mails sont parfois utilisées, il ne faut pas hésiter à modifier le JCL fourni.

V-H. Step 7 : TOHTML (PGM=SORT)

Ce step va transformer les “OK” et “KO” en leurs équivalents colorés en rouge ou vert. On va également ajouter les balises HTML pour faire un tableau, ainsi, le contenu du mail sera correctement cadré.

 
Sélectionnez
  1. SORT FIELDS=(1,80,CH,A) 
  2. SUM FIELDS=NONE 
  3. INREC IFTHEN=(WHEN=(45,2,CH,EQ,C'OK'), 
  4.   BUILD=(1,44,45:C'<FONT COLOR="GREEN">OK</FONT></TD></TR>',123X)), 
  5.   IFTHEN=(WHEN=NONE, 
  6.     BUILD=(1,44,45:C'<FONT COLOR="RED">KO</FONT></TD></TR>',119X)) 
  7. OUTREC BUILD=(C'<TR><TD>', 
  8.               1,7,C'</TD><TD>', 
  9.               9,7,C'</TD><TD>', 
  10.               17,8,C'</TD><TD>', 
  11.               26,4,C'</TD><TD>', 
  12.               31,6,C'</TD><TD>', 
  13.               38,6,C'</TD><TD>', 
  14.               44,100) 

Les lignes 1 et 2 vont trier en ordre croissant selon les 80 premières colonnes, et retirer les doublons sur ces mêmes critères.

Les lignes 3-6 vont tester l'existence d'un « OK » en entrée ; s'il existe, on recopie les caractères jusqu'au « OK », puis on ajoute des balises de coloration vert, ainsi que la fin de cellule en HTML (« </TD> ») et la fin de ligne (« </TR> »), car le OK est la dernière colonne. Si on ne trouve pas de « OK », on place un « KO » rouge en HTML, et les mêmes balises de fin de cellule et de ligne.

Les lignes 7-14 répètent le schéma précédent des colonnes mais sur la sortie : on découpe chaque colonne, et on l'encadre de début et de fin de cellule. On ajoute devant tout cela une balise d'ouverture de ligne (« <TR> »).

On peut se permettre ce découpage étrange en deux étapes, car les « OK » et « KO » sont en fin d'enregistrement, et aucun traitement sur une clé spécifique n'est effectué.

V-I. Step 8 : HMAIL1 (PGM=ICEGENER)

Dans ce step, on va préparer le header HTML, celui du mail, et les communications pour émettre un mail en SMTP. Cette méthode n'est pas la seule possible, mais elle est parfois disponible.

ICEGENER est un outil de copie similaire à IEBGENER, mais se basant parfois sur le DFSORT (suite ICETOOL). Dans notre cas, le DFSORT ne sera pas appelé, on va simplement insérer ce qui est donné en INSTREAM depuis la carte DD SYSUT1 dans le dataset indiqué en SYSUT2.

Les premières lignes de l'INSTREAM indiquent que l'on communique avec le serveur SMTP nommé « SERVER » (à adapter donc).
Sur la section purement mail, les ‘@' n'apparaissent pas, car selon le charset choisi sur le 3270, on peut envoyer des caractères qu'UNIX ne comprendra pas (il faut s'assurer de la correspondance entre z/OS et UNIX en se référant au codepage 1047 de votre terminal 3270).
Dans le contenu du mail, on trouve des balises HTML, et le début du tableau. Le tableau aurait pu être copié dans le dataset plutôt qu'ici, c'est au choix.

Les textes en INSTREAM sont « toujours » en mode LRECL 80 étant donné qu'ils sont issus d'un JCL lui-même en 80 colonnes. Pour pouvoir concaténer cet en-tête au reste de nos données, il va falloir l'agrandir, et ajouter des espaces au lieu de 0 binaires (toujours pour que le serveur SMTP ne ferme pas la connexion trop tôt).

V-J. Step 9 : HMAIL2 (PGM=SORT)

On va donc simplement recopier les 80 colonnes avec SORT, et y ajouter 120 espaces :

 
Sélectionnez
  1. SORT FIELDS=COPY 
  2. OUTREC BUILD=(1,80,120X) 

V-K. Step 10 : FMAIL1 (PGM=ICEGENER)

Comme pour HMAIL1, on va recopier de l'INSTREAM dans un dataset, mais cette fois pour terminer le contenu du mail et son enveloppe, ainsi que la communication SMTP.

V-L. Step 11 : FMAIL2 (PGM=SORT)

Comme HMAIL2, on recopie les 80 colonnes que l'on a écrites, et on ajoute des espaces.

V-M. Step 12 : SMAIL (PGM=ICEGENER)

Le step final est une concaténation de cartes DD que l'on recopie vers la file SMTP. Cette spécificité permet de façon très simple de déclarer 3 datasets en un, et de les copier les uns après les autres. La seule restriction est qu'ils doivent tous être du même format (RECFM et LRECL similaires).

 
Sélectionnez
  1. //SYSUT1   DD  DSN=&&HEADTMP2, 
  2. //             DISP=(OLD,DELETE,DELETE) 
  3. //         DD  DSN=&FILE1, 
  4. //             DISP=(OLD,DELETE,DELETE) 
  5. //         DD  DSN=&&FOOTTMP2, 
  6. //             DISP=(OLD,DELETE,DELETE) 

On va donc mettre en SYSUT1 :

  1. le contenu de HMAIL2 (soit l'ouverture de connexion SMTP, la déclaration du mail, et le début du corps de celui-ci) ;
  2. le tableau que l'on a créé à l'issue de la jointure, et que l'on a agrémenté de HTML ;
  3. le contenu de FMAIL2 (soit la fermeture du tableau et du HTML, la fin de mail, et la fermeture de connexion SMTP).

Cet ordre de concaténation sera strictement respecté par JES2. Il enverra le tout en classe A du serveur SMTP, en SYSUT2 :

 
Sélectionnez
  1. //SYSUT2   DD  SYSOUT=(A,SMTP) 

Le mail sera donc déposé dans le SMTP qui le traitera et l'enverra ou non (voir les politiques de sécurité en place).

VI. Conclusion

DFSORT est un outil très puissant et disponible sous plusieurs formes pour z/OS (JCL, REXX, C…) : tri, filtre, reformatage, jointures internes et externes… Toutes ces opérations permettent d'alléger les applications métiers en faisant un traitement externalisé des fichiers : on peut ne trier qu'une seule fois ainsi. Le fait qu'il puisse travailler sur des VSAM ou des QSAM est un avantage très intéressant.

Il réunit toutes les fonctionnalités que l'on peut trouver sur les UNIX et Linux avec plusieurs programmes (cat, grep, cut, paste, sed, awk, sh, join…) mais avec l'avantage d'utiliser une seule syntaxe uniformisée. Les PTFs nécessaires pour utiliser l'ensemble des fonctionnalités peuvent nécessiter des demandes d'ajout aux exploitants, cela risque de prendre du temps à être déployé ou même d'être refusé, mais ces plug-ins sont très souvent déjà présents.

Dans l'exemple présenté, les requêtes à SDSF et les copies ne sont pas tout à fait optimisées, mais il s'agit d'un cas d'étude. Des logiciels certifiés permettent de surveiller les tâches en cours, et prendre des actions en cas d'erreur : le code présenté effectue une simple remontée d'information.

VII. Remerciements

Cet article a été rédigé grâce aux nombreuses réponses trouvées sur le forum z/OS de developper.com, les membres m'ayant particulièrement aidé sont :

Bernard59139, Darkzinus, et Luc Orient.

La documentation officielle IBM a également été d'une très grande aide, et les liens sont disponibles dans la section « Liens Utiles ».

Je remercie également ced pour sa relecture et ses corrections.

VIII. Liens utiles