A Node module to help Node.JS developers interacting with z/OS easily, taking advantage of z/OS FTP service. If z/OS FTP service is not configured as FTP over SSL, it's recommended to be deployed on z/OS, to avoid transferring user account/password in clear-text over network. Otherwise, the secure connection of FTP over SSL is recommended. IBM SDK for Node.js - z/OS is available at https://developer.ibm.com/mainframe/products/ibm-sdk-for-node-js-z-os/.
For a Zowe CLI plugin based on this functionality, see https://github.com/zowe/zowe-cli-ftp-plugin
npm install zos-node-accessor # Put latest version in your package.json
zos-node-accessor
is rewritten in TypeScript, and defines API methods in more consistent way. So some old API methods are renamed. Here are about some details useful when you migrate the code using zos-node-accessor
v1 to v2.
listDataset(...)
is renamed to listDatasets(...)
getDataset(...)
is split and renamed to downloadFile
and downloadDataset(...)
queryJob(...)
returns the enum member of JobStatusResult
JobIdOption
, JobLogOption
, or JobListOption
rename(...)
is split and renamed to renameFile(...)
and renameDataset(...)
delete(...)
is split and renamed to deleteFile(...)
and deleteDataset(...)
Many list methods in v2 like listDatasets(...)
and listFiles(...)
returns the objects with the type like DataSetEntry
, instead of the key/value pairs in v1. To make the migration easier, you can enable migrationMode to have zos-node-accessor
return the v1 key/value pairs, so that you can take time to change code to use the types object. This migration mode will be dropped in future. Let us know, if you have problem in removing the code using the key/value pairs.
const zosAccessor = new ZosAccessor();
zosAccessor.setMigrationMode(true);
var connectionInfo = {
....
};
await zosAccessor.connect(connectionInfo)
This accessor leverages z/OS FTP server to interact with z/OS. To work with z/OS JES better, it requires JESINTERFACELevel
set to 2
on z/OS FTP settings.
Before connecting to a z/OS server, you need to initialize an instance using the constructor new ZosAccessor()
, then call the connect(option: ConnectionOption)
method, where:
ConnectionOption
tls.connect()
. Default: (none)A promise that resolves itself (ZosAccessor object), and rejects on any error.
import { ZosAccessor } from '../zosAccessor';
const accessor = new ZosAccessor();
await accessor.connect({
user: 'myname',
password: 'mypassword',
host: 'localhost',
port: 21,
pasvTimeout: 60000,
secure: true,
secureOptions: {
ca: [ caBuffer ]
}
});
allocateDataset(datasetName: string, allocateParamsOrString?: string | AllocateParams)
- Allocate sequential or partition (with the DCB attribut "PDSTYPE=PDS") dataset.
site
sub commands. The tested attributes includes BLKsize/BLOCKSIze, BLocks, CYlinders, Directory, LRecl, PDSTYPE, PRImary, RECfm, SECondary, and TRacks.Note: DSORG=PO
was defined by zos-node-accessor, not site
sub command. It's deprecated by site
sub command, PDSTYPE=PDS
or PDSTYPE=PDSE
.
The site
sub commands can be found at https://www.ibm.com/docs/en/zos/2.3.0?topic=subcommands-site-subcommandsend-site-specific-information-host.
Option Key | Description |
---|---|
BLKsize/BLOCKSIze=size | block size |
BLocks | space allocations in blocks |
CYlinders | space allocations in cylinders |
DATAClass=data_class | data class |
DCBDSN=data_set_name | the data set to be used as a model for allocation of new data sets |
Directory=size | directory blocks |
DSNTYPE=SYSTEM or BASIC or LARGE | data set name type |
EATTR=SYSTEM or NO or OPT | extended attributes |
LRecl=length | logical record length |
MGmtclass=mgmtclass | management class |
PDSTYPE=PDS or PDSE | PDS type |
PRImary=amount | primary space |
RECfm=format | record format |
RETpd=days | retention period |
SECondary=amount | secondary space |
STOrclass=storage_class | storage class |
TRacks | space allocations in tracks |
UCOUN=unit_count or P | how many devices to allocate concurrently for this allocation request |
Unit=unit_type | unit type for allocation of new data sets |
VCOUNT=volume_count | number of tape data set volumes that an allocated data set can span |
VOLume=volume_serial or (volume_serial_list) | volume serial number |
A promise that resolves on success, rejects on error.
await connection.allocateDataset('HLQ.ABC.DEF', 'LRECL=80 RECFM=FB BLKSIZE=320');
await connection.allocateDataset('HLQ.ABC.PDS', {'LRECL': 80, 'RECFM': 'FB', 'BLKSIZE': 320, 'PDSTYPE': 'PDS', 'DIRECTORY': 20});
listDatasets(dsn: string)
- Lists the datasets whose names match with the given dataset name.
Note: This method is renamed from listDataset(dsnOrDir)
in v1.0.x, to be consistent with the other list methods.
A promise that resolves a list of DatasetEntry
.
DatasetEntry
await connection.listDatasets('HQL.*.JCL');
for (const entry of list) {
console.log('name:', entry.name, 'dsorg', entry.dsOrg);
}
listMembers(partitionDsn: string)
- Lists the members of partition dataset
A promise that resolves a list of DatasetMemberEntry
.
DatasetMemberEntry
uploadDataset(input: Input, destDataset: string, transferMode: TransferMode = TransferMode.ASCII, allocateParamsOrString?: string | AllocateParams)
- Uploads data to the specified dataset on z/OS.
\r\n
. Otherwise the transfered file will get truncated.A promise that resolves on success, rejects on error.
import * as fs from 'fs';
const input = fs.readFileSync('/etc/hosts', 'utf8').replace(/\r?\n/g, '\r\n');
await connection.uploadDataset(input, 'HLQ.HOSTS');
await connection.uploadDataset(input, 'HLQ.HOSTS', "LRECL=80 RECFM=FB");
downloadDataset(dsn: string, transferMode: TransferMode = TransferMode.ASCII, stream = false, siteParams?: string)
- Downloads the specified dataset or member of patition dataset.
TransferMode.ASCII
, TransferMode.BINARY
, TransferMode.ASCII_STRIP_EOL
, TransferMode.ASCII_RDW
, TransferMode.ASCII_NO_TRAILING_BLANKS
, or TransferMode.BINARY_RDW
. The TransferMode.ASCII
, TransferMode.ASCII_STRIP_EOL
, TransferMode.ASCII_RDW
, or TransferMode.ASCII_NO_TRAILING_BLANKS
asks z/OS FTP service to convert EBCDIC
characters to ASCII
. The TransferMode.ASCII_STRIP_EOL
asks z/OS FTP service not to append a CLRF
to the end of each record. The TransferMode.ASCII_NO_TRAILING_BLANKS
asks z/OS FTP service to remove trailing blanks. The TransferMode.ASCII_RDW
or TransferMode.BINARY_RDW
supports to download variable length dataset, which add 4-byte Record Description Word (RDW) at the beginning of each record.true
if you want to obtain a ReadableStream of the data set content, or false
to read a full dataset into memory (in Buffer). The buffer accepts up to 4MB data. For large dataset, use stream=true
instead.A promise that resolves content of the dataset in either Buffer
or ReadableStream
.
const jclBuffer = await connection.downloadDataset('HQL.AA.JCL', TransferMode.ASCII);
console.log('JCL is:');
console.log(jclBuffer.toString());
const jclStream = await connection.downloadDataset('HQL.AA.JCL(MEMBER1)', TransferMode.ASCII, true);
const writable = fs.createWriteStream('file.txt');
jclStream.pipe(writable);
deleteDataset(dsn)
- Deletes the dataset or member of parition dataset whose names match with the given dataset name.
A promise that resolves on success, rejects on error.
await connection.deleteDataset('HQL.AA.JCL');
renameDataset(dsn: string, newDsn string)
- Renames dataset, member in partition dataset.
A promise that resolves on success, rejects on error.
await connection.renameDataset('HQL.AA.JCL', 'HQL.BB.JCL')
makeDirectory(directoryName: string)
- Makes USS directory with the given directory name.
A promise that resolves on success, rejects on error.
await connection.makeDirectory('/u/user/my_directory'});
listFiles(dirPath: string)
- Lists files whose names match with the given path name.
A promise that resolves a list of USSEntry
.
USSEntry
const list = await connection.listFiles('/u/user1/');
for (const entry of list) {
console.log(entry.name, entry.owner, entry.group, entry.size);
}
uploadFile(input: Input, destFilePath: string, transferMode: TransferMode = TransferMode.ASCII)
- Uploads data to the specified USS file on z/OS.
A promise that resolves on success, rejects on error.
import * as fs from 'fs';
const input = fs.readFileSync('/etc/hosts', 'utf8');
await connection.uploadFile(input, '/u/username/hosts');
downloadFile(filePath: string, transferMode: TransferMode = TransferMode.ASCII, stream = false)
- Downloads the specified USS file.
TransferMode.ASCII
, TransferMode.BINARY
. When downloading a text dataset, transferMode should be either TransferMode.ASCII
so that z/OS FTP service converts EBCDIC
characters to ASCII
.true
if you want to obtain a ReadableStream of the file content, or false
to read a full file into memory (in Buffer). The buffer accepts up to 4MB data. For large file, use stream=true
instead.A promise that resolves content of the file in either Buffer
or ReadableStream
.
const jclBuffer = await connection.downloadFile('/etc/hosts', TransferMode.ASCII);
console.log('JCL is:');
console.log(jclBuffer.toString());
const jclStream = await connection.downloadFile('/etc/hosts', TransferMode.ASCII, true);
const writable = fs.createWriteStream('file.txt');
jclStream.pipe(writable);
deleteFile(filePath: string, fileType: FileToOperate = FileToOperate.FILE_OR_DIRECTORY)
- Deletes the USS files or directory whose names match with the given file path.
A promise that resolves on success, rejects on error.
await connection.deleteFile('/u/username/myfile');
await connection.deleteFile('/u/username/mydir'); // Delete it, if it's empty
await connection.deleteFile('/u/username/mydir', FileToOperate.WHOLE_DIRECTORY); // Delete it, even if it's not empty.
renameFile(name: string, newName: string)
- Renames USS file/directory.
A promise that resolves on success, rejects on error.
await connection.renameFile('/u/username/myfile', '/u/username/newfile')
listJobs(queryOption?: JobListOption)
- Lists the jobs matching the given query option. If the query option is not provided, it will list all jobs of the current user.
JobListOption
A promise that resolves an array of Job
. For JESINTERFACELEVEL=2, Job
contains valid jobName
, jobId
, owner
, status
, class
.
Job
const jobs: Job[] = await connection.listJobs({jobName: 'TSU*', owner: 'MY-NAME'})
submitJCL(jclText: string)
- Submits job with the specified JCL text.
A promise that resolves the submitted job id.
import * as fs from 'fs';
const jcl = fs.readFileSync('./unpaxz.jcl', 'utf8');
const jobId = await connection.submitJCL(jcl);
queryJob(queryOption: JobIdOption)
- Returns the status the job identified by job id and optional job name.
JobIdOption
A promise that resolves status of the job, JobStatusResult
.
JobStatusResult
const status = await connection.queryJob(jobName, jobId);
switch(status) {
case JobStatusResult.SUCCESS:
console.log('Job succeeded');
break;
case JobStatusResult.FAIL:
console.log('Job failed');
break;
case JobStatusResult.ACTIVE:
console.log('Job is running');
break;
case JobStatusResult.WAITING:
console.log('Job is waiting');
break;
case JobStatusResult.NOT_FOUND:
console.log('Job is not found');
break;
}
getJobStatus(queryOption: JobIdOption)
- Returns the status of the job specified by query option.
JobIdOption
A promise that resolves job status, JobStatus
.
JobStatus
SpoolFile
const jobStatus = await connection.getJobStatus(jobId);
getJobLog(queryOption: JobLogOption)
- Returns job spool files identified by jobId.
JobLogOption
!! END OF JES SPOOL FILE !!
. Default: -1A promise that resolves spool files' contents.
const spoolFileContents = await connection.getJobLog({ jobName, jobId, fileId: -1 });
spoolFileContents.split(/\s*!! END OF JES SPOOL FILE !!\s*/)
.forEach(function (spoolFile, i) {
if (spoolFile.length > 0) {
console.log(`Spool file ${i}:`);
console.log(spoolFile);
}
});
deleteJob(queryOption: JobIdOption)
- Deletes the job of the specified job id.
JobIdOption
A promise that resolves on success, rejects on error.
await connection.deleteJob({ jobId: 'JOB25186' });
stat(option)
- Retrieve status information from a remote server. The following parameters are accepted:
A promise that resolves status of the specified option on success, rejects on error. If option
is not specified,
it returns all status information.
const status = await connection.stat('UMASK');
console.log(status);
site(siteCommands)
- Send site-specific information to a server. The following parameters are accepted:
A promise that resolves text from server on success, rejects on error.
await connection.site('UMASK 007');
This module adopts the Module Long Term Support (LTS) policy, with the following End Of Life (EOL) dates:
Module Version | Release Date | Minimum EOL | Node Version | EOL With | Status |
---|---|---|---|---|---|
2.x.x | May 2020 | May 2022 | v8, v10, v12 | Current | |
1.x.x | Oct 2018 | Dec 2019 | v6, v8, v10, v12 | Node v6 | EOL |