UNPKG

jupyterlab-slurm

Version:

A JupyterLab extension to interface with the Slurm workload manager.

240 lines 11.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const react_bootstrap_1 = require("react-bootstrap"); const uuid_1 = require("uuid"); // Local const utils_1 = require("../utils"); const DataTable_1 = __importDefault(require("./DataTable")); const JobSubmitModal_1 = __importDefault(require("./JobSubmitModal")); const config = __importStar(require("../slurm-config/config.json")); class SlurmManager extends react_1.Component { constructor(props) { super(props); // The column index of job ID this.JOBID_IDX = 0; // The column index of the username this.USER_IDX = 3; this.requestStatusTable = new Map(); this.state = { alerts: [], jobSubmitModalVisible: false, userOnly: config['userOnly'], processingJobs: false, reloading: false }; } toggleUserOnly() { const { userOnly } = this.state; this.setState({ userOnly: !userOnly }); } showJobSubmitModal() { this.setState({ jobSubmitModalVisible: true }); } hideJobSubmitModal() { this.setState({ jobSubmitModalVisible: false }); } addAlert(alert) { const { alerts } = this.state; this.setState({ alerts: alerts.concat([alert]) }); } makeJobRequest(route, method, body) { return __awaiter(this, void 0, void 0, function* () { const beforeResponse = () => { this.setState({ processingJobs: true }); const requestID = uuid_1.v4(); this.requestStatusTable.set(requestID, 'sent'); return [requestID]; }; const afterResponse = (response, requestID) => __awaiter(this, void 0, void 0, function* () { if (response.status !== 200) { this.requestStatusTable.set(requestID, 'error'); this.setState({ processingJobs: false }); throw Error(response.statusText); } else { const json = yield response.json(); let alert = { message: json.responseMessage }; if (json.returncode === 0) { alert.variant = 'success'; this.requestStatusTable.set(requestID, 'received'); } else { alert.variant = 'danger'; this.requestStatusTable.set(requestID, 'error'); console.log(json.errorMessage); } this.addAlert(alert); // Remove request pending classes from the element; // the element may be a table row or the entire // extension panel // TODO: do something to row with matching job id // if (element) { // element.removeClass("pending"); // } this.setState({ processingJobs: false }); // TODO: the alert and removing of the pending class // should probably occur after table reload completes, // but we'll need to rework synchronization here.. // If all current jobs have finished executing, // reload the queue (using squeue) let allJobsFinished = true; this.requestStatusTable.forEach((status, requestID) => { if (status === 'sent' || status === 'error') { allJobsFinished = false; } }); if (allJobsFinished) { console.log('All jobs finished.'); } } }); utils_1.makeRequest({ route, method, body, beforeResponse, afterResponse }); }); } processSelectedJobs(action, rows) { const { route, method } = (action => { switch (action) { case 'kill': return { route: 'scancel', method: 'DELETE' }; case 'hold': return { route: 'scontrol/hold', method: 'PATCH' }; case 'release': return { route: 'scontrol/release', method: 'PATCH' }; } })(action); // TODO: Change backend and do all of this in a single request rows.map(row => { const jobID = row[this.JOBID_IDX]; this.makeJobRequest(route, method, JSON.stringify({ jobID })); }); } submitJob(input, inputType) { this.setState({ jobSubmitDisabled: true, processingJobs: true }); let { serverRoot, filebrowser } = this.props; const fileBrowserRelativePath = filebrowser.model.path; if (serverRoot !== '/') { // Add trailing slash, but not to '/' serverRoot += '/'; } const fileBrowserPath = serverRoot + fileBrowserRelativePath; const outputDir = encodeURIComponent(fileBrowserPath); utils_1.makeRequest({ route: 'sbatch', method: 'POST', query: `?inputType=${inputType}&outputDir=${outputDir}`, body: JSON.stringify({ "input": input }), afterResponse: (response) => __awaiter(this, void 0, void 0, function* () { if (response.ok) { return yield response.json(); } return null; }) }).then((result) => { if (result === null) { this.setState({ jobSubmitError: "Unknown error encountered while submitting the script. Try again later.", jobSubmitDisabled: false, processingJobs: false }); } if (result["returncode"] !== 0) { this.setState({ jobSubmitError: result["errorMessage"] == "" ? result["responseMessage"] : result["errorMessage"], jobSubmitDisabled: false, processingJobs: false }); } else { this.hideJobSubmitModal(); this.setState({ jobSubmitDisabled: false, processingJobs: false }); } }); } render() { /** We should get rid of this. If we need a higher level item to perform actions, we can create a dispatch system */ const buttons = [{ name: 'Submit Job', id: 'submit-job', action: () => { this.showJobSubmitModal(); }, props: { variant: 'primary', }, }, { action: 'reload', id: 'reload', props: { variant: 'secondary', }, }, { action: 'clear-selected', id: 'clear-selected', props: { variant: 'warning', }, }, { name: 'Kill Selected Job(s)', id: 'kill-selected', action: (rows) => { this.processSelectedJobs('kill', rows); }, props: { variant: 'danger', }, }, { name: 'Hold Selected Job(s)', id: 'hold-selected', action: (rows) => { this.processSelectedJobs('hold', rows); }, props: { variant: 'danger', }, }, { name: 'Release Selected Job(s)', id: 'release-selected', action: (rows) => { this.processSelectedJobs('release', rows); }, props: { variant: 'danger', }, }]; const { alerts, jobSubmitModalVisible } = this.state; return (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(DataTable_1.default, { buttons: buttons, availableColumns: config['queueCols'], userOnly: this.state.userOnly, processing: this.state.processingJobs, reloading: this.state.reloading }), react_1.default.createElement("div", null, react_1.default.createElement(react_bootstrap_1.Form.Check, { type: "checkbox", id: "user-only-checkbox", label: "Show my jobs only", onChange: this.toggleUserOnly.bind(this), defaultChecked: config["userOnly"] })), react_1.default.createElement("div", { id: "alertContainer", className: "container alert-container" }, alerts.map((alert, index) => (react_1.default.createElement(react_bootstrap_1.Alert, { variant: alert.variant, key: `alert-${index}`, dismissible: true, onClose: () => { this.state.alerts.splice(index, 1); this.setState({}); } }, alert.message)))), react_1.default.createElement(JobSubmitModal_1.default, { show: jobSubmitModalVisible, error: this.state.jobSubmitError, onHide: this.hideJobSubmitModal.bind(this), submitJob: this.submitJob.bind(this), disabled: this.state.jobSubmitDisabled }))); } } exports.default = SlurmManager; //# sourceMappingURL=SlurmManager.js.map