Sunday, December 20, 2020

Writing a REXX program to copy Mainframe tape dataset to DASD

Hiya! ๐Ÿ‘‹ Welcome to my blog. In this post, let's look at a REXX (not T-REX๐Ÿฆ–) code which will take a Mainframe tape dataset name as input and let you copy it to a DASD dataset.  

Mainframe tape datasets  can't be browsed like a PS dataset. To view the contents of the tape dataset, we need to copy the tape dataset to a DASD first. 

I hope this post will be of help to those who don't have prior experience in writing REXX programs. If you read till the bottom of this post, you'll get a fair idea on the following stuff:

  • Usage of REXX in IBM z/OS. 
  • Defining File-tailoring skeletons. 
  • Addressing environments in REXX. 
  • Built-in functions.
  • IF/THEN/ELSE instructions in REXX. 
  • Invoking REXX Exec.

A short intro about REXX:

  • REXX is a programming language that was developed by IBM. 
  • REXX is easy to learn and use. To prove this, let's write the traditional Hello World๐ŸŒ program in REXX.

    • Create a new member in your own PDS

      The first line of the REXX program SHOULD be a comment (delimited by /* and */) and it must contain characters  'REXX' in it. say in the second line is a REXX function that is used to print messages to the console.

      Save the member and press F3 to go back to PDS members list view. In the line command area, type ex to execute the member.

      No caption needed๐Ÿ˜Ž

  • REXX is very readable, REXX instructions are based on English. 
  • REXX has powerful set of built-in functions. 
  • REXX is an interpreted language that does not require compilation. Each line of code is checked and interpreted into "machine understandable code" before being executed. 
  • REXX runs in all MVS address spaces and on many platforms (there's one for Android too ๐Ÿ‘€). 
REXX is a program langauge swiss army knife for z/OS System Programmers and System Administrators.  

I guess it's enough talking about REXX. Let's begin writing ✍ the program itself. The entire program is shown below and we will go through the program section by section. 


Lines 1 thru 16: 

In REXX, comment is a sequence of characters delimited by /* and */. The first 16 lines are comments and they tell the modifications that have been made to this code right from the creation of this REXX Exec. Wait, What is a REXX Exec? (you may be asking yourself ๐Ÿค”). 

Well, a REXX Exec contain REXX language instructions plus commands that are executed by the host environment. 

๐Ÿ’กIBM recommends that all REXX execs start with a comment that includes the characters 'REXX' within the first line (line 1) of the exec. Failure to do so can lead to unexpected or unintended results in your REXX exec. 


Lines 17 thru 19: 

One of the strengths of REXX is that you can use it to invoke the functions of other products. ADDRESS command temporarily or permanently changes the destination of the commands that are followed next. Commands are strings sent to an external environment. 

๐Ÿ’กADDRESS statement is more similar to sudo (Switch User and DO this) command in Linux, which will make you the root user briefly to perform root user actions like installing a package. 

To use DB2 commands in REXX, you must first address that environment. Likewise, in Line #17, address ispexec is coded to execute the command in line #18 in ISPF environment, the full panel application that we all are addicted to๐Ÿ˜€. 

LIBDEF command is used to define the application-level libraries that will be in effect when the application is running. In Line #18, we use the LIBDEF command to define the Skeleton Library ๐Ÿ’€. Gotcha! A Skeleton library is used to store skeleton files. A Skeleton file can be a JCL like the below: 


This is pretty much a simple job that has a SORT step in it to copy an input dataset, provided in SORTIN DD statement, to the output dataset, defined in SORTOUT DD statement. But, something here is so weird, isn't it?๐Ÿค” You see a lot of ampersands, don't you? Let me list out all the variable names preceded by an ampersand. 

  • &JNUM
  • &DSNME
  • &USRID
  • &MON
  • &DY
  • &HH
  • &MM
  • &SS 

There isn't an input dataset name defined in SORTIN. Instead, you've got &DSNME. You'll be issued with JCL errors when you manually submit this JCL. So, please don't do that ⚠. 

This JCL is meant for REXX. When our REXX exec is running, this skeleton file will be scanned record-by-record by the File-tailoring services in REXX (more about File Tailoring services will be covered later in this post). Each record will be scanned to find any dialog variable names, which are names preceded by an ampersand. When a variable name is found, it's current value is substituted from the REXX exec. 

๐Ÿ’กA skeleton file can be assumed as a template that is non-functional on its own. File-tailoring services read skeleton files and write tailored output that can be used to drive other functions. 

Do you get the whole picture now? We'll use File-Tailoring services in Rexx to substitute values in all those variable names, preceded by an ampersand, in the skeleton file. We'll then have a functional JCL which upon submission will copy the input dataset to the output dataset. Simple! ๐Ÿ˜Ž  


Line #19 uses the ARG instruction. ARG retrieves the argument strings provided to a program or internal routine and assigns them to variables. For example, if you pass the string "IBM z/OS" to the statement, arg company product, then

company contains 'IBM'

product contains 'z/OS'

Line #19 is used to pull the input dataset name and assign it to the variable, dsn.


Lines 20 thru 26 in REXX exec: 

We need an input tape dataset name for the REXX exec to copy it to an output dataset. When the REXX exec is invoked without an argment (i.e., an input dataset name), we should tell the user to invoke the REXX exec with an argument and exit the exec. Line 20 thru 26 does that with the help of say instruction in REXX. 

IF/THEN/ELSE instruction in REXX is used to vaildate the dsn variable. When you have more than one statement under an IF condition, enclose them between a DO and END. 


Lines 27 thru 41 in REXX exec: 

The else part starting from line #27 is executed when the user has invoked the REXX exec with an argument. 

STRIP() is a built-in function in REXX and it is being used in line #29 to remove the leading spaces and trailing spaces as well as single quotes (if any) that surrounds the input argument. The stripped value is assigned to variable, a.

RANDOM() is a built-in function in REXX and it is being used in line #30 to generate a random non-negative whole number between the min and max range that are provided as arguments i.e., 001 and 999 respectively. The random number is assigned to a variable JNUM and this variable name, preceded by an ampersand, is present in the first line of the skeleton file. The usage of RANDOM()function in this exec ensures that the job name is unique every time when the output JCL is created by the file-tailoring services. 

USERID() is a built-in function in REXX and it is being used in line #31. This function returns the TSO User ID. Note that the return value of the function is being assigned to variable that is used in the Skeleton file. 

In line #32, the stripped input dataset name available in the variable a is being assigned to the Skeleton file's variable, DSNME

Pause! Let's do a status check to see where we are right now ✅. 

Till now, we only have a valid job name and an input dataset name to be assigned to the JCL in the skeleton file. We need an output dataset name. The output dataset name should be unique every time when the REXX exec is invoked. To do that, we will be adding the Date and Time values at the last 2 qualifiers of the dataset name. At the time of writing this line, I invoked the REXX exec against a dataset and got the output dataset name as 'Z01071.TAPE.COPY.Z01071.DDEC20.T053624'. The last but 1 qualifier has got the date as 20th December and  the last qualifier contains the time in Thhmmss format. Let's continue. 

In line #33, a variable upper is assigned with sequence of characters from a to z in upper case and in line #34, a variable lower is assigned with sequence of characters from a to z in lower case. We'll be using these variables as argumens to the TRANSLATE() built-in function in REXX.  

In line #35, two built-in functions are used. SUBSTR() and DATE(),

DATE() function returns the local date in dd mon yyyy format by default. When 'U' is passed as an argument to the DATE() function, the local date will be returned in MM/DD/YY format.   The return value from the DATE() function is being used as the string for SUBSTR() function. SUBSTR() function is used to extract a portion of the string. Therefore, line #35 is used to extract the MM from the date string (in MM/DD/YY format) and value is assigned to a variable named m

The CALL instruction in line #36 calls an internal routine, find_month


This routine uses the SELECT instruction in REXX to choose one of the 12 months based on the value in variable, m. return statement at the end of the routine, returns back the control to the line next to CALL instruction. 

Line #37, translates the month value from upper case to lower case using the TRANSLATE()built-in function.  

I hope you will be able to decode the lines 38 thru 41. They use the DATE(), TIME() and SUBSTR() functions to assign values to variables that are used in the Skeleton file.

At the end of line #41, we would have assigned values to all the dialog variable names, preceded by an ampersand, in the skeleton file. It's now time to use the File-tailoring services of REXX to write a tailored JCL that can be edited and submitted by the user. 

Lines 42 thru 47:

File-tailoring services:

To use the file-tailoring services, we must first address the ISPEXEC environment. After addressing the environment, the host environment's commands are passed as strings from the lines 43 thru 47. 

  • FTOPEN TEMPPrepares the file-tailoring process and specifies whether the temporary file is to be used for output. 
  • FTINCL TCSKEL - Specifies the skeleton file named TCSKEL from the skeleton library to be used and starts the file tailoring process. The skeleton file is read record-by-record and the dialog variable names in the skeleton file are assigned with values from the variable pool created by the REXX exec.
  • FTCLOSE -  Ends the file-tailoring process. 
  • VGET ZTEMPF - The file tailoring output is directed to a temporary sequential file. The file name of the temporary file is available in the system variable ZTEMPF. Before editing or submitting the job from the temp. dataset, the VGET service should be invoked to initialize the ZTEMPF.  

In line #48, we have issued EDIT dataset command so that the temporary file will be opened in edit mode for the user. The user after checking the JCL can submit the job by issuing SUB command. 

Invoking the REXX exec:

There are several ways of invoking a REXX exec from foreground or background. 

  1. Easiest of the lot is, after saving a REXX exec, you can issue an ex  command on the member to execute the exec in foreground mode. 
  2. When you want to use the member name of the REXX exec as a line command, you must first allocate your private PDS that contain the REXX Exec(s) to the system libraries SYSEXEC/SYSPROC. Refer the first step in this link for more details on how to do that. You might have to copy paste the REXX exec to your PDS and provide you REXX PDS in the DATASET parameter of the ALLOC command. After the allocation, when you type the member name of a REXX exec, you're private PDS will be scanned by the system and the REXX exec will be executed if it's found in the PDS. 
Only option 2 works with the REXX exec that we've prepared. Invoking the exec will be lot easier when you allocate your private PDS to the system libraries. Let's see how our REXX exec fares out.

First, let's assign the private PDS to system library. 

Note that I've provided my PDS in the DATASET parameter of ALLOC command. 

Type ex on the member to execute the REXX exec. 

Allocation successful! Now, we can use the member name TC as a line command.


Type tc on a tape dataset or a DASD dataset that you want to make a copy. 


Tada! Here is the file tailored output. You may type SUB on the command line to submit this job. You may also type some SORT statements before submitting the job.  


Thx for reading. Please share your thoughts in the comments section. I would be glad to answer to your questions if any. Happy Holidays! 



Tuesday, November 10, 2020

My take-away on Interskill eLearning Course: Java on z/OS

I had enrolled for Java Programming curriculum in Interskill learning platform. The very first course was 'Java on z/OS for Java Programmers'. Despite having no prior experience with Java, I gave this course a shot and it was worth the time spent ๐Ÿ˜€

In this blog post, I wanted to write about my take-aways from the course; the very first take-away being a digital badge ๐Ÿฅ‡ issued by Interskill upon the successful completion of the module.


What I learnt? ๐Ÿค”

The course starts with some intro about Java on z/OS. 

  • Java source code can be compiled into Bytecode
  • Bytecode is portable such that all it needs to run is Java Virtual Machine (JVM). 
  • JVM is a container in which Java bytecode is executed. All Java programs must execute in a JVM.
  • We do have a JVM on IBM z/OS so that Java can also be used on z/OS. 
  • Java on z/OS uses features provided by z/OS Unix.
  • The basic Java support on z/OS is provided by the Java Software Development Kit, or Java SDK. This is a package provided at no charge by IBM. It includes the basic Java standard edition (Java SE) features including compiler, JVM, and more. 
  • There are several versions of the z/OS Java SDK and all the versions are equivalent to the same version provided by Oracle on other platforms. 
  • More than one Java SDK can be installed at the same time on z/OS. 

How Java executes on z/OS?

Interskill provides an interactive window where you can type some commands and view the results. However, the commands that you can type are LIMITED and appropriate to an environment. It serves more of doing an hands-on rather than dwelling in theory. There was an interactive window that mimicked a z/OS Unix shell and there were several steps which guided me in compiling and executing a Java program from the z/OS Unix shell. I've tried and succeded in doing the same stuff on the system available via Master the Mainframe 2020. That means there is a JVM on z/OS for Unix system in MTM2020 which run Java programs.

 It's time for an hands-on!๐Ÿ™‹  Are you ready? 

Compiling a Java program from z/OS Unix shell:

Note: In order to create/access files from your home directory /z/zxxxxx in MTM2020 system, you should first be signed up to ๐Ÿ‘‰ MTM2020 and should've finished the challenges till Level 2.4.
We’re going to use the Unix System Services (USS) on z/OS. USS is a Unix Interface within z/OS which you can login through SSH. With VS Code, we’ve got a terminal where we can get into Unix for z/OS and start issuing Unix commands. I already have a root directory under my user ID.

Let's use the touch command to make a new file and name it as HelloWorld.java

Issuing touch HelloWorld.java creates a new file. ls command lists all the files under the directory /z/z01071.

Type vi HelloWorld.java and press Enter to edit the file that you had just created. Vi is an editor used to edit Java programs.

After writing the code, type :x and press Enter to save the file.

From the root directory, type cat HelloWorld.java to view the contents of the file. 


It's time to compile the program๐Ÿ’ฅ

From the root directory, type javac HelloWorld.java and press Enter. 
javac command reads source files written in Java Programming language and compiles them into class files that run on Java Virtual Machine (JVM). 
If the compilation is error free, you'll be prompted to type next set of commands.

From the root directory, type ls and press Enter to see the files under the directory. As a result of the compilation, we can now see a new file named HelloWorld.class (newly created Java Class).


To execute the newly created Java class, we type java HelloWorld and press Enter.

The program has printed a message 'Hello, World!'. We've just executed a Java program on z/OS system๐Ÿ™Œ

A Java program executing from a z/OS UNIX shell can use the same features that you would expect from Java on other platforms:
  • java.io package can be used by the Java programs to access z/OS Unix files. Make a note that we can’t use this package to access the traditional z/OS datasets.
  • The standard Java java.util package is available in z/OS for normal Java utilities and classes.
  • The java.text package can be used for text, date, number and message manipulation.
  • The java.net package can be used for TCP/IP communications. 

The course also provided info on executing Java programs in z/OS batch mode. A JCL with BPXBATCH utility is used to do this. I've tried and failed in executing a Java program in z/OS batch on the system available via Master the Mainframe 2020. 

More info about executing Java programs in z/OS batch can be found ๐Ÿ‘‰ here 


Other methods in which we can execute Java programs in z/OS:

The course also hinted several other ways that a Java program can execute than simply from a z/OS UNIX console or batch. 
  • JAVA applications can execute within CICS Transaction server.
  • IMS provides message regions with JVMs that allow Java programs to execute. IMS also provided additional services to allow Java programs to access IMS databases.
  • The most interesting method is that DB2 Stored procedures can be written in Java on z/OS. Java programs on z/OS can access database managers such as IMS or DB2 using normal Java JDBC or SQLJ calls. However, the course didn’t provide much info about JDBC/SQLJ calls.

z/OS Specific classes:

One of the most useful tools supplied in the z/OS Java SDK is jzos. This provides Java classes and methods for accessing mainframe resources and information from Java running in batch or z/OS UNIX. 

Some of the classes are listed below: 
  • com.ibm.jzos.AccessMethodServices – This class provides a Java interface to IDCAMS. 
  • com.ibm.jzos.DfSort – This class provides a Java Interface to sort things out using IBM’s DFSORT. 
  • To allow Java programs to read/write to z/OS traditional datasets, com.ibm.jzos.RecordReader and com.ibm.jzos.RecordWriter classes can be used. 
  • com.ibm.jzos.Zfile – This class can access any traditional z/OS dataset including sequential and VSAM datasets. 
  • com.ibm.jzos.Mvs.JobSubmitter – Allow Java programs to submit z/OS batch jobs. 
  • jzos also provides a Batch launcher which can act as an alternative to execute Java programs via batch.

Some of the issues ๐Ÿ’ฃ faced when programming in Java on z/OS:

This section of the course focused on the issues that arise due to the usage of different encoding shemes ๐Ÿ” . z/OS operate in EBCDIC (Extended Binary Coded Decimal Interchange Code) and other systems like Windows and Unix operate in ASCII (American Standard Code for Information Interchange). This can cause some confusion when working with Java programs on z/OS.
  • All input source to the Java javac compiler is assumed to be encoded in the default character set, EBCDIC for z/OS. Otherwise, the encoding switch of the javac command can be leveraged to specify the character set of the source code (The ISO encoding format for ASCII is ISO8859-1).
  • Java Profiles must be in ASCII on all platforms, including z/OS.
  • When executing the program, every JVM stores strings and text internally in Unicode (UTF-16). This must be converted when performing any operations around input or output.
  • By default, Java assumes that external data is encoded in the default platform encoding. This means, in z/OS, Java program will read input data assuming it is encoded in EBCDIC. Similarly, data written out will be in EBCDIC. If the external file’s encoding is in ASCII, then Dfile.encoding parameter of the Java JVM an be used to specify it as ASCII. 
  • Java programs can convert between encodings using the getBytes method.
You may refer the ๐Ÿ‘‰  Character Encoding Summary for more info on EBCDIC and ASCII character sets. 

That's all! We've hit the bottom of this post. Share your thoughts ๐Ÿ’ญ in the Comments section below. thx! ๐Ÿ‘



Wednesday, October 7, 2020

Master the Mainframe 2020 | Level 2.9: ZOAU2: Power Through Python

Hiya! Welcome to my blog๐Ÿ˜ƒ

Master the Mainframe has been a lot interesting this year (2020) as we are using Visual Studio Code (for the first time) to establish a connection with z/OS and work with data sets and jobs, thanks to Zowe™ and IBM Z Open Editor extensions. We're now able to use the mouse to scroll through the contents of a data set๐Ÿ˜Ž.

Picture 9.1: Viewing the contents of a data set using IBM Z Open Editor in VS Code.

In this blog post, I want to share my impressions on using Python๐Ÿ and Z Open Automation Utilities (abbreviated to ZOAU) to build an app to validate credit card data (This is the challenge in Level 2.9 of MTM2020). 

๐Ÿ’ก: ZOAU lets you perform many tasks on z/OS without needing to get into JCL. 

Before getting started with Zowe and Python, we will be building the logic using COBOL; we will then compile and run the COBOL program with the help of  JCL.  Then, we will be looking at Python and ZOAU way of building the same logic. In this way, I believe you will be able to understand the differences better. You'll also get answers to  some questions like 'Why Python?', 'Why ZOAU?', 'Why not JCL and COBOL?' and so on..

Fasten your seat belts, let's take-off now! 

The requirement is to read an input file (MTM2020.PUBLIC.CUST16 is the input data set name in MTM2020 system) and print a report with list of records that has got an invalid Credit card number (aka Payment Card Number). 

The input data set conforms to Track 1 Format B of ISO/IEC 7813 format ๐Ÿ‘‰(https://en.wikipedia.org/wiki/ISO/IEC_7813). 

Picture 9.2: A snap from Wikipedia that shows the structure of Track 1 Format B. I've used this structure to come up with the record layout of the input file for COBOL.

Using COBOL program and JCL:

The COBOL program prepared by this Author๐Ÿ˜‰ is as follows: 
Picture 9.3: The COBOL program reads the input file; implements Luhn Algorithm to find if a credit card number is vaild or not; if invalid, the record is written to the output file as a report. 

What the COBOL program does? 

  • An input file is being read (the record layout of the input file, INFILE-REC is based on Track 1 Format B structure of ISO/IEC 7813). 
  • For each record that is being read, control is passed to 100-IS-INVALID-PARA. This para implements the Luhn Algorithm. 
  • The first PERFORM loop within 100-IS-INVALID-PARA computes the sum of digits present in the odd number positions in PYMT-CARD-NUMBER. For example, if the value in the PYMT-CARD-NUMBER data item is 1234567890223457846, then 1 + 3 + 5 + 7 + 9 + 2 + 3 + 5 + 8 + 6 = 49 will be stored in WS-CHECKSUM at the end of the first perform loop. 
  • The second PERFORM loop within 100-IS-INVALID-PARA multiply the digits present in the even number positions, by 2; if the resulting value is greater than 9, then 9 is subtracted from the resulting value (Please don't ask ME why we have to do this. IBM Scientist Hans Peter Luhn, created this algorithm๐Ÿ˜Š); accumulate the resulting value by adding it to WS-CHECKSUM data-item. 
  • After the 2 PERFORM loops, divide the final value in WS-CHECKSUM data item by 10. You should assign a data item for holding the remainder. If the remainder is 0, the value in PYMT-CARD-NUMBER is valid. Else, it is invalid. 
  • When an Invalid credit card number is found, control is passed to 200-WRITE-OUTPUT-PARA to write the whole record with the invalid credit card number, to the output file. When the control is passed to 200-WRITE-OUTPUT-PARA for the first time, the header record and an empty line are written to the output file. 

I was not aware of Luhn algorithm until I started this challenge. A pretty interesting one. The Luhn Algorithm is an efficient method of checking if a credit card number is valid, locally, without needing to have the bank or financial institute process it. This way, cards can be checked directly on a web page for mistakes in typing or copying digits. More information about Luhn algortihm can be found ๐Ÿ‘‰ here.

After finishing the COBOL program, we have to prepare a JCL to compile the program and run it by providing input and output files. The following JCL does that.


After the compilation and execution of the program, we get the following output.

To read an input data set with magnetic stripe data that you might find on a credit card (if it doesn’t have a chip) and to print a report out of it with the list of invalid credit card numbers, what all we did? We wrote a COBOL program, prepared a JCL to compile the program and execute it. I call this the  usual Mainframe Way (not The Milky Way๐Ÿ˜‰). 

Let's do the same task using Zowe™ and Python. This is what Level 2.9 in MTM2020 is all about. 

Using Zowe™ and Python๐Ÿ:

ZOAU supports shell scripts, Python and Node.js. The commands available through Z Open Automation Utilities in Python can be found ๐Ÿ‘‰ here. In Layman's terms, IBM has developed a bridge between z/OS and Python by making these commands available for us to use within the Python scripts. We use ๐Ÿ‘‰ zoautil_py.Datasets module extensively in this challenge. Modules in Python will have a set of functions, classes and variables defined in it. zoautil_py.Datasets module has got several functions like, 
  • create(name, type, size, format, class_name, length, offset) - Used to create a data set in z/OS
  • delete(dataset) - Used to delete a data set in z/OS
  • copy(source, target) - Used to copy the source data set into destination data set
You just have to refer to the corresponding function and pass necessary arguments in your Python script to perform the task it is intended to do. 

Building the logic using Python:

Note: In order to access python scripts from your home directory, /z/zxxxxxyou should be signed up to ๐Ÿ‘‰ MTM2020 and should've finished the challenges till Level 2.4. 
There's already a python script (cc_check.py) available under your home directory (/z/zxxxxx) in Unix Sytem Services (abbreviated to USS). There are LOTS of comments in the script to help you understand what the code is doing. You just have to build, upon this foundation, a logic which would write the invalid credit card number to the output file.  
๐Ÿ’ก: A comment in Python starts with the hash character, #
I've used Trinket to embed Interactive Python in this blog post and I've copy pasted the contents of cc_check.py file in Trinket (And nope! The script shown below isn't the solution for Level 2.9๐Ÿ˜). I'll do my best here to make you understand what needs to be done to complete Level 2.9. 

Note that you may not be able to RUN ๐Ÿƒ this script in Trinket as the ZOAU utilities for Python aren't available in Trinket.
 
 
Lines 1 thru 7: The necessary ZOAU libraries for Python are imported so that you can use them in the script. 

Lines 9 thru 16: The input data set, MTM2020.PUBLIC.CUST16 is being read into a variable called cc_contents. Line #12 uses the os.getenv() method in Python with 'USER' as argument. As the operating system that Python is running under, is z/OS, USERID variable is assigned with your TSO user ID. In Line #13, 2 strings are concatenated with '+' operator and the result is stored in output_dataset variable. Some functions (like exists, delete) in the Datasets module are being used to check if the output data set is already existing in the Mainframe. If yes, then it is deleted.  

Lines 17 and 18: Read๐Ÿ‘€ those comments in the 2 lines as they help you figure out what needs to be done.

Lines 21 thru 28: def keyword in Python is used to define functions. You all know that function is a group of statements which are intended to perform a specific task. This function's name is is_even and this function receives one argument from the caller. The purpose of this function is to check if the number passed as an argument by the caller, is even or odd. If it is even, the function returns 'True' to the caller. Else, it returns 'False'. Your task is to edit this script so that instead of finding the even numbers, it implements the Luhn algorithm to find the six invalid entries in the input file. You may use the sample code in luhn.py file to perform the checking logic. 

Lines 30 thru 35: Each record in the data set is stored as an element in a List named as cc_list. Like how we read records one by one in COBOL, a for loop in Python is being used to read the elements of cc_list, one by one. cc_line variable is used to hold the element in any given iteration of the for loop. Like how we use Reference Modification in COBOL to extract a portion of the string, slicing technique (in Line 33) is being used on the List element to extract the Payment card number. The extracted card number is then passed as an argument to is_even function.
๐Ÿ’ก: List is one of the most frequently used datatype in Python. The elements are stored in a list within a square brackets ([ ]). Lists can have any number of items in it and the elements may be of different data types. Read more about lists and how to access the elements in a list ๐Ÿ‘‰ here.

Lines 38 thru 57: Pretty much self explanatory as there are a lot of comments. Once you're ready to write the 6 invalid credit card numbers to the output file, uncomment line 57. 

And, there you go! refer luhn.py file and implement the Luhn algorithm in the place of is_even function. Bonus: Pay attention whenever you find the word hint in a comment line.

With Python and ZOAU utilities, it required only ~30 lines of code to build the logic that will look for the invalid credit card numbers from a Mainframe Input data set and write a report consisting of the invalid entries to an output data set. Power Through Python๐Ÿ’ช
You just witnessed one of the newest innovations in z/OS, the introduction of IBM Z Open Automation Utilities (ZOA Utilities) in ACTION ๐Ÿ’ฅ. ZOAU is an alternative way of interacting with tasks on z/OS through scripting instead of writing JCL's and submitting jobs. 

Hope you liked this post. Feel free to add your comments below. Thx๐Ÿ‘

Screenshot Courtesy: Mainframe access๐Ÿ’ป obtained via MTM2020 contest run by IBM.