Updated Branches: refs/heads/trunk 82a104ee6 -> fca0b5c6e
AMBARI-2641. Perf: Background Ops dialog pegs CPU on a large cluster. (Andrii Tkach via srimanth) Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/fca0b5c6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/fca0b5c6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/fca0b5c6 Branch: refs/heads/trunk Commit: fca0b5c6e412f1df7c9b7dbf5bba05e8425774f6 Parents: 82a104e Author: Srimanth Gunturi <[email protected]> Authored: Fri Jul 12 15:20:55 2013 -0700 Committer: Srimanth Gunturi <[email protected]> Committed: Fri Jul 12 15:20:55 2013 -0700 ---------------------------------------------------------------------- .../global/background_operations_controller.js | 62 ++-- ambari-web/app/utils/host_progress_popup.js | 326 ++++++++++++------- 2 files changed, 241 insertions(+), 147 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/fca0b5c6/ambari-web/app/controllers/global/background_operations_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/global/background_operations_controller.js b/ambari-web/app/controllers/global/background_operations_controller.js index 636cf70..4c016ca 100644 --- a/ambari-web/app/controllers/global/background_operations_controller.js +++ b/ambari-web/app/controllers/global/background_operations_controller.js @@ -67,40 +67,36 @@ App.BackgroundOperationsController = Em.Controller.extend({ var self = this; data.items = data.items.sort(function(a,b){return b.Requests.id - a.Requests.id}); data.items.forEach(function(request){ + var hostsMap = {}; + var isRunningTasks = false; + request.tasks.forEach(function (task) { + if (!isRunningTasks && (['QUEUED', 'IN_PROGRESS', 'PENDING'].contains(task.Tasks.status))) { + isRunningTasks = true; + } + if (hostsMap[task.Tasks.host_name]) { + hostsMap[task.Tasks.host_name].logTasks.push(task); + } else { + hostsMap[task.Tasks.host_name] = { + name: task.Tasks.host_name, + publicName: task.Tasks.host_name, + logTasks: [task] + }; + } + }, this); + var hosts = []; + for(var hostName in hostsMap){ + hosts.push(hostsMap[hostName]); + } var rq = Em.Object.create({ - id:request.Requests.id, - name: 'Request name not specified', - displayName: 'Request name not specified', - progress:10, + id: request.Requests.id, + name: request.Requests.request_context || 'Request name not specified', + displayName: request.Requests.request_context || 'Request name not specified', + progress: 10, status: "", - isRunning: false, - hosts: [] - }); - if(request.Requests.request_context != ""){ - rq.name = request.Requests.request_context; - rq.displayName = request.Requests.request_context; - } - - var runningTasks = 0; - runningTasks = request.tasks.filterProperty('Tasks.status', 'QUEUED').length; - runningTasks += request.tasks.filterProperty('Tasks.status', 'IN_PROGRESS').length; - runningTasks += request.tasks.filterProperty('Tasks.status', 'PENDING').length; - if(runningTasks > 0){ - rq.set('isRunning', true); - runningServices++; - } else { - rq.set('isRunning', false); - } - - var hostNames = request.tasks.mapProperty('Tasks.host_name').uniq(); - hostNames.forEach(function (name) { - var tasks = request.tasks.filterProperty("Tasks.host_name",name); - rq.get("hosts").push({ - name: name, - publicName: name, - logTasks: tasks - }); + isRunning: isRunningTasks, + hosts: hosts }); + runningServices += ~~isRunningTasks; self.get("services").push(rq); }); self.set("allOperationsCount",runningServices); @@ -113,9 +109,7 @@ App.BackgroundOperationsController = Em.Controller.extend({ * @return PopupObject For testing purposes */ showPopup: function(){ - if(!App.testMode){ - App.updater.immediateRun('requestMostRecent'); - } + App.updater.immediateRun('requestMostRecent'); return App.HostPopup.initPopup("", this, true); } http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/fca0b5c6/ambari-web/app/utils/host_progress_popup.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/host_progress_popup.js b/ambari-web/app/utils/host_progress_popup.js index 8f5243f..b0b2f59 100644 --- a/ambari-web/app/utils/host_progress_popup.js +++ b/ambari-web/app/utils/host_progress_popup.js @@ -62,7 +62,7 @@ App.HostPopup = Em.Object.create({ this.set("servicesInfo", null); this.set("inputData", null); this.set("inputData", this.get("serviceController.services")); - this.createPopup(); + return this.createPopup(); }, /** @@ -70,23 +70,47 @@ App.HostPopup = Em.Object.create({ * @param tasks * @return {Array} [Status, Icon type, Progressbar color, is IN_PROGRESS] */ - getStatus: function (tasks) { - if (tasks.everyProperty('Tasks.status', 'COMPLETED')) { - return ['SUCCESS', 'icon-ok', 'progress-success', false]; - } - if (tasks.someProperty('Tasks.status', 'FAILED')) { - return ['FAILED', 'icon-exclamation-sign', 'progress-danger', false]; - } - if (tasks.someProperty('Tasks.status', 'ABORTED')) { - return ['CANCELLED', 'icon-minus', 'progress-warning', false]; - } - if (tasks.someProperty('Tasks.status', 'TIMEDOUT')) { - return ['TIMEDOUT', 'icon-time', 'progress-warning', false]; + getStatus: function(tasks){ + var isCompleted = true; + var status; + var tasksLength = tasks.length; + var isFailed = false; + var isAborted = false; + var isTimedout = false; + var isInProgress = false; + for (var i = 0; i < tasksLength; i++) { + if (tasks[i].Tasks.status !== 'COMPLETED') { + isCompleted = false; + } + if(tasks[i].Tasks.status === 'FAILED'){ + isFailed = true; + } + if (tasks[i].Tasks.status === 'ABORTED') { + isAborted = true; + } + if (tasks[i].Tasks.status === 'TIMEDOUT') { + isTimedout = true; + } + if (tasks[i].Tasks.status === 'IN_PROGRESS') { + isInProgress = true; + } + }; + if (isFailed) { + status = ['FAILED', 'icon-exclamation-sign', 'progress-danger', false]; + } else if (isAborted) { + status = ['CANCELLED', 'icon-minus', 'progress-warning', false]; + } else if (isTimedout) { + status = ['TIMEDOUT', 'icon-time', 'progress-warning', false]; + } else if (isInProgress) { + status = ['IN_PROGRESS', 'icon-cogs', 'progress-info', true]; } - if (tasks.someProperty('Tasks.status', 'IN_PROGRESS')) { - return ['IN_PROGRESS', 'icon-cogs', 'progress-info', true]; + if(status){ + return status; + } else if(isCompleted){ + return ['SUCCESS', 'icon-ok', 'progress-success', false]; + } else { + return ['PENDING', 'icon-cog', 'progress-info', true]; } - return ['PENDING', 'icon-cog', 'progress-info', true]; }, /** @@ -95,16 +119,19 @@ App.HostPopup = Em.Object.create({ * @return {Number} percent of completion */ getProgress: function (tasks) { - var progress = 0; - var actionsNumber = tasks.length; - var completedActions = tasks.filterProperty('Tasks.status', 'COMPLETED').length - + tasks.filterProperty('Tasks.status', 'FAILED').length - + tasks.filterProperty('Tasks.status', 'ABORTED').length - + tasks.filterProperty('Tasks.status', 'TIMEDOUT').length; - var queuedActions = tasks.filterProperty('Tasks.status', 'QUEUED').length; - var inProgressActions = tasks.filterProperty('Tasks.status', 'IN_PROGRESS').length; - progress = Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / actionsNumber * 100); - return progress; + var completedActions = 0; + var queuedActions = 0; + var inProgressActions = 0; + tasks.forEach(function(task){ + if(['COMPLETED', 'FAILED', 'ABORTED', 'TIMEDOUT'].contains(task.Tasks.status)){ + completedActions++; + } else if(task.Tasks.status === 'QUEUED'){ + queuedActions++; + } else if(task.Tasks.status === 'IN_PROGRESS'){ + inProgressActions++; + } + }); + return Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / tasks.length * 100); }, /** @@ -161,12 +188,30 @@ App.HostPopup = Em.Object.create({ allNewServices.push(newService); }); self.set('servicesInfo', allNewServices); - if (this.get("serviceName") == "") - this.setBackgroundOperationHeader(); + if (this.get("serviceName") == "") this.setBackgroundOperationHeader(); } }.observes("inputData"), /** + * update icon of task depending on its status + * @param taskInfo + */ + updateTaskIcon: function(taskInfo){ + if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') { + taskInfo.set('icon', 'icon-cog'); + } else if (taskInfo.get('status') == 'in_progress') { + taskInfo.set('icon', 'icon-cogs'); + } else if (taskInfo.get('status') == 'completed') { + taskInfo.set('icon', ' icon-ok'); + } else if (taskInfo.get('status') == 'failed') { + taskInfo.set('icon', 'icon-exclamation-sign'); + } else if (taskInfo.get('status') == 'aborted') { + taskInfo.set('icon', 'icon-minus'); + } else if (taskInfo.get('status') == 'timedout') { + taskInfo.set('icon', 'icon-time'); + } + }, + /** * Create hosts and tasks data structure for popup * Set data for hosts and tasks */ @@ -182,8 +227,7 @@ App.HostPopup = Em.Object.create({ host.setEach("serviceName", service.name); hosts.push.apply(hosts, host); }); - } - else { + } else { if (this.get("currentServiceId") != null) { hostsData = hostsData.filterProperty("id", this.get("currentServiceId")).objectAt(0); } @@ -199,74 +243,142 @@ App.HostPopup = Em.Object.create({ } } + var existedHosts = self.get('hosts'); + if (hosts) { - /** - * sort host names by name value - */ - this.sortArray(hosts, "name"); - - hosts.forEach(function (_host) { - var tasks = _host.logTasks; - var hostInfo = Ember.Object.create({}); - hostInfo.set('name', _host.name); - hostInfo.set('publicName', _host.publicName); - hostInfo.set('progress', 0); - hostInfo.set('status', App.format.taskStatus("PENDING")); - hostInfo.set('serviceName', _host.serviceName); - hostInfo.set('isVisible', true); - hostInfo.set('icon', "icon-cog"); - hostInfo.set('barColor', "progress-info"); - hostInfo.set('barWidth', "width:0%;"); - - tasks = self.sortTasksById(tasks); - var tasksArr = []; - - if (tasks.length) { - - var hostStatus = self.getStatus(tasks); - var hostProgress = self.getProgress(tasks); - hostInfo.set('status', App.format.taskStatus(hostStatus[0])); - hostInfo.set('icon', hostStatus[1]); - hostInfo.set('barColor', hostStatus[2]); - hostInfo.set('isInProgress', hostStatus[3]); - hostInfo.set('progress', hostProgress); - hostInfo.set('barWidth', "width:" + hostProgress + "%;"); - - tasks.forEach(function (_task) { - var taskInfo = Ember.Object.create({}); - taskInfo.set('id', _task.Tasks.id); - taskInfo.set('hostName', _host.publicName); - taskInfo.set('command', _task.Tasks.command.toLowerCase()); - taskInfo.set('status', App.format.taskStatus(_task.Tasks.status)); - taskInfo.set('role', App.format.role(_task.Tasks.role)); - taskInfo.set('stderr', _task.Tasks.stderr); - taskInfo.set('stdout', _task.Tasks.stdout); - taskInfo.set('isVisible', true); - taskInfo.set('icon', 'icon-cogs'); - if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') { - taskInfo.set('icon', 'icon-cog'); - } else if (taskInfo.get('status') == 'in_progress') { - taskInfo.set('icon', 'icon-cogs'); - } else if (taskInfo.get('status') == 'completed') { - taskInfo.set('icon', ' icon-ok'); - } else if (taskInfo.get('status') == 'failed') { - taskInfo.set('icon', 'icon-exclamation-sign'); - } else if (taskInfo.get('status') == 'aborted') { - taskInfo.set('icon', 'icon-minus'); - } else if (taskInfo.get('status') == 'timedout') { - taskInfo.set('icon', 'icon-time'); + if(existedHosts && existedHosts.length === hosts.length){ + existedHosts.forEach(function(host){ + var newHostInfo = hosts.findProperty('name', host.get('name')); + if(newHostInfo){ + var hostStatus = self.getStatus(newHostInfo.logTasks); + var hostProgress = self.getProgress(newHostInfo.logTasks); + host.set('status', App.format.taskStatus(hostStatus[0])); + host.set('icon', hostStatus[1]); + host.set('barColor', hostStatus[2]); + host.set('isInProgress', hostStatus[3]); + host.set('progress', hostProgress); + host.set('barWidth', "width:" + hostProgress + "%;"); + var existTasks = host.get('tasks'); + var newTasks = newHostInfo.logTasks; + if(existTasks && newTasks && existTasks.length==newTasks.length){ + // Same number of source and destinations + var existTaskMap = {}; + var newTaskMap = {}; + host.get('tasks').forEach(function(taskInfo){ + var id = taskInfo.get('id'); + existTaskMap[id] = taskInfo; + }); + var newTasksArray = []; + newTasks.forEach(function(newTask){ + var existTask = existTaskMap[newTask.Tasks.id]; + if(existTask){ + // reuse + existTask.set('status', App.format.taskStatus(newTask.Tasks.status)); + existTask.set('stderr', newTask.Tasks.stderr); + existTask.set('stdout', newTask.Tasks.stdout); + self.updateTaskIcon(existTask); + delete existTaskMap[newTask.Tasks.id]; + }else{ + // create new + var taskInfo = Ember.Object.create({ + id: newTask.Tasks.id, + hostName: newHostInfo.publicName, + command: newTask.Tasks.command.toLowerCase(), + status: App.format.taskStatus(newTask.Tasks.status), + role: App.format.role(newTask.Tasks.role), + stderr: newTask.Tasks.stderr, + stdout: newTask.Tasks.stdout, + isVisible: true, + icon: 'icon-cogs' + }); + self.updateTaskIcon(taskInfo); + newTasksArray.push(taskInfo); + } + }); + for(var id in existTaskMap){ + host.get('tasks').removeObject(existTaskMap[id]); + } + if(newTasksArray.length){ + host.get('tasks').pushObjects(newTasksArray); + } + }else{ + // Tasks have changed + var tasksArr = []; + newTasks.forEach(function(newTask){ + var taskInfo = Ember.Object.create({ + id: newTask.Tasks.id, + hostName: newHostInfo.publicName, + command: newTask.Tasks.command.toLowerCase(), + status: App.format.taskStatus(newTask.Tasks.status), + role: App.format.role(newTask.Tasks.role), + stderr: newTask.Tasks.stderr, + stdout: newTask.Tasks.stdout, + isVisible: true, + icon: 'icon-cogs' + }); + self.updateTaskIcon(taskInfo); + tasksArr.push(taskInfo); + }); + host.set('tasks', tasksArr); } - tasksArr.push(taskInfo); - }, this); - } + } + }, this); + } else { + + //sort hosts by name + this.sortArray(hosts, "name"); + + hosts.forEach(function (_host) { + var tasks = _host.logTasks; + var hostInfo = Ember.Object.create({ + name: _host.name, + publicName: _host.publicName, + progress: 0, + status: App.format.taskStatus("PENDING"), + serviceName: _host.serviceName, + isVisible: true, + icon: "icon-cog", + barColor: "progress-info", + barWidth: "width:0%;" + }); - hostInfo.set('tasks', tasksArr); - hostsArr.push(hostInfo); - }, this); - } + var tasksArr = []; + + if (tasks.length) { + tasks = self.sortTasksById(tasks); + var hostStatus = self.getStatus(tasks); + var hostProgress = self.getProgress(tasks); + hostInfo.set('status', App.format.taskStatus(hostStatus[0])); + hostInfo.set('icon', hostStatus[1]); + hostInfo.set('barColor', hostStatus[2]); + hostInfo.set('isInProgress', hostStatus[3]); + hostInfo.set('progress', hostProgress); + hostInfo.set('barWidth', "width:" + hostProgress + "%;"); + + tasks.forEach(function (_task) { + var taskInfo = Ember.Object.create({ + id: _task.Tasks.id, + hostName: _host.publicName, + command: _task.Tasks.command.toLowerCase(), + status: App.format.taskStatus(_task.Tasks.status), + role: App.format.role(_task.Tasks.role), + stderr: _task.Tasks.stderr, + stdout: _task.Tasks.stdout, + isVisible: true, + icon: 'icon-cogs' + }); + this.updateTaskIcon(taskInfo); + tasksArr.push(taskInfo); + }, this); + } - self.set("hosts", hostsArr); - }.observes("inputData"), + hostInfo.set('tasks', tasksArr); + hostsArr.push(hostInfo); + }, this); + self.set("hosts", hostsArr); + } + } + }.observes('inputData'), /** * Sort tasks by it`s id @@ -274,21 +386,9 @@ App.HostPopup = Em.Object.create({ * @return {Array} */ sortTasksById: function (tasks) { - var result = []; - var id = 1; - for (var i = 0; i < tasks.length; i++) { - id = (tasks[i].Tasks.id > id) ? tasks[i].Tasks.id : id; - } - while (id >= 1) { - for (var j = 0; j < tasks.length; j++) { - if (id == tasks[j].Tasks.id) { - result.push(tasks[j]); - } - } - id--; - } - result.reverse(); - return result; + return tasks.sort(function (a, b) { + return (a.Tasks.id > b.Tasks.id) ? 1 : (a.Tasks.id < b.Tasks.id) ? -1 : 0; + }); }, /** @@ -582,10 +682,10 @@ App.HostPopup = Em.Object.create({ * Onclick handler for selected Task */ openTaskLogInDialog: function () { - newwindow = window.open(); - newdocument = newwindow.document; - newdocument.write($(".task-detail-log-info").html()); - newdocument.close(); + var newWindow = window.open(); + var newDocument = newWindow.document; + newDocument.write($(".task-detail-log-info").html()); + newDocument.close(); }, openedTaskId: 0,
