AMBARI-18313. Capacity Scheduler View: Xml diff view tool to show changes made and queue capacity chart (Akhil PB via pallavkul)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/62dc775e Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/62dc775e Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/62dc775e Branch: refs/heads/branch-dev-patch-upgrade Commit: 62dc775ee4156e1a43ce0b991f3f47f68155128d Parents: 26660f2 Author: Pallav Kulshreshtha <pallav....@gmail.com> Authored: Wed Oct 5 15:32:19 2016 +0530 Committer: Pallav Kulshreshtha <pallav....@gmail.com> Committed: Wed Oct 5 15:32:19 2016 +0530 ---------------------------------------------------------------------- .../src/main/resources/ui/app/components.js | 2 + .../ui/app/components/labelCapacityBar.js | 12 +- .../resources/ui/app/components/queueBadge.js | 6 +- .../ui/app/components/queueHierarchy.js | 11 + .../resources/ui/app/components/queueMapping.js | 25 +- .../resources/ui/app/components/queueSummary.js | 23 +- .../ui/app/components/sunburstChart.js | 339 +++++++++++++++++++ .../ui/app/components/xmldiffViewer.js | 101 ++++++ .../resources/ui/app/controllers/advanced.js | 21 +- .../resources/ui/app/controllers/capsched.js | 39 ++- .../resources/ui/app/controllers/editqueue.js | 66 +++- .../resources/ui/app/controllers/queuesconf.js | 106 +++++- .../resources/ui/app/controllers/scheduler.js | 21 +- .../src/main/resources/ui/app/models/queue.js | 9 +- .../src/main/resources/ui/app/router.js | 49 ++- .../resources/ui/app/styles/application.less | 59 ++++ .../src/main/resources/ui/app/templates.js | 2 + .../resources/ui/app/templates/capsched.hbs | 6 + .../ui/app/templates/capsched/advanced.hbs | 13 +- .../capsched/partials/accessControlList.hbs | 4 +- .../capsched/partials/labelCapacity.hbs | 4 +- .../templates/capsched/partials/preemption.hbs | 4 +- .../capsched/partials/queueCapacity.hbs | 34 +- .../capsched/partials/queueResources.hbs | 4 +- .../ui/app/templates/capsched/queuesconf.hbs | 15 +- .../ui/app/templates/capsched/scheduler.hbs | 6 +- .../app/templates/components/queueHierarchy.hbs | 8 +- .../app/templates/components/queueMapping.hbs | 14 +- .../app/templates/components/queueSummary.hbs | 13 - .../app/templates/components/sunburstChart.hbs | 44 +++ .../app/templates/components/xmldiffViewer.hbs | 41 +++ .../src/main/resources/ui/bower.json | 13 +- 32 files changed, 1003 insertions(+), 111 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js index 8dd7108..8cf9a64 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js @@ -39,3 +39,5 @@ require('components/editLabelCapacity'); require('components/editQueueCapacity'); require('components/labelCapacityBar'); require('components/displayNodeLabels'); +require('components/xmldiffViewer'); +require('components/sunburstChart'); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/labelCapacityBar.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/labelCapacityBar.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/labelCapacityBar.js index 7776452..032b2ec 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/labelCapacityBar.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/labelCapacityBar.js @@ -49,10 +49,14 @@ App.LabelCapacityBarComponent = Ember.Component.extend({ warnInvalidLabelCapacity: function() { var totalCap = this.get('childrenQueueLabelsTotalCapacity'); - var isInvalid = false; - if (totalCap > 100 || totalCap < 100) { - isInvalid = true; - } + var isInvalid = totalCap !== 100; + this.get('labels').forEach(function(label) { + if (isInvalid) { + label.set('overCapMessage', 'Invalid Total Capacity for label: '+label.get('name')); + } else { + label.set('overCapMessage', undefined); + } + }); this.get('labels').setEach('overCapacity', isInvalid); this.set('warnInvalidTotalLabelCapacity', isInvalid); return isInvalid; http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js index f8978c2..0403b3d 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js @@ -34,11 +34,13 @@ App.WarningInfoComponent = Em.Component.extend({ layout:Em.Handlebars.compile('<i class="fa fa-fw fa-lg fa-warning"></i>'), tagName:'span', tooltip:'Warning', + placement:'bottom', initTooltip: function(){ - var tipMsg = this.get('tooltip'); + var tipMsg = this.get('tooltip'), + postion = this.get('placement'); this.$().tooltip({ title:tipMsg, - placement:'bottom' + placement:postion }); }.on('didInsertElement'), }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js index 64a28343..674c9c7 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js @@ -29,6 +29,17 @@ .filterBy('parentPath', this.get('parent')); }.property('depth', 'parent', 'queues.length', 'queu...@each.name'), + cildrenQueues: function() { + var leafQs = this.get('leafQs'), + deltedQs = this.get('deletedQs'); + + var deletedAtDepth = deltedQs + .filterBy('depth', this.get('depth')) + .filterBy('parentPath', this.get('parent')); + + return leafQs.pushObjects(deletedAtDepth); + }.property('leafQs.length', 'deletedQs.[]'), + childDepth: function () { return this.get('leafQs.firstObject.depth') + 1; }.property('depth'), http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js index 107b377..1d07351 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js @@ -34,6 +34,7 @@ selectedLeafQueueNameForUsers: null, selectedLeafQueueNameForGroups: null, isQueueMappingsDirty: false, + scheduler: null, actions: { showMappingOptions: function(){ @@ -51,6 +52,13 @@ }, toggleMappingOverride: function() { this.toggleProperty('mappingsOverrideEnable'); + }, + rollbackProp: function(prop, item) { + if (prop === "queue_mappings") { + var oldMappings = (item.changedAttributes()[prop][0])? item.changedAttributes()[prop][0].split(',') : []; + this.set('queueMappings', oldMappings); + } + this.sendAction("rollbackProp", prop, item); } }, @@ -103,9 +111,10 @@ addCustomQueueMappings: function(csValues, selectedLeafQName){ var that = this; - csValues = csValues.trim() || '', - userOrGroupNames = csValues.split(',') || [], - mappingPattern = this.get('selectedMapping'); + csValues = csValues.trim() || '', + userOrGroupNames = csValues.split(',') || [], + mappingPattern = this.get('selectedMapping'); + userOrGroupNames.forEach(function(ugname){ that.get('queueMappings').pushObject(mappingPattern.replace('%name', ugname).replace('%qname', selectedLeafQName)); }); @@ -143,5 +152,13 @@ destroyEventListeners: function() { this.$('#collapseQueueMappingsBtn').off('click'); - }.on('willDestroyElement') + }.on('willDestroyElement'), + + isMappingsDirty: function() { + return this.get('scheduler').changedAttributes().hasOwnProperty('queue_mappings'); + }.property('scheduler.queue_mappings'), + + isOverrideEnableDirty: function() { + return this.get('scheduler').changedAttributes().hasOwnProperty('queue_mappings_override_enable'); + }.property('scheduler.queue_mappings_override_enable') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js index 2690c40..8923e8e 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js @@ -31,7 +31,7 @@ isQueueStateNeedRefresh: function() { var qsNeedRefresh = this.get('queuesNeedRefresh'), - qq = this.get('queue'); + qq = this.get('queue'); if (qsNeedRefresh && qsNeedRefresh.findBy('path', qq.get('path'))) { return true; @@ -44,7 +44,7 @@ isQueueCapacityNeedRefresh: function() { var qsNeedRefresh = this.get('queuesNeedRefresh'), - qq = this.get('queue'); + qq = this.get('queue'); if (qsNeedRefresh && qsNeedRefresh.findBy('path', qq.get('path'))) { return true; @@ -89,22 +89,5 @@ isNewQueue: function() { return this.get('queue.isNewQueue'); - }.property('queue.isNewQueue'), - - effectiveCapacity: function() { - var currentQ = this.get('queue'), - allQueues = this.get('allQueues'), - effectiveCapacityRatio = 1; - - while (currentQ !== null) { - effectiveCapacityRatio *= (currentQ.get('capacity') / 100); - currentQ = allQueues.findBy('id', currentQ.get('parentPath').toLowerCase()) || null; - } - - var effectiveCapacityPercent = effectiveCapacityRatio * 100, - absoluteCapacity = parseFloat(parseFloat(effectiveCapacityPercent).toFixed(this.get('precision'))); - this.get('queue').set('absolute_capacity', absoluteCapacity || 0); - - return absoluteCapacity; - }.property('queue.capacity', 'allQueues.@each.capacity', 'allQueues.length') + }.property('queue.isNewQueue') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/sunburstChart.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/sunburstChart.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/sunburstChart.js new file mode 100644 index 0000000..d5b19ae --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/sunburstChart.js @@ -0,0 +1,339 @@ +/** + * 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. + */ + +var App = require('app'); + +function _getSunburstChartDataForDefault(queue, json, allQueues) { + var childrenQs = allQueues.filterBy('parentPath', queue.get('path')), + qdata = { + "name": queue.get('name'), + "path": queue.get('path'), + "capacity": queue.get('capacity'), + "absoluteCapacity": queue.get('absolute_capacity')||0, + "size": queue.get('capacity'), + "isLabel": false + }; + + if (!Ember.isEmpty(childrenQs)) { + qdata["children"] = []; + } + + if (json["children"]) { + json["children"].push(qdata); + } + + childrenQs.forEach(function(child) { + return _getSunburstChartDataForDefault(child, qdata, allQueues); + }); + return json; +} + +function _getSunburstChartDataForLabel(queue, json, allQueues, labelName) { + var childrenQs = allQueues.filterBy('parentPath', queue.get('path')), + qLabel = queue.get('labels').findBy('name', labelName), + qdata = { + "name": queue.get('name'), + "path": queue.get('path'), + "capacity": qLabel?qLabel.get('capacity') : 0, + "absoluteCapacity": 0, + "size": qLabel? qLabel.get('capacity') : 0, + "isLabel": true + }; + + if (!Ember.isEmpty(childrenQs)) { + qdata["children"] = []; + } + + if (json["children"]) { + json["children"].push(qdata); + } + + childrenQs.forEach(function(child) { + return _getSunburstChartDataForLabel(child, qdata, allQueues, labelName); + }); + return json; +} + +App.SunburstChartComponent = Ember.Component.extend({ + layoutName: 'components/sunburstChart', + queues: null, + selectedQueue: null, + allNodeLabels: null, + + width: 0, + height: 0, + radius: 0, + data: null, + colorScale: null, + chartRendered: false, + queueCapacityTypeSelected: null, + + queueCapacityTypeValues: null, + + didInsertElement: function() { + this.initializeChart(); + this.initializeCapacityTypeOptions(); + this.set("queueCapacityTypeSelected", null); + this.setChartDataForDefault(); + this.renderChart(); + }, + + initializeChart: function() { + var width = this.$().width(); + this.$('.panel-default').height(width); + var height = this.$('.panel-default').height() - this.$('#top_container').outerHeight(); + + var vizWidth = width - 60; + var vizHeight = height - 80; + var radius = Math.min(vizWidth, vizHeight) / 2; + + this.set('width', vizWidth); + this.set('height', vizHeight); + this.set('radius', radius); + this.set('colorScale', d3.scale.category20()); + }, + + initializeCapacityTypeOptions: function() { + var options = [{ + name: "Default", + value: null + }]; + if (this.get('allNodeLabels') !== null) { + this.get('allNodeLabels').forEach(function(label) { + options.push({ + name: "Label: " + label.name, + value: label.name + }); + }); + } + this.set("queueCapacityTypeValues", options); + }, + + setChartDataForDefault: function() { + var queues = this.get('queues'), + rootQ = queues.findBy('name', 'root'), + dataJson = { + "name": "sunburst", + "children": [] + }; + + var data = _getSunburstChartDataForDefault(rootQ, dataJson, queues); + this.set("data", data); + }, + + setChartDataForLabel: function() { + var queues = this.get('queues'), + allNodeLabels = this.get('allNodeLabels'), + rootQ = queues.findBy('name', 'root'), + labelName = this.get('queueCapacityTypeSelected'); + + var dataJson = { + "name": "sunburst", + "children": [] + }; + var data = _getSunburstChartDataForLabel(rootQ, dataJson, queues, labelName); + this.set("data", data); + }, + + renderChart: function() { + var self = this, + width = this.get('width'), + height = this.get('height'), + radius = this.get('radius'), + data = this.get('data'), + colors = this.get('colorScale'); + + var vis = d3.select("#sunburst_chart").append("svg:svg") + .attr("width", width) + .attr("height", height) + .append("svg:g") + .attr("id", "sunburst_container") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + + var partition = d3.layout.partition() + .size([2 * Math.PI, radius * radius]) + .value(function(d) { + return d.size; + }); + + var arc = d3.svg.arc() + .startAngle(function(d) { return d.x; }) + .endAngle(function(d) { return d.x + d.dx; }) + .innerRadius(function(d) { return Math.sqrt(d.y); }) + .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); }); + + vis.append("svg:circle") + .attr("r", radius) + .style("opacity", 0); + + var nodes = partition.nodes(data) + .filter(function(d) { + return (d.dx > 0.005); + }); + + var path = vis.data([data]).selectAll("path") + .data(nodes) + .enter().append("svg:path") + .attr("id", function(d) { return d.path; }) + .attr("data-node", function(d) { + var node = { + name: d.name, + path: d.path, + capacity: d.capacity, + absoluteCapacity: d.absoluteCapacity, + isLabel: d.isLabel + }; + return JSON.stringify(node); + }) + .attr("display", function(d) { return d.depth ? null : "none"; }) + .attr("d", arc) + .attr("fill-rule", "evenodd") + .style("fill", function(d) { + var colr = colors(d.name); + d.color = colr; + return colr; + }) + .style("opacity", 1); + + this.set('chartRendered', true); + this.adjustPositions(); + }, + + mouseover: function(d, elem) { + this.showQueueInfo(d); + d3.selectAll("path").style("opacity", 0.3); + d3.select(elem).style("opacity", 1); + }, + + showQueueInfo: function(node) { + var precentage = node.isLabel? node.capacity : node.absoluteCapacity; + var percentageString = precentage + "%"; + d3.select("#capacityPercentage").text(percentageString); + d3.select("#queuePath").text(node.path); + if (node.isLabel) { + d3.select("#type_text").text("Capacity:"); + } else { + d3.select("#type_text").text("Abs Cap:"); + } + d3.select("#explanation").style("visibility", ""); + d3.select("#queue_info").style("visibility", ""); + }, + + hideQueueInfo: function() { + d3.select("#queuePath").text(""); + d3.select("#type_text").text(""); + d3.select("#capacityPercentage").text(""); + d3.select("#explanation").style("visibility", "hidden"); + d3.select("#queue_info").style("visibility", "hidden"); + }, + + mouseleave: function(d) { + d3.selectAll("path").style("opacity", 1); + d3.select("#explanation").style("visibility", "hidden"); + d3.select("#queue_info").style("visibility", "hidden"); + var queue = this.get("selectedQueue"); + if (queue) { + var node = this.$("path[id='"+queue.get('path')+"']").data('node'); + this.showQueueInfo({ + name: node.name, + path: node.path, + capacity: node.capacity, + absoluteCapacity: node.absoluteCapacity, + isLabel: node.isLabel + }); + } + }, + + hightlightSelectedQueue: function() { + this.removeHighlightFromChart(); + var width = this.get('width'), + height = this.get('height'), + queue = this.get("selectedQueue"); + if (queue) { + d3.selectAll("path").style("opacity", 0.3); + d3.select("path[id='"+queue.get('path')+"']") + .style("fill", "#3276b1") + .style("opacity", 1); + var node = this.$("path[id='"+queue.get('path')+"']").data('node'); + this.showQueueInfo({ + name: node.name, + path: node.path, + capacity: node.capacity, + absoluteCapacity: node.absoluteCapacity, + isLabel: node.isLabel + }); + } + }, + + removeHighlightFromChart: function() { + d3.selectAll("path").style("opacity", 1); + d3.selectAll("path").each(function(node) { + if (node.path) { + d3.select("path[id='"+node.path+"']") + .style("fill", node.color); + } + }); + this.hideQueueInfo(); + }, + + adjustPositions: function() { + var rootOffset = this.$("path[id='root']").offset(), + rootPathDims = this.$("path[id='root']")[0].getBoundingClientRect(), + rootW = rootPathDims.width, + rootH = rootPathDims.height, + explanationH = this.$("#explanation").height(), + explanationW = this.$("#explanation").width(); + + var rootCenterX = rootOffset.left + rootW / 2, + rootCenterY = rootOffset.top + rootH / 2, + posX = rootCenterX - explanationW / 2, + posY = rootCenterY - explanationH / 2; + + this.$("#explanation").offset({left: posX, top: posY}); + }, + + renderObserver: function() { + d3.select("#sunburst_chart svg").remove(); + this.set('chartRendered', false); + this.set("queueCapacityTypeSelected", null); + this.setChartDataForDefault(); + this.renderChart(); + this.hightlightSelectedQueue(); + }.observes('queues', 'queues.[]', 'queues.@each.capacity'), + + queueSelectionObserver: function() { + if (this.get('selectedQueue') && this.get('chartRendered')) { + this.hightlightSelectedQueue(); + } else { + this.removeHighlightFromChart(); + } + }.observes('selectedQueue', 'chartRendered'), + + watchTypeSelection: function() { + d3.select("#sunburst_chart svg").remove(); + this.set('chartRendered', false); + if (this.get("queueCapacityTypeSelected") !== null) { + this.setChartDataForLabel(); + } else { + this.setChartDataForDefault(); + } + this.renderChart(); + this.hightlightSelectedQueue(); + }.observes("queueCapacityTypeSelected") +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/xmldiffViewer.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/xmldiffViewer.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/xmldiffViewer.js new file mode 100644 index 0000000..62431f6 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/xmldiffViewer.js @@ -0,0 +1,101 @@ +/** + * 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. + */ + +var App = require('app'); + +App.XmldiffViewerComponent = Ember.Component.extend({ + layoutName: 'components/xmldiffViewer', + diffConfig: null, + viewConfig: null, + + actions: { + closeDiffViewer: function() { + this.set('diffConfig', null); + this.set('viewConfig', null); + } + }, + + diffViewObserver: function() { + if (this.get('diffConfig')) { + this.showXmlDiffView(); + } + }.observes('diffConfig'), + + viewConfigObserver: function() { + if (this.get('viewConfig')) { + this.showCapSchedViewXml(); + } + }.observes('viewConfig'), + + showXmlDiffView: function() { + this.$("#xmldiffViewerDialog").modal('show'); + var config = this.get('diffConfig'), + basetxt = difflib.stringAsLines(config.baseXML); + newtxt = difflib.stringAsLines(config.newXML), + sm = new difflib.SequenceMatcher(basetxt, newtxt), + opcodes = sm.get_opcodes(), + $diffOutput = this.$('#xmldiffOutput'); + + this.showHideOutputContainers('diffView'); + this.setDialogDimentions(); + + $diffOutput.append(diffview.buildView({ + baseTextLines: basetxt, + newTextLines: newtxt, + opcodes: opcodes, + baseTextName: 'Base XML', + newTextName: 'New XML', + contextSize: 2, + viewType: 0 + })); + }, + + showCapSchedViewXml: function() { + this.$("#xmldiffViewerDialog").modal('show'); + var config = this.get('viewConfig'), + $output = this.$('#capshedViewXml'); + + this.showHideOutputContainers('xmlView'); + this.setDialogDimentions(true); + $output.text(config.xmlConfig); + }, + + showHideOutputContainers: function(viewType) { + this.$('#xmldiffOutput').empty().hide(); + this.$('#capshedViewXml').empty().hide(); + if (viewType === 'diffView') { + this.$('#xmldiffOutput').show(); + } else { + this.$('#capshedViewXml').show(); + } + }, + + setDialogDimentions: function(hideOverflow) { + var dialogHt = this.$("#xmldiffViewerDialog").height(), + contentHt = dialogHt-60, + bodyHt = contentHt-170; + + this.$("#xmldiffViewerDialog").find('.modal-content').height(contentHt); + this.$("#xmldiffViewerDialog").find('.modal-body').height(bodyHt); + if (hideOverflow) { + this.$("#xmldiffViewerDialog").find('.modal-body').css('overflow', 'hidden'); + } else { + this.$("#xmldiffViewerDialog").find('.modal-body').css('overflow', 'auto'); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js index c079079..af15fa5 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js @@ -25,6 +25,7 @@ App.CapschedAdvancedController = Ember.Controller.extend({ actions: { rollbackQueueMappingProps: function() { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); var sched = this.get('scheduler'), attributes = sched.changedAttributes(), props = this.queueMappingProps; @@ -33,6 +34,16 @@ App.CapschedAdvancedController = Ember.Controller.extend({ sched.set(prop, attributes[prop][0]); } }); + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + }, + rollbackProp: function(prop, item) { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); + var attributes = item.changedAttributes(); + if (attributes.hasOwnProperty(prop)) { + item.set(prop, attributes[prop][0]); + } + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + this.afterRollbackProp(); }, showSaveConfigDialog: function(mode) { if (mode) { @@ -76,5 +87,13 @@ App.CapschedAdvancedController = Ember.Controller.extend({ forceRefreshRequired: function() { return !this.get('isQueueMappingsDirty') && this.get('isRefreshOrRestartNeeded'); - }.property('isQueueMappingsDirty', 'isRefreshOrRestartNeeded') + }.property('isQueueMappingsDirty', 'isRefreshOrRestartNeeded'), + + afterRollbackProp: function() { + if (this.get('isQueueMappingsDirty')) { + this.set('isRefreshOrRestartNeeded', true); + } else { + this.set('isRefreshOrRestartNeeded', false); + } + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js index 447cae0..631edbb 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js @@ -19,6 +19,11 @@ var App = require('app'); App.CapschedController = Ember.Controller.extend({ + queues: null, + precision: 2, + selectedQueue: null, + allNodeLabels: Ember.computed.alias('store.nodeLabels.content'), + actions: { loadTagged: function (tag) { this.transitionToRoute('capsched.scheduler').then(function() { @@ -27,7 +32,7 @@ App.CapschedController = Ember.Controller.extend({ }, clearAlert: function () { this.set('alertMessage',null); - }, + } }, /** @@ -42,6 +47,26 @@ App.CapschedController = Ember.Controller.extend({ */ isNotOperator: Ember.computed.not('isOperator'), + queuesWatcher: function() { + var allQueues = this.get('queues') || []; + allQueues.forEach(function(queue) { + var absCap = this.getAbsoluteCapacityForQueue(queue); + queue.set('absolute_capacity', absCap); + }.bind(this)); + }.observes('queues', 'queues.[]', 'queues.@each.capacity').on('init'), + + getAbsoluteCapacityForQueue: function(queue) { + var allQueues = this.get('queues'); + var effectCapRatio = 1; + while (queue !== null) { + effectCapRatio *= queue.get('capacity') / 100; + queue = allQueues.findBy('id', queue.get('parentPath').toLowerCase()) || null; + } + var effectCapPercent = effectCapRatio * 100, + absoluteCap = parseFloat(parseFloat(effectCapPercent).toFixed(this.get('precision')));; + return absoluteCap; + }, + alertMessage: null, tags: function () { @@ -60,5 +85,17 @@ App.CapschedController = Ember.Controller.extend({ stopSpinner: function() { this.set('showSpinner', false); + }, + + diffXmlConfig: null, + + viewConfigXmlDiff: function(config) { + this.set('diffXmlConfig', config); + }, + + viewXmlConfig: null, + + viewCapSchedXml: function(config) { + this.set('viewXmlConfig', config); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js index 7c62ef0..9d84704 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js @@ -66,16 +66,19 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ editQueueName: function() { this.set('enableEditQName', true); this.set('updatedQName', this.get('content.name')); + this.set('controllers.capschedQueuesconf.isEditingQueueName', true); }, cancelQNameEdit: function() { this.set('enableEditQName', false); this.set('isInvalidQName', false); this.set('invalidQNameMessage', ''); + this.set('controllers.capschedQueuesconf.isEditingQueueName', false); }, renameQueue: function() { if (this.validateQName()) { return; } + this.set('controllers.capschedQueuesconf.isEditingQueueName', false); this.set('content.name', this.get('updatedQName')); this.set('enableEditQName', false); }, @@ -83,6 +86,24 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ this.send('rollbackProp', 'disable_preemption', this.get('content')); this.send('rollbackProp', 'isPreemptionInherited', this.get('content')); this.set('content.isPreemptionOverriden', false); + }, + rollbackProp: function(prop, item) { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); + var attributes = item.changedAttributes(); + if (attributes.hasOwnProperty(prop)) { + item.set(prop, attributes[prop][0]); + } + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + this.afterRollbackProp(); + } + }, + + afterRollbackProp: function() { + var isAnyQDirty = this.get('allQueues').isAny('isAnyDirty', true); + if (isAnyQDirty) { + this.set('isRefreshOrRestartNeeded', true); + } else { + this.set('isRefreshOrRestartNeeded', false); } }, @@ -114,6 +135,15 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ this.validateQName(); }.observes('updatedQName', 'updatedQName.length'), + absoluteClusterCapacity: function() { + return this.get('content.absolute_capacity'); + }.property('content', 'content.absolute_capacity'), + + absoluteClusterBarWidth: function() { + var absCap = this.get('absoluteClusterCapacity'); + return this.get('widthPattern').fmt(absCap); + }.property('absoluteClusterCapacity'), + /** * Collection of modified fields in queue. * @type {Object} - { [fileldName] : {Boolean} } @@ -250,9 +280,10 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ }, isAnyQueueResourcesDirty: function() { - return this.get('queueDirtyFields.user_limit_factor') || this.get('queueDirtyFields.minimum_user_limit_percent') - || this.get('queueDirtyFields.maximum_applications') || this.get('queueDirtyFields.maximum_am_resource_percent') - || this.get('queueDirtyFields.ordering_policy') || this.get('queueDirtyFields.enable_size_based_weight'); + var changedAttrs = this.get('content').changedAttributes(); + return changedAttrs.hasOwnProperty('user_limit_factor') || changedAttrs.hasOwnProperty('minimum_user_limit_percent') + || changedAttrs.hasOwnProperty('maximum_applications') || changedAttrs.hasOwnProperty('maximum_am_resource_percent') + || changedAttrs.hasOwnProperty('ordering_policy') || changedAttrs.hasOwnProperty('enable_size_based_weight'); }.property( 'content.user_limit_factor', 'content.minimum_user_limit_percent', @@ -382,7 +413,8 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ }, isAnyAccessControlListDirty: function() { - return this.get('queueDirtyFields.acl_administer_queue') || this.get('queueDirtyFields.acl_submit_applications'); + var chagedAttrs = this.get('content').changedAttributes(); + return chagedAttrs.hasOwnProperty('acl_administer_queue') || chagedAttrs.hasOwnProperty('acl_submit_applications'); }.property('content.acl_submit_applications', 'content.acl_administer_queue'), /** @@ -443,8 +475,9 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ if (this.get('isLeafQ')) { return false; } - return this.get('queueDirtyFields.queues') || this.get('childrenQueues').anyBy('isDirtyCapacity', true) || this.get('childrenQueues').anyBy('isDirtyMaxCapacity', true); - }.property('content', 'content.queues', 'childrenQueues.@each.isDirtyCapacity', 'childrenQueues.@each.isDirtyMaxCapacity'), + return this.get('queueDirtyFields.queues') || this.get('childrenQueues').anyBy('isDirtyCapacity', true) + || this.get('childrenQueues').anyBy('isDirtyMaxCapacity', true) || this.get('warnInvalidCapacity'); + }.property('content', 'content.queues', 'childrenQueues.@each.isDirtyCapacity', 'childrenQueues.@each.isDirtyMaxCapacity', 'warnInvalidCapacity'), /** * Adds observers for each queue attribute. @@ -598,12 +631,14 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ warnInvalidTotalLabelCapacity: false, isAnyChildrenQueueLabelDirty: function() { - if (this.get('isLeafQ')) { - return this.get('queueDirtyFields.default_node_label_expression') || false; + var changedAttrs = this.get('content').changedAttributes(); + if (this.get('content.queues') === null) { + return changedAttrs.hasOwnProperty('default_node_label_expression'); } - return this.get('childrenQueues').anyBy('isLabelsDirty', true) || this.get('queueDirtyFields.default_node_label_expression') - || this.get('childrenLabelsForQueue').anyBy('isDirtyLabelCapacity', true) || this.get('childrenLabelsForQueue').anyBy('isDirtyLabelMaxCapacity', true); - }.property('content', 'childrenQueues.@each.isLabelsDirty', 'childrenLabelsForQueue.@each.isDirtyLabelCapacity', 'childrenLabelsForQueue.@each.isDirtyLabelMaxCapacity', 'content.default_node_label_expression'), + return this.get('childrenQueues').anyBy('isLabelsDirty', true) || changedAttrs.hasOwnProperty('default_node_label_expression') + || this.get('childrenLabelsForQueue').anyBy('isDirtyLabelCapacity', true) || this.get('childrenLabelsForQueue').anyBy('isDirtyLabelMaxCapacity', true) + || this.get('warnInvalidTotalLabelCapacity'); + }.property('content', 'childrenQueues.@each.isLabelsDirty', 'childrenLabelsForQueue.@each.isDirtyLabelCapacity', 'childrenLabelsForQueue.@each.isDirtyLabelMaxCapacity', 'content.default_node_label_expression', 'warnInvalidTotalLabelCapacity'), //Preemption isPreemptionSupported: Ember.computed.alias('store.isPreemptionSupported'), @@ -644,7 +679,7 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ }, isQueuePreemptionDirty: function() { - return this.get('queueDirtyFields.disable_preemption'); + return this.get('content').changedAttributes().hasOwnProperty('disable_preemption'); }.property('content.disable_preemption', 'content.isPreemptionInherited'), doOverridePreemption: function(key, value) { @@ -667,10 +702,5 @@ App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ this.set('content.isPreemptionInherited', true); this.set('content.disable_preemption', ''); } - }.observes('content.isPreemptionOverriden'), - - saveRefreshRestartWatcher: function() { - this.set('isRefreshOrRestartNeeded', this.get('isAnyQueueResourcesDirty') || this.get('isAnyAccessControlListDirty') - || this.get('isAnyChildrenQueueCapacityDirty') || this.get('isAnyChildrenQueueLabelDirty') || this.get('isQueuePreemptionDirty')); - }.observes('isAnyQueueResourcesDirty', 'isAnyAccessControlListDirty', 'isAnyChildrenQueueCapacityDirty', 'isAnyChildrenQueueLabelDirty', 'isQueuePreemptionDirty') + }.observes('content.isPreemptionOverriden') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js index 6a6717a..e7fba41 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js @@ -32,16 +32,19 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ allNodeLabelRecords: [], queuesNeedRefresh: Ember.computed.alias('store.queuesNeedRefresh'), isRefreshOrRestartNeeded: false, + deletedQueues: Ember.computed.alias('store.deletedQueues'), actions: { addNewQueue: function() { this.set('newQueueName', ''); this.set('showQueueNameInput', true); + this.set('isCreatingNewQueue', true); }, createNewQueue: function() { if (this.validateQueueName()) { return; } + this.set('isCreatingNewQueue', false); var store = this.get('store'), queueName = this.get('newQueueName'), parentPath = this.get('selectedQueue.path'), @@ -61,6 +64,7 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ freeLeafCapacity = (totalLeafCapacity < 100) ? 100 - totalLeafCapacity : 0; } var qCapacity = (newInLeaf) ? 100 : freeLeafCapacity; + var requireRestart = (this.get('selectedQueue.isLeafQ') && !this.get('selectedQueue.isNewQueue'))? true : false; newQueue = store.createRecord('queue', { id: queuePath.toLowerCase(), name: queueName, @@ -69,7 +73,8 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ depth: depth, isNewQueue: true, capacity: qCapacity, - maximum_capacity: 100 + maximum_capacity: 100, + requireRestart: requireRestart }); this.set('newQueue', newQueue); store.saveAndUpdateQueue(newQueue).then(Em.run.bind(this, 'saveAndUpdateQueueSuccess', newQueue)); @@ -86,8 +91,11 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ this.set('showQueueNameInput', false); this.set('isInvalidQueueName', false); this.set('invalidQueueNameMessage', ''); + this.set('isCreatingNewQueue', false); }, discardQueuesChanges: function() { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); + var tempRestart = this.get('isQueuesMustNeedRestart'); var allQueues = this.get('queues'); allQueues.forEach(function(qq){ var qAttrs = qq.changedAttributes(); @@ -105,12 +113,13 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ } } }); - //Setting root label capacities back to 100, - //if discard changes set root label capacity to 0. + //Setting root label capacities back to 100, if discard changes set root label capacity to 0. if (qq.get('id') === 'root') { qq.get('labels').setEach('capacity', 100); } }); + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + this.set('isQueuesMustNeedRestart', tempRestart); }, stopQueue: function() { this.set('selectedQueue.state', _stopState); @@ -139,6 +148,17 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ }, showConfirmDialog: function() { this.set('isConfirmDialogOpen', true); + }, + saveCapSchedConfigs: function() { + if (this.get('saveMode') === '') { + var holder = { + refresh: this.get('isRefreshOrRestartNeeded') + }; + this.set('tempRefreshHolder', holder); + } else { + this.set('tempRefreshHolder', null); + } + return true; } }, @@ -146,11 +166,48 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ return this.get('queues').isAny('isAnyDirty', true); }.property('queues.@each.isAnyDirty'), - isQueuesNeedRefreshOrRestart: Ember.computed.or('isAnyQueueDirty', 'needRefresh', 'needRestart', 'isRefreshOrRestartNeeded'), + isQueuesNeedRefreshOrRestart: function() { + var isNeed = this.get('isAnyQueueDirty') || this.get('needRefresh') + || this.get('queuesRequireRestart') || this.get('isRefreshOrRestartNeeded'); + return isNeed; + }.property('isAnyQueueDirty', 'needRefresh', 'queuesRequireRestart', 'isRefreshOrRestartNeeded', 'canNotSave'), + + isQueuesMustNeedRestart: false, forceRefreshOrRestartRequired: function() { - return !this.get('isAnyQueueDirty') && this.get('isRefreshOrRestartNeeded'); - }.property('isAnyQueueDirty', 'isRefreshOrRestartNeeded'), + return !this.get('isAnyQueueDirty') && (this.get('isRefreshOrRestartNeeded') || this.get('isQueuesMustNeedRestart')); + }.property('isAnyQueueDirty', 'isRefreshOrRestartNeeded', 'isQueuesMustNeedRestart'), + + refreshRestartWatcher: function() { + if (this.get('tempRefreshHolder')) { + var holder = this.get('tempRefreshHolder'); + this.set('isRefreshOrRestartNeeded', holder.refresh||false); + this.set('tempRefreshHolder', null); + } + }.observes('isRefreshOrRestartNeeded'), + + isDirtyQueuesNeedRefreshQsOp: function() { + if (this.get('isAnyQueueDirty') || this.get('needRefresh') || this.get('isRefreshOrRestartNeeded')) { + if (this.get('queuesRequireRestart')) { + return false; + } + return true; + } else { + return false; + } + }.property('isAnyQueueDirty', 'needRefresh', 'queuesRequireRestart', 'isRefreshOrRestartNeeded', 'canNotSave'), + + isDirtyQueuesNeedRestartRmOp: function() { + if (this.get('queuesRequireRestart') || this.get('isQueuesMustNeedRestart')) { + return true; + } else { + return false; + } + }.property('isAnyQueueDirty', 'queuesRequireRestart', 'isQueuesMustNeedRestart', 'canNotSave'), + + showNeedRefreshOrRestartWarning: function() { + return this.get('isQueuesNeedRefreshOrRestart') && !this.get('canNotSave'); + }.property('isQueuesNeedRefreshOrRestart', 'canNotSave'), selectedQueue: null, newQueue: null, @@ -159,6 +216,13 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ isInvalidQueueName: false, invalidQueueNameMessage: '', + isCreatingNewQueue: false, + isEditingQueueName: false, + + isCreaingOrRenamingQueue: function() { + return this.get('isCreatingNewQueue')||this.get('isEditingQueueName'); + }.property('isCreatingNewQueue', 'isEditingQueueName'), + validateQueueName: function() { var parentPath = this.get('selectedQueue.path'), queueName = this.get('newQueueName'), @@ -289,6 +353,34 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ }.bind(this)); }.observes('queues.length','queues.@each.capacity'), + newQueuesNeedRestart: function() { + return this.get('queues') + .filterBy('isNewQueue', true) + .filterBy('requireRestart', true); + }.property('queues.length', 'queues.@each.isNewQueue', 'queues.@each.requireRestart'), + + hasNewQueuesNeedRestart: Ember.computed.notEmpty('newQueuesNeedRestart.[]'), + + queuesRequireRestart: Ember.computed.or('needRestart', 'hasNewQueuesNeedRestart', 'isQueuesMustNeedRestart'), + + disableRefreshBtn: function() { + var disable = this.get('canNotSave') || this.get('queuesRequireRestart'); + return disable; + }.property('canNotSave', 'queuesRequireRestart', 'isAnyQueueDirty'), + + restartWatcher: function() { + var restart = this.get('needRestart') || this.get('hasNewQueuesNeedRestart'); + this.set('isQueuesMustNeedRestart', restart); + }.observes('needRestart', 'hasNewQueuesNeedRestart'), + + dirtyQueueWatcher: function() { + if (this.get('isAnyQueueDirty')) { + this.set('isRefreshOrRestartNeeded', true); + } else { + this.set('isRefreshOrRestartNeeded', false); + } + }.observes('isAnyQueueDirty'), + /** * True if newQueue is not empty. * @type {Boolean} @@ -347,7 +439,7 @@ App.CapschedQueuesconfController = Ember.Controller.extend({ * check if RM needs restart * @type {bool} */ - needRestart: Em.computed.and('hasDeletedQueues', 'isOperator'), + needRestart: Ember.computed.and('hasDeletedQueues', 'isOperator'), /** * check there is some changes for save http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js index 40e3e24..aa139b1 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js @@ -28,6 +28,7 @@ App.CapschedSchedulerController = Ember.Controller.extend({ actions: { rollbackSchedulerProps: function() { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); var sched = this.get('scheduler'), attributes = sched.changedAttributes(), props = this.schedulerProps; @@ -36,6 +37,16 @@ App.CapschedSchedulerController = Ember.Controller.extend({ sched.set(prop, attributes[prop][0]); } }); + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + }, + rollbackProp: function(prop, item) { + var tempRefreshNeeded = this.get('isRefreshOrRestartNeeded'); + var attributes = item.changedAttributes(); + if (attributes.hasOwnProperty(prop)) { + item.set(prop, attributes[prop][0]); + } + this.set('isRefreshOrRestartNeeded', tempRefreshNeeded); + this.afterRollbackProp(); }, showConfirmDialog: function() { this.set('isConfirmDialogOpen', true); @@ -102,5 +113,13 @@ App.CapschedSchedulerController = Ember.Controller.extend({ }, { label: 'Dominant Resource Calculator', value: 'org.apache.hadoop.yarn.util.resource.DominantResourceCalculator' - }] + }], + + afterRollbackProp: function() { + if (this.get('isSchedulerDirty')) { + this.set('isRefreshOrRestartNeeded', true); + } else { + this.set('isRefreshOrRestartNeeded', false); + } + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js index 0f68a10..e2d67bd 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js @@ -36,7 +36,6 @@ App.Label = DS.Model.extend({ isDefault: function () { return this.get('queue.default_node_label_expression') === this.get('name'); }.property('queue.default_node_label_expression'), - absoluteCapacity: 0, setCapacity: function(cap) { this.set('capacity', cap); }, @@ -244,7 +243,7 @@ App.Queue = DS.Model.extend({ capacity: DS.attr('number', { defaultValue: 0 }), maximum_capacity: DS.attr('number', { defaultValue: 0 }), - //unfunded_capacity: DS.attr('number', { defaultValue: 0 }), + absolute_capacity: 0, isDirtyCapacity: false, isDirtyMaxCapacity: false, @@ -320,5 +319,9 @@ App.Queue = DS.Model.extend({ if (Em.isEmpty(this.get('labels').findBy('name',this.get('default_node_label_expression')))) { this.set('default_node_label_expression',null); } - }.observes('labels','default_node_label_expression') + }.observes('labels','default_node_label_expression'), + + isLeafQ: function() { + return this.get('queues') === null; + }.property('queues') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js index fd996ae..d838958 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js @@ -202,12 +202,6 @@ App.ErrorRoute = Ember.Route.extend({ App.CapschedRoute = Ember.Route.extend({ actions: { - rollbackProp: function(prop, item) { - var attributes = item.changedAttributes(); - if (attributes.hasOwnProperty(prop)) { - item.set(prop, attributes[prop][0]); - } - }, saveCapSchedConfigs: function(saveMode, forceRefresh) { var store = this.get('store'), that = this, @@ -217,7 +211,8 @@ App.CapschedRoute = Ember.Route.extend({ advancedCtrl = this.controllerFor("capsched.advanced"), restartOrRefreshMap = { schedulerTab: schedulerCtrl.get('isRefreshOrRestartNeeded') || false, - queuesTab: queuesconfCtrl.get('isRefreshOrRestartNeeded') || false, + queuesTabRefresh: queuesconfCtrl.get('isRefreshOrRestartNeeded') || false, + queuesTabRestart: queuesconfCtrl.get('isQueuesMustNeedRestart') || false, advancedTab: advancedCtrl.get('isRefreshOrRestartNeeded') || false }; @@ -246,7 +241,8 @@ App.CapschedRoute = Ember.Route.extend({ Em.run.bind(that,'saveConfigsError', 'save') ).then(function () { schedulerCtrl.set('isRefreshOrRestartNeeded', restartOrRefreshMap.schedulerTab); - queuesconfCtrl.set('isRefreshOrRestartNeeded', restartOrRefreshMap.queuesTab); + queuesconfCtrl.set('isRefreshOrRestartNeeded', restartOrRefreshMap.queuesTabRefresh); + queuesconfCtrl.set('isQueuesMustNeedRestart', restartOrRefreshMap.queuesTabRestart); advancedCtrl.set('isRefreshOrRestartNeeded', restartOrRefreshMap.advancedTab); if (opt) { return store.relaunchCapSched(opt); @@ -255,6 +251,7 @@ App.CapschedRoute = Ember.Route.extend({ if (opt) { schedulerCtrl.set('isRefreshOrRestartNeeded', false); queuesconfCtrl.set('isRefreshOrRestartNeeded', false); + queuesconfCtrl.set('isQueuesMustNeedRestart', false); advancedCtrl.set('isRefreshOrRestartNeeded', false); } return store.getRmSchedulerConfigInfo(); @@ -271,6 +268,15 @@ App.CapschedRoute = Ember.Route.extend({ lastSavedXML = store.get('lastSavedConfigXML'), currentXmlConfigs = store.buildConfig('xml'), diffConfigs = {baseXML: lastSavedXML, newXML: currentXmlConfigs}; + + controller.viewConfigXmlDiff(diffConfigs); + }, + viewCapSchedConfigXml: function() { + var store = this.get('store'), + controller = this.controllerFor("capsched"), + viewXmlConfigs = store.buildConfig('xml'); + + controller.viewCapSchedXml({xmlConfig: viewXmlConfigs}); } }, beforeModel: function(transition) { @@ -383,6 +389,7 @@ App.CapschedRoute = Ember.Route.extend({ schedulerCtrl.set('isRefreshOrRestartNeeded', false); advancedCtrl.set('isRefreshOrRestartNeeded', false); queuesconfCtrl.set('isRefreshOrRestartNeeded', false); + queuesconfCtrl.set('isQueuesMustNeedRestart', false); }).catch( Em.run.bind(that, 'saveConfigsError', opt) ).finally(function() { @@ -398,6 +405,14 @@ App.CapschedIndexRoute = Ember.Route.extend({ } }); +App.CapschedSchedulerRoute = Ember.Route.extend({ + actions: { + didTransition: function() { + this.controllerFor('capsched').set('selectedQueue', null); + } + } +}); + App.CapschedQueuesconfIndexRoute = Ember.Route.extend({ beforeModel: function(transition) { var rootQ = this.store.getById('queue', 'root'); @@ -406,6 +421,16 @@ App.CapschedQueuesconfIndexRoute = Ember.Route.extend({ }); App.CapschedQueuesconfEditqueueRoute = Ember.Route.extend({ + actions: { + willTransition: function(transition) { + if (this.controllerFor('capsched.queuesconf').get('isCreaingOrRenamingQueue')) { + transition.abort(); + } + }, + didTransition: function() { + this.controllerFor('capsched').set('selectedQueue', this.controller.get('model')); + } + }, model: function(params, transition) { var queue = this.store.getById('queue', params.queue_id); if (queue) { @@ -419,6 +444,14 @@ App.CapschedQueuesconfEditqueueRoute = Ember.Route.extend({ } }); +App.CapschedAdvancedRoute = Ember.Route.extend({ + actions: { + didTransition: function() { + this.controllerFor('capsched').set('selectedQueue', null); + } + } +}); + App.CapschedTraceRoute = Ember.Route.extend({ model: function() { return this.controllerFor('capsched').get('alertMessage'); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less index bd875ba..1ab1740 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less @@ -1140,6 +1140,9 @@ vertical-align: middle; font-size: 1.5em; } + .collapsible-panel-btn { + cursor: pointer; + } } .queue-summary { @@ -1298,3 +1301,59 @@ .alert-message-dialog { display: block; } + +.xmldiff-viewer { + overflow: hidden; + .modal-dialog { + width: 98%; + .modal-body { + overflow: auto; + } + .modal-footer { + padding: 12px 20px 0 0; + } + .viewxml-textarea { + resize: none; + } + } + table.diff { + th.author { + display: none; + } + } +} + +.hierarchical-piechart { + .sunburst-chart { + position: relative; + path { + stroke: #fff; + stroke-width: 1.5px; + } + svg { + margin-left: 20px; + } + .top-container { + padding-bottom: 15px; + } + .queue-explanation { + position: absolute; + width: 70px; + text-align: center; + color: #666; + z-index: 999; + } + .percentage, .queue-name, .queue-path { + font-weight: bold; + } + .queue-info { + width: 50%; + display: inline-block; + } + .select-capacity-type { + width: 45%; + display: inline-block; + vertical-align: top; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js index 2c9609f..487d09a 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js @@ -60,3 +60,5 @@ require('templates/components/labelCapacityBar'); require('templates/components/displayRootLabel'); require('templates/components/displayLeafLabel'); require('templates/capsched/partials/preemption'); +require('templates/components/xmldiffViewer'); +require('templates/components/sunburstChart'); http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs index 14625c1..7dc0d51 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs @@ -40,6 +40,10 @@ </div> </div> <div class="col-lg-4 capsched-versions-wrapper"> + {{#if selectedQueue}} + {{sunburst-chart queues=queues selectedQueue=selectedQueue allNodeLabels=allNodeLabels}} + {{/if}} + {{partial "versionsPanel"}} </div> @@ -64,3 +68,5 @@ </div> </div> </div> + +{{xmldiff-viewer diffConfig=diffXmlConfig viewConfig=viewXmlConfig}} http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs index 5a54d85..9973708 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs @@ -20,18 +20,25 @@ {{!-- QUEUE MAPPING --}} {{#if isOperator}} <div class="hidden-sm hidden-xs"> - {{queue-mapping mappings=scheduler.queue_mappings mappingsOverrideEnable=scheduler.queue_mappings_override_enable queues=queues isQueueMappingsDirty=isQueueMappingsDirty}} + {{queue-mapping mappings=scheduler.queue_mappings + mappingsOverrideEnable=scheduler.queue_mappings_override_enable + queues=queues + isQueueMappingsDirty=isQueueMappingsDirty + scheduler=scheduler + rollbackProp="rollbackProp"}} </div> {{/if}} </div> <div class="row"> - <div class="btn btn-group-sm col-sm-offset-3"> + <div class="btn btn-group-sm col-sm-offset-2"> {{#if isQueueMappignsNeedSaveOrRefresh}} - {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Refresh queues is required"}} + {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Refresh queues is required" placement="top"}} {{/if}} <button type="button" {{bind-attr class=":btn :btn-default :btn-success isQueueMappingsDirty::disabled"}} name="saveOnly" {{action "showSaveConfigDialog"}}>Save Only</button> <button type="button" {{bind-attr class=":btn :btn-default :btn-success isQueueMappignsNeedSaveOrRefresh::disabled"}} name="saveRefresh" {{action "showSaveConfigDialog" "refresh"}}>Save And Refresh Queues</button> <button type="button" {{bind-attr class=":btn :btn-default :btn-danger isQueueMappingsDirty::disabled"}} name="cancelAdvanced" {{action "showConfirmDialog"}}>Discard Changes</button> + <button type="button" {{bind-attr class=":btn :btn-primary isQueueMappingsDirty::disabled"}} name="viewXmlDiff" {{action "viewConfigXmlDiff"}}>View Diff</button> + <button type="button" class="btn btn-default" name="viewCapSchedXml" {{action "viewCapSchedConfigXml"}}>View XML</button> </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs index 922e27e..f5b552a 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs @@ -19,12 +19,12 @@ <div class="queue-acl-container"> <div class="panel panel-default"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseQueueAclPanelBtn" href="#collapsibleQueueAclPanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn view.isAclPanelCollapsed:collapsed"}}> Access Control List {{#if isAnyAccessControlListDirty}} {{warning-info class="yellow-warning" tooltip="Need refresh queues / restart RM"}} {{/if}} - <a id="collapseQueueAclPanelBtn" href="#collapsibleQueueAclPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isAclPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isAclPanelCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa view.isAclPanelCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="collapsibleQueueAclPanel" {{bind-attr class=":panel-collapse :collapse view.isAclPanelCollapsed::in"}}> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/labelCapacity.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/labelCapacity.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/labelCapacity.hbs index 873fb6f..429ef3f 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/labelCapacity.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/labelCapacity.hbs @@ -19,7 +19,7 @@ <div class="label-capacity-container"> <div class="panel panel-default"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseLabelCapacityPanelBtn" href="#collapsibleLabelCapacityPanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn view.isLabelsPanelCollapsed:collapsed"}}> Label Capacity {{#if isAnyChildrenQueueLabelDirty}} {{#if warnInvalidTotalLabelCapacity}} @@ -28,7 +28,7 @@ {{warning-info class="yellow-warning" tooltip="Need refresh queues / restart RM"}} {{/if}} {{/if}} - <a id="collapseLabelCapacityPanelBtn" href="#collapsibleLabelCapacityPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isLabelsPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isLabelsPanelCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa view.isLabelsPanelCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="collapsibleLabelCapacityPanel" {{bind-attr class=":panel-collapse :collapse view.isLabelsPanelCollapsed::in"}}> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/preemption.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/preemption.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/preemption.hbs index d594c7e..6d398f6 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/preemption.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/preemption.hbs @@ -19,12 +19,12 @@ <div class="preemption-container"> <div class="panel panel-default"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseQueuePreemptionPanelBtn" href="#collapsibleQueuePreemptionPanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn view.isPreemptionPanelCollapsed:collapsed"}}> Preemption {{#if isQueuePreemptionDirty}} {{warning-info class="yellow-warning" tooltip="Need refresh queues / restart RM"}} {{/if}} - <a id="collapseQueuePreemptionPanelBtn" href="#collapsibleQueuePreemptionPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isPreemptionPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isPreemptionPanelCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa view.isPreemptionPanelCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="collapsibleQueuePreemptionPanel" {{bind-attr class=":panel-collapse :collapse view.isPreemptionPanelCollapsed::in"}}> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs index 608758f..fdc6800 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs @@ -19,7 +19,7 @@ <div class="queue-capacity-container"> <div class="panel panel-default"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseQueueCapacityPanelBtn" href="#collapsibleQueueCapacityPanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn view.isCapacityPanelCollapsed:collapsed"}}> Children Queue Capacites for Default Partition {{#if isAnyChildrenQueueCapacityDirty}} {{#if warnInvalidCapacity}} @@ -28,7 +28,7 @@ {{warning-info class="yellow-warning" tooltip="Need refresh queues / restart RM"}} {{/if}} {{/if}} - <a id="collapseQueueCapacityPanelBtn" href="#collapsibleQueueCapacityPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isCapacityPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isCapacityPanelCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa view.isCapacityPanelCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="collapsibleQueueCapacityPanel" {{bind-attr class=":panel-collapse :collapse view.isCapacityPanelCollapsed::in"}}> @@ -59,13 +59,23 @@ <div class="col-md-2 col-sm-2"> <label>Total Capacity</label> </div> - <div class="col-sm-5 col-md-5"> + <div class="col-sm-4 col-md-4"> <div class="progress total-capacity-progress"> <div role="progressbar" {{bind-attr class=":progress-bar warnInvalidCapacity:progress-bar-danger:progress-bar-success" style="totalCapacityBarWidth"}}> {{childrenQueuesTotalCapacity}}% </div> </div> </div> + <div class="col-md-3 col-sm-3"> + <label>Absolute Capacity of Cluster</label> + </div> + <div class="col-md-3 col-sm-3"> + <div class="progress total-capacity-progress"> + <div role="progressbar" class="progress-bar progress-bar-success" {{bind-attr style="absoluteClusterBarWidth"}}> + {{absoluteClusterCapacity}}% + </div> + </div> + </div> </div> {{#if warnInvalidCapacity}} <div class="row"> @@ -94,6 +104,18 @@ </div> </div> <div class="row"> + <div class="col-md-4 col-sm-4"> + <label>Absolute Capacity of Cluster</label> + </div> + <div class="col-md-4 col-sm-4"> + <div class="progress total-capacity-progress"> + <div role="progressbar" class="progress-bar progress-bar-success" {{bind-attr style="absoluteClusterBarWidth"}}> + {{absoluteClusterCapacity}}% + </div> + </div> + </div> + </div> + <div class="row"> <div class="col-md-10 col-sm-10"> <small><strong>For this queue:</strong> To edit capacity and maximum capacity at parent queue level</small> <small>{{#link-to 'capsched.queuesconf.editqueue' parentQueue}}Click Here{{/link-to}}</small> @@ -111,6 +133,12 @@ <span>{{content.maximum_capacity}}%</span> </div> </div> + <div class="row"> + <div class="col-md-10 col-sm-10"> + <label>Absolute Capacity of Cluster: </label> + <span>{{absoluteClusterCapacity}}%</span> + </div> + </div> {{/if}} </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs index 809c1c0..7fc5fde 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs @@ -19,12 +19,12 @@ <div class="queue-resources-container"> <div class="panel panel-default"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseResourcesPanelBtn" href="#collapsibleQueueResourcesPanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn view.isResourcesPanelCollapsed:collapsed"}}> Other Configs (User Limit factor, Max AM Resource etc) {{#if isAnyQueueResourcesDirty}} {{warning-info class="yellow-warning" tooltip="Need refresh queues / restart RM"}} {{/if}} - <a id="collapseResourcesPanelBtn" href="#collapsibleQueueResourcesPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isResourcesPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isResourcesPanelCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa view.isResourcesPanelCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="collapsibleQueueResourcesPanel" {{bind-attr class=":panel-collapse :collapse view.isResourcesPanelCollapsed::in"}}> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/queuesconf.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/queuesconf.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/queuesconf.hbs index 0c70ee5..3b8ceb5 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/queuesconf.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/queuesconf.hbs @@ -22,7 +22,7 @@ <div class="row"> <div class="col-sm-7"> <div class="list-group queue-hierarchy"> - {{queue-hierarchy depth=0 queues=queues}} + {{queue-hierarchy depth=0 queues=queues deletedQs=deletedQueues}} </div> {{#if isOperator}} {{#if selectedQueue}} @@ -81,15 +81,16 @@ </div> </div> {{#if isOperator}} - <div class="btn btn-group-sm col-sm-offset-2"> - {{#if isQueuesNeedRefreshOrRestart}} - {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Refresh queues / restart RM is required"}} + <div class="btn btn-group-sm col-sm-offset-1"> + {{#if showNeedRefreshOrRestartWarning}} + {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Require refresh queues/restart RM" placement="top"}} {{/if}} <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled needSave::disabled"}} name="saveOnly" {{action "showSaveConfigDialog"}}>Save Only</button> - <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled isQueuesNeedRefreshOrRestart::disabled"}} name="saveAndRefresh" {{action "showSaveConfigDialog" "refresh"}}>Save And Refresh Queues</button> - <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled isQueuesNeedRefreshOrRestart::disabled"}} name="saveAndRestart" {{action "showSaveConfigDialog" "restart"}}>Save And Restart RM</button> + <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled isDirtyQueuesNeedRefreshQsOp::disabled"}} name="saveAndRefresh" {{action "showSaveConfigDialog" "refresh"}}>Save And Refresh Queues</button> + <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled isDirtyQueuesNeedRestartRmOp::disabled"}} name="saveAndRestart" {{action "showSaveConfigDialog" "restart"}}>Save And Restart RM</button> <button type="button" {{bind-attr class=":btn :btn-danger isAnyQueueDirty::disabled"}} name="cancelQueuesconfBtn" {{action "showConfirmDialog"}}>Discard Changes</button> - <button type="button" {{bind-attr class=":btn :btn-primary isAnyQueueDirty::disabled"}} name="viewXmlDiff" {{action "viewConfigXmlDiff"}} style="display:none;">View XML Diff</button> + <button type="button" {{bind-attr class=":btn :btn-primary isAnyQueueDirty::disabled"}} name="viewXmlDiff" {{action "viewConfigXmlDiff"}}>View Diff</button> + <button type="button" class="btn btn-default" name="viewCapSchedXml" {{action "viewCapSchedConfigXml"}}>View XML</button> </div> {{/if}} </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/scheduler.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/scheduler.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/scheduler.hbs index 66ef1ad..26150fb 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/scheduler.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/scheduler.hbs @@ -137,13 +137,15 @@ </div> {{#if isOperator}} <div class="row"> - <div class="btn btn-group-sm col-sm-offset-3"> + <div class="btn btn-group-sm col-sm-offset-2"> {{#if isSchedulerPropsNeedSaveOrRestart}} - {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Restart RM is required"}} + {{warning-info class="yellow-warning fa-2x resfresh-restart-warning" tooltip="Restart RM is required" placement="top"}} {{/if}} <button type="button" {{bind-attr class=":btn :btn-default :btn-success isSchedulerDirty::disabled"}} name="saveOnly" {{action 'showSaveConfigDialog'}}>Save Only</button> <button type="button" {{bind-attr class=":btn :btn-default :btn-success isSchedulerPropsNeedSaveOrRestart::disabled"}} name="saveRestart" {{action 'showSaveConfigDialog' 'restart'}}>Save And Restart RM</button> <button type="button" {{bind-attr class=":btn :btn-default :btn-danger isSchedulerDirty::disabled"}} name="cancelAdvanced" {{action 'showConfirmDialog'}}>Discard Changes</button> + <button type="button" {{bind-attr class=":btn :btn-primary isSchedulerDirty::disabled"}} name="viewXmlDiff" {{action "viewConfigXmlDiff"}}>View Diff</button> + <button type="button" class="btn btn-default" name="viewCapSchedXml" {{action "viewCapSchedConfigXml"}}>View XML</button> </div> </div> {{/if}} http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueHierarchy.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueHierarchy.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueHierarchy.hbs index fb34d51..e7ff559 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueHierarchy.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueHierarchy.hbs @@ -15,7 +15,7 @@ * limitations under the License. }} -{{#each queue in leafQs}} +{{#each queue in cildrenQueues}} {{#link-to 'capsched.queuesconf.editqueue' queue classNameBindings=":list-group-item queue.overCapacity:list-group-item-danger queue.isNew:list-group-item-info" disabled=queue.isDeletedQueue}} <div class="col-md-offset-{{unbound queue.depth}} col-sm-offset-{{unbound queue.depth}}"> @@ -35,8 +35,12 @@ {{warn-badge}} {{/if}} + {{#if queue.isDeletedQueue}} + {{warning-info class="yellow-warning pull-right" tooltip="Queue is marked for deletion"}} + {{/if}} + {{queue-badge q=queue class="badge pull-right"}} </div> {{/link-to}} - {{queue-hierarchy depth=childDepth parent=queue.path queues=queues}} + {{queue-hierarchy depth=childDepth parent=queue.path queues=queues deletedQs=deletedQs}} {{/each}} http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueMapping.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueMapping.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueMapping.hbs index 33540e9..f143911 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueMapping.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueMapping.hbs @@ -17,12 +17,12 @@ <div class="panel panel-default panel-queue-mapping"> <div class="panel-heading"> - <div class="panel-title"> + <div id="collapseQueueMappingsBtn" href="#queueMappingsCollapsiblePanel" data-toggle="collapse" {{bind-attr class=":panel-title :collapsible-panel-btn isCollapsed:collapsed"}}> Queue Mappings {{#if isQueueMappingsDirty}} {{warning-info class="yellow-warning" tooltip="Need refresh queues"}} {{/if}} - <a href="#queueMappingsCollapsiblePanel" id="collapseQueueMappingsBtn" data-toggle="collapse" {{bind-attr class=":pull-right isCollapsed:collapsed"}}><i {{bind-attr class=":fa isCollapsed:fa-plus:fa-minus"}}></i></a> + <a class="pull-right"><i {{bind-attr class=":fa isCollapsed:fa-plus:fa-minus"}}></i></a> </div> </div> <div id="queueMappingsCollapsiblePanel" {{bind-attr class=":panel-collapse :collapse isCollapsed::in"}}> @@ -46,6 +46,11 @@ <div class="btn-group btn-group-sm"> <button type="button" class="btn btn-default btn-primary" name="button" {{action "showMappingOptions"}}>Add Mapping</button> </div> + {{#if isMappingsDirty}} + <div class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'queue_mappings' scheduler}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} </div> {{#if isShowing}} <div class="queue-mapping-options"> @@ -164,6 +169,11 @@ message='When checked, applications submitted with queues specified will be used other than those defined in configured queue mapping' }} </label> + {{#if isOverrideEnableDirty}} + <span class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'queue_mappings_override_enable' scheduler}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </span> + {{/if}} </div> </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueSummary.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueSummary.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueSummary.hbs index a7ae9b0..fd46308 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueSummary.hbs +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueSummary.hbs @@ -51,19 +51,6 @@ </div> <div class="row"> <div class="col-sm-5"> - <label>Absolute Capacity of Cluster</label> - </div> - <div class="col-sm-5"> - <span>{{effectiveCapacity}}%</span> - </div> - {{#if isQueueCapacityNeedRefresh}} - <div class="col-sm-2"> - {{warning-info class="yellow-warning" tooltip="Need refresh queues/restart RM to take effect"}} - </div> - {{/if}} - </div> - <div class="row"> - <div class="col-sm-5"> <label>State</label> </div> <div class="col-sm-5"> http://git-wip-us.apache.org/repos/asf/ambari/blob/62dc775e/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/sunburstChart.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/sunburstChart.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/sunburstChart.hbs new file mode 100644 index 0000000..462ddf7 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/sunburstChart.hbs @@ -0,0 +1,44 @@ +{{! +* 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. +}} + +<div class="panel panel-default hierarchical-piechart"> + <div class="panel-heading"> + <div class="panel-title"> + Queue Capacity Chart + </div> + </div> + <div class="panel-body"> + <div id="sunburst_chart" class="sunburst-chart"> + <div id="top_container" class="top-container"> + <div id="queue_info" class="queue-info" style="visibility:hidden;"> + <span>Path: </span> <span id="queuePath" class="queue-path"></span> + </div> + {{view Ember.Select + class="form-control input-sm select-capacity-type" + content=queueCapacityTypeValues + optionLabelPath="content.name" + optionValuePath="content.value" + value=queueCapacityTypeSelected + }} + </div> + <div id="explanation" class="queue-explanation" style="visibility:hidden;"> + <span id="type_text"></span> <br/> <span id="capacityPercentage" class="percentage"></span> + </div> + </div> + </div> +</div>