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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import Ember from 'ember';
|
||||
import Ember from "ember";
|
||||
import {PARTITION_LABEL} from '../constants';
|
||||
|
||||
const INBETWEEN_HEIGHT = 130;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
// Map: <queue-name, queue>
|
||||
map : undefined,
|
||||
map: undefined,
|
||||
|
||||
// Normalized data for d3
|
||||
treeData: undefined,
|
||||
|
||||
// folded queues, folded[<queue-name>] == true means <queue-name> is folded
|
||||
foldedQueues: { },
|
||||
foldedQueues: {},
|
||||
|
||||
// maxDepth
|
||||
maxDepth: 0,
|
||||
|
@ -42,17 +43,23 @@ export default Ember.Component.extend({
|
|||
used: undefined,
|
||||
max: undefined,
|
||||
|
||||
didUpdateAttrs: function({ oldAttrs, newAttrs }) {
|
||||
if (oldAttrs.filteredPartition.value !== newAttrs.filteredPartition.value) {
|
||||
this.reDraw();
|
||||
}
|
||||
},
|
||||
// Init data
|
||||
initData: function() {
|
||||
this.map = { };
|
||||
this.treeData = { };
|
||||
this.map = {};
|
||||
this.treeData = {};
|
||||
this.maxDepth = 0;
|
||||
this.numOfLeafQueue = 0;
|
||||
|
||||
this.get("model")
|
||||
.forEach(function(o) {
|
||||
this.get("model").forEach(
|
||||
function(o) {
|
||||
this.map[o.id] = o;
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// var selected = this.get("selected");
|
||||
this.used = this.get("used");
|
||||
|
@ -81,9 +88,9 @@ export default Ember.Component.extend({
|
|||
|
||||
// Init queues
|
||||
initQueue: function(queueName, depth, node) {
|
||||
if ((!queueName) || (!this.map[queueName])) {
|
||||
if (!queueName || !this.map[queueName]) {
|
||||
// Queue is not existed
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (depth > this.maxDepth) {
|
||||
this.maxDepth = this.maxDepth + 1;
|
||||
|
@ -91,6 +98,13 @@ export default Ember.Component.extend({
|
|||
|
||||
var queue = this.map[queueName];
|
||||
|
||||
if (
|
||||
this.filteredPartition &&
|
||||
!queue.get("partitions").contains(this.filteredPartition)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var names = this.getChildrenNamesArray(queue);
|
||||
|
||||
node.name = queueName;
|
||||
|
@ -100,14 +114,21 @@ export default Ember.Component.extend({
|
|||
if (names.length > 0) {
|
||||
node.children = [];
|
||||
|
||||
names.forEach(function(name) {
|
||||
var childQueueData = {};
|
||||
node.children.push(childQueueData);
|
||||
this.initQueue(name, depth + 1, childQueueData);
|
||||
}.bind(this));
|
||||
names.forEach(
|
||||
function(name) {
|
||||
var childQueueData = {};
|
||||
node.children.push(childQueueData);
|
||||
const status = this.initQueue(name, depth + 1, childQueueData);
|
||||
if (!status) {
|
||||
node.children.pop();
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
this.numOfLeafQueue = this.numOfLeafQueue + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
update: function(source, root, tree, diagonal) {
|
||||
|
@ -119,141 +140,183 @@ export default Ember.Component.extend({
|
|||
var links = tree.links(nodes);
|
||||
|
||||
// Normalize for fixed-depth.
|
||||
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";
|
||||
nodes.forEach(function(d) {
|
||||
d.y = d.depth * 200;
|
||||
});
|
||||
|
||||
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)
|
||||
.style("fill", function(d) {
|
||||
var maxCap = d.queueData.get(this.max);
|
||||
maxCap = maxCap === undefined ? 100 : maxCap;
|
||||
var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
|
||||
if (usedCap <= 60.0) {
|
||||
return "mediumaquamarine";
|
||||
} else if (usedCap <= 100.0) {
|
||||
return "coral";
|
||||
} else {
|
||||
return "salmon";
|
||||
}
|
||||
}.bind(this));
|
||||
.style(
|
||||
"fill",
|
||||
function(d) {
|
||||
const usedCapacity = getUsedCapacity(d.queueData.get("partitionMap"), this.filteredPartition);
|
||||
if (usedCapacity <= 60.0) {
|
||||
return "#60cea5";
|
||||
} else if (usedCapacity <= 100.0) {
|
||||
return "#ffbc0b";
|
||||
} else {
|
||||
return "#ef6162";
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// append percentage
|
||||
nodeEnter.append("text")
|
||||
.attr("x", function() { return 0; })
|
||||
nodeEnter
|
||||
.append("text")
|
||||
.attr("x", function() {
|
||||
return 0;
|
||||
})
|
||||
.attr("dy", ".35em")
|
||||
.attr("fill", "white")
|
||||
.attr("text-anchor", function() { return "middle"; })
|
||||
.text(function(d) {
|
||||
var maxCap = d.queueData.get(this.max);
|
||||
maxCap = maxCap === undefined ? 100 : maxCap;
|
||||
var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
|
||||
if (usedCap >= 100.0) {
|
||||
return usedCap.toFixed(0) + "%";
|
||||
} else {
|
||||
return usedCap.toFixed(1) + "%";
|
||||
}
|
||||
}.bind(this))
|
||||
.attr("text-anchor", function() {
|
||||
return "middle";
|
||||
})
|
||||
.text(
|
||||
function(d) {
|
||||
const usedCapacity = getUsedCapacity(d.queueData.get("partitionMap"), this.filteredPartition);
|
||||
if (usedCapacity >= 100.0) {
|
||||
return usedCapacity.toFixed(0) + "%";
|
||||
} else {
|
||||
return usedCapacity.toFixed(1) + "%";
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// append queue name
|
||||
nodeEnter.append("text")
|
||||
nodeEnter
|
||||
.append("text")
|
||||
.attr("x", "0px")
|
||||
.attr("dy", "45px")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.name; })
|
||||
.text(function(d) {
|
||||
return d.name;
|
||||
})
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// Transition nodes to their new position.
|
||||
var nodeUpdate = node.transition()
|
||||
var nodeUpdate = node
|
||||
.transition()
|
||||
.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("href",
|
||||
.attr("href", function(d) {
|
||||
return "#/yarn-queues/" + d.queueData.get("name");
|
||||
})
|
||||
.style(
|
||||
"stroke-width",
|
||||
function(d) {
|
||||
return "#/yarn-queues/" + d.queueData.get("name");
|
||||
})
|
||||
.style("stroke-width", function(d) {
|
||||
if (d.queueData.get("name") === this.get("selected")) {
|
||||
return 7;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}.bind(this))
|
||||
.style("stroke", function(d) {
|
||||
if (d.queueData.get("name") === this.get("selected")) {
|
||||
return "gray";
|
||||
} else {
|
||||
return "gray";
|
||||
}
|
||||
}.bind(this));
|
||||
if (d.queueData.get("name") === this.get("selected")) {
|
||||
return 7;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
.style(
|
||||
"stroke",
|
||||
function(d) {
|
||||
if (d.queueData.get("name") === this.get("selected")) {
|
||||
return "gray";
|
||||
} else {
|
||||
return "gray";
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
nodeUpdate.selectAll("text")
|
||||
.style("fill-opacity", 1);
|
||||
nodeUpdate.selectAll("text").style("fill-opacity", 1);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
var nodeExit = node.exit().transition()
|
||||
var nodeExit = node
|
||||
.exit()
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", function() { return "translate(" + source.y + "," + source.x + ")"; })
|
||||
.attr("transform", function() {
|
||||
return `translate(${source.y}, ${source.x})`;
|
||||
})
|
||||
.remove();
|
||||
|
||||
nodeExit.select("circle")
|
||||
.attr("r", 1e-6);
|
||||
nodeExit.select("circle").attr("r", 1e-6);
|
||||
|
||||
nodeExit.select("text")
|
||||
.style("fill-opacity", 1e-6);
|
||||
nodeExit.select("text").style("fill-opacity", 1e-6);
|
||||
|
||||
// Update the links…
|
||||
var link = this.mainSvg.selectAll("path.link")
|
||||
.data(links, function(d) { return d.target.id; });
|
||||
var link = this.mainSvg.selectAll("path.link").data(links, function(d) {
|
||||
return d.target.id;
|
||||
});
|
||||
|
||||
// Enter any new links at the parent's previous position.
|
||||
link.enter().insert("path", "g")
|
||||
link
|
||||
.enter()
|
||||
.insert("path", "g")
|
||||
.attr("class", "link")
|
||||
.attr("d", function() {
|
||||
var o = {x: source.x0, y: source.y0};
|
||||
return diagonal({source: o, target: o});
|
||||
var o = { x: source.x0, y: source.y0 + 50 };
|
||||
return diagonal({ source: o, target: o });
|
||||
});
|
||||
|
||||
// Transition links to their new position.
|
||||
link.transition()
|
||||
link
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("d", diagonal);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
link.exit().transition()
|
||||
link
|
||||
.exit()
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("d", function() {
|
||||
var o = {x: source.x, y: source.y};
|
||||
return diagonal({source: o, target: o});
|
||||
var o = { x: source.x, y: source.y };
|
||||
return diagonal({ source: o, target: o });
|
||||
})
|
||||
.remove();
|
||||
|
||||
|
@ -267,27 +330,32 @@ export default Ember.Component.extend({
|
|||
reDraw: function() {
|
||||
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 treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
|
||||
var width = treeWidth + margin.left + margin.right;
|
||||
var height = treeHeight + margin.top + margin.bottom;
|
||||
|
||||
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")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("class", "tree-selector")
|
||||
this.mainSvg
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var tree = d3.layout.tree().size([treeHeight, treeWidth]);
|
||||
|
||||
var diagonal = d3.svg.diagonal()
|
||||
.projection(function(d) { return [d.y, d.x]; });
|
||||
var diagonal = d3.svg.diagonal().projection(function(d) {
|
||||
return [d.y + 50, d.x];
|
||||
});
|
||||
|
||||
var root = this.treeData;
|
||||
root.x0 = height / 2;
|
||||
|
@ -299,6 +367,11 @@ export default Ember.Component.extend({
|
|||
},
|
||||
|
||||
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',
|
||||
Unit: 'unit'
|
||||
}
|
||||
|
||||
export const PARTITION_LABEL = 'Default partition';
|
|
@ -17,19 +17,39 @@
|
|||
*/
|
||||
|
||||
import Ember from 'ember';
|
||||
import {PARTITION_LABEL} from '../constants';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
needReload: true,
|
||||
selectedQueue: undefined,
|
||||
showLoading: true,
|
||||
filteredPartition: PARTITION_LABEL,
|
||||
|
||||
breadcrumbs: [{
|
||||
text: "Home",
|
||||
routeName: 'application'
|
||||
}, {
|
||||
text: "Queues",
|
||||
routeName: 'yarn-queues',
|
||||
model: 'root'
|
||||
}]
|
||||
breadcrumbs: [
|
||||
{
|
||||
text: "Home",
|
||||
routeName: "application"
|
||||
},
|
||||
{
|
||||
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';
|
||||
|
||||
export default DS.Model.extend({
|
||||
name: DS.attr('string'),
|
||||
children: DS.attr('array'),
|
||||
parent: DS.attr('string'),
|
||||
capacity: DS.attr('number'),
|
||||
maxCapacity: DS.attr('number'),
|
||||
usedCapacity: DS.attr('number'),
|
||||
absCapacity: DS.attr('number'),
|
||||
absMaxCapacity: DS.attr('number'),
|
||||
absUsedCapacity: DS.attr('number'),
|
||||
state: DS.attr('string'),
|
||||
userLimit: DS.attr('number'),
|
||||
userLimitFactor: DS.attr('number'),
|
||||
preemptionDisabled: DS.attr('number'),
|
||||
numPendingApplications: DS.attr('number'),
|
||||
numActiveApplications: DS.attr('number'),
|
||||
users: DS.hasMany('YarnUser'),
|
||||
type: DS.attr('string'),
|
||||
resources: DS.attr('object'),
|
||||
name: DS.attr("string"),
|
||||
children: DS.attr("array"),
|
||||
parent: DS.attr("string"),
|
||||
capacity: DS.attr("number"),
|
||||
partitions: DS.attr("array"),
|
||||
partitionMap: DS.attr("object"),
|
||||
maxCapacity: DS.attr("number"),
|
||||
usedCapacity: DS.attr("number"),
|
||||
absCapacity: DS.attr("number"),
|
||||
absMaxCapacity: DS.attr("number"),
|
||||
absUsedCapacity: DS.attr("number"),
|
||||
state: DS.attr("string"),
|
||||
userLimit: DS.attr("number"),
|
||||
userLimitFactor: DS.attr("number"),
|
||||
preemptionDisabled: DS.attr("number"),
|
||||
numPendingApplications: DS.attr("number"),
|
||||
numActiveApplications: DS.attr("number"),
|
||||
users: DS.hasMany("YarnUser"),
|
||||
type: DS.attr("string"),
|
||||
resources: DS.attr("object"),
|
||||
|
||||
isLeafQueue: function() {
|
||||
var len = this.get("children.length");
|
||||
|
@ -53,21 +55,29 @@ export default DS.Model.extend({
|
|||
{
|
||||
label: "Absolute Used",
|
||||
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",
|
||||
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",
|
||||
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"),
|
||||
|
||||
userUsagesDonutChartData: function() {
|
||||
var data = [];
|
||||
if (this.get("users")) {
|
||||
|
@ -97,5 +107,5 @@ export default DS.Model.extend({
|
|||
value: this.get("numActiveApplications") || 0
|
||||
}
|
||||
];
|
||||
}.property("numPendingApplications", "numActiveApplications"),
|
||||
}.property("numPendingApplications", "numActiveApplications")
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import DS from 'ember-data';
|
||||
import {PARTITION_LABEL} from '../../constants';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
|
||||
|
@ -73,6 +74,11 @@ export default DS.JSONAPISerializer.extend({
|
|||
numPendingApplications: payload.numPendingApplications,
|
||||
numActiveApplications: payload.numActiveApplications,
|
||||
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",
|
||||
},
|
||||
// Relationships
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@import 'yarn-app.scss';
|
||||
@import './compose-box.scss';
|
||||
@import 'em-table.scss';
|
||||
@import './yarn-queues.scss';
|
||||
|
||||
/**
|
||||
* 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="panel panel-default" id="tree-selector-container">
|
||||
<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>
|
||||
{{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>
|
||||
|
|
|
@ -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
|
||||
used="usedCapacity" max="absMaxCapacity"}}
|
||||
used="usedCapacity" max="absMaxCapacity" setFilter=(action setFilter)}}
|
||||
|
||||
<div class="yarn-compose-box yarn-queues-container">
|
||||
<div>
|
||||
|
@ -31,36 +31,8 @@
|
|||
{{em-table-simple-status-cell content=model.selectedQueue.state}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="top-1">
|
||||
{{#each model.selectedQueue.capacitiesBarChartData as |item|}}
|
||||
<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}}
|
||||
|
||||
{{yarn-queue-partition-capacity-labels partitionMap=model.selectedQueue.partitionMap queue=model.selectedQueue filteredPartition=filteredPartition}}
|
||||
</div>
|
||||
|
||||
<h5> Running Apps </h5>
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
<div class="queue-page-breadcrumb">
|
||||
{{breadcrumb-bar breadcrumbs=breadcrumbs}}
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
{{#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")}}
|
||||
{{yarn-queue.fair-queue model=model}}
|
||||
{{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…
Reference in New Issue