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