@zowe/cli
Version:
Zowe CLI is a command line interface (CLI) that provides a simple and streamlined way to interact with IBM z/OS.
271 lines • 14.2 kB
JavaScript
;
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const imperative_1 = require("@zowe/imperative");
const util_1 = require("util");
const GetJobs_1 = require("./GetJobs");
const JobStatus_1 = require("./types/JobStatus");
const utils_1 = require("../../../utils");
/**
* APIs for monitoring the status of a job. Use these APIs to wait for a job to enter the specified status. All APIs
* in monitor jobs invoke z/OSMF jobs REST endpoints to obtain job status information.
* @export
* @class MonitorJobs
*/
class MonitorJobs {
/**
* Given an IJob (has jobname/jobid), waits for the status of the job to be "OUTPUT". This API will poll for
* the OUTPUT status once every 3 seconds indefinitely. If the polling interval/duration is NOT sufficient, use
* "waitForStatusCommon" to adjust.
*
* See JSDoc for "waitForStatusCommon" for full details on polling and other logic.
*
* @static
* @param {AbstractSession} session - a Rest client session for z/OSMF
* @param {IJob} job - the z/OS job to wait for (see z/OSMF Jobs APIs for details)
* @returns {Promise<IJob>} - the promise to be fulfilled with IJob object (or rejected with an ImperativeError)
* @memberof MonitorJobs
*/
static waitForJobOutputStatus(session, job) {
imperative_1.ImperativeExpect.toNotBeNullOrUndefined(job, "IJob object (containing jobname and jobid) required");
return MonitorJobs.waitForStatusCommon(session, { jobname: job.jobname, jobid: job.jobid, status: JobStatus_1.JOB_STATUS.OUTPUT });
}
/**
* Given the jobname/jobid, waits for the status of the job to be "OUTPUT". This API will poll for the OUTPUT status
* once every 3 seconds indefinitely. If the polling interval/duration is NOT sufficient, use
* "waitForStatusCommon" to adjust.
*
* See JSDoc for "waitForStatusCommon" for full details on polling and other logic.
*
* @static
* @param {AbstractSession} session - a Rest client session for z/OSMF
* @param {string} jobname - the z/OS jobname of the job to wait for output status (see z/OSMF Jobs APIs for details)
* @param {string} jobid - the z/OS jobid of the job to wait for output status (see z/OSMF Jobs APIS for details)
* @returns {Promise<IJob>} - the promise to be fulfilled with IJob object (or rejected with an ImperativeError)
* @memberof MonitorJobs
*/
static waitForOutputStatus(session, jobname, jobid) {
return MonitorJobs.waitForStatusCommon(session, { jobname, jobid, status: JobStatus_1.JOB_STATUS.OUTPUT });
}
/**
* Given jobname/jobid, checks for the desired "status" (default is "OUTPUT") continuously (based on the interval
* and attempts specified).
*
* The "order" of natural job status is INPUT > ACTIVE > OUTPUT. If the requested status is earlier in the sequence
* than the current status of the job, then the method returns immediately (since the job will never enter the
* requested status) with the current status of the job.
*
* @static
* @param {AbstractSession} session - a Rest client session for z/OSMF
* @param {IMonitorJobWaitForParms} parms - monitor jobs parameters (see interface for details)
* @returns {Promise<IJob>} - the promise to be fulfilled with IJob object (or rejected with an ImperativeError)
* @memberof MonitorJobs
*/
static waitForStatusCommon(session, parms) {
return __awaiter(this, void 0, void 0, function* () {
// Validate that required parameters are specified
imperative_1.ImperativeExpect.toNotBeNullOrUndefined(parms, "IMonitorJobParms object required");
imperative_1.ImperativeExpect.keysToBeDefinedAndNonBlank(parms, ["jobname"]);
imperative_1.ImperativeExpect.keysToBeDefinedAndNonBlank(parms, ["jobid"]);
imperative_1.ImperativeExpect.toNotBeNullOrUndefined(session, "Required session must be defined");
if (parms.status != null) {
imperative_1.ImperativeExpect.toBeOneOf(parms.status, JobStatus_1.JOB_STATUS_ORDER);
}
if (parms.attempts != null) {
imperative_1.ImperativeExpect.keysToBeOfType(parms, "number", ["attempts"]);
if (parms.attempts < 0) {
throw new imperative_1.ImperativeError({ msg: `Expect Error: "attempts" must be a positive integer` });
}
}
if (parms.watchDelay != null) {
imperative_1.ImperativeExpect.keysToBeOfType(parms, "number", ["watchDelay"]);
if (parms.watchDelay < 0) {
throw new imperative_1.ImperativeError({ msg: `Expect Error: "watchDelay" must be a positive integer` });
}
}
// Log the API call (& full parms at trace level)
this.log.info(`Monitor Jobs - "waitForStatusCommon" API request: ` +
`jobname ${parms.jobname}, jobid ${parms.jobid}, attempts ${parms.attempts}, watch delay ${parms.watchDelay}`);
this.log.trace(`Parameters:\n${util_1.inspect(parms)}`);
// set defaults if not supplied
if (parms.status == null) {
parms.status = MonitorJobs.DEFAULT_STATUS;
}
if (parms.attempts == null) {
parms.attempts = MonitorJobs.DEFAULT_ATTEMPTS;
}
// Wait for the expected status (or timeout)
let response;
try {
response = yield MonitorJobs.pollForStatus(session, parms);
}
catch (pollStatusErr) {
// If a poll error occurred - reject the promise
if (pollStatusErr instanceof imperative_1.ImperativeError) {
const details = pollStatusErr.mDetails;
details.msg = this.constructErrorMsg(parms, pollStatusErr.message);
this.log.error(`${details.msg}`);
throw new imperative_1.ImperativeError(details);
}
else {
const msg = this.constructErrorMsg(parms, pollStatusErr.message);
this.log.error(`${msg}`);
throw new imperative_1.ImperativeError({ msg });
}
}
// Return the response
this.log.trace(`Monitor Jobs - "waitForStatusCommon" complete - found expected status of ${parms.status}`);
return response;
});
}
/**
* "Polls" (sets timeouts and continuously checks) for the status of the job to match the desired status.
* @private
* @static
* @param {AbstractSession} session - a Rest client session for z/OSMF
* @param {IMonitorJobWaitForParms} parms - The monitor jobs parms (see interface for details)
* @returns {Promise<IJob>} - Fulfill when the status changes as expected
* @memberof MonitorJobs
*/
static pollForStatus(session, parms) {
return __awaiter(this, void 0, void 0, function* () {
// Timeout value
const timeoutVal = parms.watchDelay || MonitorJobs.DEFAULT_WATCH_DELAY;
// Define loop control parameters
let expectedStatus = false;
let job;
let attempt = 0;
let shouldContinue = false;
// Catch any errors that might happen in MonitorJobs.checkStatus.
// Try instantiated outside the for loop because it gets created once for the entire
// loop operation, as opposed to once per operation of the loop.
try {
do {
// Check the status of the job
attempt++;
this.log.debug(`Polling for jobname "${parms.jobname}" jobid "${parms.jobid}" status "${parms.status}" ` +
`- attempt "${attempt}" (max attempts "${parms.attempts}") ...`);
// Check the status of the job
[expectedStatus, job] = yield MonitorJobs.checkStatus(session, parms);
// Logic is done this way because we don't need to sleep if we are
// exiting the loop on this operation.
shouldContinue = !expectedStatus &&
(parms.attempts > 0 && attempt < parms.attempts);
// Wait for the next poll if we didn't get the proper status.
if (shouldContinue) {
// Set a timer which will check the status on expiry
this.log.trace(`Setting timeout for next poll...`);
yield utils_1.sleep(timeoutVal);
}
} while (shouldContinue);
}
catch (e) {
this.log.error("Received error while polling");
this.log.error(e);
throw e;
}
// One of the conditions that will cause the loop to end. If this specific
// condition was encountered, then we timed out and exhausted all of our attempts
// without successfully retrieving the job.
if (!expectedStatus && parms.attempts > 0 && attempt >= parms.attempts) {
throw new imperative_1.ImperativeError({
msg: `Error Details: Reached max poll attempts of "${parms.attempts}"`
});
}
// The only way we get here is by successfully getting the job.
this.log.debug(`Expected status "${parms.status}" found for jobname "${parms.jobname}" jobid "${parms.jobid}" ` +
`- attempt "${attempt}" (max attempts "${parms.attempts}") ...`);
return job;
});
}
/**
* Checks the status of the job for the expected status (OR that the job has progressed passed the expected status).
* @private
* @static
* @param {AbstractSession} session - the session to initiate the z/OSMF getJobStatus request
* @param {IMonitorJobWaitForParms} parms - the monitor jobs parameters containing the jobname, jobid, status, etc.
* @returns {Promise<boolean>} - promise to fulfill when the job status is obtained (or imperative error)
* @memberof MonitorJobs
*/
static checkStatus(session, parms) {
return __awaiter(this, void 0, void 0, function* () {
// Log an get the status of the job
this.log.debug(`Checking for "${parms.status}" status for jobname "${parms.jobname}", jobid "${parms.jobid}"...`);
const job = yield GetJobs_1.GetJobs.getStatusCommon(session, parms);
this.log.debug(`jobname "${parms.jobname}" jobid "${parms.jobid}" has current status of "${job.status}".`);
// Ensure that the job status is defined & known
if (job.status == null || JobStatus_1.JOB_STATUS_ORDER.indexOf(job.status.toUpperCase()) < 0) {
this.log.error(`An unknown status of "${job.status}" for jobname ${job.jobname}, jobid ${job.jobid}, was received.`);
throw new imperative_1.ImperativeError({
msg: `Error Details: An unknown status "${job.status}" was received.`
});
}
// If the status matches exactly OR the jobs current status is further than the requested status (for
// example, if the jobs status is ACTIVE and the requested status is INPUT) fulfill.
if (JobStatus_1.JOB_STATUS_ORDER.indexOf(job.status) >= JobStatus_1.JOB_STATUS_ORDER.indexOf(parms.status)) {
this.log.debug(`The current status (${job.status}) for jobname ${job.jobname}, jobid ${job.jobid} ` +
`is greater than or equal to the expected of "${parms.status}"`);
return [true, job];
}
return [false, job];
});
}
/**
* Constructs the default error message (to be thrown via ImperativeError) for the monitor jobs APIs
* @private
* @static
* @param {IMonitorJobWaitForParms} parms - The parameters passed to the API
* @param {string} details - Additional error details string
* @returns {string} - The error string to be thrown via ImperativeError
* @memberof MonitorJobs
*/
static constructErrorMsg(parms, details) {
return `Error obtaining status for jobname "${parms.jobname}" jobid "${parms.jobid}".\n${details}`;
}
}
exports.MonitorJobs = MonitorJobs;
/**
* The default amount of time (in milliseconds) to wait until the next job status poll.
* @static
* @memberof MonitorJobs
*/
MonitorJobs.DEFAULT_WATCH_DELAY = 3000; // 3000 is 3 seconds
/**
* Default expected job status ("OUTPUT")
* @static
* @memberof MonitorJobs
*/
MonitorJobs.DEFAULT_STATUS = JobStatus_1.JOB_STATUS.OUTPUT;
/**
* Default number of poll attempts to check for the specified job status.
* @static
* @memberof MonitorJobs
*/
MonitorJobs.DEFAULT_ATTEMPTS = Infinity;
/**
* Obtain an instance of the app logger (Brightside).
* @private
* @static
* @type {Logger}
* @memberof MonitorJobs
*/
MonitorJobs.log = imperative_1.Logger.getAppLogger();
//# sourceMappingURL=MonitorJobs.js.map