YARN-4733. [YARN-3368] Initial commit of new YARN web UI. (wangda)
This commit is contained in:
parent
d8bab3dcb6
commit
53e661f68e
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"analytics": false
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.css]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.html]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
/**
|
||||
Ember CLI sends analytics information by default. The data is completely
|
||||
anonymous, but there are times when you might want to disable this behavior.
|
||||
|
||||
Setting `disableAnalytics` to true will prevent any data from being sent.
|
||||
*/
|
||||
"disableAnalytics": false,
|
||||
"liveReload": true,
|
||||
"watcher": "polling"
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/bower_components
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"-Promise"
|
||||
],
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
"curly": true,
|
||||
"debug": false,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"eqnull": true,
|
||||
"esnext": true,
|
||||
"unused": true
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
|
||||
- "npm config set spin false"
|
||||
- "npm install -g npm@^2"
|
||||
|
||||
install:
|
||||
- npm install -g bower
|
||||
- npm install
|
||||
- bower install
|
||||
|
||||
script:
|
||||
- npm test
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"ignore_dirs": ["tmp"]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# Yarn-ui
|
||||
*This is a WIP project, nobody should use it in production.*
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need the following things properly installed on your computer.
|
||||
|
||||
* Install Node.js with NPM: https://nodejs.org/download/
|
||||
* After Node.js installed, install bower: `npm install -g bower`.
|
||||
* Install Ember-cli: `npm install -g ember-cli`
|
||||
|
||||
## Installation
|
||||
|
||||
* Goto root directory of yarn-ui project: `hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui`
|
||||
* `npm install && bower install`, it will take a while to finish.
|
||||
|
||||
## Try it
|
||||
|
||||
* Packaging and deploying Hadoop in this branch (You can use latest trunk after YARN-4417 committed to trunk)
|
||||
* Modify `app/adapters/yarn-app.js`, change `host` to your YARN RM web address
|
||||
* If you running YARN RM in your localhost, you should install `npm install -g corsproxy` and run `corsproxy` to avoid CORS errors. More details: `https://www.npmjs.com/package/corsproxy`. And the `host` of `app/adapters/yarn-app.js` should start with `localhost:1337`.
|
||||
* Run `ember server`
|
||||
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
host: 'http://localhost:1337/localhost:8088', // configurable
|
||||
namespace: 'ws/v1/cluster', // common const
|
||||
pathForType(modelName) {
|
||||
return ''; // move to some common place, return path by modelname.
|
||||
},
|
||||
ajax(url, method, hash) {
|
||||
hash = hash || {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
host: 'http://localhost:1337/localhost:8088', // configurable
|
||||
namespace: 'ws/v1/cluster/metrics', // common const
|
||||
pathForType(modelName) {
|
||||
return ''; // move to some common place, return path by modelname.
|
||||
},
|
||||
ajax(url, method, hash) {
|
||||
hash = hash || {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
host: 'http://localhost:1337/localhost:8088', // configurable
|
||||
namespace: 'ws/v1/cluster', // common const
|
||||
|
||||
urlForQuery(query, modelName) {
|
||||
var url = this._buildURL();
|
||||
return url + '/apps/' + query.appId + "/appattempts";
|
||||
},
|
||||
|
||||
urlForFindRecord(id, modelName, snapshot) {
|
||||
var url = this._buildURL();
|
||||
var url = url + '/apps/' +
|
||||
Converter.attemptIdToAppId(id) + "/appattempts/" + id;
|
||||
console.log(url);
|
||||
return url;
|
||||
},
|
||||
|
||||
ajax(url, method, hash) {
|
||||
hash = {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
host: 'http://localhost:1337/localhost:8088', // configurable
|
||||
namespace: 'ws/v1/cluster', // common const
|
||||
pathForType(modelName) {
|
||||
return 'apps'; // move to some common place, return path by modelname.
|
||||
},
|
||||
/*
|
||||
urlForQuery(query, modelName) {
|
||||
var url = this._buildURL();
|
||||
return url + '/apps/' + query.appId + "/appattempts";
|
||||
},
|
||||
*/
|
||||
ajax(url, method, hash) {
|
||||
hash = hash || {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
rmHost: 'http://localhost:1337/localhost:8088',
|
||||
tsHost: 'http://localhost:1337/localhost:8188',
|
||||
host: function() {
|
||||
return undefined
|
||||
}.property(),
|
||||
rmNamespace: 'ws/v1/cluster',
|
||||
tsNamespace: 'ws/v1/applicationhistory',
|
||||
namespace: function() {
|
||||
return undefined
|
||||
}.property(),
|
||||
|
||||
urlForQuery(query, modelName) {
|
||||
if (query.is_rm) {
|
||||
this.set("host", this.rmHost);
|
||||
this.set("namespace", this.rmNamespace);
|
||||
} else {
|
||||
this.set("host", this.tsHost);
|
||||
this.set("namespace", this.tsNamespace);
|
||||
}
|
||||
|
||||
var url = this._buildURL();
|
||||
url = url + '/apps/' + Converter.attemptIdToAppId(query.app_attempt_id)
|
||||
+ "/appattempts/" + query.app_attempt_id + "/containers";
|
||||
console.log(url);
|
||||
return url;
|
||||
},
|
||||
|
||||
ajax(url, method, hash) {
|
||||
hash = {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPIAdapter.extend({
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
host: 'http://localhost:1337/localhost:8088', // configurable
|
||||
namespace: 'ws/v1/cluster', // common const
|
||||
pathForType(modelName) {
|
||||
return 'scheduler'; // move to some common place, return path by modelname.
|
||||
},
|
||||
ajax(url, method, hash) {
|
||||
hash = hash || {};
|
||||
hash.crossDomain = true;
|
||||
hash.xhrFields = {withCredentials: true};
|
||||
hash.targetServer = "RM";
|
||||
return this._super(url, method, hash);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import Ember from 'ember';
|
||||
import Resolver from 'ember/resolver';
|
||||
import loadInitializers from 'ember/load-initializers';
|
||||
import config from './config/environment';
|
||||
import Sorter from 'yarn-ui/utils/sorter';
|
||||
|
||||
var App;
|
||||
|
||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||
|
||||
App = Ember.Application.extend({
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix,
|
||||
Resolver: Resolver
|
||||
});
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
Sorter.initDataTableSorter();
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
import Ember from 'ember';
|
||||
import BaseChartComponent from 'yarn-ui/components/base-chart-component';
|
||||
|
||||
export default BaseChartComponent.extend({
|
||||
// data:
|
||||
// [{label=label1, value=value1}, ...]
|
||||
// ...
|
||||
renderBarChart: function(data, title, textWidth = 50) {
|
||||
var g = this.chart.g;
|
||||
var layout = this.getLayout();
|
||||
this.renderTitleAndBG(g, title, layout);
|
||||
|
||||
var maxValue = -1;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i] instanceof Array) {
|
||||
if (data[i][0].value > maxValue) {
|
||||
maxValue = data[i][0].value;
|
||||
}
|
||||
} else {
|
||||
if (data[i].value > maxValue) {
|
||||
maxValue = data[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var singleBarHeight = 30;
|
||||
|
||||
// 50 is for text
|
||||
var maxBarWidth = layout.x2 - layout.x1 - 2 * layout.margin - textWidth - 50;
|
||||
|
||||
// 30 is for title
|
||||
var maxBarsHeight = layout.y2 - layout.y1 - 2 * layout.margin - 30;
|
||||
var gap = (maxBarsHeight - data.length * singleBarHeight) / (data.length -
|
||||
1);
|
||||
|
||||
var xScaler = d3.scale.linear()
|
||||
.domain([0, maxValue])
|
||||
.range([0, maxBarWidth]);
|
||||
|
||||
// show bar text
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
g.append("text")
|
||||
.text(
|
||||
function() {
|
||||
return data[i].label;
|
||||
})
|
||||
.attr("y", function() {
|
||||
return layout.y1 + singleBarHeight / 2 + layout.margin + (gap +
|
||||
singleBarHeight) * i + 30;
|
||||
})
|
||||
.attr("x", layout.x1 + layout.margin);
|
||||
}
|
||||
|
||||
// show bar
|
||||
var bar = g.selectAll("bars")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("y", function(d, i) {
|
||||
return layout.y1 + 30 + layout.margin + (gap + singleBarHeight) * i;
|
||||
})
|
||||
.attr("x", layout.x1 + layout.margin + textWidth)
|
||||
.attr("height", singleBarHeight)
|
||||
.attr("fill", function(d, i) {
|
||||
return this.colors[i];
|
||||
}.bind(this))
|
||||
.attr("width", 0);
|
||||
|
||||
this.bindTooltip(bar);
|
||||
|
||||
bar.transition()
|
||||
.duration(500)
|
||||
.attr("width", function(d) {
|
||||
var w;
|
||||
w = xScaler(d.value);
|
||||
// At least each item has 3 px
|
||||
w = Math.max(w, 3);
|
||||
return w;
|
||||
});
|
||||
|
||||
// show bar value
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
g.append("text")
|
||||
.text(
|
||||
function() {
|
||||
return data[i].value;
|
||||
})
|
||||
.attr("y", function() {
|
||||
return layout.y1 + singleBarHeight / 2 + layout.margin + (gap +
|
||||
singleBarHeight) * i + 30;
|
||||
})
|
||||
.attr("x", layout.x1 + layout.margin + textWidth + 15 + xScaler(data[i].value));
|
||||
}
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
this.initChart();
|
||||
this.renderBarChart(this.get("data"), this.get("title"), this.get("textWidth"));
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.draw();
|
||||
},
|
||||
})
|
|
@ -0,0 +1,109 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
chart: undefined,
|
||||
tooltip : undefined,
|
||||
colors: d3.scale.category10().range(),
|
||||
|
||||
initChart: function() {
|
||||
this.chart = {
|
||||
svg: undefined,
|
||||
g: undefined,
|
||||
h: 0,
|
||||
w: 0,
|
||||
tooltip: undefined
|
||||
};
|
||||
|
||||
// Init tooltip if it is not initialized
|
||||
this.tooltip = d3.select("#chart-tooltip");
|
||||
if (!this.tooltip) {
|
||||
this.tooltip = d3.select("body")
|
||||
.append("div")
|
||||
.attr("class", "tooltip")
|
||||
.attr("id", "chart-tooltip")
|
||||
.style("opacity", 0);
|
||||
}
|
||||
|
||||
// Init svg
|
||||
var svg = this.chart.svg;
|
||||
if (svg) {
|
||||
svg.remove();
|
||||
}
|
||||
|
||||
var parentId = this.get("parentId");
|
||||
var parent = d3.select("#" + parentId);
|
||||
var bbox = parent.node().getBoundingClientRect();
|
||||
this.chart.w = bbox.width - 30;
|
||||
|
||||
var ratio = 0.75; // 4:3 by default
|
||||
if (this.get("ratio")) {
|
||||
ratio = this.get("ratio");
|
||||
}
|
||||
this.chart.h = bbox.width * ratio;
|
||||
|
||||
if (this.get("maxHeight")) {
|
||||
this.chart.h = Math.min(this.get("maxHeight"), this.chart.h);
|
||||
}
|
||||
|
||||
this.chart.svg = parent.append("svg")
|
||||
.attr("width", this.chart.w)
|
||||
.attr("height", this.chart.h);
|
||||
|
||||
this.chart.g = this.chart.svg.append("g");
|
||||
},
|
||||
|
||||
renderTitleAndBG: function(g, title, layout) {
|
||||
var bg = g.append("g");
|
||||
bg.append("text")
|
||||
.text(title)
|
||||
.attr("x", (layout.x1 + layout.x2) / 2)
|
||||
.attr("y", layout.y1 + layout.margin + 20)
|
||||
.attr("class", "chart-title");
|
||||
|
||||
bg.append("rect")
|
||||
.attr("x", layout.x1)
|
||||
.attr("y", layout.y1)
|
||||
.attr("width", layout.x2 - layout.x1)
|
||||
.attr("height", layout.y2 - layout.y1)
|
||||
.attr("class", "chart-frame");
|
||||
},
|
||||
|
||||
bindTooltip: function(d) {
|
||||
d.on("mouseover", function(d) {
|
||||
this.tooltip
|
||||
.style("left", (d3.event.pageX) + "px")
|
||||
.style("top", (d3.event.pageY - 28) + "px");
|
||||
}.bind(this))
|
||||
.on("mousemove", function(d) {
|
||||
// Handle pie chart case
|
||||
var data = d;
|
||||
if (d.data) {
|
||||
data = d.data;
|
||||
}
|
||||
|
||||
this.tooltip.style("opacity", .9);
|
||||
this.tooltip.html(data.label + " = " + data.value)
|
||||
.style("left", (d3.event.pageX) + "px")
|
||||
.style("top", (d3.event.pageY - 28) + "px");
|
||||
}.bind(this))
|
||||
.on("mouseout", function(d) {
|
||||
this.tooltip.style("opacity", 0);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getLayout: function() {
|
||||
var x1 = 0;
|
||||
var y1 = 0;
|
||||
var x2 = this.chart.w;
|
||||
var y2 = this.chart.h;
|
||||
|
||||
var layout = {
|
||||
x1: x1,
|
||||
y1: y1,
|
||||
x2: x2 - 10,
|
||||
y2: y2 - 10,
|
||||
margin: 10
|
||||
};
|
||||
return layout;
|
||||
},
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
});
|
|
@ -0,0 +1,148 @@
|
|||
import Ember from 'ember';
|
||||
import BaseChartComponent from 'yarn-ui/components/base-chart-component';
|
||||
|
||||
export default BaseChartComponent.extend({
|
||||
/*
|
||||
* data = [{label="xx", value=},{...}]
|
||||
*/
|
||||
renderDonutChart: function(data, title, showLabels = false,
|
||||
middleLabel = "Total", middleValue = undefined) {
|
||||
var g = this.chart.g;
|
||||
var layout = this.getLayout();
|
||||
this.renderTitleAndBG(g, title, layout);
|
||||
|
||||
var total = 0;
|
||||
var allZero = true;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
total += data[i].value;
|
||||
if (data[i].value > 1e-6) {
|
||||
allZero = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!middleValue) {
|
||||
middleValue = total;
|
||||
}
|
||||
|
||||
//Width and height
|
||||
var h = layout.y2 - layout.y1;
|
||||
|
||||
// 50 is for title
|
||||
var outerRadius = (h - 50 - 2 * layout.margin) / 2;
|
||||
var innerRadius = outerRadius * 0.618;
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(outerRadius);
|
||||
|
||||
var cx;
|
||||
var cy = layout.y1 + 50 + layout.margin + outerRadius;
|
||||
if (showLabels) {
|
||||
cx = layout.x1 + layout.margin + outerRadius;
|
||||
} else {
|
||||
cx = (layout.x1 + layout.x2) / 2;
|
||||
}
|
||||
|
||||
var pie = d3.layout.pie();
|
||||
pie.sort(null);
|
||||
pie.value(function(d) {
|
||||
var v = d.value;
|
||||
// make sure it > 0
|
||||
v = Math.max(v, 1e-6);
|
||||
return v;
|
||||
});
|
||||
|
||||
//Set up groups
|
||||
var arcs = g
|
||||
.selectAll("g.arc")
|
||||
.data(pie(data))
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("class", "arc")
|
||||
.attr("transform", "translate(" + cx + "," + cy + ")");
|
||||
|
||||
function tweenPie(finish) {
|
||||
var start = {
|
||||
startAngle: 0,
|
||||
endAngle: 0
|
||||
};
|
||||
var i = d3.interpolate(start, finish);
|
||||
return function(d) {
|
||||
return arc(i(d));
|
||||
};
|
||||
}
|
||||
|
||||
//Draw arc paths
|
||||
var path = arcs.append("path")
|
||||
.attr("fill", function(d, i) {
|
||||
if (d.value > 1e-6) {
|
||||
return this.colors[i];
|
||||
} else {
|
||||
return "white";
|
||||
}
|
||||
}.bind(this))
|
||||
.attr("d", arc)
|
||||
.attr("stroke", function(d, i) {
|
||||
if (allZero) {
|
||||
return this.colors[i];
|
||||
}
|
||||
}.bind(this))
|
||||
.attr("stroke-dasharray", function(d, i) {
|
||||
if (d.value <= 1e-6) {
|
||||
return "10,10";
|
||||
}
|
||||
}.bind(this));
|
||||
this.bindTooltip(path);
|
||||
|
||||
// Show labels
|
||||
if (showLabels) {
|
||||
var lx = layout.x1 + layout.margin + outerRadius * 2 + 30;
|
||||
var squareW = 15;
|
||||
var margin = 10;
|
||||
|
||||
var select = g.selectAll(".rect")
|
||||
.data(data)
|
||||
.enter();
|
||||
select.append("rect")
|
||||
.attr("fill", function(d, i) {
|
||||
return this.colors[i];
|
||||
}.bind(this))
|
||||
.attr("x", lx)
|
||||
.attr("y", function(d, i) {
|
||||
return layout.y1 + 50 + (squareW + margin) * i + layout.margin;
|
||||
})
|
||||
.attr("width", squareW)
|
||||
.attr("height", squareW);
|
||||
select.append("text")
|
||||
.attr("x", lx + squareW + margin)
|
||||
.attr("y", function(d, i) {
|
||||
return layout.y1 + 50 + (squareW + margin) * i + layout.margin + squareW / 2;
|
||||
})
|
||||
.text(function(d) {
|
||||
return d.label + ' = ' + d.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (middleLabel) {
|
||||
var highLightColor = this.colors[0];
|
||||
g.append("text").text(middleLabel).attr("x", cx).attr("y", cy - 10).
|
||||
attr("class", "donut-highlight-text").attr("fill", highLightColor);
|
||||
g.append("text").text(middleValue).attr("x", cx).attr("y", cy + 20).
|
||||
attr("class", "donut-highlight-text").attr("fill", highLightColor).
|
||||
style("font-size", "30px");
|
||||
}
|
||||
|
||||
path.transition()
|
||||
.duration(500)
|
||||
.attrTween('d', tweenPie);
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
this.initChart();
|
||||
this.renderDonutChart(this.get("data"), this.get("title"), this.get("showLabels"),
|
||||
this.get("middleLabel"), this.get("middleValue"));
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.draw();
|
||||
},
|
||||
})
|
|
@ -0,0 +1,21 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
didInsertElement: function() {
|
||||
$(".js-example-basic-single").select2(
|
||||
{
|
||||
width: '100%',
|
||||
placeholder: "Select a queue"
|
||||
});
|
||||
var elementId = this.get("element-id");
|
||||
var prefix = this.get("prefix");
|
||||
|
||||
var element = d3.select("#" + elementId);
|
||||
|
||||
if (element) {
|
||||
this.get("model").forEach(function(o) {
|
||||
element.append("option").attr("value", o.get("name")).text(prefix + o.get("name"));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
});
|
|
@ -0,0 +1,272 @@
|
|||
import Ember from 'ember';
|
||||
import ChartUtilsMixin from 'yarn-ui/mixins/charts-utils';
|
||||
|
||||
export default Ember.Component.extend(ChartUtilsMixin, {
|
||||
queues: {
|
||||
data: undefined,
|
||||
foldedQueues: {},
|
||||
selectedQueueCircle: undefined,
|
||||
maxDepth: -1,
|
||||
},
|
||||
|
||||
queueColors: d3.scale.category20().range(),
|
||||
|
||||
renderQueue: function(now, depth, sequence) {
|
||||
if (depth > this.queues.maxDepth) {
|
||||
this.queues.maxDepth = depth;
|
||||
}
|
||||
|
||||
var cx = 20 + depth * 30;
|
||||
var cy = 20 + sequence * 30;
|
||||
var name = now.get("name");
|
||||
|
||||
var g = this.queues.dataGroup.append("g")
|
||||
.attr("id", "queue-" + name + "-g");
|
||||
|
||||
var folded = this.queues.foldedQueues[name];
|
||||
var isParentQueue = false;
|
||||
|
||||
// render its children
|
||||
var children = [];
|
||||
var childrenNames = now.get("children");
|
||||
if (childrenNames) {
|
||||
childrenNames.forEach(function(name) {
|
||||
isParentQueue = true;
|
||||
var child = this.queues.data[name];
|
||||
if (child) {
|
||||
children.push(child);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
if (folded) {
|
||||
children = [];
|
||||
}
|
||||
var linefunction = d3.svg.line()
|
||||
.interpolate("basis")
|
||||
.x(function(d) {
|
||||
return d.x;
|
||||
})
|
||||
.y(function(d) {
|
||||
return d.y;
|
||||
});
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
sequence = sequence + 1;
|
||||
// Get center of children queue
|
||||
var cc = this.renderQueue(children[i],
|
||||
depth + 1, sequence);
|
||||
g.append("path")
|
||||
.attr("class", "queue")
|
||||
.attr("d", linefunction([{
|
||||
x: cx,
|
||||
y: cy
|
||||
}, {
|
||||
x: cc.x - 20,
|
||||
y: cc.y
|
||||
}, cc]));
|
||||
}
|
||||
|
||||
var circle = g.append("circle")
|
||||
.attr("cx", cx)
|
||||
.attr("cy", cy)
|
||||
.attr("class", "queue");
|
||||
|
||||
circle.on('mouseover', function() {
|
||||
circle.style("fill", this.queueColors[1]);
|
||||
}.bind(this));
|
||||
circle.on('mouseout', function() {
|
||||
if (circle != this.queues.selectedQueueCircle) {
|
||||
circle.style("fill", this.queueColors[0]);
|
||||
}
|
||||
}.bind(this));
|
||||
circle.on('click', function() {
|
||||
circle.style("fill", this.queueColors[2]);
|
||||
var pre = this.queues.selectedQueueCircle;
|
||||
this.queues.selectedQueueCircle = circle;
|
||||
if (pre) {
|
||||
pre.on('mouseout')();
|
||||
}
|
||||
this.renderCharts(name);
|
||||
}.bind(this));
|
||||
circle.on('dblclick', function() {
|
||||
if (!isParentQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.queues.foldedQueues[name]) {
|
||||
delete this.queues.foldedQueues[name];
|
||||
} else {
|
||||
this.queues.foldedQueues[name] = now;
|
||||
}
|
||||
this.renderQueues();
|
||||
}.bind(this));
|
||||
|
||||
var text = name;
|
||||
if (folded) {
|
||||
text = name + " (+)";
|
||||
}
|
||||
|
||||
// print queue's name
|
||||
g.append("text")
|
||||
.attr("x", cx + 30)
|
||||
.attr("y", cy + 5)
|
||||
.text(text)
|
||||
.attr("class", "queue");
|
||||
|
||||
return {
|
||||
x: cx,
|
||||
y: cy
|
||||
};
|
||||
},
|
||||
|
||||
renderQueues: function() {
|
||||
if (this.queues.dataGroup) {
|
||||
this.queues.dataGroup.remove();
|
||||
}
|
||||
// render queues
|
||||
this.queues.dataGroup = this.canvas.svg.append("g")
|
||||
.attr("id", "queues-g");
|
||||
var rootQueue = undefined;
|
||||
|
||||
if (this.queues.data) {
|
||||
this.renderQueue(this.queues.data['root'], 0, 0);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
this.queues.data = {};
|
||||
this.get("model")
|
||||
.forEach(function(o) {
|
||||
this.queues.data[o.id] = o;
|
||||
}.bind(this));
|
||||
|
||||
// get w/h of the svg
|
||||
var bbox = d3.select("#main-container")
|
||||
.node()
|
||||
.getBoundingClientRect();
|
||||
this.canvas.w = bbox.width;
|
||||
this.canvas.h = Math.max(Object.keys(this.queues.data)
|
||||
.length * 35, 1500);
|
||||
|
||||
this.canvas.svg = d3.select("#main-container")
|
||||
.append("svg")
|
||||
.attr("width", this.canvas.w)
|
||||
.attr("height", this.canvas.h)
|
||||
.attr("id", "main-svg");
|
||||
|
||||
this.renderBackground();
|
||||
|
||||
this.renderQueues();
|
||||
this.renderCharts("root");
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.draw();
|
||||
},
|
||||
|
||||
/*
|
||||
* data = [{label="xx", value=},{...}]
|
||||
*/
|
||||
renderTable: function(data, title, layout) {
|
||||
d3.select("#main-svg")
|
||||
.append('table')
|
||||
.selectAll('tr')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('tr')
|
||||
.selectAll('td')
|
||||
.data(function(d) {
|
||||
return d;
|
||||
})
|
||||
.enter()
|
||||
.append('td')
|
||||
.text(function(d) {
|
||||
return d;
|
||||
});
|
||||
},
|
||||
|
||||
renderQueueCapacities: function(queue, layout) {
|
||||
// Render bar chart
|
||||
this.renderBarChart(this.charts.g, [{
|
||||
label: "Cap",
|
||||
value: queue.get("capacity")
|
||||
}, {
|
||||
label: "MaxCap",
|
||||
value: queue.get("maxCapacity")
|
||||
}, {
|
||||
label: "UsedCap",
|
||||
value: queue.get("usedCapacity")
|
||||
}], "Queue Capacities", layout, 60);
|
||||
},
|
||||
|
||||
renderChildrenCapacities: function(queue, layout) {
|
||||
var data = [];
|
||||
var children = queue.get("children");
|
||||
if (children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = this.queues.data[children[i]];
|
||||
data.push({
|
||||
label: child.get("name"),
|
||||
value: child.get("capacity")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.renderDonutChart(this.charts.g, data, "Children Capacities", layout, true);
|
||||
},
|
||||
|
||||
renderChildrenUsedCapacities: function(queue, layout) {
|
||||
var data = [];
|
||||
var children = queue.get("children");
|
||||
if (children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = this.queues.data[children[i]];
|
||||
data.push({
|
||||
label: child.get("name"),
|
||||
value: child.get("usedCapacity")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.renderDonutChart(this.charts.g, data, "Children Used Capacities", layout, true);
|
||||
},
|
||||
|
||||
renderLeafQueueUsedCapacities: function(layout) {
|
||||
var leafQueueUsedCaps = [];
|
||||
for (var queueName in this.queues.data) {
|
||||
var q = this.queues.data[queueName];
|
||||
if ((!q.get("children")) || q.get("children")
|
||||
.length == 0) {
|
||||
// it's a leafqueue
|
||||
leafQueueUsedCaps.push({
|
||||
label: q.get("name"),
|
||||
value: q.get("usedCapacity")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.renderDonutChart(this.charts.g, leafQueueUsedCaps, "LeafQueues Used Capacities",
|
||||
layout, true);
|
||||
},
|
||||
|
||||
renderCharts: function(queueName) {
|
||||
this.charts.leftBannerLen = this.queues.maxDepth * 30 + 100;
|
||||
this.initCharts();
|
||||
|
||||
var queue = this.queues.data[queueName];
|
||||
var idx = 0;
|
||||
|
||||
if (queue.get("name") == "root") {
|
||||
this.renderLeafQueueUsedCapacities(this.getLayout(idx++));
|
||||
}
|
||||
if (queue.get("name") != "root") {
|
||||
this.renderQueueCapacities(queue, this.getLayout(idx++));
|
||||
}
|
||||
if (queue.get("children") && queue.get("children")
|
||||
.length > 0) {
|
||||
this.renderChildrenCapacities(queue, this.getLayout(idx++));
|
||||
this.renderChildrenUsedCapacities(queue, this.getLayout(idx++));
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
didInsertElement: function() {
|
||||
var paging = this.get("paging") ? true : this.get("paging");
|
||||
var ordering = this.get("ordering") ? true : this.get("ordering");
|
||||
var info = this.get("info") ? true : this.get("info");
|
||||
var bFilter = this.get("bFilter") ? true : this.get("bFilter");
|
||||
|
||||
var colDefs = [];
|
||||
if (this.get("colTypes")) {
|
||||
var typesArr = this.get("colTypes").split(' ');
|
||||
var targetsArr = this.get("colTargets").split(' ');
|
||||
for (var i = 0; i < typesArr.length; i++) {
|
||||
colDefs.push({
|
||||
type: typesArr[i],
|
||||
targets: parseInt(targetsArr[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#' + this.get('table-id')).DataTable({
|
||||
"paging": paging,
|
||||
"ordering": ordering,
|
||||
"info": info,
|
||||
"bFilter": bFilter,
|
||||
columnDefs: colDefs
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,250 @@
|
|||
import Ember from 'ember';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
canvas: {
|
||||
svg: undefined,
|
||||
h: 0,
|
||||
w: 0,
|
||||
tooltip: undefined
|
||||
},
|
||||
|
||||
clusterMetrics: undefined,
|
||||
modelArr: [],
|
||||
colors: d3.scale.category10().range(),
|
||||
_selected: undefined,
|
||||
|
||||
selected: function() {
|
||||
return this._selected;
|
||||
}.property(),
|
||||
|
||||
tableComponentName: function() {
|
||||
return "app-attempt-table";
|
||||
}.property(),
|
||||
|
||||
setSelected: function(d) {
|
||||
if (this._selected == d) {
|
||||
return;
|
||||
}
|
||||
|
||||
// restore color
|
||||
if (this._selected) {
|
||||
var dom = d3.select("#timeline-bar-" + this._selected.get("id"));
|
||||
dom.attr("fill", this.colors[0]);
|
||||
}
|
||||
|
||||
this._selected = d;
|
||||
this.set("selected", d);
|
||||
dom = d3.select("#timeline-bar-" + d.get("id"));
|
||||
dom.attr("fill", this.colors[1]);
|
||||
},
|
||||
|
||||
getPerItemHeight: function() {
|
||||
var arrSize = this.modelArr.length;
|
||||
|
||||
if (arrSize < 20) {
|
||||
return 30;
|
||||
} else if (arrSize < 100) {
|
||||
return 10;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
},
|
||||
|
||||
getPerItemGap: function() {
|
||||
var arrSize = this.modelArr.length;
|
||||
|
||||
if (arrSize < 20) {
|
||||
return 5;
|
||||
} else if (arrSize < 100) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
|
||||
getCanvasHeight: function() {
|
||||
return (this.getPerItemHeight() + this.getPerItemGap()) * this.modelArr.length + 200;
|
||||
},
|
||||
|
||||
draw: function(start, end) {
|
||||
// get w/h of the svg
|
||||
var bbox = d3.select("#" + this.get("parent-id"))
|
||||
.node()
|
||||
.getBoundingClientRect();
|
||||
this.canvas.w = bbox.width;
|
||||
this.canvas.h = this.getCanvasHeight();
|
||||
|
||||
this.canvas.svg = d3.select("#" + this.get("parent-id"))
|
||||
.append("svg")
|
||||
.attr("width", this.canvas.w)
|
||||
.attr("height", this.canvas.h)
|
||||
.attr("id", this.get("my-id"));
|
||||
this.renderTimeline(start, end);
|
||||
},
|
||||
|
||||
renderTimeline: function(start, end) {
|
||||
var border = 30;
|
||||
var singleBarHeight = this.getPerItemHeight();
|
||||
var gap = this.getPerItemGap();
|
||||
var textWidth = 50;
|
||||
/*
|
||||
start-time end-time
|
||||
|--------------------------------------|
|
||||
==============
|
||||
==============
|
||||
==============
|
||||
===============
|
||||
*/
|
||||
var xScaler = d3.scale.linear()
|
||||
.domain([start, end])
|
||||
.range([0, this.canvas.w - 2 * border - textWidth]);
|
||||
|
||||
/*
|
||||
* Render frame of timeline view
|
||||
*/
|
||||
this.canvas.svg.append("line")
|
||||
.attr("x1", border + textWidth)
|
||||
.attr("y1", border - 5)
|
||||
.attr("x2", this.canvas.w - border)
|
||||
.attr("y2", border - 5)
|
||||
.attr("class", "chart");
|
||||
|
||||
this.canvas.svg.append("line")
|
||||
.attr("x1", border + textWidth)
|
||||
.attr("y1", border - 10)
|
||||
.attr("x2", border + textWidth)
|
||||
.attr("y2", border - 5)
|
||||
.attr("class", "chart");
|
||||
|
||||
this.canvas.svg.append("line")
|
||||
.attr("x1", this.canvas.w - border)
|
||||
.attr("y1", border - 10)
|
||||
.attr("x2", this.canvas.w - border)
|
||||
.attr("y2", border - 5)
|
||||
.attr("class", "chart");
|
||||
|
||||
this.canvas.svg.append("text")
|
||||
.text(Converter.timeStampToDate(start))
|
||||
.attr("y", border - 15)
|
||||
.attr("x", border + textWidth)
|
||||
.attr("class", "bar-chart-text")
|
||||
.attr("text-anchor", "left");
|
||||
|
||||
this.canvas.svg.append("text")
|
||||
.text(Converter.timeStampToDate(end))
|
||||
.attr("y", border - 15)
|
||||
.attr("x", this.canvas.w - border)
|
||||
.attr("class", "bar-chart-text")
|
||||
.attr("text-anchor", "end");
|
||||
|
||||
// show bar
|
||||
var bar = this.canvas.svg.selectAll("bars")
|
||||
.data(this.modelArr)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("y", function(d, i) {
|
||||
return border + (gap + singleBarHeight) * i;
|
||||
})
|
||||
.attr("x", function(d, i) {
|
||||
return border + textWidth + xScaler(d.get("startTs"));
|
||||
})
|
||||
.attr("height", singleBarHeight)
|
||||
.attr("fill", function(d, i) {
|
||||
return this.colors[0];
|
||||
}.bind(this))
|
||||
.attr("width", function(d, i) {
|
||||
var finishedTs = xScaler(d.get("finishedTs"));
|
||||
finishedTs = finishedTs > 0 ? finishedTs : xScaler(end);
|
||||
return finishedTs - xScaler(d.get("startTs"));
|
||||
})
|
||||
.attr("id", function(d, i) {
|
||||
return "timeline-bar-" + d.get("id");
|
||||
});
|
||||
bar.on("click", function(d) {
|
||||
this.setSelected(d);
|
||||
}.bind(this));
|
||||
|
||||
this.bindTooltip(bar);
|
||||
|
||||
if (this.modelArr.length <= 20) {
|
||||
// show bar texts
|
||||
for (var i = 0; i < this.modelArr.length; i++) {
|
||||
this.canvas.svg.append("text")
|
||||
.text(this.modelArr[i].get(this.get("label")))
|
||||
.attr("y", border + (gap + singleBarHeight) * i + singleBarHeight / 2)
|
||||
.attr("x", border)
|
||||
.attr("class", "bar-chart-text");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
bindTooltip: function(d) {
|
||||
d.on("mouseover", function(d) {
|
||||
this.tooltip
|
||||
.style("left", (d3.event.pageX) + "px")
|
||||
.style("top", (d3.event.pageY - 28) + "px");
|
||||
}.bind(this))
|
||||
.on("mousemove", function(d) {
|
||||
this.tooltip.style("opacity", .9);
|
||||
this.tooltip.html(d.get("tooltipLabel"))
|
||||
.style("left", (d3.event.pageX) + "px")
|
||||
.style("top", (d3.event.pageY - 28) + "px");
|
||||
}.bind(this))
|
||||
.on("mouseout", function(d) {
|
||||
this.tooltip.style("opacity", 0);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
initTooltip: function() {
|
||||
this.tooltip = d3.select("body")
|
||||
.append("div")
|
||||
.attr("class", "tooltip")
|
||||
.attr("id", "chart-tooltip")
|
||||
.style("opacity", 0);
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
// init tooltip
|
||||
this.initTooltip();
|
||||
|
||||
// init model
|
||||
if (this.get("rmModel")) {
|
||||
this.get("rmModel").forEach(function(o) {
|
||||
this.modelArr.push(o);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if (this.get("tsModel")) {
|
||||
this.get("tsModel").forEach(function(o) {
|
||||
this.modelArr.push(o);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.modelArr.sort(function(a, b) {
|
||||
var tsA = a.get("startTs");
|
||||
var tsB = b.get("startTs");
|
||||
|
||||
return tsA - tsB;
|
||||
});
|
||||
if (this.modelArr.length > 0) {
|
||||
var begin = this.modelArr[0].get("startTs");
|
||||
}
|
||||
var end = 0;
|
||||
for (var i = 0; i < this.modelArr.length; i++) {
|
||||
var ts = this.modelArr[i].get("finishedTs");
|
||||
if (ts > end) {
|
||||
end = ts;
|
||||
}
|
||||
}
|
||||
if (end < begin) {
|
||||
end = Date.now();
|
||||
}
|
||||
|
||||
this.draw(begin, end);
|
||||
|
||||
if (this.modelArr.length > 0) {
|
||||
this.setSelected(this.modelArr[0]);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,257 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
// Map: <queue-name, queue>
|
||||
map : undefined,
|
||||
|
||||
// Normalized data for d3
|
||||
treeData: undefined,
|
||||
|
||||
// folded queues, folded[<queue-name>] == true means <queue-name> is folded
|
||||
foldedQueues: { },
|
||||
|
||||
// maxDepth
|
||||
maxDepth: 0,
|
||||
|
||||
// num of leaf queue, folded queue is treated as leaf queue
|
||||
numOfLeafQueue: 0,
|
||||
|
||||
// mainSvg
|
||||
mainSvg: undefined,
|
||||
|
||||
// Init data
|
||||
initData: function() {
|
||||
this.map = { };
|
||||
this.treeData = { };
|
||||
this.maxDepth = 0;
|
||||
this.numOfLeafQueue = 0;
|
||||
|
||||
this.get("model")
|
||||
.forEach(function(o) {
|
||||
this.map[o.id] = o;
|
||||
}.bind(this));
|
||||
|
||||
var selected = this.get("selected");
|
||||
|
||||
this.initQueue("root", 1, this.treeData);
|
||||
},
|
||||
|
||||
// get Children array of given queue
|
||||
getChildrenNamesArray: function(q) {
|
||||
var namesArr = [];
|
||||
|
||||
// Folded queue's children is empty
|
||||
if (this.foldedQueues[q.get("name")]) {
|
||||
return namesArr;
|
||||
}
|
||||
|
||||
var names = q.get("children");
|
||||
if (names) {
|
||||
names.forEach(function(name) {
|
||||
namesArr.push(name);
|
||||
});
|
||||
}
|
||||
|
||||
return namesArr;
|
||||
},
|
||||
|
||||
// Init queues
|
||||
initQueue: function(queueName, depth, node) {
|
||||
if ((!queueName) || (!this.map[queueName])) {
|
||||
// Queue is not existed
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth > this.maxDepth) {
|
||||
this.maxDepth = this.maxDepth + 1;
|
||||
}
|
||||
|
||||
var queue = this.map[queueName];
|
||||
|
||||
var names = this.getChildrenNamesArray(queue);
|
||||
|
||||
node.name = queueName;
|
||||
node.parent = queue.get("parent");
|
||||
node.queueData = queue;
|
||||
|
||||
if (names.length > 0) {
|
||||
node.children = [];
|
||||
|
||||
names.forEach(function(name) {
|
||||
var childQueueData = {};
|
||||
node.children.push(childQueueData);
|
||||
this.initQueue(name, depth + 1, childQueueData);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.numOfLeafQueue = this.numOfLeafQueue + 1;
|
||||
}
|
||||
},
|
||||
|
||||
update: function(source, root, tree, diagonal) {
|
||||
var duration = 300;
|
||||
var i = 0;
|
||||
|
||||
// Compute the new tree layout.
|
||||
var nodes = tree.nodes(root).reverse();
|
||||
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(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
|
||||
.on("click", function(d,i){
|
||||
if (d.queueData.get("name") != this.get("selected")) {
|
||||
document.location.href = "yarnQueue/" + d.queueData.get("name");
|
||||
}
|
||||
}.bind(this));
|
||||
// .on("click", click);
|
||||
|
||||
nodeEnter.append("circle")
|
||||
.attr("r", 1e-6)
|
||||
.style("fill", function(d) {
|
||||
var usedCap = d.queueData.get("usedCapacity");
|
||||
if (usedCap <= 60.0) {
|
||||
return "LimeGreen";
|
||||
} else if (usedCap <= 100.0) {
|
||||
return "DarkOrange";
|
||||
} else {
|
||||
return "LightCoral";
|
||||
}
|
||||
});
|
||||
|
||||
// append percentage
|
||||
nodeEnter.append("text")
|
||||
.attr("x", function(d) { return 0; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", function(d) { return "middle"; })
|
||||
.text(function(d) {
|
||||
var usedCap = d.queueData.get("usedCapacity");
|
||||
if (usedCap >= 100.0) {
|
||||
return usedCap.toFixed(0) + "%";
|
||||
} else {
|
||||
return usedCap.toFixed(1) + "%";
|
||||
}
|
||||
})
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// append queue name
|
||||
nodeEnter.append("text")
|
||||
.attr("x", function(d) { return 40; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", function(d) { return "start"; })
|
||||
.text(function(d) { return d.name; })
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// Transition nodes to their new position.
|
||||
var nodeUpdate = node.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
|
||||
|
||||
nodeUpdate.select("circle")
|
||||
.attr("r", 20)
|
||||
.attr("href",
|
||||
function(d) {
|
||||
return "yarnQueues/" + d.queueData.get("name");
|
||||
})
|
||||
.style("stroke", function(d) {
|
||||
if (d.queueData.get("name") == this.get("selected")) {
|
||||
return "red";
|
||||
} else {
|
||||
return "gray";
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
nodeUpdate.selectAll("text")
|
||||
.style("fill-opacity", 1);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
var nodeExit = node.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
|
||||
.remove();
|
||||
|
||||
nodeExit.select("circle")
|
||||
.attr("r", 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; });
|
||||
|
||||
// Enter any new links at the parent's previous position.
|
||||
link.enter().insert("path", "g")
|
||||
.attr("class", "link")
|
||||
.attr("d", function(d) {
|
||||
var o = {x: source.x0, y: source.y0};
|
||||
return diagonal({source: o, target: o});
|
||||
});
|
||||
|
||||
// Transition links to their new position.
|
||||
link.transition()
|
||||
.duration(duration)
|
||||
.attr("d", diagonal);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
link.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("d", function(d) {
|
||||
var o = {x: source.x, y: source.y};
|
||||
return diagonal({source: o, target: o});
|
||||
})
|
||||
.remove();
|
||||
|
||||
// Stash the old positions for transition.
|
||||
nodes.forEach(function(d) {
|
||||
d.x0 = d.x;
|
||||
d.y0 = d.y;
|
||||
});
|
||||
},
|
||||
|
||||
reDraw: function() {
|
||||
this.initData();
|
||||
|
||||
var margin = {top: 20, right: 120, bottom: 20, left: 120};
|
||||
var treeWidth = this.maxDepth * 200;
|
||||
var treeHeight = this.numOfLeafQueue * 80;
|
||||
var width = treeWidth + margin.left + margin.right;
|
||||
var height = treeHeight + margin.top + margin.bottom;
|
||||
var layout = { };
|
||||
|
||||
if (this.mainSvg) {
|
||||
this.mainSvg.remove();
|
||||
}
|
||||
|
||||
this.mainSvg = d3.select("#" + this.get("parentId")).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("class", "tree-selector")
|
||||
.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 root = this.treeData;
|
||||
root.x0 = height / 2;
|
||||
root.y0 = 0;
|
||||
|
||||
d3.select(self.frameElement).style("height", height);
|
||||
|
||||
this.update(root, root, tree, diagonal);
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.reDraw();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loading: true,
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
needReload: true,
|
||||
selectedQueue: undefined,
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>YarnUi</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for 'head'}}
|
||||
|
||||
<link rel="stylesheet" href="assets/vendor.css">
|
||||
<link rel="stylesheet" href="assets/yarn-ui.css">
|
||||
|
||||
{{content-for 'head-footer'}}
|
||||
</head>
|
||||
<body>
|
||||
{{content-for 'body'}}
|
||||
|
||||
<script src="assets/vendor.js"></script>
|
||||
<script src="assets/yarn-ui.js"></script>
|
||||
|
||||
{{content-for 'body-footer'}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
startedOn: DS.attr('string'),
|
||||
state: DS.attr('string'),
|
||||
haState: DS.attr('string'),
|
||||
rmStateStoreName: DS.attr('string'),
|
||||
resourceManagerVersion: DS.attr('string'),
|
||||
resourceManagerBuildVersion: DS.attr('string'),
|
||||
hadoopVersion: DS.attr('string'),
|
||||
hadoopBuildVersion: DS.attr('string'),
|
||||
hadoopVersionBuiltOn: DS.attr('string')
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
appsSubmitted: DS.attr('number'),
|
||||
appsCompleted: DS.attr('number'),
|
||||
appsPending: DS.attr('number'),
|
||||
appsRunning: DS.attr('number'),
|
||||
appsFailed: DS.attr('number'),
|
||||
appsKilled: DS.attr('number'),
|
||||
reservedMB: DS.attr('number'),
|
||||
availableMB: DS.attr('number'),
|
||||
allocatedMB: DS.attr('number'),
|
||||
reservedVirtualCores: DS.attr('number'),
|
||||
availableVirtualCores: DS.attr('number'),
|
||||
allocatedVirtualCores: DS.attr('number'),
|
||||
containersAllocated: DS.attr('number'),
|
||||
containersReserved: DS.attr('number'),
|
||||
containersPending: DS.attr('number'),
|
||||
totalMB: DS.attr('number'),
|
||||
totalVirtualCores: DS.attr('number'),
|
||||
totalNodes: DS.attr('number'),
|
||||
lostNodes: DS.attr('number'),
|
||||
unhealthyNodes: DS.attr('number'),
|
||||
decommissionedNodes: DS.attr('number'),
|
||||
rebootedNodes: DS.attr('number'),
|
||||
activeNodes: DS.attr('number'),
|
||||
|
||||
getFinishedAppsDataForDonutChart: function() {
|
||||
var arr = [];
|
||||
arr.push({
|
||||
label: "Completed",
|
||||
value: this.get("appsCompleted")
|
||||
});
|
||||
arr.push({
|
||||
label: "Killed",
|
||||
value: this.get("appsKilled")
|
||||
});
|
||||
arr.push({
|
||||
label: "Failed",
|
||||
value: this.get("appsFailed")
|
||||
});
|
||||
|
||||
return arr;
|
||||
}.property("appsCompleted", "appsKilled", "appsFailed"),
|
||||
|
||||
getRunningAppsDataForDonutChart: function() {
|
||||
var arr = [];
|
||||
|
||||
arr.push({
|
||||
label: "Pending",
|
||||
value: this.get("appsPending")
|
||||
});
|
||||
arr.push({
|
||||
label: "Running",
|
||||
value: this.get("appsRunning")
|
||||
});
|
||||
|
||||
return arr;
|
||||
}.property("appsPending", "appsRunning"),
|
||||
|
||||
getNodesDataForDonutChart: function() {
|
||||
var arr = [];
|
||||
arr.push({
|
||||
label: "Active",
|
||||
value: this.get("activeNodes")
|
||||
});
|
||||
arr.push({
|
||||
label: "Unhealthy",
|
||||
value: this.get("unhealthyNodes")
|
||||
});
|
||||
arr.push({
|
||||
label: "Decomissioned",
|
||||
value: this.get("decommissionedNodes")
|
||||
});
|
||||
return arr;
|
||||
}.property("activeNodes", "unhealthyNodes", "decommissionedNodes"),
|
||||
|
||||
getMemoryDataForDonutChart: function() {
|
||||
var type = "MB";
|
||||
var arr = [];
|
||||
arr.push({
|
||||
label: "Allocated",
|
||||
value: this.get("allocated" + type)
|
||||
});
|
||||
arr.push({
|
||||
label: "Reserved",
|
||||
value: this.get("reserved" + type)
|
||||
});
|
||||
arr.push({
|
||||
label: "Available",
|
||||
value: this.get("available" + type)
|
||||
});
|
||||
|
||||
return arr;
|
||||
}.property("allocatedMB", "reservedMB", "availableMB"),
|
||||
|
||||
getVCoreDataForDonutChart: function() {
|
||||
var type = "VirtualCores";
|
||||
var arr = [];
|
||||
arr.push({
|
||||
label: "Allocated",
|
||||
value: this.get("allocated" + type)
|
||||
});
|
||||
arr.push({
|
||||
label: "Reserved",
|
||||
value: this.get("reserved" + type)
|
||||
});
|
||||
arr.push({
|
||||
label: "Available",
|
||||
value: this.get("available" + type)
|
||||
});
|
||||
|
||||
return arr;
|
||||
}.property("allocatedVirtualCores", "reservedVirtualCores", "availableVirtualCores"),
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.Model.extend({
|
||||
startTime: DS.attr('string'),
|
||||
finishedTime: DS.attr('string'),
|
||||
containerId: DS.attr('string'),
|
||||
nodeHttpAddress: DS.attr('string'),
|
||||
nodeId: DS.attr('string'),
|
||||
logsLink: DS.attr('string'),
|
||||
|
||||
startTs: function() {
|
||||
return Converter.dateToTimeStamp(this.get("startTime"));
|
||||
}.property("startTime"),
|
||||
|
||||
finishedTs: function() {
|
||||
var ts = Converter.dateToTimeStamp(this.get("finishedTime"));
|
||||
return ts;
|
||||
}.property("finishedTime"),
|
||||
|
||||
shortAppAttemptId: function() {
|
||||
return "attempt_" +
|
||||
parseInt(Converter.containerIdToAttemptId(this.get("containerId")).split("_")[3]);
|
||||
}.property("containerId"),
|
||||
|
||||
elapsedTime: function() {
|
||||
var elapsedMs = this.get("finishedTs") - this.get("startTs");
|
||||
if (elapsedMs <= 0) {
|
||||
elapsedMs = Date.now() - this.get("startTs");
|
||||
}
|
||||
|
||||
return Converter.msToElapsedTime(elapsedMs);
|
||||
}.property(),
|
||||
|
||||
tooltipLabel: function() {
|
||||
return "<p>Id:" + this.get("id") +
|
||||
"</p><p>ElapsedTime:" +
|
||||
String(this.get("elapsedTime")) + "</p>";
|
||||
}.property(),
|
||||
|
||||
link: function() {
|
||||
return "/yarnAppAttempt/" + this.get("id");
|
||||
}.property(),
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
import Converter from 'yarn-ui/utils/converter';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
appName: DS.attr('string'),
|
||||
user: DS.attr('string'),
|
||||
queue: DS.attr('string'),
|
||||
state: DS.attr('string'),
|
||||
startTime: DS.attr('string'),
|
||||
elapsedTime: DS.attr('string'),
|
||||
finalStatus: DS.attr('string'),
|
||||
finishedTime: DS.attr('finishedTime'),
|
||||
progress: DS.attr('number'),
|
||||
diagnostics: DS.attr('string'),
|
||||
amContainerLogs: DS.attr('string'),
|
||||
amHostHttpAddress: DS.attr('string'),
|
||||
logAggregationStatus: DS.attr('string'),
|
||||
unmanagedApplication: DS.attr('string'),
|
||||
amNodeLabelExpression: DS.attr('string'),
|
||||
applicationTags: DS.attr('string'),
|
||||
priority: DS.attr('number'),
|
||||
allocatedMB: DS.attr('number'),
|
||||
allocatedVCores: DS.attr('number'),
|
||||
runningContainers: DS.attr('number'),
|
||||
memorySeconds: DS.attr('number'),
|
||||
vcoreSeconds: DS.attr('number'),
|
||||
preemptedResourceMB: DS.attr('number'),
|
||||
preemptedResourceVCores: DS.attr('number'),
|
||||
numNonAMContainerPreempted: DS.attr('number'),
|
||||
numAMContainerPreempted: DS.attr('number'),
|
||||
|
||||
isFailed: function() {
|
||||
return this.get('finalStatus') == "FAILED"
|
||||
}.property("finalStatus"),
|
||||
|
||||
allocatedResource: function() {
|
||||
return Converter.resourceToString(this.get("allocatedMB"), this.get("allocatedVCores"));
|
||||
}.property("allocatedMB", "allocatedVCores"),
|
||||
|
||||
preemptedResource: function() {
|
||||
return Converter.resourceToString(this.get("preemptedResourceMB"), this.get("preemptedResourceVCores"));
|
||||
}.property("preemptedResourceMB", "preemptedResourceVCores"),
|
||||
|
||||
aggregatedResourceUsage: function() {
|
||||
return Converter.resourceToString(this.get("memorySeconds"), this.get("vcoreSeconds")) + " (× Secs)";
|
||||
}.property("memorySeconds", "vcoreSeconds"),
|
||||
|
||||
progressStyle: function() {
|
||||
return "width: " + this.get("progress") + "%";
|
||||
}.property("progress"),
|
||||
|
||||
finalStatusStyle: function() {
|
||||
var style = "default";
|
||||
var finalStatus = this.get("finalStatus");
|
||||
if (finalStatus == "KILLED") {
|
||||
style = "warning";
|
||||
} else if (finalStatus == "FAILED") {
|
||||
style = "danger";
|
||||
} else {
|
||||
style = "success";
|
||||
}
|
||||
|
||||
return "label label-" + style;
|
||||
}.property("finalStatus")
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.Model.extend({
|
||||
allocatedMB: DS.attr('number'),
|
||||
allocatedVCores: DS.attr('number'),
|
||||
assignedNodeId: DS.attr('string'),
|
||||
priority: DS.attr('number'),
|
||||
startedTime: DS.attr('number'),
|
||||
finishedTime: DS.attr('number'),
|
||||
logUrl: DS.attr('string'),
|
||||
containerExitStatus: DS.attr('number'),
|
||||
containerState: DS.attr('string'),
|
||||
nodeHttpAddress: DS.attr('string'),
|
||||
|
||||
startTs: function() {
|
||||
return Converter.dateToTimeStamp(this.get("startedTime"));
|
||||
}.property("startedTime"),
|
||||
|
||||
finishedTs: function() {
|
||||
var ts = Converter.dateToTimeStamp(this.get("finishedTime"));
|
||||
return ts;
|
||||
}.property("finishedTime"),
|
||||
|
||||
elapsedTime: function() {
|
||||
var elapsedMs = this.get("finishedTs") - this.get("startTs");
|
||||
if (elapsedMs <= 0) {
|
||||
elapsedMs = Date.now() - this.get("startTs");
|
||||
}
|
||||
|
||||
return Converter.msToElapsedTime(elapsedMs);
|
||||
}.property(),
|
||||
|
||||
tooltipLabel: function() {
|
||||
return "<p>Id:" + this.get("id") +
|
||||
"</p><p>ElapsedTime:" +
|
||||
String(this.get("elapsedTime")) + "</p>";
|
||||
}.property(),
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
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'),
|
||||
|
||||
isLeafQueue: function() {
|
||||
var len = this.get("children.length");
|
||||
if (!len) {
|
||||
return true;
|
||||
}
|
||||
return len <= 0;
|
||||
}.property("children"),
|
||||
|
||||
capacitiesBarChartData: function() {
|
||||
return [
|
||||
{
|
||||
label: "Absolute Capacity",
|
||||
value: this.get("name") == "root" ? 100 : this.get("absCapacity")
|
||||
},
|
||||
{
|
||||
label: "Absolute Used",
|
||||
value: this.get("name") == "root" ? this.get("usedCapacity") : this.get("absUsedCapacity")
|
||||
},
|
||||
{
|
||||
label: "Absolute Max Capacity",
|
||||
value: this.get("name") == "root" ? 100 : this.get("absMaxCapacity")
|
||||
}
|
||||
]
|
||||
}.property("absCapacity", "absUsedCapacity", "absMaxCapacity"),
|
||||
|
||||
userUsagesDonutChartData: function() {
|
||||
var data = [];
|
||||
if (this.get("users")) {
|
||||
this.get("users").forEach(function(o) {
|
||||
data.push({
|
||||
label: o.get("name"),
|
||||
value: o.get("usedMemoryMB")
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}.property("users"),
|
||||
|
||||
hasUserUsages: function() {
|
||||
return this.get("userUsagesDonutChartData").length > 0;
|
||||
}.property(),
|
||||
|
||||
numOfApplicationsDonutChartData: function() {
|
||||
return [
|
||||
{
|
||||
label: "Pending Apps",
|
||||
value: this.get("numPendingApplications") || 0 // TODO, fix the REST API so root will return #applications as well.
|
||||
},
|
||||
{
|
||||
label: "Active Apps",
|
||||
value: this.get("numActiveApplications") || 0
|
||||
}
|
||||
]
|
||||
}.property(),
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
name: DS.attr('string'),
|
||||
queueName: DS.attr('string'),
|
||||
usedMemoryMB: DS.attr('number'),
|
||||
usedVCore: DS.attr('number')
|
||||
})
|
|
@ -0,0 +1,16 @@
|
|||
import Ember from 'ember';
|
||||
import config from './config/environment';
|
||||
|
||||
var Router = Ember.Router.extend({
|
||||
location: config.locationType
|
||||
});
|
||||
|
||||
Router.map(function() {
|
||||
this.route('yarnApps');
|
||||
this.route('yarnQueue', { path: '/yarnQueue/:queue_name' });
|
||||
this.route('clusterOverview');
|
||||
this.route('yarnApp', { path: '/yarnApp/:app_id' });
|
||||
this.route('yarnAppAttempt', { path: '/yarnAppAttempt/:app_attempt_id'});
|
||||
});
|
||||
|
||||
export default Router;
|
|
@ -0,0 +1,11 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll('ClusterMetric');
|
||||
},
|
||||
|
||||
afterModel() {
|
||||
this.controllerFor("ClusterOverview").set("loading", false);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model(param) {
|
||||
return Ember.RSVP.hash({
|
||||
attempt: this.store.findRecord('yarnAppAttempt', param.app_attempt_id),
|
||||
|
||||
rmContainers: this.store.query('yarnContainer',
|
||||
{
|
||||
app_attempt_id: param.app_attempt_id,
|
||||
is_rm: true
|
||||
}),
|
||||
|
||||
tsContainers: this.store.query('yarnContainer',
|
||||
{
|
||||
app_attempt_id: param.app_attempt_id,
|
||||
is_rm: false
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model(param) {
|
||||
return Ember.RSVP.hash({
|
||||
app: this.store.find('yarnApp', param.app_id),
|
||||
attempts: this.store.query('yarnAppAttempt', { appId: param.app_id})
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
var apps = this.store.findAll('yarnApp');
|
||||
return apps
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model(param) {
|
||||
return Ember.RSVP.hash({
|
||||
selected : param.queue_name,
|
||||
queues: this.store.findAll('yarnQueue'),
|
||||
selectedQueue : undefined,
|
||||
apps: undefined, // apps of selected queue
|
||||
});
|
||||
},
|
||||
|
||||
afterModel(model) {
|
||||
model.selectedQueue = this.store.peekRecord('yarnQueue', model.selected);
|
||||
model.apps = this.store.findAll('yarnApp');
|
||||
model.apps.forEach(function(o) {
|
||||
console.log(o);
|
||||
})
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
export default Ember.Route.extend({
|
||||
beforeModel() {
|
||||
this.transitionTo('yarnQueues.root');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll('yarnQueue');
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var fixedPayload = {
|
||||
id: id,
|
||||
type: primaryModelClass.modelName,
|
||||
attributes: payload
|
||||
};
|
||||
|
||||
return this._super(store, primaryModelClass, fixedPayload, id,
|
||||
requestType);
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = [
|
||||
this.normalizeSingleResponse(store, primaryModelClass,
|
||||
payload.clusterInfo, payload.clusterInfo.id, requestType)
|
||||
];
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var fixedPayload = {
|
||||
id: id,
|
||||
type: primaryModelClass.modelName,
|
||||
attributes: payload
|
||||
};
|
||||
|
||||
return this._super(store, primaryModelClass, fixedPayload, id,
|
||||
requestType);
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = [
|
||||
this.normalizeSingleResponse(store, primaryModelClass,
|
||||
payload.clusterMetrics, 1, requestType)
|
||||
];
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
internalNormalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
|
||||
if (payload.appAttempt) {
|
||||
payload = payload.appAttempt;
|
||||
}
|
||||
|
||||
var fixedPayload = {
|
||||
id: payload.appAttemptId,
|
||||
type: primaryModelClass.modelName, // yarn-app
|
||||
attributes: {
|
||||
startTime: Converter.timeStampToDate(payload.startTime),
|
||||
finishedTime: Converter.timeStampToDate(payload.finishedTime),
|
||||
containerId: payload.containerId,
|
||||
nodeHttpAddress: payload.nodeHttpAddress,
|
||||
nodeId: payload.nodeId,
|
||||
state: payload.nodeId,
|
||||
logsLink: payload.logsLink
|
||||
}
|
||||
};
|
||||
|
||||
return fixedPayload;
|
||||
},
|
||||
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var p = this.internalNormalizeSingleResponse(store,
|
||||
primaryModelClass, payload, id, requestType);
|
||||
return { data: p };
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = payload.appAttempts.appAttempt.map(singleApp => {
|
||||
return this.internalNormalizeSingleResponse(store, primaryModelClass,
|
||||
singleApp, singleApp.id, requestType);
|
||||
}, this);
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
internalNormalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
if (payload.app) {
|
||||
payload = payload.app;
|
||||
}
|
||||
|
||||
var fixedPayload = {
|
||||
id: id,
|
||||
type: primaryModelClass.modelName, // yarn-app
|
||||
attributes: {
|
||||
appName: payload.name,
|
||||
user: payload.user,
|
||||
queue: payload.queue,
|
||||
state: payload.state,
|
||||
startTime: Converter.timeStampToDate(payload.startedTime),
|
||||
elapsedTime: Converter.msToElapsedTime(payload.elapsedTime),
|
||||
finishedTime: Converter.timeStampToDate(payload.finishedTime),
|
||||
finalStatus: payload.finalStatus,
|
||||
progress: payload.progress,
|
||||
diagnostics: payload.diagnostics,
|
||||
amContainerLogs: payload.amContainerLogs,
|
||||
amHostHttpAddress: payload.amHostHttpAddress,
|
||||
logAggregationStatus: payload.logAggregationStatus,
|
||||
unmanagedApplication: payload.unmanagedApplication,
|
||||
amNodeLabelExpression: payload.amNodeLabelExpression,
|
||||
priority: payload.priority,
|
||||
allocatedMB: payload.allocatedMB,
|
||||
allocatedVCores: payload.allocatedVCores,
|
||||
runningContainers: payload.runningContainers,
|
||||
memorySeconds: payload.memorySeconds,
|
||||
vcoreSeconds: payload.vcoreSeconds,
|
||||
preemptedResourceMB: payload.preemptedResourceMB,
|
||||
preemptedResourceVCores: payload.preemptedResourceVCores,
|
||||
numNonAMContainerPreempted: payload.numNonAMContainerPreempted,
|
||||
numAMContainerPreempted: payload.numAMContainerPreempted
|
||||
}
|
||||
};
|
||||
|
||||
return fixedPayload;
|
||||
},
|
||||
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var p = this.internalNormalizeSingleResponse(store,
|
||||
primaryModelClass, payload, id, requestType);
|
||||
return { data: p };
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = payload.apps.app.map(singleApp => {
|
||||
return this.internalNormalizeSingleResponse(store, primaryModelClass,
|
||||
singleApp, singleApp.id, requestType);
|
||||
}, this);
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
import DS from 'ember-data';
|
||||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
internalNormalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
|
||||
var fixedPayload = {
|
||||
id: payload.containerId,
|
||||
type: primaryModelClass.modelName, // yarn-app
|
||||
attributes: {
|
||||
allocatedMB: payload.allocatedMB,
|
||||
allocatedVCores: payload.allocatedVCores,
|
||||
assignedNodeId: payload.assignedNodeId,
|
||||
priority: payload.priority,
|
||||
startedTime: Converter.timeStampToDate(payload.startedTime),
|
||||
finishedTime: Converter.timeStampToDate(payload.finishedTime),
|
||||
elapsedTime: payload.elapsedTime,
|
||||
logUrl: payload.logUrl,
|
||||
containerExitStatus: payload.containerExitStatus,
|
||||
containerState: payload.containerState,
|
||||
nodeHttpAddress: payload.nodeHttpAddress
|
||||
}
|
||||
};
|
||||
|
||||
return fixedPayload;
|
||||
},
|
||||
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var p = this.internalNormalizeSingleResponse(store,
|
||||
primaryModelClass, payload, id, requestType);
|
||||
return { data: p };
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
if (payload && payload.container) {
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = payload.container.map(singleContainer => {
|
||||
return this.internalNormalizeSingleResponse(store, primaryModelClass,
|
||||
singleContainer, singleContainer.id, requestType);
|
||||
}, this);
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
|
||||
normalizedArrayResponse.data = [];
|
||||
return normalizedArrayResponse;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,127 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JSONAPISerializer.extend({
|
||||
|
||||
normalizeSingleResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var children = [];
|
||||
if (payload.queues) {
|
||||
payload.queues.queue.forEach(function(queue) {
|
||||
children.push(queue.queueName);
|
||||
});
|
||||
}
|
||||
|
||||
var includedData = [];
|
||||
var relationshipUserData = [];
|
||||
|
||||
// update user models
|
||||
if (payload.users && payload.users.user) {
|
||||
payload.users.user.forEach(function(u) {
|
||||
includedData.push({
|
||||
type: "YarnUser",
|
||||
id: u.username + "_" + payload.queueName,
|
||||
attributes: {
|
||||
name: u.username,
|
||||
queueName: payload.queueName,
|
||||
usedMemoryMB: u.resourcesUsed.memory || 0,
|
||||
usedVCore: u.resourcesUsed.vCores || 0,
|
||||
}
|
||||
});
|
||||
|
||||
relationshipUserData.push({
|
||||
type: "YarnUser",
|
||||
id: u.username + "_" + payload.queueName,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var fixedPayload = {
|
||||
id: id,
|
||||
type: primaryModelClass.modelName, // yarn-queue
|
||||
attributes: {
|
||||
name: payload.queueName,
|
||||
parent: payload.myParent,
|
||||
children: children,
|
||||
capacity: payload.capacity,
|
||||
usedCapacity: payload.usedCapacity,
|
||||
maxCapacity: payload.maxCapacity,
|
||||
absCapacity: payload.absoluteCapacity,
|
||||
absMaxCapacity: payload.absoluteMaxCapacity,
|
||||
absUsedCapacity: payload.absoluteUsedCapacity,
|
||||
state: payload.state,
|
||||
userLimit: payload.userLimit,
|
||||
userLimitFactor: payload.userLimitFactor,
|
||||
preemptionDisabled: payload.preemptionDisabled,
|
||||
numPendingApplications: payload.numPendingApplications,
|
||||
numActiveApplications: payload.numActiveApplications,
|
||||
},
|
||||
// Relationships
|
||||
relationships: {
|
||||
users: {
|
||||
data: relationshipUserData
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
queue: this._super(store, primaryModelClass, fixedPayload, id, requestType),
|
||||
includedData: includedData
|
||||
}
|
||||
},
|
||||
|
||||
handleQueue(store, primaryModelClass, payload, id, requestType) {
|
||||
var data = [];
|
||||
var includedData = []
|
||||
var result = this.normalizeSingleResponse(store, primaryModelClass,
|
||||
payload, id, requestType);
|
||||
|
||||
data.push(result.queue);
|
||||
includedData = includedData.concat(result.includedData);
|
||||
|
||||
if (payload.queues) {
|
||||
for (var i = 0; i < payload.queues.queue.length; i++) {
|
||||
var queue = payload.queues.queue[i];
|
||||
queue.myParent = payload.queueName;
|
||||
var childResult = this.handleQueue(store, primaryModelClass, queue,
|
||||
queue.queueName,
|
||||
requestType);
|
||||
|
||||
data = data.concat(childResult.data);
|
||||
includedData = includedData.concat(childResult.includedData);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: data,
|
||||
includedData, includedData
|
||||
}
|
||||
},
|
||||
|
||||
normalizeArrayResponse(store, primaryModelClass, payload, id,
|
||||
requestType) {
|
||||
var normalizedArrayResponse = {};
|
||||
var result = this.handleQueue(store,
|
||||
primaryModelClass,
|
||||
payload.scheduler.schedulerInfo, "root", requestType);
|
||||
|
||||
normalizedArrayResponse.data = result.data;
|
||||
normalizedArrayResponse.included = result.includedData;
|
||||
|
||||
console.log(normalizedArrayResponse);
|
||||
|
||||
return normalizedArrayResponse;
|
||||
|
||||
/*
|
||||
// return expected is { data: [ {}, {} ] }
|
||||
var normalizedArrayResponse = {};
|
||||
|
||||
// payload has apps : { app: [ {},{},{} ] }
|
||||
// need some error handling for ex apps or app may not be defined.
|
||||
normalizedArrayResponse.data = payload.apps.app.map(singleApp => {
|
||||
return this.normalizeSingleResponse(store, primaryModelClass, singleApp, singleApp.id, requestType);
|
||||
}, this);
|
||||
return normalizedArrayResponse;
|
||||
*/
|
||||
}
|
||||
});
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Over all style
|
||||
*/
|
||||
text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
text.small {
|
||||
font: 8px sans-serif;
|
||||
}
|
||||
|
||||
html, body
|
||||
{
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
queue's style (left banner of queues)
|
||||
*/
|
||||
|
||||
text.queue {
|
||||
font-family : sans-serif;
|
||||
font-size : 15px;
|
||||
fill : gray;
|
||||
}
|
||||
|
||||
path.queue {
|
||||
stroke: gray;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
circle.queue {
|
||||
r: 10;
|
||||
fill: Steelblue;
|
||||
}
|
||||
|
||||
/*
|
||||
background style
|
||||
*/
|
||||
line.grid {
|
||||
stroke: WhiteSmoke;
|
||||
}
|
||||
|
||||
line.chart {
|
||||
stroke: Gray;
|
||||
}
|
||||
|
||||
/*
|
||||
charts styles
|
||||
*/
|
||||
text.chart-title {
|
||||
font-size: 30px;
|
||||
font-family: sans-serif;
|
||||
text-anchor: middle;
|
||||
fill: Gray;
|
||||
}
|
||||
|
||||
text.donut-highlight-text {
|
||||
font-size: 20px;
|
||||
font-family: sans-serif;
|
||||
text-anchor: middle;
|
||||
fill: Gray;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
rect.chart-frame {
|
||||
fill: none;
|
||||
stroke: gray;
|
||||
stroke-dasharray: 10,10;
|
||||
}
|
||||
|
||||
line.chart-leftbanner {
|
||||
stroke-width: 2;
|
||||
stroke: gray;
|
||||
stroke-dasharray: 10,10;
|
||||
}
|
||||
|
||||
text.bar-chart-text {
|
||||
font-size: 8px;
|
||||
font-family: sans-serif;
|
||||
vertical-align: middle;
|
||||
fill: Gray;;
|
||||
}
|
||||
|
||||
div.tooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
/*height: 28px;*/
|
||||
padding: 2px;
|
||||
font: 12px sans-serif;
|
||||
background: lightsteelblue;
|
||||
border: 0px;
|
||||
border-radius: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data table
|
||||
*/
|
||||
|
||||
table.dataTable thead .sorting {
|
||||
background-image: url("/assets/images/datatables/sort_both.png");
|
||||
}
|
||||
table.dataTable thead .sorting_asc {
|
||||
background-image: url("/assets/images/datatables/sort_asc.png");
|
||||
}
|
||||
table.dataTable thead .sorting_desc {
|
||||
background-image: url("/assets/images/datatables/sort_desc.png");
|
||||
}
|
||||
table.dataTable thead .sorting_asc_disabled {
|
||||
background-image: url("/assets/images/datatables/sort_asc_disabled.png");
|
||||
}
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
background-image: url("/assets/images/datatables/sort_desc_disabled.png");
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue selector
|
||||
*/
|
||||
.node {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.node circle {
|
||||
fill: #fff;
|
||||
stroke: steelblue;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.node text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
.link {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-width: 2px;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">Apache Hadoop YARN</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="yarnQueue/root">Queues<span class="sr-only">(current)</span></a></li>
|
||||
<li class="active"><a href="yarnApps">Applications<span class="sr-only">(current)</span></a></li>
|
||||
<li class="active"><a href="clusterOverview">Cluster Overview<span class="sr-only">(current)</span></a></li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,56 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-3 container-fluid" id="finishedapps-donut-chart">
|
||||
{{donut-chart data=model.firstObject.getFinishedAppsDataForDonutChart
|
||||
title="Finished Apps"
|
||||
showLabels=true
|
||||
parentId="finishedapps-donut-chart"
|
||||
ratio=0.55
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 container-fluid" id="runningapps-donut-chart">
|
||||
{{donut-chart data=model.firstObject.getRunningAppsDataForDonutChart
|
||||
title="Running Apps"
|
||||
showLabels=true
|
||||
parentId="runningapps-donut-chart"
|
||||
ratio=0.55
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3 container-fluid" id="nodes-donut-chart">
|
||||
{{donut-chart data=model.firstObject.getNodesDataForDonutChart
|
||||
title="Node Managers"
|
||||
showLabels=true
|
||||
parentId="nodes-donut-chart"
|
||||
ratio=0.55
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3 container-fluid" id="mem-donut-chart">
|
||||
{{donut-chart data=model.firstObject.getMemoryDataForDonutChart
|
||||
title="Resource - Memory"
|
||||
showLabels=true
|
||||
parentId="mem-donut-chart"
|
||||
ratio=0.55
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 container-fluid" id="vcore-donut-chart">
|
||||
{{donut-chart data=model.firstObject.getVCoreDataForDonutChart
|
||||
title="Resource - VCores"
|
||||
showLabels=true
|
||||
parentId="vcore-donut-chart"
|
||||
ratio=0.6
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,28 @@
|
|||
<table id="app-attempt-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Application Attempt Id</td>
|
||||
<td>{{attempt.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start Time</td>
|
||||
<td>{{attempt.startTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AM Container Id</td>
|
||||
<td>{{attempt.containerId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AM Node Web UI</td>
|
||||
<td><a href={{attempt.nodeHttpAddress}}>{{attempt.nodeHttpAddress}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AM Node Id</td>
|
||||
<td>{{attempt.nodeId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log</td>
|
||||
<td><a href={{attempt.logsLink}}>link</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,62 @@
|
|||
<table id={{table-id}} class="display table table-striped table-bordered" cellspacing="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Application ID</th>
|
||||
<th>Name</th>
|
||||
<th>User</th>
|
||||
<th>Queue</th>
|
||||
<th>State</th>
|
||||
<th>Final Status</th>
|
||||
<th>Start Time</th>
|
||||
<th>Elapsed Time</th> <!-- idx = 7 -->
|
||||
<th>Finished Time</th>
|
||||
<th>Priority</th>
|
||||
<th>Progress</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#if arr}}
|
||||
{{#each arr as |app|}}
|
||||
<tr>
|
||||
<td><a href="yarnApp/{{app.id}}">{{app.id}}</a></td>
|
||||
<td>{{app.appName}}</td>
|
||||
<td>{{app.user}}</td>
|
||||
<td>{{app.queue}}</td>
|
||||
<td>{{app.state}}</td>
|
||||
<td><span class={{app.finalStatusStyle}}>{{app.finalStatus}}</span></td>
|
||||
<td>{{app.startTime}}</td>
|
||||
<td>{{app.elapsedTime}}</td>
|
||||
<td>{{app.finishedTime}}</td>
|
||||
<td>{{app.priority}}</td>
|
||||
<td>
|
||||
<div class="progress" style="margin-bottom: 0;">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style={{app.progressStyle}}>
|
||||
{{app.progress}}%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<tr>
|
||||
<td><a href="yarnApp/{{app.id}}">{{app.id}}</a></td>
|
||||
<td>{{app.appName}}</td>
|
||||
<td>{{app.user}}</td>
|
||||
<td>{{app.queue}}</td>
|
||||
<td>{{app.state}}</td>
|
||||
<td><span class={{app.finalStatusStyle}}>{{app.finalStatus}}</span></td>
|
||||
<td>{{app.startTime}}</td>
|
||||
<td>{{app.elapsedTime}}</td>
|
||||
<td>{{app.finishedTime}}</td>
|
||||
<td>{{app.priority}}</td>
|
||||
<td>
|
||||
<div class="progress" style="margin-bottom: 0;">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style={{app.progressStyle}}>
|
||||
{{app.progress}}%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,36 @@
|
|||
<table id="container-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Start Time</td>
|
||||
<td>{{container.startedTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Finished Time</td>
|
||||
<td>{{container.finishedTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Elapsed Time</td>
|
||||
<td>{{container.elapsedTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Priority</td>
|
||||
<td>{{container.priority}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log</td>
|
||||
<td><a href={{container.logUrl}}>link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exit Status</td>
|
||||
<td>{{container.containerExitStatus}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State</td>
|
||||
<td>{{container.containerState}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NodeManager UI</td>
|
||||
<td>{{container.nodeHttpAddress}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,40 @@
|
|||
<table id="queue-configuration-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><b>Configurations</b></td>
|
||||
<td>Value</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Queue Name</td>
|
||||
<td>{{queue.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Configured Capacity</td>
|
||||
<td>{{queue.capacity}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Configured Max Capacity</td>
|
||||
<td>{{queue.maxCapacity}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State</td>
|
||||
<td>{{queue.state}}</td>
|
||||
</tr>
|
||||
{{#if queue.isLeafQueue}}
|
||||
<tr>
|
||||
<td>User Limit Percent</td>
|
||||
<td>{{queue.userLimit}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User Limit Factor</td>
|
||||
<td>{{queue.userLimitFactor}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Preemption Disabled</td>
|
||||
<td>{{queue.preemptionDisabled}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,18 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<select class="js-example-basic-single" width="100%" id="queue-name-selector">
|
||||
{{item-selector element-id="queue-name-selector" prefix="Queue : " model=model}}
|
||||
</select>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<!-- queue selector -->
|
||||
<div class="row">
|
||||
<div class="col-md-12 container-fluid" id="tree-selector-container">
|
||||
{{tree-selector model=model parentId="tree-selector-container" selected=selected}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,35 @@
|
|||
<div class="col-md-12 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{#if attemptModel}}
|
||||
Application Attempts
|
||||
{{else}}
|
||||
Containers
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-8 container-fluid" id={{parent-id}}>
|
||||
</div>
|
||||
|
||||
<!-- diag info -->
|
||||
<div class="col-md-4 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{#if selected.link}}
|
||||
<a href={{selected.link}}>{{selected.id}}</a>
|
||||
{{else}}
|
||||
{{selected.id}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if attemptModel}}
|
||||
{{app-attempt-table attempt=selected}}
|
||||
{{else}}
|
||||
{{container-table container=selected}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,12 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
{{app-attempt-table attempt=model.attempt}}
|
||||
</div>
|
||||
|
||||
<!-- containers table -->
|
||||
<div class="row">
|
||||
{{timeline-view parent-id="containers-timeline-div" my-id="timeline-view" height="400" rmModel=model.rmContainers tsModel=model.tsContainers label="shortAppAttemptId" attemptModel=false}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,145 @@
|
|||
<div class="container-fluid">
|
||||
<!-- app table -->
|
||||
<div class="row">
|
||||
<div class="col-md-12 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Application Basic Information
|
||||
</div>
|
||||
{{app-table table-id="app-table" app=model.app}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- diag info and other infos -->
|
||||
<div class="row">
|
||||
<!-- diag info -->
|
||||
<div class="col-md-4 container-fluid">
|
||||
{{#if model.app.isFailed}}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading">
|
||||
Diagnostics
|
||||
</div>
|
||||
<div class="panel-body">{{model.app.diagnostics}}</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
Diagnostics
|
||||
</div>
|
||||
<div class="panel-footer">{{model.app.diagnostics}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-5 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Scheduling Info</div>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Allocated Resource</td>
|
||||
<td>{{model.app.allocatedResource}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Running Containers</td>
|
||||
<td>{{model.app.runningContainers}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Preempted Resource</td>
|
||||
<td>{{model.app.preemptedResource}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Num Non-AM container preempted</td>
|
||||
<td>{{model.app.numAMContainerPreempted}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Num AM container preempted</td>
|
||||
<td>{{model.app.numAMContainerPreempted}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aggregated Resource Usage</td>
|
||||
<td>{{model.app.aggregatedResourceUsage}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- other info -->
|
||||
<div class="col-md-3 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Other Info</div>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>AM Container Log</td>
|
||||
<td><a href={{model.app.amContainerLogs}}>Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AM Host Http Addr</td>
|
||||
<td><a href={{model.app.amHostHttpAddress}}>Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log Aggregation Status</td>
|
||||
<td>{{model.app.logAggregationStatus}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Is Unmanaged AM</td>
|
||||
<td>{{model.app.unmanagedApplication}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AM Node Label Expression</td>
|
||||
<td>{{model.app.amNodeLabelExpression}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="row">
|
||||
<div class="col-md-12 container-fluid">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Application Attempts
|
||||
</div>
|
||||
<table id="app-attempt-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Start Time</th>
|
||||
<th>Master ContainerId</th>
|
||||
<th>Node Http Address</th>
|
||||
<th>Node Id</th>
|
||||
<th>Logs Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model.attempts as |attempt|}}
|
||||
<tr>
|
||||
<td>{{attempt.startTime}}</td>
|
||||
<td>{{attempt.containerId}}</td>
|
||||
<td><a href={{attempt.nodeHttpAddress}}>{{attempt.nodeHttpAddress}}</a></td>
|
||||
<td>{{attempt.nodeId}}</td>
|
||||
<td><a href={{attempt.logsLink}}>link</a></td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<!-- timeline view of children -->
|
||||
<div class="row">
|
||||
{{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
{{simple-table table-id="app-attempt-table" paging=false bFilter=false}}
|
||||
-->
|
||||
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,3 @@
|
|||
{{app-table table-id="apps-table" arr=model}}
|
||||
{{simple-table table-id="apps-table" bFilter=true colTypes="elapsed-time" colTargets="7"}}
|
||||
{{outlet}}
|
|
@ -0,0 +1,48 @@
|
|||
<div class="container-fluid">
|
||||
{{queue-navigator model=model.queues selected=model.selected}}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3 container-fluid">
|
||||
{{queue-configuration-table queue=model.selectedQueue}}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 container-fluid" id="capacity-bar-chart">
|
||||
{{bar-chart data=model.selectedQueue.capacitiesBarChartData
|
||||
title="Queue Capacities"
|
||||
parentId="capacity-bar-chart"
|
||||
textWidth=150
|
||||
ratio=0.5
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
|
||||
{{#if model.selectedQueue.hasUserUsages}}
|
||||
<div class="col-lg-3 container-fluid" id="userusage-donut-chart">
|
||||
{{donut-chart data=model.selectedQueue.userUsagesDonutChartData
|
||||
title="User Usages"
|
||||
showLabels=true
|
||||
parentId="userusage-donut-chart"
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="col-lg-3 container-fluid" id="numapplications-donut-chart">
|
||||
{{donut-chart data=model.selectedQueue.numOfApplicationsDonutChartData
|
||||
title="Running Apps"
|
||||
showLabels=true
|
||||
parentId="numapplications-donut-chart"
|
||||
ratio=0.5
|
||||
maxHeight=350}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 container-fluid">
|
||||
{{app-table table-id="apps-table" arr=model.apps}}
|
||||
{{simple-table table-id="apps-table" bFilter=true colTypes="elapsed-time" colTargets="7"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{outlet}}
|
|
@ -0,0 +1,74 @@
|
|||
export default {
|
||||
containerIdToAttemptId: function(containerId) {
|
||||
if (containerId) {
|
||||
var arr = containerId.split('_');
|
||||
var attemptId = ["appattempt", arr[1],
|
||||
arr[2], this.padding(arr[3], 6)];
|
||||
return attemptId.join('_');
|
||||
}
|
||||
},
|
||||
attemptIdToAppId: function(attemptId) {
|
||||
if (attemptId) {
|
||||
var arr = attemptId.split('_');
|
||||
var appId = ["application", arr[1],
|
||||
arr[2]].join('_');
|
||||
return appId;
|
||||
}
|
||||
},
|
||||
padding: function(str, toLen=2) {
|
||||
str = str.toString();
|
||||
if (str.length >= toLen) {
|
||||
return str;
|
||||
}
|
||||
return '0'.repeat(toLen - str.length) + str;
|
||||
},
|
||||
resourceToString: function(mem, cpu) {
|
||||
mem = Math.max(0, mem);
|
||||
cpu = Math.max(0, cpu);
|
||||
return mem + " MBs, " + cpu + " VCores";
|
||||
},
|
||||
msToElapsedTime: function(timeInMs) {
|
||||
var sec_num = timeInMs / 1000; // don't forget the second param
|
||||
var hours = Math.floor(sec_num / 3600);
|
||||
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||
var seconds = sec_num - (hours * 3600) - (minutes * 60);
|
||||
|
||||
var timeStrArr = [];
|
||||
|
||||
if (hours > 0) {
|
||||
timeStrArr.push(hours + ' Hrs');
|
||||
}
|
||||
if (minutes > 0) {
|
||||
timeStrArr.push(minutes + ' Mins');
|
||||
}
|
||||
if (seconds > 0) {
|
||||
timeStrArr.push(Math.round(seconds) + " Secs");
|
||||
}
|
||||
return timeStrArr.join(' : ');
|
||||
},
|
||||
elapsedTimeToMs: function(elapsedTime) {
|
||||
elapsedTime = elapsedTime.toLowerCase();
|
||||
var arr = elapsedTime.split(' : ');
|
||||
var total = 0;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i].indexOf('hr') > 0) {
|
||||
total += parseInt(arr[i].substring(0, arr[i].indexOf(' '))) * 3600;
|
||||
} else if (arr[i].indexOf('min') > 0) {
|
||||
total += parseInt(arr[i].substring(0, arr[i].indexOf(' '))) * 60;
|
||||
} else if (arr[i].indexOf('sec') > 0) {
|
||||
total += parseInt(arr[i].substring(0, arr[i].indexOf(' ')));
|
||||
}
|
||||
}
|
||||
return total * 1000;
|
||||
},
|
||||
timeStampToDate: function(timeStamp) {
|
||||
var dateTimeString = moment(parseInt(timeStamp)).format("YYYY/MM/DD HH:mm:ss");
|
||||
return dateTimeString;
|
||||
},
|
||||
dateToTimeStamp: function(date) {
|
||||
if (date) {
|
||||
var ts = moment(date, "YYYY/MM/DD HH:mm:ss").valueOf();
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import Converter from 'yarn-ui/utils/converter';
|
||||
|
||||
export default {
|
||||
_initElapsedTimeSorter: function() {
|
||||
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
"elapsed-time-pre": function (a) {
|
||||
return Converter.padding(Converter.elapsedTimeToMs(a), 20);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
initDataTableSorter: function() {
|
||||
this._initElapsedTimeSorter();
|
||||
},
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "yarn-ui",
|
||||
"dependencies": {
|
||||
"ember": "2.0.1",
|
||||
"ember-cli-shims": "ember-cli/ember-cli-shims#0.0.4",
|
||||
"ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
|
||||
"ember-data": "2.0.0",
|
||||
"ember-load-initializers": "ember-cli/ember-load-initializers#0.1.6",
|
||||
"ember-qunit": "0.4.9",
|
||||
"ember-qunit-notifications": "0.0.7",
|
||||
"ember-resolver": "~0.1.18",
|
||||
"jquery": "^1.11.3",
|
||||
"loader.js": "ember-cli/loader.js#3.2.1",
|
||||
"qunit": "~1.18.0",
|
||||
"bootstrap": "~3.3.2",
|
||||
"d3": "~3.5.6",
|
||||
"datatables": "~1.10.8",
|
||||
"spin.js": "~2.3.2",
|
||||
"momentjs": "~2.10.6",
|
||||
"select2": "4.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* jshint node: true */
|
||||
|
||||
module.exports = function(environment) {
|
||||
var ENV = {
|
||||
modulePrefix: 'yarn-ui',
|
||||
environment: environment,
|
||||
baseURL: '/',
|
||||
locationType: 'auto',
|
||||
EmberENV: {
|
||||
FEATURES: {
|
||||
// Here you can enable experimental features on an ember canary build
|
||||
// e.g. 'with-controller': true
|
||||
}
|
||||
},
|
||||
|
||||
APP: {
|
||||
// Here you can pass flags/options to your application instance
|
||||
// when it is created
|
||||
}
|
||||
};
|
||||
|
||||
if (environment === 'development') {
|
||||
// ENV.APP.LOG_RESOLVER = true;
|
||||
// ENV.APP.LOG_ACTIVE_GENERATION = true;
|
||||
// ENV.APP.LOG_TRANSITIONS = true;
|
||||
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
|
||||
// ENV.APP.LOG_VIEW_LOOKUPS = true;
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
// Testem prefers this...
|
||||
ENV.baseURL = '/';
|
||||
ENV.locationType = 'none';
|
||||
|
||||
// keep test console output quieter
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
|
||||
ENV.APP.rootElement = '#ember-testing';
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
|
||||
}
|
||||
|
||||
return ENV;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/* global require, module */
|
||||
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
module.exports = function(defaults) {
|
||||
var app = new EmberApp(defaults, {
|
||||
// Add options here
|
||||
});
|
||||
|
||||
app.import("bower_components/datatables/media/css/jquery.dataTables.min.css");
|
||||
app.import("bower_components/datatables/media/js/jquery.dataTables.min.js");
|
||||
app.import("bower_components/momentjs/min/moment.min.js");
|
||||
app.import("bower_components/select2/dist/css/select2.min.css");
|
||||
app.import("bower_components/select2/dist/js/select2.min.js");
|
||||
|
||||
// Use `app.import` to add additional libraries to the generated
|
||||
// output files.
|
||||
//
|
||||
// If you need to use different assets in different
|
||||
// environments, specify an object as the first parameter. That
|
||||
// object's keys should be the environment name and the values
|
||||
// should be the asset to use in that environment.
|
||||
//
|
||||
// If the library that you are including contains AMD or ES6
|
||||
// modules that you would like to import into your application
|
||||
// please specify an object with the list of modules as keys
|
||||
// along with the exports of each module as its value.
|
||||
|
||||
return app.toTree();
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "yarn-ui",
|
||||
"version": "0.0.0",
|
||||
"description": "Small description for yarn-ui goes here",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"doc": "doc",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "ember build",
|
||||
"start": "ember server",
|
||||
"test": "ember test"
|
||||
},
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"broccoli-asset-rev": "^2.1.2",
|
||||
"ember-bootstrap": "0.2.0",
|
||||
"ember-cli": "1.13.8",
|
||||
"ember-cli-app-version": "0.5.0",
|
||||
"ember-cli-babel": "^5.1.3",
|
||||
"ember-cli-content-security-policy": "0.4.0",
|
||||
"ember-cli-dependency-checker": "^1.0.1",
|
||||
"ember-cli-htmlbars": "0.7.9",
|
||||
"ember-cli-htmlbars-inline-precompile": "^0.2.0",
|
||||
"ember-cli-ic-ajax": "0.2.1",
|
||||
"ember-cli-inject-live-reload": "^1.3.1",
|
||||
"ember-cli-qunit": "^1.0.0",
|
||||
"ember-cli-release": "0.2.3",
|
||||
"ember-cli-sri": "^1.0.3",
|
||||
"ember-cli-uglify": "^1.2.0",
|
||||
"ember-d3": "0.1.0",
|
||||
"ember-data": "1.13.8",
|
||||
"ember-disable-proxy-controllers": "^1.0.0",
|
||||
"ember-export-application-global": "^1.0.3",
|
||||
"ember-spin-spinner": "0.2.3",
|
||||
"select2": "4.0.0"
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 894 B |
Binary file not shown.
After Width: | Height: | Size: 160 B |
Binary file not shown.
After Width: | Height: | Size: 148 B |
Binary file not shown.
After Width: | Height: | Size: 201 B |
Binary file not shown.
After Width: | Height: | Size: 158 B |
Binary file not shown.
After Width: | Height: | Size: 146 B |
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
|
||||
<cross-domain-policy>
|
||||
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
|
||||
|
||||
<!-- Most restrictive policy: -->
|
||||
<site-control permitted-cross-domain-policies="none"/>
|
||||
|
||||
<!-- Least restrictive policy: -->
|
||||
<!--
|
||||
<site-control permitted-cross-domain-policies="all"/>
|
||||
<allow-access-from domain="*" to-ports="*" secure="false"/>
|
||||
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
|
||||
-->
|
||||
</cross-domain-policy>
|
|
@ -0,0 +1,3 @@
|
|||
# http://www.robotstxt.org
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"framework": "qunit",
|
||||
"test_page": "tests/index.html?hidepassed",
|
||||
"disable_watching": true,
|
||||
"launch_in_ci": [
|
||||
"PhantomJS"
|
||||
],
|
||||
"launch_in_dev": [
|
||||
"PhantomJS",
|
||||
"Chrome"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"location",
|
||||
"setTimeout",
|
||||
"$",
|
||||
"-Promise",
|
||||
"define",
|
||||
"console",
|
||||
"visit",
|
||||
"exists",
|
||||
"fillIn",
|
||||
"click",
|
||||
"keyEvent",
|
||||
"triggerEvent",
|
||||
"find",
|
||||
"findWithAssert",
|
||||
"wait",
|
||||
"DS",
|
||||
"andThen",
|
||||
"currentURL",
|
||||
"currentPath",
|
||||
"currentRouteName"
|
||||
],
|
||||
"node": false,
|
||||
"browser": false,
|
||||
"boss": true,
|
||||
"curly": true,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"eqnull": true,
|
||||
"esnext": true,
|
||||
"unused": true
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import Resolver from 'ember/resolver';
|
||||
import config from '../../config/environment';
|
||||
|
||||
var resolver = Resolver.create();
|
||||
|
||||
resolver.namespace = {
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix
|
||||
};
|
||||
|
||||
export default resolver;
|
|
@ -0,0 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
import Application from '../../app';
|
||||
import config from '../../config/environment';
|
||||
|
||||
export default function startApp(attrs) {
|
||||
var application;
|
||||
|
||||
var attributes = Ember.merge({}, config.APP);
|
||||
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
|
||||
|
||||
Ember.run(function() {
|
||||
application = Application.create(attributes);
|
||||
application.setupForTesting();
|
||||
application.injectTestHelpers();
|
||||
});
|
||||
|
||||
return application;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>YarnUi Tests</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for 'head'}}
|
||||
{{content-for 'test-head'}}
|
||||
|
||||
<link rel="stylesheet" href="assets/vendor.css">
|
||||
<link rel="stylesheet" href="assets/yarn-ui.css">
|
||||
<link rel="stylesheet" href="assets/test-support.css">
|
||||
|
||||
{{content-for 'head-footer'}}
|
||||
{{content-for 'test-head-footer'}}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{{content-for 'body'}}
|
||||
{{content-for 'test-body'}}
|
||||
<script src="assets/vendor.js"></script>
|
||||
<script src="assets/test-support.js"></script>
|
||||
<script src="assets/yarn-ui.js"></script>
|
||||
<script src="testem.js"></script>
|
||||
<script src="assets/test-loader.js"></script>
|
||||
|
||||
{{content-for 'body-footer'}}
|
||||
{{content-for 'test-body-footer'}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
import resolver from './helpers/resolver';
|
||||
import {
|
||||
setResolver
|
||||
} from 'ember-qunit';
|
||||
|
||||
setResolver(resolver);
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('adapter:yarn-app', 'Unit | Adapter | yarn app', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['serializer:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var adapter = this.subject();
|
||||
assert.ok(adapter);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:yarn-apps', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:yarn-queues', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import ChartsMixin from '../../../mixins/charts';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | charts');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
var ChartsObject = Ember.Object.extend(ChartsMixin);
|
||||
var subject = ChartsObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleForModel, test } from 'ember-qunit';
|
||||
|
||||
moduleForModel('yarn-app', 'Unit | Model | yarn app', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: []
|
||||
});
|
||||
|
||||
test('it exists', function(assert) {
|
||||
var model = this.subject();
|
||||
// var store = this.store();
|
||||
assert.ok(!!model);
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue