YARN-7620. Allow node partition filters on Queues page of new YARN UI. Contributed by Vasudevan Skm.
This commit is contained in:
parent
e040c97b77
commit
fe5b057c81
@ -16,7 +16,27 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Ember from 'ember';
|
import Ember from "ember";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend(Ember.TargetActionSupport,{
|
||||||
|
actions: {
|
||||||
|
filterQueuesByPartition(filter) {
|
||||||
|
this.set("filteredPartition", filter);
|
||||||
|
this.sendAction("setFilter", filter);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
didInsertElement: function() {
|
||||||
|
$(".js-filter-queue-by-labels").select2({
|
||||||
|
width: "350px",
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".js-filter-queue-by-labels").on("select2:select", e => {
|
||||||
|
this.triggerAction({
|
||||||
|
action: "filterQueuesByPartition",
|
||||||
|
target: this,
|
||||||
|
actionContext: e.params.data.text
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
@ -16,19 +16,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Ember from 'ember';
|
import Ember from "ember";
|
||||||
|
import {PARTITION_LABEL} from '../constants';
|
||||||
|
|
||||||
const INBETWEEN_HEIGHT = 130;
|
const INBETWEEN_HEIGHT = 130;
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
// Map: <queue-name, queue>
|
// Map: <queue-name, queue>
|
||||||
map : undefined,
|
map: undefined,
|
||||||
|
|
||||||
// Normalized data for d3
|
// Normalized data for d3
|
||||||
treeData: undefined,
|
treeData: undefined,
|
||||||
|
|
||||||
// folded queues, folded[<queue-name>] == true means <queue-name> is folded
|
// folded queues, folded[<queue-name>] == true means <queue-name> is folded
|
||||||
foldedQueues: { },
|
foldedQueues: {},
|
||||||
|
|
||||||
// maxDepth
|
// maxDepth
|
||||||
maxDepth: 0,
|
maxDepth: 0,
|
||||||
@ -42,17 +43,23 @@ export default Ember.Component.extend({
|
|||||||
used: undefined,
|
used: undefined,
|
||||||
max: undefined,
|
max: undefined,
|
||||||
|
|
||||||
|
didUpdateAttrs: function({ oldAttrs, newAttrs }) {
|
||||||
|
if (oldAttrs.filteredPartition.value !== newAttrs.filteredPartition.value) {
|
||||||
|
this.reDraw();
|
||||||
|
}
|
||||||
|
},
|
||||||
// Init data
|
// Init data
|
||||||
initData: function() {
|
initData: function() {
|
||||||
this.map = { };
|
this.map = {};
|
||||||
this.treeData = { };
|
this.treeData = {};
|
||||||
this.maxDepth = 0;
|
this.maxDepth = 0;
|
||||||
this.numOfLeafQueue = 0;
|
this.numOfLeafQueue = 0;
|
||||||
|
|
||||||
this.get("model")
|
this.get("model").forEach(
|
||||||
.forEach(function(o) {
|
function(o) {
|
||||||
this.map[o.id] = o;
|
this.map[o.id] = o;
|
||||||
}.bind(this));
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
// var selected = this.get("selected");
|
// var selected = this.get("selected");
|
||||||
this.used = this.get("used");
|
this.used = this.get("used");
|
||||||
@ -81,9 +88,9 @@ export default Ember.Component.extend({
|
|||||||
|
|
||||||
// Init queues
|
// Init queues
|
||||||
initQueue: function(queueName, depth, node) {
|
initQueue: function(queueName, depth, node) {
|
||||||
if ((!queueName) || (!this.map[queueName])) {
|
if (!queueName || !this.map[queueName]) {
|
||||||
// Queue is not existed
|
// Queue is not existed
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (depth > this.maxDepth) {
|
if (depth > this.maxDepth) {
|
||||||
this.maxDepth = this.maxDepth + 1;
|
this.maxDepth = this.maxDepth + 1;
|
||||||
@ -91,6 +98,13 @@ export default Ember.Component.extend({
|
|||||||
|
|
||||||
var queue = this.map[queueName];
|
var queue = this.map[queueName];
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.filteredPartition &&
|
||||||
|
!queue.get("partitions").contains(this.filteredPartition)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var names = this.getChildrenNamesArray(queue);
|
var names = this.getChildrenNamesArray(queue);
|
||||||
|
|
||||||
node.name = queueName;
|
node.name = queueName;
|
||||||
@ -100,14 +114,21 @@ export default Ember.Component.extend({
|
|||||||
if (names.length > 0) {
|
if (names.length > 0) {
|
||||||
node.children = [];
|
node.children = [];
|
||||||
|
|
||||||
names.forEach(function(name) {
|
names.forEach(
|
||||||
var childQueueData = {};
|
function(name) {
|
||||||
node.children.push(childQueueData);
|
var childQueueData = {};
|
||||||
this.initQueue(name, depth + 1, childQueueData);
|
node.children.push(childQueueData);
|
||||||
}.bind(this));
|
const status = this.initQueue(name, depth + 1, childQueueData);
|
||||||
|
if (!status) {
|
||||||
|
node.children.pop();
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.numOfLeafQueue = this.numOfLeafQueue + 1;
|
this.numOfLeafQueue = this.numOfLeafQueue + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
update: function(source, root, tree, diagonal) {
|
update: function(source, root, tree, diagonal) {
|
||||||
@ -119,141 +140,183 @@ export default Ember.Component.extend({
|
|||||||
var links = tree.links(nodes);
|
var links = tree.links(nodes);
|
||||||
|
|
||||||
// Normalize for fixed-depth.
|
// Normalize for fixed-depth.
|
||||||
nodes.forEach(function(d) { d.y = d.depth * 200; });
|
nodes.forEach(function(d) {
|
||||||
|
d.y = d.depth * 200;
|
||||||
// Update the nodes…
|
|
||||||
var node = this.mainSvg.selectAll("g.node")
|
|
||||||
.data(nodes, function(d) { return d.id || (d.id = ++i); });
|
|
||||||
|
|
||||||
// Enter any new nodes at the parent's previous position.
|
|
||||||
var nodeEnter = node.enter().append("g")
|
|
||||||
.attr("class", "node")
|
|
||||||
.attr("transform", function() { return "translate(" + source.y0 + "," + source.x0 + ")"; })
|
|
||||||
.on("click", function(d){
|
|
||||||
if (d.queueData.get("name") !== this.get("selected")) {
|
|
||||||
document.location.href = "#/yarn-queues/" + d.queueData.get("name") + "!";
|
|
||||||
}
|
|
||||||
|
|
||||||
Ember.run.later(this, function () {
|
|
||||||
var treeWidth = this.maxDepth * 200;
|
|
||||||
var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
|
|
||||||
var tree = d3.layout.tree().size([treeHeight, treeWidth]);
|
|
||||||
var diagonal = d3.svg.diagonal()
|
|
||||||
.projection(function(d) { return [d.y, d.x]; });
|
|
||||||
|
|
||||||
this.update(this.treeData, this.treeData, tree, diagonal);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
}.bind(this))
|
|
||||||
.on("dblclick", function (d) {
|
|
||||||
document.location.href = "#/yarn-queue/" + d.queueData.get("name") + "/apps";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
nodeEnter.append("circle")
|
// Update the nodes…
|
||||||
|
var node = this.mainSvg.selectAll("g.node").data(nodes, function(d) {
|
||||||
|
return d.id || (d.id = ++i);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enter any new nodes at the parent's previous position.
|
||||||
|
var nodeEnter = node
|
||||||
|
.enter()
|
||||||
|
.append("g")
|
||||||
|
.attr("class", "node")
|
||||||
|
.attr("transform", function() {
|
||||||
|
return `translate(${source.y0 + 50}, ${source.x0})`;
|
||||||
|
})
|
||||||
|
.on(
|
||||||
|
"click",
|
||||||
|
function(d) {
|
||||||
|
if (d.queueData.get("name") !== this.get("selected")) {
|
||||||
|
document.location.href =
|
||||||
|
"#/yarn-queues/" + d.queueData.get("name") + "!";
|
||||||
|
}
|
||||||
|
|
||||||
|
Ember.run.later(
|
||||||
|
this,
|
||||||
|
function() {
|
||||||
|
var treeWidth = this.maxDepth * 200;
|
||||||
|
var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
|
||||||
|
var tree = d3.layout.tree().size([treeHeight, treeWidth]);
|
||||||
|
var diagonal = d3.svg.diagonal().projection(function(d) {
|
||||||
|
return [d.y + 50, d.x];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update(this.treeData, this.treeData, tree, diagonal);
|
||||||
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
.on("dblclick", function(d) {
|
||||||
|
document.location.href =
|
||||||
|
"#/yarn-queue/" + d.queueData.get("name") + "/apps";
|
||||||
|
});
|
||||||
|
|
||||||
|
nodeEnter
|
||||||
|
.append("circle")
|
||||||
.attr("r", 1e-6)
|
.attr("r", 1e-6)
|
||||||
.style("fill", function(d) {
|
.style(
|
||||||
var maxCap = d.queueData.get(this.max);
|
"fill",
|
||||||
maxCap = maxCap === undefined ? 100 : maxCap;
|
function(d) {
|
||||||
var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
|
const usedCapacity = getUsedCapacity(d.queueData.get("partitionMap"), this.filteredPartition);
|
||||||
if (usedCap <= 60.0) {
|
if (usedCapacity <= 60.0) {
|
||||||
return "mediumaquamarine";
|
return "#60cea5";
|
||||||
} else if (usedCap <= 100.0) {
|
} else if (usedCapacity <= 100.0) {
|
||||||
return "coral";
|
return "#ffbc0b";
|
||||||
} else {
|
} else {
|
||||||
return "salmon";
|
return "#ef6162";
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
// append percentage
|
// append percentage
|
||||||
nodeEnter.append("text")
|
nodeEnter
|
||||||
.attr("x", function() { return 0; })
|
.append("text")
|
||||||
|
.attr("x", function() {
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.attr("fill", "white")
|
.attr("fill", "white")
|
||||||
.attr("text-anchor", function() { return "middle"; })
|
.attr("text-anchor", function() {
|
||||||
.text(function(d) {
|
return "middle";
|
||||||
var maxCap = d.queueData.get(this.max);
|
})
|
||||||
maxCap = maxCap === undefined ? 100 : maxCap;
|
.text(
|
||||||
var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
|
function(d) {
|
||||||
if (usedCap >= 100.0) {
|
const usedCapacity = getUsedCapacity(d.queueData.get("partitionMap"), this.filteredPartition);
|
||||||
return usedCap.toFixed(0) + "%";
|
if (usedCapacity >= 100.0) {
|
||||||
} else {
|
return usedCapacity.toFixed(0) + "%";
|
||||||
return usedCap.toFixed(1) + "%";
|
} else {
|
||||||
}
|
return usedCapacity.toFixed(1) + "%";
|
||||||
}.bind(this))
|
}
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
.style("fill-opacity", 1e-6);
|
.style("fill-opacity", 1e-6);
|
||||||
|
|
||||||
// append queue name
|
// append queue name
|
||||||
nodeEnter.append("text")
|
nodeEnter
|
||||||
|
.append("text")
|
||||||
.attr("x", "0px")
|
.attr("x", "0px")
|
||||||
.attr("dy", "45px")
|
.attr("dy", "45px")
|
||||||
.attr("text-anchor", "middle")
|
.attr("text-anchor", "middle")
|
||||||
.text(function(d) { return d.name; })
|
.text(function(d) {
|
||||||
|
return d.name;
|
||||||
|
})
|
||||||
.style("fill-opacity", 1e-6);
|
.style("fill-opacity", 1e-6);
|
||||||
|
|
||||||
// Transition nodes to their new position.
|
// Transition nodes to their new position.
|
||||||
var nodeUpdate = node.transition()
|
var nodeUpdate = node
|
||||||
|
.transition()
|
||||||
.duration(duration)
|
.duration(duration)
|
||||||
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
|
.attr("transform", function(d) {
|
||||||
|
return `translate(${d.y + 50}, ${d.x})`;
|
||||||
|
});
|
||||||
|
|
||||||
nodeUpdate.select("circle")
|
nodeUpdate
|
||||||
|
.select("circle")
|
||||||
.attr("r", 30)
|
.attr("r", 30)
|
||||||
.attr("href",
|
.attr("href", function(d) {
|
||||||
|
return "#/yarn-queues/" + d.queueData.get("name");
|
||||||
|
})
|
||||||
|
.style(
|
||||||
|
"stroke-width",
|
||||||
function(d) {
|
function(d) {
|
||||||
return "#/yarn-queues/" + d.queueData.get("name");
|
if (d.queueData.get("name") === this.get("selected")) {
|
||||||
})
|
return 7;
|
||||||
.style("stroke-width", function(d) {
|
} else {
|
||||||
if (d.queueData.get("name") === this.get("selected")) {
|
return 2;
|
||||||
return 7;
|
}
|
||||||
} else {
|
}.bind(this)
|
||||||
return 2;
|
)
|
||||||
}
|
.style(
|
||||||
}.bind(this))
|
"stroke",
|
||||||
.style("stroke", function(d) {
|
function(d) {
|
||||||
if (d.queueData.get("name") === this.get("selected")) {
|
if (d.queueData.get("name") === this.get("selected")) {
|
||||||
return "gray";
|
return "gray";
|
||||||
} else {
|
} else {
|
||||||
return "gray";
|
return "gray";
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
nodeUpdate.selectAll("text")
|
nodeUpdate.selectAll("text").style("fill-opacity", 1);
|
||||||
.style("fill-opacity", 1);
|
|
||||||
|
|
||||||
// Transition exiting nodes to the parent's new position.
|
// Transition exiting nodes to the parent's new position.
|
||||||
var nodeExit = node.exit().transition()
|
var nodeExit = node
|
||||||
|
.exit()
|
||||||
|
.transition()
|
||||||
.duration(duration)
|
.duration(duration)
|
||||||
.attr("transform", function() { return "translate(" + source.y + "," + source.x + ")"; })
|
.attr("transform", function() {
|
||||||
|
return `translate(${source.y}, ${source.x})`;
|
||||||
|
})
|
||||||
.remove();
|
.remove();
|
||||||
|
|
||||||
nodeExit.select("circle")
|
nodeExit.select("circle").attr("r", 1e-6);
|
||||||
.attr("r", 1e-6);
|
|
||||||
|
|
||||||
nodeExit.select("text")
|
nodeExit.select("text").style("fill-opacity", 1e-6);
|
||||||
.style("fill-opacity", 1e-6);
|
|
||||||
|
|
||||||
// Update the links…
|
// Update the links…
|
||||||
var link = this.mainSvg.selectAll("path.link")
|
var link = this.mainSvg.selectAll("path.link").data(links, function(d) {
|
||||||
.data(links, function(d) { return d.target.id; });
|
return d.target.id;
|
||||||
|
});
|
||||||
|
|
||||||
// Enter any new links at the parent's previous position.
|
// Enter any new links at the parent's previous position.
|
||||||
link.enter().insert("path", "g")
|
link
|
||||||
|
.enter()
|
||||||
|
.insert("path", "g")
|
||||||
.attr("class", "link")
|
.attr("class", "link")
|
||||||
.attr("d", function() {
|
.attr("d", function() {
|
||||||
var o = {x: source.x0, y: source.y0};
|
var o = { x: source.x0, y: source.y0 + 50 };
|
||||||
return diagonal({source: o, target: o});
|
return diagonal({ source: o, target: o });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Transition links to their new position.
|
// Transition links to their new position.
|
||||||
link.transition()
|
link
|
||||||
|
.transition()
|
||||||
.duration(duration)
|
.duration(duration)
|
||||||
.attr("d", diagonal);
|
.attr("d", diagonal);
|
||||||
|
|
||||||
// Transition exiting nodes to the parent's new position.
|
// Transition exiting nodes to the parent's new position.
|
||||||
link.exit().transition()
|
link
|
||||||
|
.exit()
|
||||||
|
.transition()
|
||||||
.duration(duration)
|
.duration(duration)
|
||||||
.attr("d", function() {
|
.attr("d", function() {
|
||||||
var o = {x: source.x, y: source.y};
|
var o = { x: source.x, y: source.y };
|
||||||
return diagonal({source: o, target: o});
|
return diagonal({ source: o, target: o });
|
||||||
})
|
})
|
||||||
.remove();
|
.remove();
|
||||||
|
|
||||||
@ -267,27 +330,32 @@ export default Ember.Component.extend({
|
|||||||
reDraw: function() {
|
reDraw: function() {
|
||||||
this.initData();
|
this.initData();
|
||||||
|
|
||||||
var margin = {top: 20, right: 120, bottom: 20, left: 120};
|
var margin = { top: 20, right: 120, bottom: 20, left: 120 };
|
||||||
var treeWidth = this.maxDepth * 200;
|
var treeWidth = this.maxDepth * 200;
|
||||||
var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
|
var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
|
||||||
var width = treeWidth + margin.left + margin.right;
|
var width = treeWidth + margin.left + margin.right;
|
||||||
var height = treeHeight + margin.top + margin.bottom;
|
var height = treeHeight + margin.top + margin.bottom;
|
||||||
|
|
||||||
if (this.mainSvg) {
|
if (this.mainSvg) {
|
||||||
this.mainSvg.remove();
|
this.mainSvg.selectAll("*").remove();
|
||||||
|
} else {
|
||||||
|
this.mainSvg = d3
|
||||||
|
.select("#" + this.get("parentId"))
|
||||||
|
.append("svg")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height)
|
||||||
|
.attr("class", "tree-selector");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mainSvg = d3.select("#" + this.get("parentId")).append("svg")
|
this.mainSvg
|
||||||
.attr("width", width)
|
|
||||||
.attr("height", height)
|
|
||||||
.attr("class", "tree-selector")
|
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|
||||||
var tree = d3.layout.tree().size([treeHeight, treeWidth]);
|
var tree = d3.layout.tree().size([treeHeight, treeWidth]);
|
||||||
|
|
||||||
var diagonal = d3.svg.diagonal()
|
var diagonal = d3.svg.diagonal().projection(function(d) {
|
||||||
.projection(function(d) { return [d.y, d.x]; });
|
return [d.y + 50, d.x];
|
||||||
|
});
|
||||||
|
|
||||||
var root = this.treeData;
|
var root = this.treeData;
|
||||||
root.x0 = height / 2;
|
root.x0 = height / 2;
|
||||||
@ -299,6 +367,11 @@ export default Ember.Component.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this.reDraw();
|
this.reDraw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const getUsedCapacity = (partitionMap, filter=PARTITION_LABEL) => {
|
||||||
|
return partitionMap[filter].absoluteUsedCapacity;
|
||||||
|
};
|
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Ember from "ember";
|
||||||
|
import { PARTITION_LABEL } from "../constants";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
didUpdateAttrs: function({ oldAttrs, newAttrs }) {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.set("data", this.initData());
|
||||||
|
},
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.set("data", this.initData());
|
||||||
|
},
|
||||||
|
|
||||||
|
initData() {
|
||||||
|
const queue = this.get("queue");
|
||||||
|
const partitionMap = this.get("partitionMap");
|
||||||
|
const filteredParition = this.get("filteredPartition") || PARTITION_LABEL;
|
||||||
|
const userLimit = queue.get("userLimit");
|
||||||
|
const userLimitFactor = queue.get("userLimitFactor");
|
||||||
|
const isLeafQueue = queue.get("isLeafQueue");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...partitionMap[filteredParition],
|
||||||
|
userLimit,
|
||||||
|
userLimitFactor,
|
||||||
|
isLeafQueue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -35,3 +35,5 @@ export const Entities = {
|
|||||||
Resource: 'resource',
|
Resource: 'resource',
|
||||||
Unit: 'unit'
|
Unit: 'unit'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PARTITION_LABEL = 'Default partition';
|
@ -17,19 +17,39 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import {PARTITION_LABEL} from '../constants';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
needReload: true,
|
needReload: true,
|
||||||
selectedQueue: undefined,
|
selectedQueue: undefined,
|
||||||
showLoading: true,
|
showLoading: true,
|
||||||
|
filteredPartition: PARTITION_LABEL,
|
||||||
|
|
||||||
breadcrumbs: [{
|
breadcrumbs: [
|
||||||
text: "Home",
|
{
|
||||||
routeName: 'application'
|
text: "Home",
|
||||||
}, {
|
routeName: "application"
|
||||||
text: "Queues",
|
},
|
||||||
routeName: 'yarn-queues',
|
{
|
||||||
model: 'root'
|
text: "Queues",
|
||||||
}]
|
routeName: "yarn-queues",
|
||||||
|
model: "root"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
setFilter(partition) {
|
||||||
|
this.set("filteredPartition", partition);
|
||||||
|
const model = this.get('model');
|
||||||
|
const {selectedQueue} = model;
|
||||||
|
// If the selected queue does not have the filtered partition
|
||||||
|
// reset it to root
|
||||||
|
if (!selectedQueue.get('partitions').contains(partition)) {
|
||||||
|
const root = model.queues.get('firstObject');
|
||||||
|
document.location.href = "#/yarn-queues/" + root.get("id") + "!";
|
||||||
|
this.set("model.selectedQueue", root);
|
||||||
|
this.set("model.selected", root.get('id'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -20,24 +20,26 @@ import DS from 'ember-data';
|
|||||||
import Converter from 'yarn-ui/utils/converter';
|
import Converter from 'yarn-ui/utils/converter';
|
||||||
|
|
||||||
export default DS.Model.extend({
|
export default DS.Model.extend({
|
||||||
name: DS.attr('string'),
|
name: DS.attr("string"),
|
||||||
children: DS.attr('array'),
|
children: DS.attr("array"),
|
||||||
parent: DS.attr('string'),
|
parent: DS.attr("string"),
|
||||||
capacity: DS.attr('number'),
|
capacity: DS.attr("number"),
|
||||||
maxCapacity: DS.attr('number'),
|
partitions: DS.attr("array"),
|
||||||
usedCapacity: DS.attr('number'),
|
partitionMap: DS.attr("object"),
|
||||||
absCapacity: DS.attr('number'),
|
maxCapacity: DS.attr("number"),
|
||||||
absMaxCapacity: DS.attr('number'),
|
usedCapacity: DS.attr("number"),
|
||||||
absUsedCapacity: DS.attr('number'),
|
absCapacity: DS.attr("number"),
|
||||||
state: DS.attr('string'),
|
absMaxCapacity: DS.attr("number"),
|
||||||
userLimit: DS.attr('number'),
|
absUsedCapacity: DS.attr("number"),
|
||||||
userLimitFactor: DS.attr('number'),
|
state: DS.attr("string"),
|
||||||
preemptionDisabled: DS.attr('number'),
|
userLimit: DS.attr("number"),
|
||||||
numPendingApplications: DS.attr('number'),
|
userLimitFactor: DS.attr("number"),
|
||||||
numActiveApplications: DS.attr('number'),
|
preemptionDisabled: DS.attr("number"),
|
||||||
users: DS.hasMany('YarnUser'),
|
numPendingApplications: DS.attr("number"),
|
||||||
type: DS.attr('string'),
|
numActiveApplications: DS.attr("number"),
|
||||||
resources: DS.attr('object'),
|
users: DS.hasMany("YarnUser"),
|
||||||
|
type: DS.attr("string"),
|
||||||
|
resources: DS.attr("object"),
|
||||||
|
|
||||||
isLeafQueue: function() {
|
isLeafQueue: function() {
|
||||||
var len = this.get("children.length");
|
var len = this.get("children.length");
|
||||||
@ -53,21 +55,29 @@ export default DS.Model.extend({
|
|||||||
{
|
{
|
||||||
label: "Absolute Used",
|
label: "Absolute Used",
|
||||||
style: "primary",
|
style: "primary",
|
||||||
value: this.get("name") === "root" ? floatToFixed(this.get("usedCapacity")) : floatToFixed(this.get("absUsedCapacity"))
|
value:
|
||||||
|
this.get("name") === "root"
|
||||||
|
? floatToFixed(this.get("usedCapacity"))
|
||||||
|
: floatToFixed(this.get("absUsedCapacity"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Absolute Capacity",
|
label: "Absolute Capacity",
|
||||||
style: "primary",
|
style: "primary",
|
||||||
value: this.get("name") === "root" ? 100 : floatToFixed(this.get("absCapacity"))
|
value:
|
||||||
|
this.get("name") === "root"
|
||||||
|
? 100
|
||||||
|
: floatToFixed(this.get("absCapacity"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Absolute Max Capacity",
|
label: "Absolute Max Capacity",
|
||||||
style: "secondary",
|
style: "secondary",
|
||||||
value: this.get("name") === "root" ? 100 : floatToFixed(this.get("absMaxCapacity"))
|
value:
|
||||||
|
this.get("name") === "root"
|
||||||
|
? 100
|
||||||
|
: floatToFixed(this.get("absMaxCapacity"))
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}.property("absCapacity", "usedCapacity", "absMaxCapacity"),
|
}.property("absCapacity", "usedCapacity", "absMaxCapacity"),
|
||||||
|
|
||||||
userUsagesDonutChartData: function() {
|
userUsagesDonutChartData: function() {
|
||||||
var data = [];
|
var data = [];
|
||||||
if (this.get("users")) {
|
if (this.get("users")) {
|
||||||
@ -97,5 +107,5 @@ export default DS.Model.extend({
|
|||||||
value: this.get("numActiveApplications") || 0
|
value: this.get("numActiveApplications") || 0
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}.property("numPendingApplications", "numActiveApplications"),
|
}.property("numPendingApplications", "numActiveApplications")
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
|
import {PARTITION_LABEL} from '../../constants';
|
||||||
|
|
||||||
export default DS.JSONAPISerializer.extend({
|
export default DS.JSONAPISerializer.extend({
|
||||||
|
|
||||||
@ -73,6 +74,11 @@ export default DS.JSONAPISerializer.extend({
|
|||||||
numPendingApplications: payload.numPendingApplications,
|
numPendingApplications: payload.numPendingApplications,
|
||||||
numActiveApplications: payload.numActiveApplications,
|
numActiveApplications: payload.numActiveApplications,
|
||||||
resources: payload.resources,
|
resources: payload.resources,
|
||||||
|
partitions: payload.capacities.queueCapacitiesByPartition.map(cap => cap.partitionName || PARTITION_LABEL),
|
||||||
|
partitionMap: payload.capacities.queueCapacitiesByPartition.reduce((init, cap) => {
|
||||||
|
init[cap.partitionName || PARTITION_LABEL] = cap;
|
||||||
|
return init;
|
||||||
|
}, {}),
|
||||||
type: "capacity",
|
type: "capacity",
|
||||||
},
|
},
|
||||||
// Relationships
|
// Relationships
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
@import 'yarn-app.scss';
|
@import 'yarn-app.scss';
|
||||||
@import './compose-box.scss';
|
@import './compose-box.scss';
|
||||||
@import 'em-table.scss';
|
@import 'em-table.scss';
|
||||||
|
@import './yarn-queues.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.filter-partitions {
|
||||||
|
padding: 15px;
|
||||||
|
margin-left: auto;
|
||||||
|
label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.filter-queue-by-labels {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
||||||
|
}
|
@ -21,9 +21,25 @@
|
|||||||
<div class="col-md-12 container-fluid">
|
<div class="col-md-12 container-fluid">
|
||||||
<div class="panel panel-default" id="tree-selector-container">
|
<div class="panel panel-default" id="tree-selector-container">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
Scheduler: {{model.firstObject.type}}
|
{{#if filteredPartition}}
|
||||||
|
{{model.firstObject.type}} scheduler - Showing queues from partition {{lower filteredPartition}}
|
||||||
|
{{else}}
|
||||||
|
{{model.firstObject.type}} scheduler - Showing queues from all partitions
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{tree-selector model=model parentId="tree-selector-container" selected=selected used=used max=max}}
|
{{#if (eq model.firstObject.type "capacity")}}
|
||||||
|
<div class="flex">
|
||||||
|
<div class="filter-partitions flex-right">
|
||||||
|
<label><i class="glyphicon glyphicon-filter"/> Partitions: </label>
|
||||||
|
<select onchange={{action "filterQueuesByPartition" value="target.value"}} class="form-control js-filter-queue-by-labels">
|
||||||
|
{{#each model.firstObject.partitions as |part|}}
|
||||||
|
<option value={{part}}>{{part}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{tree-selector model=model parentId="tree-selector-container" selected=selected used=used max=max filteredPartition=filteredPartition}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
{{!
|
||||||
|
* 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="top-1">
|
||||||
|
<span class="yarn-label primary">
|
||||||
|
<span class="label-key">absolute used</span>
|
||||||
|
<span class="label-value">{{data.absoluteUsedCapacity}}%</span>
|
||||||
|
</span>
|
||||||
|
<span class="yarn-label primary">
|
||||||
|
<span class="label-key">absolute capacity</span>
|
||||||
|
<span class="label-value">{{data.absoluteCapacity}}%</span>
|
||||||
|
</span>
|
||||||
|
<span class="yarn-label secondary">
|
||||||
|
<span class="label-key">absolute max capacity</span>
|
||||||
|
<span class="label-value">{{data.absoluteMaxCapacity}}%</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="top-1">
|
||||||
|
<span class="yarn-label secondary">
|
||||||
|
<span class="label-key">configured capacity</span>
|
||||||
|
<span class="label-value">{{data.capacity}}%</span>
|
||||||
|
</span>
|
||||||
|
<span class="yarn-label secondary">
|
||||||
|
<span class="label-key">configured max capacity</span>
|
||||||
|
<span class="label-value">{{data.maxCapacity}}%</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{#if data.isLeafQueue}}
|
||||||
|
<div class="top-1">
|
||||||
|
<span class="yarn-label secondary">
|
||||||
|
<span class="label-key">user limit</span>
|
||||||
|
<span class="label-value">{{data.userLimit}}%</span>
|
||||||
|
</span>
|
||||||
|
<span class="yarn-label secondary">
|
||||||
|
<span class="label-key">user limit factor</span>
|
||||||
|
<span class="label-value">{{data.userLimitFactor}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -17,7 +17,7 @@
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
{{queue-navigator model=model.queues selected=model.selected
|
{{queue-navigator model=model.queues selected=model.selected
|
||||||
used="usedCapacity" max="absMaxCapacity"}}
|
used="usedCapacity" max="absMaxCapacity" setFilter=(action setFilter)}}
|
||||||
|
|
||||||
<div class="yarn-compose-box yarn-queues-container">
|
<div class="yarn-compose-box yarn-queues-container">
|
||||||
<div>
|
<div>
|
||||||
@ -31,36 +31,8 @@
|
|||||||
{{em-table-simple-status-cell content=model.selectedQueue.state}}
|
{{em-table-simple-status-cell content=model.selectedQueue.state}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="top-1">
|
|
||||||
{{#each model.selectedQueue.capacitiesBarChartData as |item|}}
|
{{yarn-queue-partition-capacity-labels partitionMap=model.selectedQueue.partitionMap queue=model.selectedQueue filteredPartition=filteredPartition}}
|
||||||
<span class="yarn-label {{item.style}}">
|
|
||||||
<span class="label-key"> {{lower item.label}}</span>
|
|
||||||
<span class="label-value">{{item.value}}%</span>
|
|
||||||
</span>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="top-1">
|
|
||||||
<span class="yarn-label secondary">
|
|
||||||
<span class="label-key">configured capacity</span>
|
|
||||||
<span class="label-value">{{model.selectedQueue.capacity}}%</span>
|
|
||||||
</span>
|
|
||||||
<span class="yarn-label secondary">
|
|
||||||
<span class="label-key">configured max capacity</span>
|
|
||||||
<span class="label-value">{{model.selectedQueue.maxCapacity}}%</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{#if model.selectedQueue.isLeafQueue}}
|
|
||||||
<div class="top-1">
|
|
||||||
<span class="yarn-label secondary">
|
|
||||||
<span class="label-key">user limit</span>
|
|
||||||
<span class="label-value">{{model.selectedQueue.userLimit}}%</span>
|
|
||||||
</span>
|
|
||||||
<span class="yarn-label secondary">
|
|
||||||
<span class="label-key">user limit factor</span>
|
|
||||||
<span class="label-value">{{model.selectedQueue.userLimitFactor}}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5> Running Apps </h5>
|
<h5> Running Apps </h5>
|
||||||
|
@ -18,9 +18,10 @@
|
|||||||
<div class="queue-page-breadcrumb">
|
<div class="queue-page-breadcrumb">
|
||||||
{{breadcrumb-bar breadcrumbs=breadcrumbs}}
|
{{breadcrumb-bar breadcrumbs=breadcrumbs}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
{{#if (eq model.queues.firstObject.type "capacity")}}
|
{{#if (eq model.queues.firstObject.type "capacity")}}
|
||||||
{{yarn-queue.capacity-queue model=model}}
|
{{yarn-queue.capacity-queue model=model setFilter=(action "setFilter") filteredPartition=filteredPartition}}
|
||||||
{{else if (eq model.queues.firstObject.type "fair")}}
|
{{else if (eq model.queues.firstObject.type "fair")}}
|
||||||
{{yarn-queue.fair-queue model=model}}
|
{{yarn-queue.fair-queue model=model}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { moduleForComponent, test } from 'ember-qunit';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
moduleForComponent('yarn-queue-partition-capacity-labels', 'Integration | Component | yarn queue partition capacity labels', {
|
||||||
|
integration: true
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders', function(assert) {
|
||||||
|
|
||||||
|
// Set any properties with this.set('myProperty', 'value');
|
||||||
|
// Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
|
||||||
|
|
||||||
|
this.render(hbs`{{yarn-queue-partition-capacity-labels}}`);
|
||||||
|
|
||||||
|
assert.equal(this.$().text().trim(), '');
|
||||||
|
|
||||||
|
// Template block usage:" + EOL +
|
||||||
|
this.render(hbs`
|
||||||
|
{{#yarn-queue-partition-capacity-labels}}
|
||||||
|
template block text
|
||||||
|
{{/yarn-queue-partition-capacity-labels}}
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.equal(this.$().text().trim(), 'template block text');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user