/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Ember from 'ember'; import Constants from 'yarn-ui/constants'; export default Ember.Controller.extend({ queryParams: ["service", "attempt", "containerid"], service: undefined, attempt: undefined, containerid: undefined, selectedAttemptId: "", attemptContainerList: null, selectedContainerId: "", selectedLogFileName: "", containerLogFiles: null, selectedContainerThreaddumpContent: "", containerNodeMappings: [], threaddumpErrorMessage: "", stdoutLogsFetchFailedError: "", appAttemptToStateMappings: [], _isLoadingTopPanel: false, _isLoadingBottomPanel: false, _isThreaddumpAttemptFailed: false, _isStdoutLogsFetchFailed: false, initializeSelect: function(selector = ".js-fetch-attempt-containers") { Ember.run.schedule("afterRender", this, function() { $(selector).select2({ width: "350px", multiple: false }); }); }, actions: { showContainersForAttemptId(attemptId, containerId = "") { this.set("selectedAttemptId", ""); if (attemptId) { this.set("_isLoadingTopPanel", true); this.set("selectedAttemptId", attemptId); this.fetchRunningContainersForAttemptId(attemptId) .then(hash => { let containers = null; let containerIdArr = {}; var containerNodeMapping = null; var nodeHttpAddress = null; // Getting RUNNING containers from the RM first. if ( hash.rmContainers.get("length") > 0 && hash.rmContainers.get("content") ) { // Create a container-to-NMnode mapping for later use. hash.rmContainers.get("content").forEach( function(containerInfo) { nodeHttpAddress = containerInfo._data.nodeHttpAddress; nodeHttpAddress = nodeHttpAddress .replace(/(^\w+:|^)\/\//, ''); containerNodeMapping = Ember.Object.create({ name: containerInfo.id, value: nodeHttpAddress }); this.containerNodeMappings.push(containerNodeMapping); containerIdArr[containerInfo.id] = true; }.bind(this)); containers = (containers || []).concat( hash.rmContainers.get("content") ); } this.set("attemptContainerList", containers); this.initializeSelect(".js-fetch-threaddump-containers"); if (containerId) { this.send("showThreaddumpForContainer", containerId); } }) .finally(() => { this.set("_isLoadingTopPanel", false); }); } else { this.set("attemptContainerList", null); this.set("selectedContainerId", ""); this.set("containerLogFiles", null); this.set("selectedLogFileName", ""); this.set("selectedContainerThreaddumpContent", ""); this.set("containerNodeMappings", []); } }, showThreaddumpForContainer(containerIdForThreaddump) { Ember.$("#logContentTextArea1234554321").val(""); this.set("showFullThreaddump", false); this.set("selectedContainerId", containerIdForThreaddump); this.set("_isLoadingBottomPanel", true); this.set("_isStdoutLogsFetchFailed", false); this.set("stdoutLogsFetchFailedError", ""); this.set("_isThreaddumpAttemptFailed", false); this.set("threaddumpErrorMessage", ""); if (containerIdForThreaddump) { this.set("selectedContainerThreaddumpContent", ""); this.fetchThreaddumpForContainer(containerIdForThreaddump) .then(function() { Ember.Logger.log("Threaddump attempt has been successful!"); var currentContainerId = null; var currentContainerNode = null; var nodeForContainerThreaddump = null; // Fetch the NM node for the selected container from the // mappings stored above when this.containerNodeMappings.forEach(function(item) { currentContainerId = item.name; currentContainerNode = item.value; if ((currentContainerId === containerIdForThreaddump) && nodeForContainerThreaddump == null) { nodeForContainerThreaddump = currentContainerNode; } }); if (nodeForContainerThreaddump) { // Fetch stdout logs after 1.2 seconds of a successful POST // request to RM. This is to allow for sufficient time for the NM // to heartbeat into RM (default of 1 second) whereupon it will // receive RM's signal to post a threaddump that will be captured // in the stdout logs of the container. The extra 200ms is to // allow for any network/IO delays. Ember.run.later((function() { this.fetchLogsForContainer(nodeForContainerThreaddump, containerIdForThreaddump, "stdout") .then( hash => { this.set("selectedContainerThreaddumpContent", hash.logs.get('logs').trim()); }.bind(this)) .catch(function(error) { this.set("_isStdoutLogsFetchFailed", true); this.set("stdoutLogsFetchFailedError", error); }.bind(this)) .finally(function() { this.set("_isLoadingBottomPanel", false); }.bind(this)); }.bind(this)), 1200); } }.bind(this), function(error) { this.set("_isThreaddumpAttemptFailed", true); this.set("threaddumpErrorMessage", error); this.set("_isLoadingBottomPanel", false); }.bind(this)).finally(function() { this.set("selectedContainerThreaddumpContent", ""); }.bind(this)); } else { this.set("selectedContainerId", ""); this.set("selectedContainerThreaddumpContent", ""); } } }, // Set up the running attempt for the first time threaddump button is clicked. runningAttempt: Ember.computed("model.attempts", function() { let attempts = this.get("model.attempts"); let runningAttemptId = null; if (attempts && attempts.get("length") && attempts.get("content")) { attempts.get("content").forEach(function(appAttempt) { if (appAttempt._data.state === "RUNNING") { runningAttemptId = appAttempt._data.appAttemptId; } }); } return runningAttemptId; }), /** * Create and send fetch running containers request. */ fetchRunningContainersForAttemptId(attemptId) { let request = {}; request["rmContainers"] = this.store .query("yarn-container", { app_attempt_id: attemptId }) .catch(function(error) { return Ember.A(); }); return Ember.RSVP.hash(request); }, /** * Create and send fetch logs request for a selected container, from a * specific node. */ fetchLogsForContainer(nodeForContainer, containerId, logFile) { let request = {}; request["logs"] = this.store .queryRecord("yarn-node-container-log", { nodeHttpAddr: nodeForContainer, containerId: containerId, fileName: logFile }) return Ember.RSVP.hash(request); }, /** * Send out a POST request to RM to signal a threaddump for the selected * container for the currently logged-in user. */ fetchThreaddumpForContainer(containerId) { var adapter = this.store.adapterFor("yarn-container-threaddump"); let requestedUser = ""; if (this.model && this.model.userInfo && this.model.userInfo.get('firstObject')) { requestedUser = this.model.userInfo .get('firstObject') .get('requestedUser'); } return adapter.signalContainerForThreaddump({}, containerId, requestedUser); }, /** * Reset attributes after a refresh. */ resetAfterRefresh() { this.set("selectedAttemptId", ""); this.set("attemptContainerList", null); this.set("selectedContainerId", ""); this.set("selectedLogFileName", ""); this.set("containerLogFiles", null); this.set("selectedContainerThreaddumpContent", ""); this.set("containerNodeMappings", []); this.set("_isThreaddumpAttemptFailed", false); this.set("threaddumpErrorMessage", ""); this.set("_isStdoutLogsFetchFailed", false); this.set("stdoutLogsFetchFailedError", ""); this.set("appAttemptToStateMappings", []); }, // Set the threaddump content in the content area. showThreaddumpContent: Ember.computed( "selectedContainerThreaddumpContent", function() { return this.get("selectedContainerThreaddumpContent"); } ), /** * Check if the application for the container whose threaddump is being attempted * is even running. */ isRunningApp: Ember.computed('model.app.state', function() { return this.get('model.app.state') === "RUNNING"; }) });