Archivos de January, 2007

Tratamiento de Datasets en el Mainframe con JCLs

El presente documento explicará, siguiendo el ejemplo del “Hola Mamones” de la entrega anterior, como realizar operaciones con ficheros o datasets. Antes que nada, voy a explicar lo que es un dataset:

Un dataset es una colección de registros/datos que los utiliza/genera cualquier programa. Viene a ser un “fichero”. Hay dos tipos de datasets: Secuenciales, cuya información esta grabada consecutivamente y se lee de la misma manera (como un fichero cualquiera) y Particionados, cuya diferencia radica en que en este tipo de datasets hay uno o mas miembros secuenciales en su interior (es como si fuera una carpeta que aloja varios secuenciales).

Basándonos en el JCL que escribimos en la otra entrega, que lo que hacía era sacar por la salida A los datos que le dábamos en el propio jobstream, haciendo uso de la DD *.

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  *
**********************************************************
*                                                        *
*                       Hola, Mamones!!                  *
*                                                        *
**********************************************************
/*
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Lo que vamos a hacer ahora, es cambiar la ficha SUSUT1 con lo siguiente:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

SYSUT1 tiene una definición que apunta a un DataSet Name (DSN) llamado YGGDRASL.GODDESS.WISHLIB, el cual es un dataset particionado porque tiene miembros en su interior, uno de los cuales se llama BELDANDY (El nombre real es Belldandy, pero por restricciones de nomenclatura (no pueden tener mas de 8 caracteres, se ha tenido que eliminar una “L” ;) ).

Lo que viene detrás es la DISPosición del dataset, es decir, el bloqueo que vamos a realizar sobre ese fichero cuando ejecutemos el job y accedamos a él. DISP puede tener hasta tres sub-parámetros.

El primero de los sub-parámetros es el bloqueo del dataset en la ejecución del job, existiendo las siguientes opciones:
DISP=SHR, para un dataset que existe y que solo lo voy a leer (SHR de Shared es porque como solo lo leo, muchos otros pueden leerlo también sin joderles la marrana).
DISP=OLD, para un dataset que existe y que voy a re-escribir/cambiar/borrar.
DISP=MOD, para un dataset al que le quiero añadir nueva información al final, no cambiando la existente).
DISP=NEW, para un dataset que crearé nuevo en ese paso.
En las ultimas 3 disposiciones, el bloqueo será exclusivo: mientras mi job este en ejecución, ninguna otra persona podrá acceder a ese dataset.

El segundo sub-parámetro sirve para saber que hacer con ese dataset cuando el paso termine. Puede tener los siguientes valores:
KEEP: Deja el dataset como esta
CATLG: Deja el dataset como esta y lo cataloga en el catalogo correspondiente.
PASS: Deja el fichero como esta, pero la decisión de que hacer con él lo decidirá el siguiente paso.
DELETE: Borra el dataset del disco y del catalogo.

Y el tercer y último sub-parámetro se utiliza para saber que hacer con el fichero si el job casca estrepitosamente. Tiene los mismos valores que el segundo.

Por ejemplo, si a un dataset le ponemos DISP=(NEW,CATG,DELETE), le estamos diciendo que ese paso crea el fichero, y si el paso del job termina bien, lo cataloga en el SO, pero si el job casca, lo borra. Fácil, ¿no?

¿Que se consigue con este JCL? Pues es imprimir el miembro BELDANDY que esta dentro del dataset YGGDRASL.GODDESS.WISHLIB.

Pues vamos más allá. Si quiero copiar el miembro BELDANDY a otro dataset, ¿como lo hago? Pues sencillo, basta con modificar la ficha DD relacionada con la salida, es decir, SYSUT2, y especificar ahí el dataset de destino. La cosa quedaría entonces así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(NEW,CATLG,DELETE)
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Esto hará que se copie el miembro BELDANDY en un nuevo dataset, llamado YGGDRASL.HUMANS.KEIICHI. Este dataset es secuencial, ya que no tiene miembros, es decir, será el propio dataset el contenedor de los datos. Ojo, si ese dataset ya existe, el JCL daría un error, por lo que habría que modificar la disposición por la adecuada (DISP=MOD, u OLD). Pero continuamos entrando un poco más en el ajo.

El job tal y como está funciona, ya que el propio IEBGENER exportará la información en la creación de ese nuevo dataset. Pero si queremos especificar la forma y donde grabar, debemos dar mas datos al JCL.
Antiguamente, el sistema de almacenamiento era extremadamente caro, por lo que se debía aprovechar hasta el último bit y no reservar mas espacio del necesario. Así que cuando se creaba un dataset, se solía dar la información estimada de lo que iba a ocupar, y se reservaba ese espacio para el dataset. Así que se añadía un parámetro adicional, llamado SPACE que tenía varios sub-parámetros: SPACE=(Unidad (Primaria, Secundaria, Directorio)).

Por Unidad, entendemos la métrica que utilizaremos. Puede ser TRK (Tracks o Pistas), CYL (Cilindros), o puede ser un número natural que constituya los Bytes.

Cabe decir que según el modelo de disco que estemos utilizando, el tamaño en bytes de un cilindro o una pista puede variar considerablemente. Por ejemplo, en un disco IBM 3330 de la época, había 13.030 bytes por pista, y tenía 19 pistas por cilindro. Por lo tanto, un cilindro tenía 247.570 bytes. En cambio, en los nuevos discos IBM 3390, tiene 56.664 bytes por pista y 15 pistas por cilindro, por lo que un cilindro tiene 849.960 bytes.

El sub-parámetro Primario, dice cuanto se va a reservar en lo que marque la Unidad, y el sub-parámetro Secundario, dice cuanto más se va a reservar si se llena la zona Primaria. Por último, el  sub-parámetro Directorio, nos dice cuantos bloques de 256 bytes reservaremos para los índices en caso que nuestro dataset sea particionado (es decir, con miembros en su interior). Como esto puede que sea lioso de entender, con unos ejemplos quizás se vea mejor:

- SPACE=(TRK,10): Esto significa que reservaremos 10 pistas para nuestro dataset.

- SPACE=(CYL,(10,2)): Con esto, estamos diciendo que se reserve un espacio de 10 cilindros y 2 cilindros como extensión secundaria. ¿Que significa esto? Que si llenamos los 10 cilindros, nos extenderá el tamaño 2 cilindros más. ¿Y si los llenamos también? Pues se extenderá el espacio otros 2 cilindros más, así hasta un máximo de 15 veces, y llegado a ese punto, el dataset quedará lleno completamente y no se extenderá más.

- SPACE=(4096,(100,50)): Esto es que queremos que reserve 100 bloques de 4096 bytes de tamaño, llenados los cuales, hará que se extienda el tamaño otros 50 bloques mas hasta un máximo de 15 extensiones.

- SPACE=(CYL,(10,2,5)): Esto solo tiene sentido si estamos creando un dataset particionado: Queremos 10 cilindros de espacio, con 2 cilindros de extensión (hasta 15 veces más de extensión) y 5 bloques de 512 bytes que guardarán la tabla de índices para los miembros. Si ponemos mas bloques, mas índices entrarán. Como cada bloque puede guardar la información de direccionamiento de al menos 5 miembros, con ese 5 le estamos diciendo que nuestro dataset puede almacenar hasta un máximo de 25 miembros en su interior. Suele ser recomendable dar más bloques por si acaso, no vaya a ser que no puedas crear más miembros y tengas el espacio reservado en cilindros a medio llenar, con lo que todo ese espacio estaría desaprovechado.

Así pues, una vez introducido este parámetro, el JCL nos quedaría así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(1,1))
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Claro, la sentencia SPACE tiene sentido para dataset que vaya a ser guardad en disco. Para la información que vayamos a guardar en cinta, esto no se aplica. Vale, hasta aquí sabemos CUANTO podemos llenar un dataset. Pero claro, falta saber DONDE lo grabamos. Para eso tenemos otro parámetro: UNIT.

Especificando UNIT, introduciremos el modelo del disco o cinta donde se grabará la información, por lo que deberemos conocer que instalación mainframe tenemos y que dispositivos tiene conectados. Por ejemplo, si ponemos UNIT=3390, ese dataset lo grabaremos en un disco modelo 3390. Si quisiéramos almacenarlo en cinta, deberíamos saber que modelo de cintas tiene nuestra instalación, de tal forma que si tuviéramos una IBM 3490, deberíamos poner UNIT=3490 en nuestro job.

Afortunadamente, para que no tengamos que saber los detalles, marcas y modelos de nuestro almacenamiento, IBM creo unos nombres llamados Esotéricos que lo que hacen es, en tiempo de definición de la configuración IODF, asignar un nombre conocido por todos a un modelo de dispositivo concreto, y así no tenemos por que conocer las particularidades de la instalación. El SO ya proporciona un esotérico por defecto llamado SYSDA, que hace referencia a cualquier disco que tengamos en la instalación. Así que poniendo UNIT=SYSDA, le decimos a nuestro JCL que ese dataset irá a parar a disco.

Yo también puedo definir otros esotéricos en tiempo de configuración. He visto JCLs en otras instalaciones que si quieres grabar en cinta de carrete de cinta de los antiguos, pones UNIT=CARRETE, y en cambio, si quieres grabarlo en cintas mas nuevas, pues pones UNIT=CARTUCHO. Esto funciona porque alguien habrá decido que una unidad 3420 o 3422 que es de carrete, tenga como esotérico CARRETE, y lo mismo con el CARTUCHO, que tendrá probablemente unas definiciones de unidades de cartuchos 3480 o 3490.

Pero volviendo al JCL, como queremos grabar nuestro dataset a disco, le añadimos el UNIT, con lo que nos quedaría el tema así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Con esto, ya podemos estar seguros de que nuestro dataset YGGDRASL.HUMANS.KEIICHI se va a grabar a disco. Pero… ¿A qué disco? Si no ponemos nada, el SO elegirá uno al azar, pero bueno, lo ideal es saber donde guardamos nuestro dataset. Así que se añade un parámetro adicional, que es el VOL=SER=Nombre, siendo Nombre un nombre (valga la redundancia) alfanumérico de 6 caracteres (no 8 como pudieras imaginar) que identifica al disco (VOLume SERial number). Este nombre se le da al disco en tiempo de inicialización y debe ser único en la instalación. Así que suponiendo que tengamos un disco llamado WORK01, y queremos que nuestro dataset se grabe ahí, el JCL quedaría como sigue:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(1,1)),UNIT=SYSDA,
//             VOL=SER=WORK01
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

El VOL=SER no sólo identifica discos, sino también las cintas, de modo que si quisiéramos grabar en una cinta concreta el dataset, con poner el nombre de la cinta en cuestión, la unidad de cintas mostraría al operador un mensaje luminoso que le indicaría que debe montar la cinta con la etiqueta que le hemos asignado.

Con esto, nuestro JCL está listo para copiar la información del miembro BELDANDY a nuestro nuevo dataset secuencial. Si no añadimos más parámetros descriptivos, IEBGENER se basará en grabar la información en el nuevo dataset, siguiendo el patrón del miembro BELDANDY. Pero, si por circunstancias del destino, queremos ser todavía mas descriptivos en el nuevo dataset en la disposición de los datos, utilizaremos un nuevo parámetro llamado DCB (Dataset Control Block). Esto indicará COMO grabar los datos.

En el DCB especificaremos 3 cosas: Formato del registro (RECFM, RECord ForMat), El tamaño del registro lógico (LRECL Logical RECord Length) y el tamaño del bloque (BLKSIZE, BLocK SIZE). Usando un ejemplo, si decimos que tenemos un dataset con DCB=(RECFM=FB,LRECL=80,BLKSIZE=5600), le estamos diciendo que nuestro dataset tiene un tamaño de registro Fijo distribuido en Bloques (FB), cuya longitud de registro lógico es de 80 bytes y que el tamaño del bloque utilizado es de 5600 bytes. El formato puede ser Fijo (F), Variable (V) y  no especificado (U). Así pues, si queremos que nuestro nuevo dataset tenga ese tipo de disposición, añadimos al JCL esa línea:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.GODDESS.WISHLIB(BELDANDY),DISP=SHR
//SYSUT2   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(1,1)),UNIT=SYSDA,
//             VOL=SER=WORK01,
//             DCB=(RECFM=FB,LRECL=80,BLKSIZE=5600)
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Con esto, hay que tener cuidado: Si el Dataset de origen (YGGDRASL. GODDESS. WISHLIST (BELDANDY))  no tiene el mismo formato de registro, dará un error o los datos de destino se pueden corromper en la copia, ya que al ser los registros secuenciales, un bit de más o de menos en el registro podría mover toda la estructura.

Otro ejemplo: ¿Qué pasa si quiero copiar un dataset secuencial (YGGDRASL.HUMANS.KEIICHI) a un miembro nuevo (KEIICHI) de un dataset particionado, también nuevo (YGGDRASL.GODDESS.BELDANDY), y borrar el dataset secuencial antiguo?

Pues que se modifica un poquito el JCL:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//COPIADS  JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=YGGDRASL.HUMANS.KEIICHI,DISP=(OLD,DELETE,KEEP)
//SYSUT2   DD  DSN=YGGDRASL.GODDESS.BELDANDY(KEIICHI),
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(10,1,10)),UNIT=SYSDA,
//             VOL=SER=WORK01,
//             DCB=YGGDRASL.GODDESS.URD
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Que hace este job? Primero, en SYSUT1, pondremos la información que queremos leer, que es un dataset secuencial. Importante ver la DISPosición, que significa que es un dataset que existe y que vamos a modificar porque luego lo borraremos (OLD), y si la copia sale bien, nos lo cargaremos (DELETE), pero que si algo ocurre con el job, que lo deje como está (KEEP).
No se si os habéis dado cuenta que aquí no he dicho ni UNIT, ni VOL=SER, ni nada por el estilo. No los he dicho porque asumo que el dataset YGGDRASL.HUMANS.KEIICHI es un dataset que esta catalogado (porque en el JCL anterior, le hemos dicho que nos los catalogue con un CATLG). Si no lo estuviera, habría que dar los datos de donde se encuentra.
En SYSUT2, ponemos el nuevo dataset (NEW) y entre paréntesis, el nombre del miembro. Le decimos que si sale bien, nos lo catalogue (CATLG) y que si sale mal, que lo borre (DELETE). Además, le damos un espacio de 10 cilindros de partida, los cuales se pueden ampliar a un cilindro más si esos 10 se llenan y así hasta 15 veces, y le damos un tamaño para los miembros de 10, es decir, que se pueden guardar hasta 50 miembros además del nuevo miembro a crear. También, le decimos que ese dataset particionado nos lo deje en el volumen de disco WORK01 y que la disposición de los registros, la obtenga usando como plantilla el dataset particionado YGGDRASL.GODDESS.URD que fue creado hace tiempo (así te evitas poner el RECFM, LRECL y BLKSIZE).
Se entiende, ¿no?

Pues si volvemos al primer ejemplo de todos, podemos hacer que un dataset lo llenemos de información proveniente del propio JCL, como por ejemplo, almacenar la información del banner Hola Mamones en un dataset. Para ello, basta con lanzar el siguiente job:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  *
**********************************************************
*                                                        *
*                       Hola, Mamones!!                  *
*                                                        *
**********************************************************
/*
//SYSUT2   DD  DSN=YGGDRASL.PRUEBAS.HOLA,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(1,1)),UNIT=SYSDA,
//             VOL=SER=WORK01,
//             DCB=(RECFM=FB,LRECL=80,BLKSIZE=5600)
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Ester job, dejará el banner en un dataset secuencial nuevo que se denominará YGGDRASL.PRUEBAS.HOLA.

Por último, comentar que la gran mayoría de los datasets secuenciales (sobre todo los que se usan para guardar JCLs y demás) suelen se de tipo FB (bloque fijo) con LRECL=80, o dicho de otro modo, que cada línea o registro tiene una longitud de 80 caracteres, la misma longitud que las antiguas fichas perforadas. Como veréis, todo esto tiene sentido, ¿verdad?

Si esto os ha quedado claro, ya estáis en condiciones de lidiar con los datasets dentro del mainframe.

El siguiente artículo seguirá explicando temas de datasets y otras UTILITIES que nos pueden venir bien para sacar mejor partido a nuestros datos.

Primeros pasos en el Mainframe: Lenguaje JCL

Bueno, el documento de hoy estará enfocado en explicar mas profundamente como hacer el trabajo día a día en un Host, y nos centraremos en los distintos procesos, y mas concretamente, en el lenguaje JCL (Job Control Lenguaje). Al término de este documento, seremos capaces de ejecutar un job que nos deje en salida impresa, el texto “Hola Mamones” (lo del “hola mundo” esta muy trillado xDDDD).

Lo primero que debemos hacer es un reseteo de nuestro cerebro, porque en el mundo host, las cosas son muy distintas que en el mundo PC. Por ejemplo, crear un fichero no es algo tan sencillo como pinchar con el ratón sobre el menú Archivo, para luego hacer clic en Nuevo. En un host, todo son unidades de trabajo o jobs que al ejecutarse, producen un resultado (aunque a veces, no el deseado ;) ).

Vamos un poco con la explicación de lo que es un Job o Trabajo: Un Job puede ser interactivo (ej, una sesión TSO), puede ser una tarea que se inicia y permanece en memoria para siempre (como un daemon en los distintos sabores de Unix), o puede ser un Job batch, es decir, un proceso por lotes.

Todo job se basa en los mismos principios y sigue el mismo flujo: Existe una entrada, un procesamiento y luego una salida con el resultado. De la infraestructura software que dará soporte a todo este flujo de trabajo, se encarga el JES2 (Job Entry Subsystem). Para ello, este subsistema esta organizado de la siguiente manera:
- Existen una o mas clases de entrada (una clase viene a ser una cola FIFO) a la que puedes configurar diversos parámetros de prioridad, ejecución, etc. En este tipo de clase, se alojan los jobs que están esperando para ser ejecutados.
- Los iniciadores o INITs son los encargados de coger un job que esta esperando ser procesado y lo ejecuta. Evidentemente, un iniciador estará asociado a una o varias clases. Vendría a ser como un Thread. Cuantos más threads haya, mas proceso paralelo existirá. Un iniciador también puede hacer caso a más de una clase.
- Y por último, están las clases de salida, que, al igual que las de entrada, son las colas donde se dejan los resultados de los jobs que posteriormente serán impresos.

En el trabajo diario de un host, para cualquier cosa que debas hacer, necesitas ejecutar muchos jobs para que la máquina haga las cosas que le pides (a este proceso se le llama submit).

Si no tenemos ningún iniciador lanzado (cosa rara en un host arrancado), siempre se puede ir a la Master Console y teclear el siguiente Comando: S INIT.INIT1,,,A . Esto hará que lancemos un iniciador que lo hemos llamado INIT1 (pero puede ser cualquier nombre) que solo ejecutará los jobs que sean de la Clase A. Con esto, tenemos todo el sistema listo: Si ejecutamos un job de clase A, entrará por INIT1, y si mientras está en ejecución, lanzamos 20 jobs más de tipo A, se quedarán en la cola de entrada hasta que les llegue su turno (a menos que lancemos mas iniciadores que hagan caso de la clase A, con lo que podrían ejecutarse en paralelo). Los jobs, una vez ejecutados, quedarán en la clase o cola de salida que especifiquemos en el job hasta que una impresora que haga caso a dicha clase los imprima.

Hasta aquí, creo que no hay muchas dudas, no? Bueno, antes de seguir, vamos a exponer un par de definiciones:
- Paso o Step: Conjunto de líneas asociadas a un programa, que incluyen el programa en si y los datos que utilizará, así como sus parámetros.
- Job: Conjunto de Pasos que se ejecutan consecutivamente.

Bien, nos metemos de lleno al tema: Para definir o crear un job, haremos uso del lenguaje JCL. Para ello, hay que tener clara la sintaxis, ya que existen ciertas reglas a cumplir, que son las siguientes:

1.- La gran mayoría de las sentencias JCL comienzan con // (existe un delimitador /* que mas tarde comentaré).
2.- Todo lo que se escriba a partir de la columna 72, será ignorado.
3.- Todo Job comienza con la sentencia JOB
4.- Se debe escribir todo en MAYUSCULAS (si editas el JCL en el ISPF te lo convierte en mayúsculas directamente, pero si no se edita desde ese editor, hay que tenerlo muy en cuenta)
5.- Todo job debe tener como mínimo 1 Paso (sentencia EXEC)
6.- Todo paso debe incluir como mínimo una definición de datos (sentencia DD, viene a ser como el “var” de Pascal).
7.- El job se dará por finalizado cuando se encuentre una única línea con nada mas que //.

Bueno, con estas 7 reglas sencillas, ya podemos meternos de lleno a escribir nuestro JCL. Empecemos por la primera ficha (realmente es una línea, pero antaño cada línea era una ficha perforada y a fuerza de oír a mis mayores, me he quedado con ficha, además, da un aire retro-actual que pa que  xDDDDD):

         1         2         3         4         5         6         7
----+----0----+----0----+----0----+----0----+----0----+----0----+----0-- 
//MAMONJOB JOB GODDESS,URD,CLASS=A

Los caracteres y números de encima son simplemente para ver en la columna que estamos, así que no hacer demasiado caso.

Analicemos esta ficha: después de las //, aparece el nombre del Job. Puedes poner el que te de la gana, siempre y cuando tenga de 1 a 8 caracteres alfanuméricos, excepto el primer carácter, que debe ser siempre alfabético. He elegido como nombre MAMONJOB porque es un job que imprimirá “Hola Mamones”. Lo siguiente es la sentencia JOB, que indica al SO (en este caso al JES2) que comienza nuestro Job.

Lo siguiente a explicar son los parámetros que puede llevar una sentencia JOB. Los hay de dos tipos: Parámetros posicionales y de Keyword o Referencia. Los de Keyword se diferencian en los posicionales en que llevan un = que les da un valor. Los posicionales, son en sí variables y de relevancia para el JES2. Como podéis ver, los parámetros se separan con comas. Eso si, hay una regla: Si vas a tener los dos tipos de parámetros en el job, debes poner primero los posicionales y luego los de keyword.

En nuestro ejemplo, la primera ficha muestra 3 parámetros: dos posicionales y un Keyword. Para el parser del JES2, el primer parámetro (opcional) GODDESS hace referencia al Accounting del job, es decir, si existe una entrada en  la contabilidad llamada GODDESS, los ciclos de CPU que consuma este job irán a parar a esa entrada, de forma que puedes conocer cuanta CPU ha consumido dicha entrada a lo largo del mes, por ejemplo. El segundo parámetro lo toma el JES2 como el nombre del/la programador/a, que en este caso es URD (una de las mejoras administradoras del mundo), y el ultimo parámetro, que es Keyword, indica la clase donde se ejecutará el job. Este último parámetro dará la pista sobre que iniciador se ejecutara. Sencillo, verdad?

Existen mas parámetros (algunos opcionales, otros no tanto) que se pueden poner en el job, como descripciones del job y demás, pero nos vamos a encontrar con que no nos van a entrar en una sola ficha. Por lo que se debe escribir una ficha o línea de continuación. Y ¿Cómo se hace esto? Tirado:

He comentado antes que los parámetros se ponen separados por comas. Si el JES2 encuentra una coma y luego un espacio en blanco, asume que los parámetros siguen en la siguiente ficha, en algún lugar de entre las columnas 4 y 16. Se recomienda que la continuación la escribamos a partir de la columna 16, porque queda mas tabulado. Así que si queremos poner más parámetros, basta con añdir en la segunda línea, en los caracteres 1 y 2, las // y continuar en la columna 16, quedando el tema tal que así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1)

Tenemos 2 sentencias nuevas:
- MSGCLASS que indica en qué cola donde dejar los mensajes que genere la ejecución del JCL
- MSGLEVEL que, mediante dos sub-parámetros posicionales (van entre paréntesis) que como veis se separan asimismo por comas, indican que queremos todos los mensajes JCL que se generen en el sistema (el primer 1) y luego, que también queremos los mensajes relacionados si reservamos o “allocamos” ficheros que vaya a usar el job (el segundo 1). Si sólo quisiéramos los mensajes JCL, el MSGLEVEL quedaría así: MSGLEVEL(1,0). De todas formas, yo recomiendo siempre que salga todo (de hecho, en el trabajo diario, la ficha job yo suelo usar siempre la misma copiando y pegando).

Por finalizar, si ya no hay comas detrás del último parámetro, el parser da por terminada la sentencia JOB.

Ahora, vamos con la ejecución del programa. Cooooño! Que no hemos escrito ningún programa! Bueno, afortunadamente IBM nos proporciona cientos de programas con el SO MVS, así que vamos a hacer uso de ellos. Estos programas se llaman UTILITIES en la jerga maiframera. De modo que haremos uso de una utility muy socorrida, llamada IEBGENER. Esta utility, entre otras muchas cosas, copia datos de un sitio a otro. O copia datos a un fichero, vamos, copia cualquier tipo de cosas.

Nuestro JCL quedaría así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1) 
//PASO1   EXEC PGM=IEBGENER

Claro, esa utility necesita ser colocada en memoria. Como ignoro lo que ocupa, vamos a decirle al JCL que por lo menos el JES2 reserve 256KB de RAM para que dicha utility pueda ser ejecutada. Eso se consigue de dos maneras:
1.- Añadiendo REGION=256K a la línea en la ficha JOB, con lo que todo el job tendrá 256 KB de RAM para su uso.
2.- Añadiendo REGION=256K al paso en cuestión, con lo que si nuestro job tuviera mas de un paso, cada uno podría tener su propia memoria reservada.

Pero para no marear la perdiz, añadiremos al todo el job la reserva de memoria. Así pues, nuestro job quedará así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER

Como antes, comentaremos esta nueva ficha: PASO1 es un nombre opcional para el paso de ejecución, y le aplica las mismas normas de nomenclatura que para el nombre del JOB.

Respecto al IEBGENER, tiene una serie de parámetros que se deben introducir, como por ejemplo de donde copiar, a donde copiar, donde introducir parámetros especiales. Para ello, si vamos al manual de MVS System Utilities, y buscamos IEBGENER, nos encontramos con los siguientes parámetros que utiliza esta utility:
- SYSUT1: Desde donde vas a copiar.
- SYSUT2: Hacia donde copias.
- SYSIN: Parámetros adicionales que le quieres meter para condicionar la copia
- SYSPRINT: Salida del resultado de esos parámetros adicionales.
En definitiva, IEBGENER copia lo que esta en SYSUT1 a SYSUT2 de acuerdo a los parámetros de SYSIN dejando el resultado de esos parámetros en SYSPRINT.

Por lo tanto, debemos meter esas 4 definiciones de datos en nuestro JCL. Para ello, haremos uso de la sentencia DD (Data Definition). Como he dicho mas arriba, DD es como el “var” de Pascal para definir las variables. Así que vamos a actualizar nuestro JCL, añadiendo también las dos // del final del job:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER 
//SYSUT1   DD  *
Hola, Mamones!!
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Atentos a este tema que es el quiz de la cuestión: Como no disponemos de un fichero cuyo contenido sea Hola Mamones, hay una manera de incluir ese dato dentro del propio jobstream o flujo del job: Utilizando la definición DD *. Cuando el parser se encuentra con esta construcción, asume que las siguientes líneas serán el contenido del dato en si, que dejara en SYSUT1.  ¿Cuando termina de asumir que las siguientes fichas son datos? Cuando se encuentra con un /* o una ficha nueva del job que se verá en seguida porque aparecen las // en las columnas 1 y 2. De hecho, es más bonito visualmente que el job quede así, añadiendo ese /*:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  * 
Hola, Mamones!!
/* 
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Entonces, si podemos añadir el dato directamente en el job, podríamos hasta hacer un banner y todo:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  *
**********************************************************
*                                                        *
*                       Hola, Mamones!!                  *
*                                                        *
********************************************************** 
/*
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Pero, ¿ que pasaría si en lugar de asteriscos, queremos poner todo /? El parser se volvería loco porque interpretaría que las siguientes fichas son sentencias de JCL, dando un error como una catedral. Pues hay un truco: Si en lugar de poner DD * se pone DD DATA, todo lo que encuentre desde ahí hasta el /* para él será un dato. Por lo que nuestro job podría quedar así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DATA
/////////////////////////////////////////////////////////
//                                                     //
//                      Hola, Mamones!!                //
//                                                     //
///////////////////////////////////////////////////////// 
/*
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Ya os veo venir con la siguiente pregunta:  ¿Y que pasa si en algún lugar de nuestro dato, aparece un /*? Pues que también esta previsto, cacho-perros!! xDDDD En ese caso, se crea un delimitador con una serie de caracteres que sabes que no se van a repetir en todo el job (por ejemplo, $$ o <>), con la sentencia DD DATA DLM=$$ o DD DATA DLM=<> quedando el tema así:

         1     1   2         3         4         5         6         7 7
----+----0----+6---0----+----0----+----0----+----0----+----0----+----0-2
//MAMONJOB JOB GODDESS,URD,CLASS=A,MSGCLASS=A,
//             MSGLEVEL=(1,1),REGION=256K
//PASO1   EXEC PGM=IEBGENER
//SYSUT1   DD  DATA,DLM=$$ 
/*******************************************************/
/*                                                     */
/*                      Hola, Mamones!!                */
/*                                                     */
/*******************************************************/
$$ 
//SYSUT2   DD  SYSOUT=A
//SYSPRINT DD  SYSOUT=A
//SYSIN    DD  DUMMY
//

Contentos? xDDD Con esto, ya hemos definido lo que queremos copiar en SYSUT1.

Pero seguimos explicando el JOB: SYSUT2 tiene una Data Definition que apunta a la clase de salida A (SYSOUT=A), lo mismo que SYSPRINT. Es decir, que el resultado se escribirá en la salida de la clase A. Y por último tenemos una ultima DD que es SYSIN, y que en este caso hay un DUMMY. DUMMY se pone cuando no quieres poner parámetros adicionales. Es como poner /dev/null. Por lo tanto, es de esperar que SYSPRINT no escriba nada en la cola A ya que no hay ningún SYSIN.

Entonces, que hará este job? Usando IEBGENER, copiara el banner que está definido en SYSUT1 y lo dejará en SYSUT2, que, como esta última variable tiene asociada una clase de salida, dejará el banner en esa salida. Y, si tenemos una impresora asociada a esa clase, imprimirá dicho banner. Tirado, ¿no?

Hasta aquí el primer capítulo de JCL. En el siguiente explicaré como se tratan los ficheros o datasets, basándonos en este ejemplo del JCL (si tenéis un host a mano o habéis instalado el MVS freeware, probad este job, porque funciona).

Como montar en tu PC un mainframe con MVS

El texto de a continuación explica como montar un sistema host usando tu PC. Esto se consigue utilizando un emulador completamente GNU Open Source y por lo tanto libre, llamado Hercules, que emula con pelos y señales toda la estructura y el juego de instrucciones del host mas actual.

Evidentemente, tiene sus limitaciones, ya que no hay elementos hardware externos ni se tiene soporte a ciertas funcionalidades de los hosts actuales (Parallel Syxplex, LPAR), pero para montar un sistema robusto y fiable, que además nos sirve para jugar con el y aprender, va que chuta.

Por cierto, la web del proyecto es el punto de partida para entender el emulador Hercules, y como veréis, se puede instalar en muchas plataformas. Echadle un ojo.

De hecho, emula casi todos los periféricos que se pueden conectar a un host, desde unidades de cinta, pasado por discos, terminales y hasta impresoras.

¿Cómo se hace esto?

Emular un disco es como emular un CD bajo linux: si tu grabas un fichero ISO y lo montas en un directorio, al entrar en el es como si estuvieras accediendo al CD realmente. Pues aquí pasa lo mismo. Si un disco 3390 modelo 3 ocupa 2,8 GB realmente, un disco 3390-3 emulado en Hercules no será más que un fichero que ocupe 2,8 GB, ni mas ni menos.

Con las cintas, pasa lo mismo: Si grabo una cinta, realmente me está creando un fichero donde se almacenara el contenido de esa cinta y lo que es interesante, si tengo otro Hercules en otra maquina, puedo copiar ese fichero “cinta”, montarlo en la unidad de cinta emulada correspondiente y leer de ella, al igual que pasa con los mainframes reales. Y es mas, luego existen utilidades libres que cargándolas en un host real, puedes hacer que ese fichero de cinta emulada la puedas convertir en real y viceversa, es decir, que podrías pasar información de un Host real a un Host emulado sin mayores problemas.

¿Las impresoras? Parecido, solo que en vez de imprimir, generan un TXT con la información impresa. Interesante, ¿no?.

Con los terminales, es algo mas complejo, pero basta que sepáis que Hercules utiliza la pila TCP/IP para encapsular todo el tráfico SNA del host, por lo que al conectarte al puerto correspondiente (mas abajo lo explicaré) por un emulador 3270 a la IP del equipo que tiene Hercules, adquiere una dirección “lógica” como si de un terminal físico real se tratara. No sé si me he explicado bien.

El tema quizás más importante es la configuración que le debemos pasar a Hercules para que emule una instalación completa. Esto incluye unos discos donde resida el SO y las diversas utilidades (a este conjunto de discos, se les llama “residentes”), algunos discos de trabajo donde guardar nuestros datos (ya que no es recomendable mezclar datos y sistema, de la misma forma que no es recomendable meter documentos y demás en el directorio Windows), y luego ya unos periféricos de E/S que pueden ser cintas, troqueladoras de tarjetas impresoras y terminales. Una vez sabido que necesitaremos, crearemos un fichero llamado hercules.cnf que contendrá un contenido similar al que pongo de ejemplo:

CPUSERIAL 000000 # CPU serial number
CPUMODEL 9672 # CPU model number
MAINSIZE 768 # Main storage size in megabytes
XPNDSIZE 0 # Expanded storage size in megabytes
CNSLPORT 23 # TCP port number to which consoles connect
NUMCPU 1 # Number of CPUs
TZOFFSET +0100
OSTAILOR OS/390 # OS tailoring
PANRATE FAST # Panel refresh rate
# Architecture mode S/370, ESA/390 or ESAME
ARCHMODE ESAME
# Allow OS/390 and Z/OS systems to run
PGMPRDOS LICENSED

#
# IPL parameter
#
LOADPARM 0A82DC..
# Device list
#--- ---- --------------------
0500 3490 *
0560 3480 *
0700 3270
0701 3270
0702 3270
0900 3270
0901 3270
0A80 3390 /ZOS14/s4res1.a80
0A81 3390 /ZOS14/s4res2.a81
0A82 3390 /ZOS14/os39m1.a82
0A83 3390 /ZOS14/s4db21.a83
0A84 3390 /ZOS14/s4cic1.a84
0A85 3390 /ZOS14/s4dis1.a85
0A86 3390 /ZOS14/s4dis2.a86
0A87 3390 /ZOS14/s4uss1.a87
0A88 3390 /ZOS14/s4dis3.a88
0A89 3390 /ZOS14/s4ims1.a89
0A8A 3390 /ZOS14/s4was1.a8a
0A8B 3390 /ZOS14/s4was2.a8b
0A8C 3390 /ZOS14/sares1.a8c
0A8D 3390 /ZOS14/s4dis4.a8d
0A8F 3390 /ZOS14/saipl1.a8f
0E20 3088 CTCI 10.10.10.1 10.10.10.2

Son importantes los siguientes parámetros:

* MAINSIZE: que será el tamaño de memoria RAM que asignaremos a nuestra máquina host, muy parecido a cuando configuras un VMware. Por cierto, aunque tengas 1 GB de RAM, no poner de MAINSIZE 1024 ya que de lo contrario, Hercules copará toda la RAM de la máquina y eso obligará al SO a paginarse, por lo que nuestro rendimiento del PC caerá en picado. Es por ello que es mejor poner 768 MB, así por lo menos dejas 256 MB al SO y no habrá problemas de ningún tipo.
* CNLSPORT: es el puerto donde nuestra emulación 3270 se conecte al mainframe. Como aquí no tenemos unidades de control ni líneas físicas, Hercules se encarga de utilizar TCP/IP para emular la infraestructura de comunicaciones, encapsulando el tráfico SNA dentro de TCP/IP. Así pues, si hemos puesto el puerto 23, cuando el Hercules este arrancado y ejecutemos nuestro emulador dentro de la misma maquina, nos conectaremos vía IP “localhost” y puerto 23.Esto hará que esa emulación adquiera la dirección 0700 (es decir, que nuestra pantalla emulada emulara un terminal real cuyo IODEVICE es 0700, el primero del fichero de configuración). Si abrimos una segunda ventana de terminal, según nuestro fichero de ejemplo, esa emulación nueva tendrá la dirección 0701 y así sucesivamente.
* LOADPARM: es aquel parámetro de carga que condiciona el arranque del mainframe. En la entrega anterior del proceso de arranque de un mainframe, lo explique, así que no entraré en detalles.

Y por ultimo, comentar las líneas por debajo del device list. Imagino que recordaréis que cada dispositivo host tiene una dirección hexadecimal única, llamada IODEVICE.

Pues bien, la lista que se muestra, es la configuración IODF que tiene nuestro mainframe, de tal modo que en la dirección 0560, tenemos una unidad de cintas “emulada” IBM 3480, en la dirección 0700, tenemos un terminal emulado, en la dirección 0A80 tenemos un disco 3390, etc.

En nuestro caso, como partiremos de un MVS ya generado, es decir, que no crearemos nuestro propio SO como si de una Gentoo se tratara (que se puede hacer, pero creo que a estas alturas no tenemos todos la formación suficiente para enfrentarse a un periplo de ese pelo), es importante saber que IODF tiene ese MVS ya que debe coincidir con la configuración del Hercules.

En el ejemplo del fichero de configuración, he puesto un ejemplo sacado de Internet de cómo configurar un z/OS (antes llamado MVS) V1R4 bajo Hercules, y las direcciones que están reflejadas ahí de los discos, cintas y terminales, son las que tiene el z/OS cuando se generó.

Es decir, que si el z/OS se genero con el disco residente en la dirección A80, poner una dirección a boleo en nuestro fichero de configuración hará que en el arranque, el z/OS de un casque estrepitoso porque su configuración y la real no coinciden. De hecho, con los terminales, peor: Si el MVS sabe que la master Console esta en el 700 y vamos nosotros y configuramos un terminal en el 800, pues no podrá dejar un mensaje y eso dará otro casque. Así que mucho cuidado con este tema.

Abordaremos esta instalación desde 2 puntos distintos, según el SO que tengas instalado. Si es Windows, va a ser algo más fácil que en Linux, pero en Linux dará mas rendimiento (sobre todo si andamos con una máquina justilla).

Antes que nada, hay que saber que equipo PC tenemos y que utilidades necesitaremos. Uno de los primeros requisitos es saber que potencia tenemos, que RAM, etc. El emulador Hercules en sí no consume nada, lo que consumirá será el SO que estará ejecutándose bajo el, por lo que teniendo en cuenta que un host de por si es muchísimo mas potente que un PC, en función de la potencia de nuestro PC puede que el SO host vaya arrastrándose.

Pero tratándose de el SO MVS que instalaremos, que data del año 1975, un Pentium III a 500 MHz con 128 MB de RAM dará un buen rendimiento. Además, solo nos conectaremos nosotros, así que tampoco hará falta mucho mas. Eso si, si conseguís por la mula el último z/OS, estar seguros que si no tenéis 1 GB de RAM y un Pentium 4 a 3,6 DualCore como requisito mínimo, os tardara unos 20 minutos en arrancar y luego será un sistema con una lentitud tan lenta que darle al Intro será una tarea de ir a tomarse un café hasta que responda y tal.

Desde el punto de vista software, únicamente hace falta un emulador de terminal por el que nos conectaremos al Hercules como si fuera un terminal de toda la vida de un host. Atentos a este tema, porque no nos vale con el putty, el emulador de terminal DEBE ser un emulador 3270, NO un telnet común y corriente. Según el SO donde queramos instalar el hercules, tendremos varios para elegir:

En Windows, existen programas como el Vista3270 que te puedes bajar de internet, pero la verdad, no he visto mejor emulador en mi vida que el IBM Personal Communications, aunque es de pago.

En GNU/Linux, en cambio, el c3270 para consola o el x3270 para las X-Window también están muy bien y da una respuesta y fiabilidad increíbles. Y con esto, ya estamos en condiciones de instalar el emulador sin problemas.
Instalación del Hercules

Si es Windows, con descargarlo sobra. Si es GNU/Linux, tienes que bajarte las fuentes y te lo compilas. En mi web, hay un artículo sobre como instalarlo bajo Debian.

Instalación del MVS bajo Hercules.

Existe en la actualidad un sistema MVS versión 3.8J que IBM liberó hace algunos años, por lo que es freeware y por lo tanto, completamente legal. Se puede conseguir de cbttape.org y generártelo como ya he dicho, pero un maromo llamado Volker Bandke ha creado un CD con todo el sistema MVS generado y grabado en discos emulados con un .bat que te instala todo, incluido el Hercules.

Si tenéis Windows, esta opción os instala todo a la vez, incluidos emuladores 3270, así que recomiendo el CD para empezar a tomar contacto con el tema mainframe. Además, como este CD instala TODO lo necesario, no te tienes que preocupar por nada. Su web es http://www.bsp-gmbh.com/hercules/ y se ha currado un manual y todo paso a paso de cómo entrar en la máquina.

También ha explicado cómo hacer la instalación bajo linux, también de manera muy sencilla.

En Google si buscáis por MVS TURNKEY SYSTEM, os conducirán a varias webs donde está disponible la ISO de ese CD para su descarga.

Una vez instalado el CD (la opción full, porque puedes elegir que te lo genere pero son mas pasos y mas complejos), tendréis una serie de discos emulados en ficheros y una serie de cintas emuladas también, así como el hercules.cnf que estará configurado de acuerdo al IODF del MVS.

Únicamente tenéis que tener en cuenta dos cosas:

1. Cuando Hercules se inicia, debéis abrir siempre un emulador 3270, ya que de lo contrario, no se creará un enlace con el Master Console (este tema ya lo comente en anteriores entregas) y el MVS dará un error quejándose por ello. Y claro, si solo abrís una emulación, pues el MVS ya no se quejará pero cuando queráis conectaros al VTAM para usar el TSO, no podréis hacerlo ya que como en la inicialización del VTAM no habían terminales disponibles, deberíais abrir otro terminal y luego desde el Master Console hacer un Vary online del terminal.Por tanto, es mas sencillo que a la vez que ejecutéis el Hercules, pero antes de hacer IPL al Hercules y cargar el MVS, que tengáis abiertos 2 ventanas de emulación (mínimo) que cojan las direcciones 0700 (Master Console) y 0701 (VTAM, TSO) según el ejemplo del fichero que he puesto y así no tendréis problemas.
2. En la web explica como responder a ciertas preguntas en la Master Console cuando se inicia el MVS, como el tipo de arranque, formateo de los HASPACES del JES2, etc. Recomendable tenerlo abierto cuando estéis iniciando por primera vez el tema.

Y una vez en la pantalla Welcome del terminal, con poner un LOGON IBMUSER y luego en el prompt READY, escribir RPF (un editor de ficheros y demás) podréis empezar a jugar. La web también describe muy bien el proceso.

Y creo que eso es todo. Solo me queda animaros a conocer este interesante mundillo de informática REAL y no de juguete como todo lo relacionado con los PCs ;)

Proceso de Arranque de un Mainframe

En otras entregas, he explicado el hardware, un poco como está constituido el SO, sistemas de ficheros y demás. Ahora, trataré de explicar el proceso de arranque que sigue la máquina desde que enciendes el interruptor hasta que ves un terminal con el logo de tu empresa invitándote a hacer login en un terminal 3270.

En primer lugar, debemos conocer como se llega al bootstrap o texto IPL de la maquina, o lo que es lo mismo, el sector de arranque de un disco duro de un PC. En un PC, la BIOS se encarga de buscar el sector 0 del disco duro que estará configurado como Primary Master (si el sistema es IDE). Una vez encontrado el sector 0, este sector tiene un puntero que apunta hacia un fichero donde a su vez carga un microprograma y punteros a otros ficheros del sistema. En sistemas Host, el proceso es algo mas complicado, pero básicamente se parecen mucho.

Cuando te plantas delante de un z/Series, y pones en la posición I el interruptor rojo que queda a la izquierda del frontal, lo primero que ocurre es que las fuentes redundantes de alimentación dan corriente de partida a todos los bastidores. No es que se encienda toda la máquina, solo envía una corriente de 5 V para que se verifique que la corriente llega a los bastidores y que todo esta alimentado (es lo que se denomina el DC GOOD).

En ese preciso instante, esta corriente también empieza a alimentar el portátil (en los z/Series, llevan 2 por redundancia) también llamado Support Element. Dicho portátil viene a ser una Mega-BIOS de la máquina, y carga un programa con ventanitas e iconitos que gestiona el microcódigo del z/Series, comprobando todos los componentes configurados. Una vez comprobados, este portátil envía una señal que inicia la secuencia de encendido (si lo tienes configurado así, porque también puedes hacer doble clic sobre el icono del “enchufe” y se enciende), y en ese momento ya se pueden oír los ventiladores a plena potencia evacuando el aire de los bastidores, y las luces de los bastidores en verde indicando que ya tienen corriente.

Una vez finalizada la secuencia de encendido, el Support Element inicia lo que se denomina Power On Reset. Este proceso es como pulsar al Reset del PC, que lo que hace es que lee la configuración de canales (en una entrega anterior os comente como se definían) almacenada en el Support Element (IOCDS) y realiza una segunda comprobación de los elementos que han sido definidos, con el fin de comprobar que todo lo configurado ha sido inicializado correctamente.

En este punto también se entraría en la definición de las LPAR (particiones lógicas), pero no entrare a comentarlo por no complicar más las cosas, así que supondremos que nuestra maquina esta en modo nativo o Single Image (es decir, una sola maquina con un solo SO).

Cuando el Power On Reset finaliza, estamos en condiciones de hacer una IPL (Initial Program Load) de la máquina. Para ello, pinchamos sobre el icono Load del Support Element, y esto hace que nos abra una ventana con dos valores fundamentales: La dirección de carga y los parámetros de carga.

La dirección de carga es la dirección hexadecimal compuesta por 4 dígitos del disco donde está el bootstrap o texto IPL del SO. Como un host no tiene unidad C o D, sino cientos de discos, todos ellos se configuran con un IODEVICE (que ya explique en otra entrega anterior) que es una dirección hexadecimal única en la instalación. Así que para realizar la carga, se introduce esta dirección, para que vaya a la pista 0 del volumen de disco a buscar el bootstrap.

El segundo valor que hay que introducir es el de parámetros. Es un valor de 8 caracteres. Los primeros 4 se utilizan para poner la dirección del volumen de disco donde reside el IODF, o lo que es lo mismo, el dataset de la configuración de canales y dispositivos de la máquina cuya copia ha utilizado el Support Element para hacer el Power On Reset como he explicado antes.

Los dos caracteres siguientes, indican el miembro LOADXX dentro del dataset SYS1.IPLPARM que se llamará en tiempo de IPL (el miembro LOAD es un miembro de configuración inicial y puedo tener los que quiera para tener perfiles de arranque configurados -algo así como tener un menú de inicio de Windows y elegir con la tecla F8 distintos perfiles-) y por último quedan otros dos caracteres: Uno es para configurar el IMSI, o lo que es lo mismo, el numero de mensajes informativos que deseas que aparezcan en la Master Console en la inicialización de la maquina, el otro carácter que queda define el núcleo alternativo de arranque en caso de que el primero casque.

Un apunte sobre los miembros de LOAD: Puedo tener tantos como quiera, y cada uno lo puedo configurar como me de la gana. Estos miembros tienen, entre otros datos, el nombre del Catalogo Maestro y en que disco reside (recordar que el Master catalog tiene la información de todos los datasets del SO), el disco donde esta el IODF de configuración, y el sufijo a usar dentro de los miembros de la SYS1.PARMLIB (dataset donde hay docenas de miembros de configuración), que generalmente coincide con el miembro LOADXX cargado.

Por ejemplo, puedo tener un LOAD01 que me cargue una configuración y un LOAD02 que me cargue otra. Así pues, podría tener dos catálogos y arrancar la maquina de una forma completamente distinta en función del miembro LOAD que cargue cada vez. Vamos, es tan configurable que llega a hacerse muy complicado si no tienes las ideas muy muy claras.

Después de este lío, bueno, volvamos al portátil del z/Series y hacemos click en Load. El proceso es el siguiente: Con la dirección hexadecimal del volumen donde esta el bootstrap, la maquina accede al cilindro 0 y pista 0 y lee el bootstrap, colocándo dicho contenido en memoria y luego pasándole el control. Este, a su vez se encargará de la tarea de colocar todo el SO en memoria. Para ello, lee el miembro IEAIPL00 denominado comúnmente como “texto IPL” y a partir de aquí empiezan a sucederse una serie de historias:

1. Pone a ceros binarios la burrada de Gigas de RAM de la máquina y luego define las regiones de memoria donde se colocará el Master Scheduler (explicado en otra entrega anterior).
2. Busca el dataset llamado SYS1.NUCLEUS donde reside el mega-kernel del SO y carga una serie de programitas llamados IRIMs (IPL Resource Initialization Modules) para empezar a cargar en memoria el SO. Estos IRIMs pillan el dato de los parámetros (esos 8 caracteres explicados mas arriba) que le hemos pasado en tiempo de carga y buscan los miembros LOADXX dentro del dataset SYS1.IPLPARM, y si los encuentra, los carga.

Como los miembros LOADXX tienen los sufijos, lo siguiente que hace es irse a la SYS1.PARMLIB y construir una configuración en memoria leyendo el miembro IEASYSXX. Este, a su vez, llamara a PROGXX, y a muchos más miembros de configuración para tener listo toda la configuración de los subsistemas que luego serán cargados. Por ejemplo, se leerá el miembro JES2PARM y se cargará en memoria el JES2, y así con el resto de los productos. También se leerá la configuración del VTAM, TSO, RACF, CICS, DB2, etc, hasta que por tu terminal aparezca el login de pantalla.

Explicar la carga de estos productos sería escribir más entregas, así que si queréis mas información, os recomiendo que os leáis los ABCs que están en mi web si tenéis mas curiosidad.

Creo que hasta aquí he llegado. No se me ocurre que mas explicar sin entrar en las particularidades concretas de cada producto, así que lo que me queda por comentaros es que si tenéis inquietudes, y estáis interesados, podría escribir un último texto sobre cómo montar en tu propio PC, un sistema MVS usando un emulador llamado Hercules, y ver como recrear un sistema en explotación con JES2, TSO, JCLs, etc.