UNPKG

zowe-cli-cics-deploy-plugin

Version:

IBM CICS Bundle generation and deployment for Zowe CLI

392 lines 18.3 kB
/* * 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 IBM Corp, 2019 * */ "use strict"; 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 }); exports.BundleDeployer = void 0; const imperative_1 = require("@zowe/imperative"); const cli_1 = require("@zowe/cli"); const ParmValidator_1 = require("./ParmValidator"); const path = require("path"); const ZosmfConfig_1 = require("../BundlePush/ZosmfConfig"); /** * Class to represent a CICS Bundle Deployer. * * @export * @class BundleDeployer */ class BundleDeployer { /** * Constructor for a BundleDeployer. * @param {IHandlerParameters} params - The Imperative handler parameters * @throws ImperativeError * @memberof BundleDeployer */ constructor(params) { this.PROGRESS_BAR_INTERVAL = 375; // milliseconds this.PROGRESS_BAR_INCREMENT = 0.25; // percent this.PROGRESS_BAR_MAX = 67; // percent this.parmsValidated = false; this.hlqsValidated = false; this.useResponseProgressBar = true; this.jobOutput = ""; this.params = params; this.progressBar = { percentComplete: 0, stageName: imperative_1.TaskStage.NOT_STARTED, statusMessage: "" }; } /** * Deploy a CICS Bundle * @param {AbstractSession} session - An optional zOSMF session * @returns {Promise<string>} * @throws ImperativeError * @memberof BundleDeployer */ deployBundle(session, task) { return __awaiter(this, void 0, void 0, function* () { // Validate that the parms are valid for Deploy this.validateDeployParms(); // Create a zosMF session if (session === undefined) { session = yield this.createZosMFSession(); } // Check that the CICS dataset value looks valid and can be viewed yield this.checkHLQDatasets(session); // Generate some DFHDPLOY JCL const jcl = this.getDeployJCL(); // Submit it return this.submitJCL(jcl, "DEPLOY", session, task); }); } /** * Undeploy a CICS Bundle * @param {AbstractSession} session - An optional zOSMF session * @returns {Promise<string>} * @throws ImperativeError * @memberof BundleDeployer */ undeployBundle(session, task) { return __awaiter(this, void 0, void 0, function* () { // Validate that the parms are valid for Undeploy this.validateUndeployParms(); // Create a zosMF session if (session === undefined) { session = yield this.createZosMFSession(); } // Check that the CICS dataset value looks valid and can be viewed yield this.checkHLQDatasets(session); // Generate some DFHDPLOY JCL const jcl = this.getUndeployJCL(); // Submit it return this.submitJCL(jcl, "UNDEPLOY", session, task); }); } /** * Validate the input parameters are suitable for the DEPLOY action * @returns {Promise<string>} * @throws ImperativeError * @memberof BundleDeployer */ validateDeployParms() { if (this.parmsValidated === false) { ParmValidator_1.ParmValidator.validateDeploy(this.params); this.parmsValidated = true; } } /** * Validate the input parameters are suitable for the UNDEPLOY action * @returns {Promise<string>} * @throws ImperativeError * @memberof BundleDeployer */ validateUndeployParms() { if (this.parmsValidated === false) { ParmValidator_1.ParmValidator.validateUndeploy(this.params); this.parmsValidated = true; } } /** * Retrieves the output from the most recently completed DFHDPLOY JCL job. * @returns {string} * @throws ImperativeError * @memberof BundleDeployer */ getJobOutput() { return this.jobOutput; } wrapLongLineForJCL(lineOfText) { const MAX_LINE_LEN = 71; let tempVal = lineOfText; let returnVal = ""; while (tempVal.length > MAX_LINE_LEN) { returnVal = returnVal + tempVal.substring(0, MAX_LINE_LEN) + "\n"; tempVal = " " + tempVal.substring(MAX_LINE_LEN); } returnVal = returnVal + tempVal; return returnVal; } /** * Construct the DFHDPLOY DEPLOY BUNDLE JCL. * @returns {string} * @throws ImperativeError * @memberof BundleDeployer */ getDeployJCL() { // Get rid of any extra slashes which may be needed on git-bash to avoid path munging const bundledir = path.posix.normalize(this.params.arguments.bundledir); let jcl = this.generateCommonJCLHeader() + this.wrapLongLineForJCL("DEPLOY BUNDLE(" + this.params.arguments.name + ")\n") + this.wrapLongLineForJCL(" BUNDLEDIR(" + bundledir + ")\n"); if (this.params.arguments.description !== undefined) { jcl += this.wrapLongLineForJCL(" DESCRIPTION(" + this.params.arguments.description + ")\n"); } jcl += this.generateCommonJCLFooter(); return jcl; } /** * Construct the DFHDPLOY UNDEPLOY BUNDLE JCL. * @returns {string} * @throws ImperativeError * @memberof BundleDeployer */ getUndeployJCL() { const jcl = this.generateCommonJCLHeader() + this.wrapLongLineForJCL("UNDEPLOY BUNDLE(" + this.params.arguments.name + ")\n") + this.generateCommonJCLFooter(); return jcl; } generateCommonJCLHeader() { const jcl = this.params.arguments.jobcard + "\n" + "//DFHDPLOY EXEC PGM=DFHDPLOY,REGION=100M\n" + "//STEPLIB DD DISP=SHR,DSN=" + this.params.arguments.cicshlq + ".SDFHLOAD\n" + "// DD DISP=SHR,DSN=" + this.params.arguments.cpsmhlq + ".SEYUAUTH\n" + "//SYSTSPRT DD SYSOUT=*\n" + "//SYSIN DD *\n" + "*\n" + this.wrapLongLineForJCL("SET CICSPLEX(" + this.params.arguments.cicsplex + ");\n") + this.wrapLongLineForJCL("*\n"); return jcl; } generateCommonJCLFooter() { let jcl = this.wrapLongLineForJCL(" SCOPE(" + this.params.arguments.scope + ")\n") + this.wrapLongLineForJCL(" STATE(" + this.params.arguments.targetstate + ")\n"); if (this.params.arguments.timeout !== undefined) { jcl = jcl + this.wrapLongLineForJCL(" TIMEOUT(" + this.params.arguments.timeout + ")\n"); } if (this.params.arguments.csdgroup !== undefined) { jcl = jcl + this.wrapLongLineForJCL(" CSDGROUP(" + this.params.arguments.csdgroup + ");\n"); } if (this.params.arguments.resgroup !== undefined) { jcl = jcl + this.wrapLongLineForJCL(" RESGROUP(" + this.params.arguments.resgroup + ");\n"); } // finally add a terminator jcl = jcl + "/*\n"; return jcl; } createZosMFSession() { return __awaiter(this, void 0, void 0, function* () { // Create a zosMF session let zosmfProfile; try { zosmfProfile = this.params.profiles.get("zosmf"); } catch (error) { // No-op, we can cope with there being no profile. } if (zosmfProfile === undefined) { zosmfProfile = {}; } ZosmfConfig_1.ZosmfConfig.mergeProfile(zosmfProfile, this.params); return cli_1.ZosmfSession.createBasicZosmfSession(zosmfProfile); }); } checkHLQDatasets(session) { return __awaiter(this, void 0, void 0, function* () { // No need to revalidate multiple times during a push command if (this.hlqsValidated === true) { return; } // Check that the CICS dataset value looks valid and can be viewed // Access errors will trigger an Exception const cicspds = this.params.arguments.cicshlq + ".SDFHLOAD"; let listResp; try { listResp = yield cli_1.List.allMembers(session, cicspds, {}); } catch (error) { throw new Error("Validation of --cicshlq dataset failed: " + error.message); } if (JSON.stringify(listResp).indexOf("DFHDPLOY") === -1) { throw new Error("DFHDPLOY not found in SDFHLOAD within the --cicshlq dataset: " + cicspds); } // Check that the CPSM dataset value looks valid and can be viewed // Access errors will trigger an Exception const cpsmpds = this.params.arguments.cpsmhlq + ".SEYUAUTH"; try { listResp = yield cli_1.List.allMembers(session, cpsmpds, {}); } catch (error) { throw new Error("Validation of --cpsmhlq dataset failed: " + error.message); } if (JSON.stringify(listResp).indexOf("EYU9ABSI") === -1) { throw new Error("EYU9ABSI not found in SEYUAUTH within the --cpsmhlq dataset: " + cpsmpds); } this.hlqsValidated = true; }); } updateProgressBar(action) { // Increment the progress bar. This will refresh what the user sees on the console. this.progressBar.percentComplete += this.PROGRESS_BAR_INCREMENT; // Have a look at the status message for the progress bar, has it been updated with // the jobid yet? If so, parse it out and refresh the message. if (this.jobId === "UNKNOWN" && this.progressBar.statusMessage) { const statusWords = this.progressBar.statusMessage.split(" "); if (statusWords.length >= 2) { if (statusWords[2] !== undefined && statusWords[2].indexOf("JOB") === 0) { this.jobId = statusWords[2]; this.progressBar.statusMessage = "Running DFHDPLOY (" + action + "), jobid " + this.jobId; this.endProgressBar(); // log the jobid for posterity if (this.params.arguments.verbose) { this.params.response.console.log(this.progressBar.statusMessage + "\n"); } if (this.params.arguments.silent === undefined) { const logger = imperative_1.Logger.getAppLogger(); logger.debug(this.progressBar.statusMessage); } this.startProgressBar(); } } } // Continue iterating on progress updates until we've reached the max value, // or until the processing has completed. if (this.progressBar.percentComplete < this.PROGRESS_BAR_MAX && this.progressBar.stageName === imperative_1.TaskStage.IN_PROGRESS) { setTimeout(this.updateProgressBar.bind(this), this.PROGRESS_BAR_INTERVAL, action); } } submitJCL(jcl, action, session, task) { return __awaiter(this, void 0, void 0, function* () { let spoolOutput; if (task) { this.progressBar = task; this.useResponseProgressBar = false; } this.progressBar.percentComplete = imperative_1.TaskProgress.TEN_PERCENT; this.progressBar.statusMessage = "Submitting DFHDPLOY JCL for the " + action + " action"; this.progressBar.stageName = imperative_1.TaskStage.IN_PROGRESS; this.startProgressBar(); this.jobId = "UNKNOWN"; this.jobOutput = ""; // Refresh the progress bar by 1% every second or so up to a max of 67%. // SubmitJobs will initialise it to 30% and set it to 70% when it // completes, we tweak it every so often until then for purely cosmetic purposes. setTimeout(this.updateProgressBar.bind(this), this.PROGRESS_BAR_INTERVAL, action); try { spoolOutput = yield cli_1.SubmitJobs.submitJclString(session, jcl, { jclSource: "", task: this.progressBar, viewAllSpoolContent: true }); } catch (error) { this.progressBar.stageName = imperative_1.TaskStage.FAILED; throw new Error("Failure occurred submitting DFHDPLOY JCL for jobid " + this.jobId + ": '" + error.message + "'. Most recent status update: '" + this.progressBar.statusMessage + "'."); } // Find the output for (const file of spoolOutput) { // we're looking for the SYSTSPRT output from the DFHDPLOY step. if (file.ddName === "SYSTSPRT" && file.stepName === "DFHDPLOY") { if (file.data === undefined || file.data.length === 0) { this.progressBar.stageName = imperative_1.TaskStage.FAILED; throw new Error("DFHDPLOY did not generate any output for jobid " + this.jobId + ". Most recent status update: '" + this.progressBar.statusMessage + "'."); } // log the full output for serviceability to the log if (this.params.arguments.silent === undefined) { const logger = imperative_1.Logger.getAppLogger(); logger.debug(file.data); } this.jobOutput = file.data; // Finish the progress bar this.progressBar.statusMessage = "Completed DFHDPLOY"; this.endProgressBar(); // Did DFHDPLOY fail? if (file.data.indexOf("DFHRL2055I") > -1) { this.progressBar.stageName = imperative_1.TaskStage.FAILED; // log the error output to the console this.params.response.console.log(file.data); throw new Error("DFHDPLOY stopped processing for jobid " + this.jobId + " due to an error."); } if (file.data.indexOf("DFHRL2043I") > -1) { this.progressBar.stageName = imperative_1.TaskStage.COMPLETE; // log the error output to the console this.params.response.console.log(file.data); return "DFHDPLOY completed with warnings."; } if (file.data.indexOf("DFHRL2012I") > -1) { this.progressBar.stageName = imperative_1.TaskStage.COMPLETE; // only log the output to the console if verbose output is enabled if (this.params.arguments.verbose) { this.params.response.console.log(file.data); } return "Bundle deployment successful."; } if (file.data.indexOf("DFHRL2037I") > -1) { this.progressBar.stageName = imperative_1.TaskStage.COMPLETE; // only log the output to the console if verbose output is enabled if (this.params.arguments.verbose) { this.params.response.console.log(file.data); } return "Bundle undeployment successful."; } this.progressBar.stageName = imperative_1.TaskStage.FAILED; // log the error output to the console this.params.response.console.log(file.data); throw new Error("DFHDPLOY command completed for jobid " + this.jobId + ", but status cannot be determined."); } } // If we haven't found SYSTSPRT then echo JESMSGLG instead for (const file of spoolOutput) { if (file.ddName === "JESMSGLG") { this.progressBar.stageName = imperative_1.TaskStage.FAILED; // log the error output to the console this.params.response.console.log(file.data); throw new Error("DFHDPLOY command completed in error for jobid " + this.jobId + " without generating SYSTSPRT output."); } } this.progressBar.stageName = imperative_1.TaskStage.FAILED; throw new Error("SYSTSPRT and JESMSGLG output from DFHDPLOY not found for jobid " + this.jobId + ". Most recent status update: '" + this.progressBar.statusMessage + "'."); }); } startProgressBar() { if (this.params.arguments.verbose !== true && this.useResponseProgressBar === true) { this.params.response.progress.startBar({ task: this.progressBar }); } } endProgressBar() { if (this.params.arguments.verbose !== true && this.useResponseProgressBar === true) { this.params.response.progress.endBar(); } } } exports.BundleDeployer = BundleDeployer; //# sourceMappingURL=BundleDeployer.js.map