فهرست منبع

YARN-4733. [YARN-3368] Initial commit of new YARN web UI. (wangda)

Wangda Tan 9 سال پیش
والد
کامیت
596acc5a82
100فایلهای تغییر یافته به همراه3482 افزوده شده و 0 حذف شده
  1. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.bowerrc
  2. 34 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.editorconfig
  3. 11 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.ember-cli
  4. 17 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.gitignore
  5. 32 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.jshintrc
  6. 23 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.travis.yml
  7. 3 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.watchmanconfig
  8. 24 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/README.md
  9. 19 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-info.js
  10. 19 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-metric.js
  11. 31 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app-attempt.js
  12. 25 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app.js
  13. 42 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-container.js
  14. 19 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-queue.js
  15. 20 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/app.js
  16. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/.gitkeep
  17. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/app-attempt-table.js
  18. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/app-table.js
  19. 104 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/bar-chart.js
  20. 109 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/base-chart-component.js
  21. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/container-table.js
  22. 148 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/donut-chart.js
  23. 21 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/item-selector.js
  24. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-configuration-table.js
  25. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-navigator.js
  26. 272 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-view.js
  27. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/simple-table.js
  28. 250 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/timeline-view.js
  29. 257 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/tree-selector.js
  30. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/.gitkeep
  31. 5 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/cluster-overview.js
  32. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/yarn-apps.js
  33. 6 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/yarn-queue.js
  34. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/.gitkeep
  35. 25 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/index.html
  36. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/.gitkeep
  37. 13 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/cluster-info.js
  38. 115 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/cluster-metric.js
  39. 44 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app-attempt.js
  40. 65 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app.js
  41. 39 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-container.js
  42. 76 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-queue.js
  43. 8 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-user.js
  44. 16 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/router.js
  45. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/.gitkeep
  46. 11 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/cluster-overview.js
  47. 21 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-app-attempt.js
  48. 10 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-app.js
  49. 8 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-apps.js
  50. 20 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queue.js
  51. 5 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queues/index.js
  52. 7 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queues/queues-selector.js
  53. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/cluster-info.js
  54. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/cluster-metric.js
  55. 49 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-app-attempt.js
  56. 66 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-app.js
  57. 54 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-container.js
  58. 127 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-queue.js
  59. 141 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/styles/app.css
  60. 25 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/application.hbs
  61. 56 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/cluster-overview.hbs
  62. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/.gitkeep
  63. 28 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/app-attempt-table.hbs
  64. 62 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/app-table.hbs
  65. 36 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/container-table.hbs
  66. 40 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/queue-configuration-table.hbs
  67. 18 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/queue-navigator.hbs
  68. 35 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/timeline-view.hbs
  69. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-app-attempt.hbs
  70. 145 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-app.hbs
  71. 3 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-apps.hbs
  72. 48 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-queue.hbs
  73. 74 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/converter.js
  74. 15 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/sorter.js
  75. 22 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/bower.json
  76. 47 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/config/environment.js
  77. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/ember-cli-build.js
  78. 6 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/jsconfig.json
  79. 44 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/package.json
  80. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/Sorting icons.psd
  81. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/favicon.ico
  82. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_asc.png
  83. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_asc_disabled.png
  84. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_both.png
  85. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_desc.png
  86. BIN
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_desc_disabled.png
  87. 15 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/crossdomain.xml
  88. 3 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/robots.txt
  89. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/testem.json
  90. 52 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/.jshintrc
  91. 11 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/helpers/resolver.js
  92. 18 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/helpers/start-app.js
  93. 33 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/index.html
  94. 6 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/test-helper.js
  95. 0 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/.gitkeep
  96. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-app-test.js
  97. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/controllers/yarn-apps-test.js
  98. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/controllers/yarn-queues-test.js
  99. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/mixins/charts-test.js
  100. 12 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-app-test.js

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.bowerrc

@@ -0,0 +1,4 @@
+{
+  "directory": "bower_components",
+  "analytics": false
+}

+ 34 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.editorconfig

@@ -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

+ 11 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.ember-cli

@@ -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"
+}

+ 17 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.gitignore

@@ -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

+ 32 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.jshintrc

@@ -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
+}

+ 23 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.travis.yml

@@ -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

+ 3 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/.watchmanconfig

@@ -0,0 +1,3 @@
+{
+  "ignore_dirs": ["tmp"]
+}

+ 24 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/README.md

@@ -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).
+

+ 19 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-info.js

@@ -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); 
+  }
+});

+ 19 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-metric.js

@@ -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); 
+  }
+});

+ 31 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app-attempt.js

@@ -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); 
+  }
+});

+ 25 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app.js

@@ -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); 
+  }
+});

+ 42 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-container.js

@@ -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); 
+  }
+});

+ 19 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-queue.js

@@ -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); 
+  }
+});

+ 20 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/app.js

@@ -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
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/.gitkeep


+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/app-attempt-table.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/app-table.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

+ 104 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/bar-chart.js

@@ -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();
+  },
+})

+ 109 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/base-chart-component.js

@@ -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;
+  },
+});

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/container-table.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

+ 148 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/donut-chart.js

@@ -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();
+  },
+})

+ 21 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/item-selector.js

@@ -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"));
+      });
+    }
+  }
+});

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-configuration-table.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-navigator.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

+ 272 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/queue-view.js

@@ -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++));
+		}
+	},
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/simple-table.js

@@ -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
+    });
+  }
+});

+ 250 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/timeline-view.js

@@ -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]);
+    }
+  },
+});

+ 257 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/tree-selector.js

@@ -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
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/.gitkeep


+ 5 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/cluster-overview.js

@@ -0,0 +1,5 @@
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  loading: true,
+});

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/yarn-apps.js

@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+});

+ 6 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/yarn-queue.js

@@ -0,0 +1,6 @@
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+	needReload: true,
+  selectedQueue: undefined,
+});

+ 0 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/.gitkeep


+ 25 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/index.html

@@ -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
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/.gitkeep


+ 13 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/cluster-info.js

@@ -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')
+});

+ 115 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/cluster-metric.js

@@ -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"),
+});

+ 44 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app-attempt.js

@@ -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(),
+});

+ 65 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app.js

@@ -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")
+});

+ 39 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-container.js

@@ -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(),
+});

+ 76 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-queue.js

@@ -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(),
+});

+ 8 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-user.js

@@ -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')
+})

+ 16 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/router.js

@@ -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
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/.gitkeep


+ 11 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/cluster-overview.js

@@ -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);
+  }
+});

+ 21 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-app-attempt.js

@@ -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
+        }),
+    });
+  }
+});

+ 10 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-app.js

@@ -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})
+    });
+  }
+});

+ 8 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-apps.js

@@ -0,0 +1,8 @@
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+  	var apps = this.store.findAll('yarnApp');
+    return apps
+  }
+});

+ 20 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queue.js

@@ -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);
+    })
+  }
+});

+ 5 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queues/index.js

@@ -0,0 +1,5 @@
+export default Ember.Route.extend({
+  beforeModel() {
+    this.transitionTo('yarnQueues.root');
+  }
+});

+ 7 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-queues/queues-selector.js

@@ -0,0 +1,7 @@
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+    return this.store.findAll('yarnQueue');
+  },
+});

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/cluster-info.js

@@ -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;
+    }
+});

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/cluster-metric.js

@@ -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;
+    }
+});

+ 49 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-app-attempt.js

@@ -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;
+    }
+});

+ 66 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-app.js

@@ -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;
+    }
+});

+ 54 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-container.js

@@ -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;
+    }
+});

+ 127 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-queue.js

@@ -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;
+      */
+    }
+});

+ 141 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/styles/app.css

@@ -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;
+}

+ 25 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/application.hbs

@@ -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}}

+ 56 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/cluster-overview.hbs

@@ -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
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/.gitkeep


+ 28 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/app-attempt-table.hbs

@@ -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>

+ 62 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/app-table.hbs

@@ -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>

+ 36 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/container-table.hbs

@@ -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>

+ 40 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/queue-configuration-table.hbs

@@ -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>

+ 18 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/queue-navigator.hbs

@@ -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}}

+ 35 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/components/timeline-view.hbs

@@ -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}}

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-app-attempt.hbs

@@ -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}}

+ 145 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-app.hbs

@@ -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}}

+ 3 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-apps.hbs

@@ -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}}

+ 48 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-queue.hbs

@@ -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}}

+ 74 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/converter.js

@@ -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;
+    }
+  }
+}

+ 15 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/sorter.js

@@ -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();
+  },
+}

+ 22 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/bower.json

@@ -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"
+  }
+}

+ 47 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/config/environment.js

@@ -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;
+};

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/ember-cli-build.js

@@ -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();
+};

+ 6 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/jsconfig.json

@@ -0,0 +1,6 @@
+{
+    "compilerOptions": {
+        "target": "ES6",
+        "module": "commonjs"
+    }
+}

+ 44 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/package.json

@@ -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"
+  }
+}

BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/Sorting icons.psd


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/favicon.ico


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_asc.png


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_asc_disabled.png


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_both.png


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_desc.png


BIN
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/assets/images/datatables/sort_desc_disabled.png


+ 15 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/crossdomain.xml

@@ -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>

+ 3 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/public/robots.txt

@@ -0,0 +1,3 @@
+# http://www.robotstxt.org
+User-agent: *
+Disallow:

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/testem.json

@@ -0,0 +1,12 @@
+{
+  "framework": "qunit",
+  "test_page": "tests/index.html?hidepassed",
+  "disable_watching": true,
+  "launch_in_ci": [
+    "PhantomJS"
+  ],
+  "launch_in_dev": [
+    "PhantomJS",
+    "Chrome"
+  ]
+}

+ 52 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/.jshintrc

@@ -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
+}

+ 11 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/helpers/resolver.js

@@ -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;

+ 18 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/helpers/start-app.js

@@ -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;
+}

+ 33 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/index.html

@@ -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>

+ 6 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/test-helper.js

@@ -0,0 +1,6 @@
+import resolver from './helpers/resolver';
+import {
+  setResolver
+} from 'ember-qunit';
+
+setResolver(resolver);

+ 0 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/.gitkeep


+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-app-test.js

@@ -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);
+});

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/controllers/yarn-apps-test.js

@@ -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);
+});

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/controllers/yarn-queues-test.js

@@ -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);
+});

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/mixins/charts-test.js

@@ -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);
+});

+ 12 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-app-test.js

@@ -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);
+});

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است