Browse Source

YARN-4517. Add nodes page and fix bunch of license issues. (Varun Saxena via wangda)

Wangda Tan 9 years ago
parent
commit
0a5f652071
80 changed files with 3840 additions and 36 deletions
  1. 3 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-info.js
  2. 3 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-metric.js
  3. 2 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app-attempt.js
  4. 2 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app.js
  5. 74 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-container-log.js
  6. 3 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-container.js
  7. 63 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-node-app.js
  8. 64 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-node-container.js
  9. 40 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-node.js
  10. 2 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-queue.js
  11. 45 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-rm-node.js
  12. 33 5
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/simple-table.js
  13. 27 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/config.js
  14. 24 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/constants.js
  15. 55 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/application.js
  16. 31 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/divide.js
  17. 48 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/log-files-comma.js
  18. 37 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/node-link.js
  19. 66 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/node-menu.js
  20. 7 7
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app.js
  21. 25 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-container-log.js
  22. 44 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-node-app.js
  23. 57 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-node-container.js
  24. 33 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-node.js
  25. 92 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-rm-node.js
  26. 13 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/router.js
  27. 38 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/application.js
  28. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/index.js
  29. 2 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-apps.js
  30. 55 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-container-log.js
  31. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-app.js
  32. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-apps.js
  33. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-container.js
  34. 28 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-containers.js
  35. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node.js
  36. 25 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-nodes.js
  37. 39 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-container-log.js
  38. 86 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-node-app.js
  39. 74 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-node-container.js
  40. 56 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-node.js
  41. 77 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-rm-node.js
  42. 1 3
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/application.hbs
  43. 19 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/error.hbs
  44. 20 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/notfound.hbs
  45. 2 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-apps.hbs
  46. 36 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-container-log.hbs
  47. 60 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-app.hbs
  48. 51 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-apps.hbs
  49. 70 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-container.hbs
  50. 58 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-containers.hbs
  51. 94 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node.hbs
  52. 65 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-nodes.hbs
  53. 19 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/converter.js
  54. 41 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/sorter.js
  55. 1 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/bower.json
  56. 0 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/config/environment.js
  57. 73 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-container-log-test.js
  58. 93 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-node-app-test.js
  59. 93 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-node-container-test.js
  60. 42 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-node-test.js
  61. 44 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-rm-node-test.js
  62. 48 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-container-log-test.js
  63. 65 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-node-app-test.js
  64. 78 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-node-container-test.js
  65. 58 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-node-test.js
  66. 95 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-rm-node-test.js
  67. 120 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-container-log-test.js
  68. 56 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-app-test.js
  69. 60 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-apps-test.js
  70. 61 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-container-test.js
  71. 68 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-containers-test.js
  72. 84 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-test.js
  73. 74 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-nodes-test.js
  74. 49 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-container-log-test.js
  75. 102 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-app-test.js
  76. 128 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-container-test.js
  77. 69 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-test.js
  78. 153 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-rm-node-test.js
  79. 45 3
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/utils/converter-test.js
  80. 26 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/utils/sorter-test.js

+ 3 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-info.js

@@ -1,10 +1,11 @@
 import DS from 'ember-data';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  host: 'http://localhost:1337/localhost:8088', // configurable
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT, // configurable
   namespace: 'ws/v1/cluster', // common const
   pathForType(modelName) {
     return ''; // move to some common place, return path by modelname.
@@ -16,4 +17,4 @@ export default DS.JSONAPIAdapter.extend({
     hash.targetServer = "RM";
     return this._super(url, method, hash); 
   }
-});
+});

+ 3 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/cluster-metric.js

@@ -1,10 +1,11 @@
 import DS from 'ember-data';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  host: 'http://localhost:1337/localhost:8088', // configurable
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT, // configurable
   namespace: 'ws/v1/cluster/metrics', // common const
   pathForType(modelName) {
     return ''; // move to some common place, return path by modelname.
@@ -16,4 +17,4 @@ export default DS.JSONAPIAdapter.extend({
     hash.targetServer = "RM";
     return this._super(url, method, hash); 
   }
-});
+});

+ 2 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app-attempt.js

@@ -1,11 +1,12 @@
 import DS from 'ember-data';
 import Converter from 'yarn-ui/utils/converter';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  host: 'http://localhost:1337/localhost:8088', // configurable
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT, // configurable
   namespace: 'ws/v1/cluster', // common const
 
   urlForQuery(query, modelName) {

+ 2 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-app.js

@@ -1,10 +1,11 @@
 import DS from 'ember-data';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  host: 'http://localhost:1337/localhost:8088', // configurable
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT, // configurable
   namespace: 'ws/v1/cluster', // common const
   pathForType(modelName) {
     return 'apps'; // move to some common place, return path by modelname.

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

@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Ember from 'ember';
+import Converter from 'yarn-ui/utils/converter';
+
+/**
+ * REST URL's response when fetching container logs will be
+ * in plain text format and not JSON.
+ */
+export default DS.RESTAdapter.extend({
+  headers: {
+    Accept: 'text/plain'
+  },
+  host: 'http://localhost:1337/',
+  namespace: 'ws/v1/node',
+
+  urlForFindRecord(id, modelName, snapshot) {
+    var splits = Converter.splitForContainerLogs(id);
+    var nodeHttpAddr = splits[0];
+    var containerId = splits[1];
+    var filename = splits[2];
+    this.host = this.host + nodeHttpAddr;
+    var url = this._buildURL();
+    url = url + "/containerlogs/" + containerId + "/" + filename;
+    return url;
+  },
+
+  ajax(url, method, hash) {
+    hash = hash || {};
+    hash.crossDomain = true;
+    hash.xhrFields = {withCredentials: true};
+    hash.targetServer = "NM";
+    return this._super(url, method, hash);
+  },
+
+  /**
+   * Override options so that result is not expected to be JSON
+   */
+  ajaxOptions: function (url, type, options) {
+    var hash = options || {};
+    hash.url = url;
+    hash.type = type;
+    // Make sure jQuery does not try to convert response to JSON.
+    hash.dataType = 'text';
+    hash.context = this;
+
+    var headers = Ember.get(this, 'headers');
+    if (headers != undefined) {
+      hash.beforeSend = function (xhr) {
+        Object.keys(headers).forEach(function (key) {
+          return xhr.setRequestHeader(key, headers[key]);
+        });
+      };
+    }
+    return hash;
+  },
+});

+ 3 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-container.js

@@ -1,12 +1,13 @@
 import DS from 'ember-data';
 import Converter from 'yarn-ui/utils/converter';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  rmHost: 'http://localhost:1337/localhost:8088',
-  tsHost: 'http://localhost:1337/localhost:8188',
+  rmHost: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT,
+  tsHost: 'http://localhost:1337/' + Config.TS_HOST + ':' + Config.TS_PORT,
   host: function() {
     return undefined
   }.property(),

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

@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.JSONAPIAdapter.extend({
+  headers: {
+    Accept: 'application/json'
+  },
+  host: 'http://localhost:1337/',
+  namespace: 'ws/v1/node',
+
+  urlForQuery(query) {
+    this.host = this.host + query.nodeAddr;
+    var url = this._buildURL();
+    url = url + "/apps";
+    return url;
+  },
+
+  urlForQueryRecord: function (query) {
+    this.host = this.host + query.nodeAddr;
+    var url = this._buildURL();
+    url = url + "/apps/" + query.appId;
+    return url;
+  },
+
+  query: function (store, type, query) {
+    var url = this.urlForQuery(query);
+    // Query params not required.
+    query = null;
+    return this.ajax(url, 'GET', { data: query });
+  },
+
+  queryRecord: function (store, type, query) {
+    var url = this.urlForQueryRecord(query);
+    // Query params not required.
+    query = null;
+    return this.ajax(url, 'GET', { data: query });
+  },
+
+  ajax(url, method, hash) {
+    hash = hash || {};
+    hash.crossDomain = true;
+    hash.xhrFields = {withCredentials: true};
+    hash.targetServer = "NM";
+    return this._super(url, method, hash);
+  }
+});

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

@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.JSONAPIAdapter.extend({
+  headers: {
+    Accept: 'application/json'
+  },
+  host: 'http://localhost:1337/',
+  namespace: 'ws/v1/node',
+
+  urlForQuery(query) {
+    this.host = this.host + query.nodeHttpAddr;
+    var url = this._buildURL();
+    url = url + "/containers";
+    return url;
+  },
+
+  urlForQueryRecord(query) {
+    this.host = this.host + query.nodeHttpAddr;
+    var url = this._buildURL();
+    url = url + "/containers/" + query.containerId;
+    return url;
+  },
+
+  query: function (store, type, query) {
+    var url = this.urlForQuery(query);
+    // Query params not required.
+    query = null;
+    return this.ajax(url, 'GET', { data: query });
+  },
+
+  queryRecord: function (store, type, query) {
+    var url = this.urlForQueryRecord(query);
+    // Query params not required.
+    query = null;
+    console.log(url);
+    return this.ajax(url, 'GET', { data: query });
+  },
+
+  ajax(url, method, hash) {
+    hash = hash || {};
+    hash.crossDomain = true;
+    hash.xhrFields = {withCredentials: true};
+    hash.targetServer = "NM";
+    return this._super(url, method, hash);
+  }
+});

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

@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.JSONAPIAdapter.extend({
+  headers: {
+    Accept: 'application/json'
+  },
+  host: 'http://localhost:1337/',
+  namespace: 'ws/v1/node',
+
+  urlForFindRecord(id, modelName, snapshot) {
+    this.host = this.host + id;
+    var url = this._buildURL();
+    return url;
+  },
+  ajax(url, method, hash) {
+    hash = hash || {};
+    hash.crossDomain = true;
+    hash.xhrFields = {withCredentials: true};
+    hash.targetServer = "NM";
+    return this._super(url, method, hash);
+  }
+});

+ 2 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-queue.js

@@ -1,10 +1,11 @@
 import DS from 'ember-data';
+import Config from 'yarn-ui/config';
 
 export default DS.JSONAPIAdapter.extend({
   headers: {
     Accept: 'application/json'
   },
-  host: 'http://localhost:1337/localhost:8088', // configurable
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT, // configurable
   namespace: 'ws/v1/cluster', // common const
   pathForType(modelName) {
     return 'scheduler'; // move to some common place, return path by modelname.

+ 45 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/adapters/yarn-rm-node.js

@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Config from 'yarn-ui/config';
+
+export default DS.JSONAPIAdapter.extend({
+  headers: {
+    Accept: 'application/json'
+  },
+  host: 'http://localhost:1337/' + Config.RM_HOST + ':' + Config.RM_PORT,
+  namespace: 'ws/v1/cluster',
+  pathForType(modelName) {
+    return 'nodes';
+  },
+
+  urlForFindRecord(id, modelName, snapshot) {
+    var url = this._buildURL();
+    url = url + "/nodes/" + id;
+    return url;
+  },
+
+  ajax(url, method, hash) {
+    hash = hash || {};
+    hash.crossDomain = true;
+    hash.xhrFields = {withCredentials: true};
+    hash.targetServer = "RM";
+    return this._super(url, method, hash);
+  }
+});

+ 33 - 5
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/components/simple-table.js

@@ -7,24 +7,52 @@ export default Ember.Component.extend({
     var info = this.get("info") ? true : this.get("info");
     var bFilter = this.get("bFilter") ? true : this.get("bFilter");
 
+    // Defines sorter for the columns if not default.
+    // Can also specify a custom sorter.
+    var i;
     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++) {
+      for (i = 0; i < typesArr.length; i++) {
+        console.log(typesArr[i] + " " + targetsArr[i]);
         colDefs.push({
           type: typesArr[i],
           targets: parseInt(targetsArr[i])
         });
       }
     }
-
-    $('#' + this.get('table-id')).DataTable({
+    // Defines initial column and sort order.
+    var orderArr = [];
+    if (this.get("colsOrder")) {
+      var cols = this.get("colsOrder").split(' ');
+      for (i = 0; i < cols.length; i++) {
+        var col = cols[i].split(',');
+        if (col.length != 2) {
+          continue;
+        }
+        var order = col[1].trim();
+        if (order != 'asc' && order != 'desc') {
+          continue;
+        }
+        var colOrder = [];
+        colOrder.push(parseInt(col[0]));
+        colOrder.push(order);
+        orderArr.push(colOrder);
+      }
+    }
+    if (orderArr.length == 0) {
+      var defaultOrder = [0, 'asc'];
+      orderArr.push(defaultOrder);
+    }
+    console.log(orderArr[0]);
+    Ember.$('#' + this.get('table-id')).DataTable({
       "paging":   paging,
       "ordering": ordering, 
       "info":     info,
       "bFilter": bFilter,
-      columnDefs: colDefs
+      "order": orderArr,
+      "columnDefs": colDefs
     });
   }
-});
+});

+ 27 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/config.js

@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Host and port configurations
+ */
+export default {
+  RM_HOST: 'localhost',
+  RM_PORT: '8088',
+  TS_HOST: 'localhost',
+  TS_PORT: '8188',
+};

+ 24 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/constants.js

@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Application level global constants go here.
+ */
+export default {
+  PARAM_SEPARATOR: '!',
+};

+ 55 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/controllers/application.js

@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+/**
+ * Base controller for application.
+ */
+export default Ember.Controller.extend({
+  /**
+   * Output main top UI menu which is common across all pages.
+   * Menu item will be made active based on current path.
+   */
+  outputMainMenu: function(){
+    var path = this.get('currentPath');
+    var html = '<li';
+    if (path == 'yarnQueue') {
+      html = html + ' class="active"';
+    }
+    html = html + '><a href="yarnQueue/root">Queues<span class="sr-only">' +
+        '(current)</span></a></li><li';
+    if (path.lastIndexOf('yarnApp', 0) == 0) {
+      html = html + ' class="active"';
+    }
+    html = html + '><a href="yarnApps">Applications<span class="sr-only">' +
+        '(current)</span></a></li><li';
+    if (path == 'clusterOverview') {
+      html = html + ' class="active"';
+    }
+    html = html + '><a href="clusterOverview">Cluster Overview<span class=' +
+        '"sr-only">(current)</span></a></li><li';
+    if (path.lastIndexOf('yarnNode', 0) == 0) {
+      html = html + ' class="active"';
+    }
+    html = html + '><a href="yarnNodes">Nodes<span class="sr-only">' +
+        '(current)</span></a></li>';
+    return Ember.String.htmlSafe(html);
+  }.property('currentPath')
+});
+

+ 31 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/divide.js

@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Ember from 'ember';
+
+/**
+ * Helper assumes values are numeric. num means numerator and
+ * den means denominator.
+ */
+export default Ember.Helper.helper(function(params,hash) {
+  var num = hash.num;
+  var den = hash.den;
+  if (den == 0) {
+    return 0;
+  }
+  return Math.floor(num/den);
+});

+ 48 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/log-files-comma.js

@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+/**
+ * Represent log files as comma separated list.
+ */
+export default Ember.Helper.helper(function(params,hash) {
+  var logFiles = hash.logFiles;
+  if (logFiles == null) {
+    return "";
+  }
+  var logFilesLen = logFiles.length;
+  if (logFilesLen == 0) {
+    return "";
+  }
+  var nodeId = hash.nodeId;
+  var nodeAddr = hash.nodeAddr;
+  var containerId = hash.containerId;
+  var html = '<td>';
+  var logFilesCommaSeparated = "";
+  for (var i = 0; i < logFilesLen; i++) {
+    html = html + '<a href="yarnContainerLog/' + nodeId + '/' +
+        nodeAddr + '/' + containerId + '/' + logFiles[i] + '">' + logFiles[i] +
+        '</a>';
+    if (i != logFilesLen - 1) {
+      html = html + ",";
+    }
+  }
+  html = html + '</td>';
+  return Ember.String.htmlSafe(html);
+});

+ 37 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/node-link.js

@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+/**
+ * Generate link to node page if its not SHUTDOWN or LOST.
+ */
+export default Ember.Helper.helper(function(params,hash) {
+  var nodeState = hash.nodeState;
+  var nodeHTTPAddress = hash.nodeHTTPAddress;
+  var nodeId = hash.nodeId;
+  var html = '<td>';
+  if (nodeState == "SHUTDOWN" || nodeState == "LOST") {
+    html = html + nodeHTTPAddress;
+  } else {
+    html = html + '<a href="yarnNode/' + nodeId + "/" + nodeHTTPAddress + '">' +
+        nodeHTTPAddress + '</a>';
+  }
+  html = html + '</td>';
+  return Ember.String.htmlSafe(html);
+});

+ 66 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/helpers/node-menu.js

@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Ember from 'ember';
+
+/**
+ * Create left hand side node manager menu with menu item activated based
+ * on page being accessed.
+ */
+export default Ember.Helper.helper(function(params,hash) {
+  // Place a menu within a panel inside col-md-2 container.
+  var nodeIdSplitAtPort = hash.nodeId;
+  var portIndex = nodeIdSplitAtPort.indexOf(':');
+  if (portIndex != -1) {
+    nodeIdSplitAtPort = nodeIdSplitAtPort.substring(0, portIndex) +
+        ':&#8203;' + nodeIdSplitAtPort.substring(portIndex + 1);
+  }
+  var normalizedNodeId = '';
+  var splitsAlongDots = nodeIdSplitAtPort.split('.');
+  if (splitsAlongDots) {
+    var len = splitsAlongDots.length;
+    for (var i = 0; i < len; i++) {
+      normalizedNodeId = normalizedNodeId + splitsAlongDots[i];
+      if (i != len - 1) {
+        normalizedNodeId = normalizedNodeId + '.&#8203;';
+      }
+    }
+  } else {
+    normalizedNodeId = nodeIdSplitAtPort;
+  }
+
+  var html = '<div class="col-md-2 container-fluid"><div class="panel panel-default">'+
+      '<div class="panel-heading"><h4>Node Manager<br>(' + normalizedNodeId + ')</h4></div>'+
+      '<div class="panel-body"><ul class="nav nav-pills nav-stacked" id="stacked-menu">' +
+      '<ul class="nav nav-pills nav-stacked collapse in"><li';
+  if (hash.path == 'yarnNode') {
+    html = html + ' class="active"';
+  }
+  html = html + '><a href="yarnNode/' + hash.nodeId + '/' + hash.nodeAddr +
+      '">Node Information</a></li><li';
+  if (hash.path == 'yarnNodeApps') {
+    html = html + ' class="active"';
+  }
+  html = html + '><a href="yarnNodeApps/' + hash.nodeId + '/' + hash.nodeAddr +
+      '">List of Applications</a></li><li';
+  if (hash.path == 'yarnNodeContainers') {
+    html = html + ' class="active"';
+  }
+  html = html + '><a href="yarnNodeContainers/' +hash.nodeId + '/' + hash.nodeAddr +
+      '">List of Containers</a></li></ul></ul></div>';
+  return Ember.String.htmlSafe(html);
+});

+ 7 - 7
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-app.js

@@ -2,12 +2,12 @@ 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'),
+  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'),
@@ -62,4 +62,4 @@ export default DS.Model.extend({
 
     return "label label-" + style;
   }.property("finalStatus")
-});
+});

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

@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  logs: DS.attr('string'),
+  containerID: DS.attr('string'),
+  logFileName: DS.attr('string')
+});

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

@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  appId: DS.attr('string'),
+  state: DS.attr('string'),
+  user: DS.attr('string'),
+  containers: DS.attr('array'),
+  /**
+   * Indicates no rows were retrieved from backend
+   */
+  isDummyApp: function() {
+    return this.get('id') == "dummy";
+  }.property("id"),
+
+  appStateStyle: function() {
+    var style = "default";
+    var appState = this.get("state");
+    if (appState == "RUNNING" || appState == "FINISHING_CONTAINERS_WAIT" ||
+        appState == "APPLICATION_RESOURCES_CLEANINGUP") {
+      style = "primary";
+    } else if (appState == "FINISHED") {
+      style = "success";
+    }
+    return "label label-" + style;
+  }.property("state")
+});

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

@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  containerId: DS.attr('string'),
+  state: DS.attr('string'),
+  user: DS.attr('string'),
+  exitCode: DS.attr('string'),
+  diagnostics: DS.attr('string'),
+  totalMemoryNeeded: DS.attr('number'),
+  totalVCoresNeeded: DS.attr('number'),
+  containerLogFiles: DS.attr('array'),
+
+  /**
+   * Indicates that there was no container retrieved from backend.
+   */
+  isDummyContainer: function() {
+    return this.get('id') == "dummy";
+  }.property("id"),
+
+  containerStateStyle: function() {
+    var style = "primary";
+    var containerState = this.get('state');
+    var containerExitCode = this.get('exitCode');
+    if (containerState == "DONE") {
+      if (containerExitCode == "0") {
+        style = "success";
+      } else if (containerExitCode != "N/A") {
+        style = "danger";
+      }
+    }
+    if (containerState == "EXITED_WITH_SUCCESS") {
+      style = "success";
+    }
+    if (containerState == "EXITED_WITH_FAILURE") {
+      style = "danger";
+    }
+    return "label label-" + style;
+  }.property("state", "exitCode")
+});

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

@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  totalVmemAllocatedContainersMB: DS.attr('number'),
+  totalPmemAllocatedContainersMB: DS.attr('number'),
+  totalVCoresAllocatedContainers: DS.attr('number'),
+  vmemCheckEnabled: DS.attr('boolean'),
+  pmemCheckEnabled: DS.attr('boolean'),
+  nodeHealthy: DS.attr('boolean'),
+  lastNodeUpdateTime: DS.attr('string'),
+  healthReport: DS.attr('string'),
+  nmStartupTime: DS.attr('string'),
+  nodeManagerBuildVersion: DS.attr('string'),
+  hadoopBuildVersion: DS.attr('string'),
+});

+ 92 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/models/yarn-rm-node.js

@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  rack: DS.attr('string'),
+  state: DS.attr('string'),
+  nodeHostName: DS.attr('string'),
+  nodeHTTPAddress: DS.attr('string'),
+  lastHealthUpdate: DS.attr('string'),
+  healthReport: DS.attr('string'),
+  numContainers: DS.attr('number'),
+  usedMemoryMB: DS.attr('number'),
+  availMemoryMB: DS.attr('number'),
+  usedVirtualCores: DS.attr('number'),
+  availableVirtualCores: DS.attr('number'),
+  version: DS.attr('string'),
+  nodeLabels: DS.attr('array'),
+
+  nodeLabelsAsString: function() {
+    var labels = this.get("nodeLabels");
+    var labelToReturn = "";
+    // Only one label per node supported.
+    if (labels && labels.length > 0) {
+      labelToReturn = labels[0];
+    }
+    return labelToReturn;
+  }.property("nodeLabels"),
+
+  /**
+   * Indicates no rows were retrieved from backend
+   */
+  isDummyNode: function() {
+    return this.get('id') == "dummy";
+  }.property("id"),
+
+  nodeStateStyle: function() {
+    var style = "default";
+    var nodeState = this.get("state");
+    if (nodeState == "REBOOTED") {
+      style = "warning";
+    } else if (nodeState == "UNHEALTHY" || nodeState == "DECOMMISSIONED" ||
+          nodeState == "LOST" || nodeState == "SHUTDOWN") {
+      style = "danger";
+    } else if (nodeState == "RUNNING") {
+      style = "success";
+    }
+    return "label label-" + style;
+  }.property("state"),
+
+  getMemoryDataForDonutChart: function() {
+    var arr = [];
+    arr.push({
+      label: "Used",
+      value: this.get("usedMemoryMB")
+    });
+    arr.push({
+      label: "Available",
+      value: this.get("availMemoryMB")
+    });
+    return arr;
+  }.property("availMemoryMB", "usedMemoryMB"),
+
+  getVCoreDataForDonutChart: function() {
+    var arr = [];
+    arr.push({
+      label: "Used",
+      value: this.get("usedVirtualCores")
+    });
+    arr.push({
+      label: "Available",
+      value: this.get("availableVirtualCores")
+    });
+    return arr;
+  }.property("availableVirtualCores", "usedVirtualCores"),
+});

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

@@ -7,10 +7,23 @@ var Router = Ember.Router.extend({
 
 Router.map(function() {
   this.route('yarnApps');
+  this.route('yarnNodes');
+  this.route('yarnNode', { path: '/yarnNode/:node_id/:node_addr' });
+  this.route('yarnNodeApps', { path: '/yarnNodeApps/:node_id/:node_addr' });
+  this.route('yarnNodeApp',
+      { path: '/yarnNodeApp/:node_id/:node_addr/:app_id' });
+  this.route('yarnNodeContainers',
+      { path: '/yarnNodeContainers/:node_id/:node_addr' });
+  this.route('yarnNodeContainer',
+      { path: '/yarnNodeContainer/:node_id/:node_addr/:container_id' });
+  this.route('yarnContainerLog', { path:
+      '/yarnContainerLog/:node_id/:node_addr/:container_id/:filename' });
   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'});
+  this.route('error');
+  this.route('notfound', { path: '*:' });
 });
 
 export default Router;

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

@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  actions: {
+    /**
+     * Base error handler for the application.
+     * If specific routes do not handle the error, it will bubble up to
+     * this handler. Here we redirect to either 404 page or a generic
+     * error handler page.
+     */
+    error: function (error) {
+      if (error && error.errors[0] &&
+          error.errors[0].status == 404) {
+        this.intermediateTransitionTo('/notfound');
+      } else {
+        this.intermediateTransitionTo('/error');
+      }
+    }
+  }
+});

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

@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  /**
+   * Redirect root URL to cluster overview page.
+   */
+  beforeModel: function() {
+    this.replaceWith('clusterOverview');
+  }
+});
+

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

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

+ 55 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-container-log.js

@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import Constants from 'yarn-ui/constants';
+
+export default Ember.Route.extend({
+  model(param) {
+    var id = param.node_addr + Constants.PARAM_SEPARATOR + param.container_id +
+        Constants.PARAM_SEPARATOR + param.filename;
+    return Ember.RSVP.hash({
+      containerLog: this.store.findRecord('yarnContainerLog', id),
+      nodeInfo: { id: param.node_id, addr: param.node_addr }
+    }).then(function(hash) {
+      // Just return as its success.
+      return hash;
+    }, function(reason) {
+      if (reason.errors && reason.errors[0]) {
+        // This means HTTP error response was sent by adapter.
+        return reason;
+      } else {
+        // Assume empty response received from server.
+        return { nodeInfo: { id: param.node_id, addr: param.node_addr },
+            containerLog: { logs: "", containerID: param.container_id,
+                logFileName: param.filename}};
+      }
+    });
+  },
+
+  afterModel(model) {
+    // Handle errors and redirect if promise is rejected.
+    if (model.errors && model.errors[0]) {
+      if (model.errors[0].status == 404) {
+        this.replaceWith('/notfound');
+      } else {
+        this.replaceWith('/error');
+      }
+    }
+  }
+});

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

@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    return Ember.RSVP.hash({
+      nodeApp: this.store.queryRecord('yarnNodeApp',
+          { nodeAddr : param.node_addr, appId: param.app_id }),
+      nodeInfo: { id: param.node_id, addr: param.node_addr }
+    });
+  }
+});

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

@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    // Get all apps running on a specific node. Node is contacted by using node_addr.
+    return Ember.RSVP.hash({
+      apps: this.store.query('yarnNodeApp', { nodeAddr: param.node_addr }),
+      nodeInfo: { id: param.node_id, addr: param.node_addr }
+    });
+  }
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-container.js

@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    // Get a specific container running on a specific node.
+    return Ember.RSVP.hash({
+      nodeContainer: this.store.queryRecord('yarnNodeContainer',
+          { nodeHttpAddr: param.node_addr, containerId: param.container_id }),
+      nodeInfo: { id: param.node_id, addr: param.node_addr }
+    });
+  }
+});

+ 28 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/routes/yarn-node-containers.js

@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    // Get all containers running on specific node.
+    return Ember.RSVP.hash({
+      containers: this.store.query('yarnNodeContainer', { nodeHttpAddr: param.node_addr }),
+      nodeInfo: { id: param.node_id, addr: param.node_addr }
+    });
+  }
+});

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

@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    // Fetches data from both NM and RM. RM is queried to get node usage info.
+    return Ember.RSVP.hash({
+      node: this.store.findRecord('yarnNode', param.node_addr),
+      rmNode: this.store.findRecord('yarnRmNode', param.node_id)
+    });
+  }
+});

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

@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+    return this.store.findAll('yarnRmNode');
+  }
+});

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

@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Converter from 'yarn-ui/utils/converter';
+
+export default DS.JSONAPISerializer.extend({
+  normalizeSingleResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // Convert plain text response into JSON.
+    // ID is of the form nodeAddress!containerId!fileName
+    var splits = Converter.splitForContainerLogs(id);
+    var convertedPayload = {
+      id: id,
+      type: primaryModelClass.modelName,
+      attributes: {
+        logs: payload,
+        containerID: splits[1],
+        logFileName: splits[2]
+      }
+    };
+    return { data: convertedPayload };
+  },
+});

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

@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Ember from 'ember';
+
+export default DS.JSONAPISerializer.extend({
+  internalNormalizeSingleResponse(store, primaryModelClass, payload) {
+    if (payload.app) {
+      payload = payload.app;
+    }
+
+    var fixedPayload = {
+      id: payload.id,
+      type: primaryModelClass.modelName,
+      attributes: {
+        appId: payload.id,
+        state: payload.state,
+        user: payload.user,
+        containers: payload.containerids
+      }
+    };
+    return fixedPayload;
+  },
+
+  normalizeSingleResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // payload is of the form {"app":{}}
+    var p = this.internalNormalizeSingleResponse(store,
+        primaryModelClass, payload);
+    return { data: p };
+  },
+
+  normalizeArrayResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // expected return response is of the form { data: [ {}, {} ] }
+    var normalizedArrayResponse = {};
+    // payload is of the form { "apps" : { "app": [ {},{},{} ]  } }
+    if (payload.apps) {
+      normalizedArrayResponse.data = payload.apps.app.map(singleApp => {
+          return this.internalNormalizeSingleResponse(store, primaryModelClass,
+              singleApp);
+          }, this);
+    } else {
+      // No container reported inside containers.
+      // Response of the form { "apps": null }
+      normalizedArrayResponse.data = Ember.makeArray({
+          id: "dummy",
+          type: primaryModelClass.modelName,
+          attributes: {}});
+    }
+    return normalizedArrayResponse;
+  }
+});

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

@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Ember from 'ember';
+
+export default DS.JSONAPISerializer.extend({
+  internalNormalizeSingleResponse(store, primaryModelClass, payload) {
+    if (payload.container) {
+      payload = payload.container;
+    }
+    var fixedPayload = {
+      id: payload.id,
+      type: primaryModelClass.modelName,
+      attributes: {
+        containerId: payload.id,
+        state: payload.state,
+        user: payload.user,
+        diagnostics: payload.diagnostics,
+        exitCode: payload.exitCode,
+        totalMemoryNeeded: payload.totalMemoryNeededMB,
+        totalVCoresNeeded: payload.totalVCoresNeeded,
+        containerLogFiles: payload.containerLogFiles
+      }
+    };
+
+    return fixedPayload;
+  },
+
+  normalizeSingleResponse(store, primaryModelClass, payload, id,
+    requestType) {
+    // payload is of the form {"container":{}}
+    var p = this.internalNormalizeSingleResponse(store,
+        primaryModelClass, payload);
+    return { data: p };
+  },
+
+  normalizeArrayResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // expected return response is of the form { data: [ {}, {} ] }
+    var normalizedArrayResponse = {};
+    if (payload.containers) {
+      // payload is of the form { "containers" : { "container": [ {},{},{} ]  } }
+      normalizedArrayResponse.data =
+          payload.containers.container.map(singleContainer => {
+            return this.internalNormalizeSingleResponse(store, primaryModelClass,
+                singleContainer);
+          }, this);
+    } else {
+      // No container reported inside containers.
+      // Response of the form { "containers": null }
+      normalizedArrayResponse.data = Ember.makeArray({
+          id: "dummy",
+          type: primaryModelClass.modelName,
+          attributes: {}});
+    }
+    return normalizedArrayResponse;
+  }
+});

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

@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import Converter from 'yarn-ui/utils/converter';
+
+export default DS.JSONAPISerializer.extend({
+  internalNormalizeSingleResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    if (payload.nodeInfo) {
+      payload = payload.nodeInfo;
+    }
+
+    var fixedPayload = {
+      id: id,
+      type: primaryModelClass.modelName,
+      attributes: {
+        totalVmemAllocatedContainersMB: payload.totalVmemAllocatedContainersMB,
+        totalPmemAllocatedContainersMB: payload.totalPmemAllocatedContainersMB,
+        totalVCoresAllocatedContainers: payload.totalVCoresAllocatedContainers,
+        vmemCheckEnabled: payload.vmemCheckEnabled,
+        pmemCheckEnabled: payload.pmemCheckEnabled,
+        nodeHealthy: payload.nodeHealthy,
+        lastNodeUpdateTime: Converter.timeStampToDate(payload.lastNodeUpdateTime),
+        healthReport: payload.healthReport,
+        nmStartupTime: Converter.timeStampToDate(payload.nmStartupTime),
+        nodeManagerBuildVersion: payload.nodeManagerBuildVersion,
+        hadoopBuildVersion: payload.hadoopBuildVersion
+      }
+    };
+    return fixedPayload;
+  },
+
+  normalizeSingleResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // payload is of the form {"nodeInfo":{}}
+    var p = this.internalNormalizeSingleResponse(store,
+        primaryModelClass, payload, id, requestType);
+    return { data: p };
+  },
+});

+ 77 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/serializers/yarn-rm-node.js

@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import DS from 'ember-data';
+import Converter from 'yarn-ui/utils/converter';
+
+export default DS.JSONAPISerializer.extend({
+  internalNormalizeSingleResponse(store, primaryModelClass, payload, id) {
+    if (payload.node) {
+      payload = payload.node;
+    }
+
+    var fixedPayload = {
+      id: id,
+      type: primaryModelClass.modelName,
+      attributes: {
+        rack: payload.rack,
+        state: payload.state,
+        nodeHostName: payload.nodeHostName,
+        nodeHTTPAddress: payload.nodeHTTPAddress,
+        lastHealthUpdate: Converter.timeStampToDate(payload.lastHealthUpdate),
+        healthReport: payload.healthReport,
+        numContainers: payload.numContainers,
+        usedMemoryMB: payload.usedMemoryMB,
+        availMemoryMB: payload.availMemoryMB,
+        usedVirtualCores: payload.usedVirtualCores,
+        availableVirtualCores: payload.availableVirtualCores,
+        version: payload.version,
+        nodeLabels: payload.nodeLabels
+      }
+    };
+    return fixedPayload;
+  },
+
+  normalizeSingleResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // payload is of the form {"nodeInfo":{}}
+    var p = this.internalNormalizeSingleResponse(store,
+        primaryModelClass, payload, id);
+    return { data: p };
+  },
+
+  normalizeArrayResponse(store, primaryModelClass, payload, id,
+      requestType) {
+    // expected response is of the form { data: [ {}, {} ] }
+    var normalizedArrayResponse = {};
+    if (payload.nodes) {
+      // payload is of the form { "nodes": { "node": [ {},{},{} ]  } }
+      normalizedArrayResponse.data = payload.nodes.node.map(singleNode => {
+        return this.internalNormalizeSingleResponse(store, primaryModelClass,
+          singleNode, singleNode.id);
+          }, this);
+    } else {
+      normalizedArrayResponse.data = Ember.makeArray({
+          id: "dummy",
+          type: primaryModelClass.modelName,
+          attributes: {}});
+    }
+    return normalizedArrayResponse;
+  }
+});

+ 1 - 3
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/application.hbs

@@ -14,9 +14,7 @@
     <!-- 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>
+        {{outputMainMenu}}
       </ul>
     </div><!-- /.navbar-collapse -->
   </div><!-- /.container-fluid -->

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

@@ -0,0 +1,19 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<h3 align = "center">Sorry, Error Occured.</h3>

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

@@ -0,0 +1,20 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<h2 align = "center">404, Not Found</h2>
+<h4 align = "center">Please Check your URL</h4>

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

@@ -1,3 +1,3 @@
 {{app-table table-id="apps-table" arr=model}}
-{{simple-table table-id="apps-table" bFilter=true colTypes="elapsed-time" colTargets="7"}}
-{{outlet}}
+{{simple-table table-id="apps-table" bFilter=true colsOrder="0,desc" colTypes="natural elapsed-time" colTargets="0 7"}}
+{{outlet}}

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

@@ -0,0 +1,36 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  {{node-menu path="yarnContainerLog" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}
+  <div class="col-md-10">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h5 align="center"><b>{{model.containerLog.logFileName}} for {{model.containerLog.containerID}}</b></h5>
+      </div>
+      <div class="panel-body">
+        {{#if model.containerLog.logs}}
+          <pre>{{model.containerLog.logs}}</pre>
+        {{else}}
+          <p>No logs were written in {{model.containerLog.logFileName}}.</p>
+        {{/if}}
+      </div>
+    </div>
+  </div>
+</div>
+{{outlet}}

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

@@ -0,0 +1,60 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{node-menu path="yarnNodeApp" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}
+    <div class="col-md-10 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading"><b>Application Information</b></div>
+        <table class="table">
+          <tbody>
+            <tr>
+              <td>Application ID</td>
+              <td>{{model.nodeApp.appId}}</td>
+            </tr>
+            <tr>
+              <td>Application State</td>
+              <td>{{model.nodeApp.state}}</td>
+            </tr>
+            <tr>
+              <td>User</td>
+              <td>{{model.nodeApp.user}}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <table id="node-app-table" class="display table table-striped table-bordered" cellspacing="0">
+        <thead>
+          <tr>
+            <th>Containers for {{model.nodeApp.appId}}</th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#each model.nodeApp.containers as |container|}}
+            <tr>
+              <td><a href="yarnNodeContainer/{{model.nodeInfo.id}}/{{model.nodeInfo.addr}}/{{container}}">{{container}}</a></td>
+            </tr>
+          {{/each}}
+       </tbody>
+     </table>
+     {{simple-table table-id="node-app-table" bFilter=true colsOrder="0,desc" colTypes="natural" colTargets="0"}}
+   </div>
+  </div>
+ </div>
+{{outlet}}

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

@@ -0,0 +1,51 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{node-menu path="yarnNodeApps" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}
+    <div class="col-md-10 container-fluid">
+      <table id="node-apps-table" class="display table table-striped table-bordered" cellspacing="0" width="100%">
+        <thead>
+          <tr>
+            <th>Application ID</th>
+            <th>State</th>
+            <th>User</th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#if model.apps}}
+            {{#each model.apps as |app|}}
+              {{#if app.isDummyApp}}
+                <tr><td colspan="3" align="center">No apps found on this node</td></tr>
+              {{else}}
+                <tr>
+                  <td><a href="yarnNodeApp/{{model.nodeInfo.id}}/{{model.nodeInfo.addr}}/{{app.appId}}">{{app.appId}}</a></td>
+                  <td><span class={{app.appStateStyle}}>{{app.state}}</span></td>
+                  <td>{{app.user}}</td>
+                </tr>
+              {{/if}}
+            {{/each}}
+          {{/if}}
+        </tbody>
+      </table>
+      {{simple-table table-id="node-apps-table" bFilter=true colsOrder="0,desc" colTypes="natural" colTargets="0"}}
+    </div>
+  </div>
+</div>
+{{outlet}}

+ 70 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-container.hbs

@@ -0,0 +1,70 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{node-menu path="yarnNodeContainer" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}
+    <div class="col-md-10 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading"><b>Container Information</b></div>
+        <table class="table">
+          <tbody>
+            <tr>
+              <td>Container ID</td>
+              <td>{{model.nodeContainer.containerId}}</td>
+            </tr>
+            <tr>
+              <td>Container State</td>
+              <td>{{model.nodeContainer.state}}</td>
+            </tr>
+            <tr>
+              <td>Exit Code</td>
+              <td>{{model.nodeContainer.exitCode}}</td>
+            </tr>
+            <tr>
+              <td>Diagnostics</td>
+              <td>{{model.nodeContainer.diagnostics}}</td>
+            </tr>
+            <tr>
+              <td>User</td>
+              <td>{{model.nodeContainer.user}}</td>
+            </tr>
+            <tr>
+              <td>Total Memory Needed</td>
+              <td>{{model.nodeContainer.totalMemoryNeeded}} MB</td>
+            </tr>
+            <tr>
+              <td>Total VCores Needed</td>
+              <td>{{model.nodeContainer.totalVCoresNeeded}}</td>
+            </tr>
+            <tr>
+              <td>Link to Logs</td>
+              <td>
+                {{log-files-comma nodeId=model.nodeInfo.id
+                    nodeAddr=model.nodeInfo.addr
+                    containerId=model.nodeContainer.containerId
+                    logFiles=model.nodeContainer.containerLogFiles}}
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+   </div>
+  </div>
+ </div>
+{{outlet}}

+ 58 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/templates/yarn-node-containers.hbs

@@ -0,0 +1,58 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{node-menu path="yarnNodeContainers" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}
+    <div class="col-md-10 container-fluid">
+      <table id="node-containers-table" class="display table table-striped table-bordered" cellspacing="0" width="100%">
+        <thead>
+          <tr>
+            <th>Container ID</th>
+            <th>Container State</th>
+            <th>User</th>
+            <th>Logs</th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#if model.containers}}
+            {{#each model.containers as |container|}}
+              {{#if container.isDummyContainer}}
+                <tr><td colspan="4" align="center">No containers found on this node</td></tr>
+              {{else}}
+                <tr>
+                  <td><a href="yarnNodeContainer/{{model.nodeInfo.id}}/{{model.nodeInfo.addr}}/{{container.containerId}}">{{container.containerId}}</a></td>
+                  <td><span class={{container.containerStateStyle}}>{{container.state}}</span></td>
+                  <td>{{container.user}}</td>
+                  <td>
+                    {{log-files-comma nodeId=model.nodeInfo.id
+                        nodeAddr=model.nodeInfo.addr
+                        containerId=container.containerId
+                        logFiles=container.containerLogFiles}}
+                  </td>
+                </tr>
+              {{/if}}
+            {{/each}}
+          {{/if}}
+        </tbody>
+      </table>
+      {{simple-table table-id="node-containers-table" bFilter=true colsOrder="0,desc" colTypes="natural" colTargets="0"}}
+    </div>
+  </div>
+</div>
+{{outlet}}

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

@@ -0,0 +1,94 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{node-menu path="yarnNode" nodeId=model.rmNode.id nodeAddr=model.node.id}}
+    <div class="col-md-10 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">Node Information</div>
+          <table class="table">
+            <tbody>
+              <tr>
+                <td>Total Vmem allocated for Containers</td>
+                <td>{{divide num=model.node.totalVmemAllocatedContainersMB den=1024}} GB</td>
+              </tr>
+              <tr>
+                <td>Vmem enforcement enabled</td>
+                <td>{{model.node.vmemCheckEnabled}}</td>
+              </tr>
+              <tr>
+                <td>Total Pmem allocated for Containers</td>
+                <td>{{divide num=model.node.totalPmemAllocatedContainersMB den=1024}} GB</td>
+              </tr>
+              <tr>
+                <td>Pmem enforcement enabled</td>
+                <td>{{model.node.pmemCheckEnabled}}</td>
+              </tr>
+              <tr>
+                <td>Total VCores allocated for Containers</td>
+                <td>{{model.node.totalVCoresAllocatedContainers}}</td>
+              </tr>
+              <tr>
+                <td>Node Healthy Status</td>
+                <td>{{model.node.nodeHealthy}}</td>
+              </tr>
+              <tr>
+                <td>Last Node Health Report Time</td>
+                <td>{{model.node.lastNodeUpdateTime}}</td>
+              </tr>
+              <tr>
+                <td>Node Health Report</td>
+                <td>{{model.node.healthReport}}</td>
+              </tr>
+              <tr>
+                <td>Node Manager Start Time</td>
+                <td>{{model.node.nmStartupTime}}</td>
+              </tr>
+              <tr>
+                <td>Node Manager Version</td>
+                <td>{{model.node.nodeManagerBuildVersion}}</td>
+              </tr>
+              <tr>
+                <td>Hadoop Version</td>
+                <td>{{model.node.hadoopBuildVersion}}</td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      <div class="col-lg-4 container-fluid" id="mem-donut-chart">
+        {{donut-chart data=model.rmNode.getMemoryDataForDonutChart
+          title="Resource - Memory (in MB)"
+          showLabels=true
+          parentId="mem-donut-chart"
+          ratio=0.55
+          maxHeight=350}}
+      </div>
+
+      <div class="col-lg-4 container-fluid" id="vcore-donut-chart">
+        {{donut-chart data=model.rmNode.getVCoreDataForDonutChart
+          title="Resource - VCores"
+          showLabels=true
+          parentId="vcore-donut-chart"
+          ratio=0.55
+          maxHeight=350}}
+      </div>
+      </div>
+    </div>
+</div>
+{{outlet}}

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

@@ -0,0 +1,65 @@
+{{!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--}}
+
+<table id="nodes-table" class="display table table-striped table-bordered" cellspacing="0" width="100%">
+  <thead>
+    <tr>
+          <th>Node Labels</th>
+          <th>Rack</th>
+          <th>Node State</th>
+          <th>Node Address</th>
+          <th>Node HTTP Address</th>
+          <th>Last Health Update</th>
+          <th>Health-Report</th>
+          <th>Containers</th>
+          <th>Mem Used</th>
+          <th>Mem Avail</th>
+          <th>VCores Used</th>
+          <th>VCores Avail</th>
+          <th>Version</th>
+    </tr>
+  </thead>
+  <tbody>
+    {{#if model}}
+      {{#each model as |node|}}
+        {{#if node.isDummyNode}}
+          <tr><td colspan="13" align="center">No nodes found on this cluster</td></tr>
+        {{else}}
+          <tr>
+            <td>{{node.nodeLabelsAsString}}</td>
+            <td>{{node.rack}}</td>
+            <td><span class={{node.nodeStateStyle}}>{{node.state}}</span></td>
+            <td>{{node.id}}</td>
+            {{node-link nodeId=node.id nodeHTTPAddress=node.nodeHTTPAddress nodeState=node.state}}
+            <td>{{node.lastHealthUpdate}}</td>
+            <td>{{node.healthReport}}</td>
+            <td>{{node.numContainers}}</td>
+            <td>{{divide num=node.usedMemoryMB den=1024}} GB</td>
+            <td>{{divide num=node.availMemoryMB den=1024}} GB</td>
+            <td>{{node.usedVirtualCores}}</td>
+            <td>{{node.availableVirtualCores}}</td>
+            <td>{{node.version}}</td>
+          </tr>
+        {{/if}}
+      {{/each}}
+    {{/if}}
+  </tbody>
+</table>
+
+{{simple-table table-id="nodes-table" bFilter=true}}
+{{outlet}}

+ 19 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/converter.js

@@ -1,3 +1,5 @@
+import Constants from 'yarn-ui/constants';
+
 export default {
   containerIdToAttemptId: function(containerId) {
     if (containerId) {
@@ -70,5 +72,20 @@ export default {
       var ts = moment(date, "YYYY/MM/DD HH:mm:ss").valueOf();
       return ts;
     }
-  }
-}
+  },
+  splitForContainerLogs: function(id) {
+    if (id) {
+      var splits = id.split(Constants.PARAM_SEPARATOR);
+      var splitLen = splits.length;
+      if (splitLen < 3) {
+        return null;
+      }
+      var fileName = splits[2];
+      var index;
+      for (index = 3; index < splitLen; index++) {
+        fileName = fileName + Constants.PARAM_SEPARATOR + splits[index];
+      }
+      return [splits[0], splits[1], fileName];
+    }
+  },
+};

+ 41 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/app/utils/sorter.js

@@ -1,15 +1,55 @@
 import Converter from 'yarn-ui/utils/converter';
+import Ember from 'ember';
 
 export default {
   _initElapsedTimeSorter: function() {
-    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
+    Ember.$.extend(Ember.$.fn.dataTableExt.oSort, {
       "elapsed-time-pre": function (a) {
          return Converter.padding(Converter.elapsedTimeToMs(a), 20);
       },
     });
   },
 
+  _initNaturalSorter: function() {
+    Ember.$.extend(Ember.$.fn.dataTableExt.oSort, {
+      "natural-asc": function (a, b) {
+        return naturalSort(a,b);
+      },
+      "natural-desc": function (a, b) {
+        return naturalSort(a,b) * -1;
+      },
+    });
+  },
+
   initDataTableSorter: function() {
     this._initElapsedTimeSorter();
+    this._initNaturalSorter();
   },
+};
+
+/**
+ * Natural sort implementation.
+ * Typically used to sort application Ids'.
+ */
+function naturalSort(a, b) {
+  var diff = a.length - b.length;
+  if (diff != 0) {
+    var splitA = a.split("_");
+    var splitB = b.split("_");
+    if (splitA.length != splitB.length) {
+      return a.localeCompare(b);
+    }
+    for (var i = 1; i < splitA.length; i++) {
+      var splitdiff = splitA[i].length - splitB[i].length;
+      if (splitdiff != 0) {
+        return splitdiff;
+      }
+      var splitCompare = splitA[i].localeCompare(splitB[i]);
+      if (splitCompare != 0) {
+        return splitCompare;
+      }
+    }
+    return diff;
+  }
+  return a.localeCompare(b);
 }

+ 1 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/bower.json

@@ -9,7 +9,7 @@
     "ember-qunit": "0.4.9",
     "ember-qunit-notifications": "0.0.7",
     "ember-resolver": "~0.1.18",
-    "jquery": "^1.11.3",
+    "jquery": "1.11.3",
     "loader.js": "ember-cli/loader.js#3.2.1",
     "qunit": "~1.18.0",
     "bootstrap": "~3.3.2",

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

@@ -12,7 +12,6 @@ module.exports = function(environment) {
         // e.g. 'with-controller': true
       }
     },
-
     APP: {
       // Here you can pass flags/options to your application instance
       // when it is created

+ 73 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-container-log-test.js

@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+import Constants from 'yarn-ui/constants';
+
+moduleFor('adapter:yarn-container-log', 'Unit | Adapter | ContainerLog', {
+});
+
+test('Basic creation', function(assert) {
+  let adapter = this.subject();
+
+  assert.ok(adapter);
+  assert.ok(adapter.urlForFindRecord);
+  assert.ok(adapter.ajax);
+  assert.ok(adapter.headers);
+  assert.ok(adapter.host);
+  assert.ok(adapter.namespace);
+  assert.equal(adapter.headers.Accept, "text/plain");
+  assert.equal(adapter.namespace, "ws/v1/node");
+});
+
+test('urlForFindRecord test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(adapter.urlForFindRecord("localhost:8042" +
+      Constants.PARAM_SEPARATOR + "container_e27_11111111111_0001_01_000001" +
+      Constants.PARAM_SEPARATOR + "syslog"),
+      host + "localhost:8042/ws/v1/node/containerlogs/" +
+      "container_e27_11111111111_0001_01_000001/syslog");
+});
+
+test('ajaxOptions test', function(assert) {
+  let adapter = this.subject();
+  var hash = adapter.ajaxOptions('/containerlogs', 'type', {});
+  assert.equal(hash.dataType, 'text');
+});
+
+test('findRecord test', function(assert) {
+  let adapter = this.subject(),
+      testModel = { modelName: "testModel" },
+      testStore = {},
+      testSnapshot = {};
+  let host = adapter.host;
+  let testId = "localhost:8042" + Constants.PARAM_SEPARATOR +
+      "container_e27_11111111111_0001_01_000001" + Constants.PARAM_SEPARATOR +
+      "syslog";
+  assert.expect(2);
+
+  adapter.ajax = function (url, method) {
+    assert.equal(url, host + "localhost:8042/ws/v1/node/containerlogs/" +
+        "container_e27_11111111111_0001_01_000001/syslog");
+    assert.equal(method, 'GET');
+  };
+
+  adapter.findRecord(testStore, testModel, testId, testSnapshot);
+});
+

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

@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('adapter:yarn-node-app', 'Unit | Adapter | NodeApp', {
+});
+
+test('Basic creation', function(assert) {
+  let adapter = this.subject();
+  assert.expect(11);
+  assert.ok(adapter);
+  assert.ok(adapter.urlForQueryRecord);
+  assert.ok(adapter.queryRecord);
+  assert.ok(adapter.urlForQuery);
+  assert.ok(adapter.query);
+  assert.ok(adapter.ajax);
+  assert.ok(adapter.headers);
+  assert.ok(adapter.host);
+  assert.ok(adapter.namespace);
+  assert.equal("application/json", adapter.headers.Accept);
+  assert.equal("ws/v1/node", adapter.namespace);
+});
+
+test('urlForQueryRecord test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(
+      host + "localhost:8042/ws/v1/node/apps/application_1111111111_1111",
+      adapter.urlForQueryRecord(
+      {nodeAddr: "localhost:8042", appId: "application_1111111111_1111"}));
+});
+
+test('urlForQuery test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(host + "localhost:8042/ws/v1/node/apps",
+      adapter.urlForQuery({nodeAddr: "localhost:8042"}));
+});
+
+test('query test', function(assert) {
+  let adapter = this.subject(),
+      testModel = { modelName: "testModel" },
+      testStore = {},
+      testQuery = {nodeAddr: "localhost:8042"};
+  let host = adapter.host;
+  assert.expect(3);
+
+  adapter.ajax = function (url, method, hash) {
+    assert.equal(host + "localhost:8042/ws/v1/node/apps", url);
+    assert.equal('GET', method);
+    assert.equal(null, hash.data);
+  };
+
+  adapter.query(testStore, testModel, testQuery);
+});
+
+test('queryRecord test', function(assert) {
+  let adapter = this.subject(),
+      testModel = { modelName: "testModel" },
+      testStore = {},
+      testQuery = {
+        nodeAddr: "localhost:8042",
+        appId: "application_1111111111_1111"
+      };
+  let host = adapter.host;
+  assert.expect(3);
+
+  adapter.ajax = function (url, method, hash) {
+    assert.equal(
+        host + "localhost:8042/ws/v1/node/apps/application_1111111111_1111",
+        url);
+    assert.equal('GET', method);
+    assert.equal(null, hash.data);
+  };
+
+  adapter.queryRecord(testStore, testModel, testQuery);
+});

+ 93 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-node-container-test.js

@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('adapter:yarn-node-container', 'Unit | Adapter | NodeContainer', {
+});
+
+test('Basic creation', function(assert) {
+  let adapter = this.subject();
+  assert.expect(11);
+  assert.ok(adapter);
+  assert.ok(adapter.urlForQueryRecord);
+  assert.ok(adapter.queryRecord);
+  assert.ok(adapter.urlForQuery);
+  assert.ok(adapter.query);
+  assert.ok(adapter.ajax);
+  assert.ok(adapter.headers);
+  assert.ok(adapter.host);
+  assert.ok(adapter.namespace);
+  assert.equal("application/json", adapter.headers.Accept);
+  assert.equal("ws/v1/node", adapter.namespace);
+});
+
+test('urlForQueryRecord test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(host + "localhost:8042/ws/v1/node/containers/" +
+      "container_e27_11111111111_0001_01_000001",
+      adapter.urlForQueryRecord(
+      {nodeHttpAddr: "localhost:8042",
+      containerId: "container_e27_11111111111_0001_01_000001"}));
+});
+
+test('urlForQuery test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(host + "localhost:8042/ws/v1/node/containers",
+      adapter.urlForQuery({nodeHttpAddr: "localhost:8042"}));
+});
+
+test('query test', function(assert) {
+  let adapter = this.subject(),
+      testModel = { modelName: "testModel" },
+      testStore = {},
+      testQuery = {nodeHttpAddr: "localhost:8042"};
+  let host = adapter.host;
+  assert.expect(3);
+
+  adapter.ajax = function (url, method, hash) {
+    assert.equal(host + "localhost:8042/ws/v1/node/containers", url);
+    assert.equal('GET', method);
+    assert.equal(null, hash.data);
+  };
+
+  adapter.query(testStore, testModel, testQuery);
+});
+
+test('queryRecord test', function(assert) {
+  let adapter = this.subject(),
+      testModel = { modelName: "testModel" },
+      testStore = {},
+      testQuery = {
+        nodeHttpAddr: "localhost:8042",
+        containerId: "container_e27_11111111111_0001_01_000001"
+      };
+  let host = adapter.host;
+  assert.expect(3);
+
+  adapter.ajax = function (url, method, hash) {
+    assert.equal(host + "localhost:8042/ws/v1/node/containers/" +
+        "container_e27_11111111111_0001_01_000001", url);
+    assert.equal('GET', method);
+    assert.equal(null, hash.data);
+  };
+
+  adapter.queryRecord(testStore, testModel, testQuery);
+});

+ 42 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-node-test.js

@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('adapter:yarn-node', 'Unit | Adapter | Node', {
+});
+
+test('Basic creation', function(assert) {
+  let adapter = this.subject();
+
+  assert.ok(adapter);
+  assert.ok(adapter.urlForFindRecord);
+  assert.ok(adapter.ajax);
+  assert.ok(adapter.headers);
+  assert.ok(adapter.host);
+  assert.ok(adapter.namespace);
+  assert.equal(adapter.headers.Accept, "application/json");
+  assert.equal(adapter.namespace, "ws/v1/node");
+});
+
+test('urlForFindRecord test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(adapter.urlForFindRecord("localhost:8042"),
+      host + "localhost:8042/ws/v1/node");
+});

+ 44 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/adapters/yarn-rm-node-test.js

@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('adapter:yarn-rm-node', 'Unit | Adapter | RMNode', {
+  // Specify the other units that are required for this test.
+  // needs: ['serializer:foo']
+});
+
+test('Basic creation', function(assert) {
+  let adapter = this.subject();
+
+  assert.ok(adapter);
+  assert.ok(adapter.urlForFindRecord);
+  assert.ok(adapter.ajax);
+  assert.ok(adapter.headers);
+  assert.ok(adapter.host);
+  assert.ok(adapter.namespace);
+  assert.equal(adapter.headers.Accept, "application/json");
+  assert.equal(adapter.namespace, "ws/v1/cluster");
+});
+
+test('urlForFindRecord test', function(assert) {
+  let adapter = this.subject();
+  let host = adapter.host;
+  assert.equal(adapter.urlForFindRecord("localhost:8042"),
+      host + "/ws/v1/cluster/nodes/localhost:8042");
+});

+ 48 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-container-log-test.js

@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('yarn-container-log', 'Unit | Model | ContainerLog', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('Basic creation test', function(assert) {
+  let model = this.subject();
+  assert.ok(model);
+  assert.ok(model._notifyProperties);
+  assert.ok(model.didLoad);
+  assert.ok(model.logs);
+  assert.ok(model.containerID);
+  assert.ok(model.logFileName);
+});
+
+test('test fields', function(assert) {
+  let model = this.subject();
+
+  Ember.run(function () {
+    model.set("logs", "This is syslog");
+    model.set("containerID", "container_e32_1456000363780_0002_01_000001");
+    model.set("logFileName", "syslog");
+    assert.equal(model.get("logs"), "This is syslog");
+    assert.equal(model.get("containerID"), "container_e32_1456000363780_0002_01_000001");
+    assert.equal(model.get("logFileName"), "syslog");
+  });
+});
+

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

@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('yarn-node-app', 'Unit | Model | NodeApp', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('Basic creation test', function(assert) {
+  let model = this.subject();
+
+  assert.ok(model);
+  assert.ok(model._notifyProperties);
+  assert.ok(model.didLoad);
+  assert.ok(model.appId);
+  assert.ok(model.state);
+  assert.ok(model.user);
+  assert.ok(model.containers);
+});
+
+test('test fields', function(assert) {
+  let model = this.subject();
+
+  assert.expect(9);
+  Ember.run(function () {
+    model.set("appId", "application_1456251210105_0002");
+    model.set("id", "application_1456251210105_0002");
+    model.set("state", "RUNNING");
+    model.set("user", "hadoop");
+    model.set("containers", ["container_e38_1456251210105_0002_01_000001",
+        "container_e38_1456251210105_0002_01_000002"]);
+    assert.equal(model.get("appId"), "application_1456251210105_0002");
+    assert.equal(model.get("state"), "RUNNING");
+    assert.equal(model.get("user"), "hadoop");
+    assert.deepEqual(model.get("containers"),
+        ["container_e38_1456251210105_0002_01_000001",
+        "container_e38_1456251210105_0002_01_000002"]);
+    assert.equal(model.get("appStateStyle"), "label label-primary");
+    assert.equal(model.get("isDummyApp"), false);
+    model.set("id", "dummy");
+    assert.equal(model.get("isDummyApp"), true);
+    model.set("state", "FINISHED");
+    assert.equal(model.get("appStateStyle"), "label label-success");
+    model.set("state", "NEW");
+    assert.equal(model.get("appStateStyle"), "label label-default");
+  });
+});
+

+ 78 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-node-container-test.js

@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('yarn-node-container', 'Unit | Model | NodeContainer', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('Basic creation test', function(assert) {
+  let model = this.subject();
+
+  assert.ok(model);
+  assert.ok(model._notifyProperties);
+  assert.ok(model.didLoad);
+  assert.ok(model.containerId);
+  assert.ok(model.state);
+  assert.ok(model.user);
+  assert.ok(model.exitCode);
+  assert.ok(model.totalMemoryNeeded);
+  assert.ok(model.totalVCoresNeeded);
+  assert.ok(model.containerLogFiles);
+  assert.ok(model.isDummyContainer);
+  assert.ok(model.containerStateStyle);
+});
+
+test('test fields', function(assert) {
+  let model = this.subject();
+
+  Ember.run(function () {
+    model.set("containerId", "container_e32_1456000363780_0002_01_000003");
+    model.set("state", "RUNNING");
+    model.set("exitCode", "-1000");
+    model.set("user", "hadoop");
+    model.set("id", "container_e32_1456000363780_0002_01_000003");
+    model.set("totalMemoryNeeded", 1024);
+    model.set("totalVCoresNeeded", 1);
+    model.set("containerLogFiles", ["syslog", "stderr", "stdout"]);
+    assert.equal(model.get("containerId"), "container_e32_1456000363780_0002_01_000003");
+    assert.equal(model.get("id"), "container_e32_1456000363780_0002_01_000003");
+    assert.equal(model.get("totalMemoryNeeded"), 1024);
+    assert.equal(model.get("totalVCoresNeeded"), 1);
+    assert.equal(model.get("user"), "hadoop");
+    assert.equal(model.get("exitCode"), "-1000");
+    assert.equal(model.get("containerLogFiles").length, 3);
+    assert.deepEqual(model.get("containerLogFiles"), ["syslog", "stderr", "stdout"]);
+    assert.equal(model.get("isDummyContainer"), false);
+    assert.equal(model.get("containerStateStyle"), "label label-primary");
+    model.set("id", "dummy");
+    assert.equal(model.get("isDummyContainer"), true);
+    model.set("state", "EXITED_WITH_SUCCESS");
+    assert.equal(model.get("containerStateStyle"), "label label-success");
+    model.set("state", "EXITED_WITH_FAILURE");
+    assert.equal(model.get("containerStateStyle"), "label label-danger");
+    model.set("state", "DONE");
+    model.set("exitCode", "0");
+    assert.equal(model.get("containerStateStyle"), "label label-success");
+    model.set("exitCode", "-105");
+    assert.equal(model.get("containerStateStyle"), "label label-danger");
+  });
+});
+

+ 58 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-node-test.js

@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('yarn-node', 'Unit | Model | Node', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('Basic creation test', function(assert) {
+  let model = this.subject();
+
+  assert.ok(model);
+  assert.ok(model._notifyProperties);
+  assert.ok(model.didLoad);
+  assert.ok(model.totalVmemAllocatedContainersMB);
+  assert.ok(model.vmemCheckEnabled);
+  assert.ok(model.pmemCheckEnabled);
+  assert.ok(model.nodeHealthy);
+  assert.ok(model.lastNodeUpdateTime);
+  assert.ok(model.healthReport);
+  assert.ok(model.nmStartupTime);
+  assert.ok(model.nodeManagerBuildVersion);
+  assert.ok(model.hadoopBuildVersion);
+});
+
+test('test fields', function(assert) {
+  let model = this.subject();
+
+  assert.expect(4);
+  Ember.run(function () {
+    model.set("totalVmemAllocatedContainersMB", 4096);
+    model.set("totalPmemAllocatedContainersMB", 2048);
+    model.set("totalVCoresAllocatedContainers", 4);
+    model.set("hadoopBuildVersion", "3.0.0-SNAPSHOT");
+    assert.equal(model.get("totalVmemAllocatedContainersMB"), 4096);
+    assert.equal(model.get("totalPmemAllocatedContainersMB"), 2048);
+    assert.equal(model.get("totalVCoresAllocatedContainers"), 4);
+    assert.equal(model.get("hadoopBuildVersion"), "3.0.0-SNAPSHOT");
+  });
+});
+

+ 95 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/models/yarn-rm-node-test.js

@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('yarn-rm-node', 'Unit | Model | RMNode', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('Basic creation test', function(assert) {
+  let model = this.subject();
+
+  assert.ok(model);
+  assert.ok(model._notifyProperties);
+  assert.ok(model.didLoad);
+  assert.ok(model.rack);
+  assert.ok(model.state);
+  assert.ok(model.nodeHostName);
+  assert.ok(model.nodeHTTPAddress);
+  assert.ok(model.lastHealthUpdate);
+  assert.ok(model.healthReport);
+  assert.ok(model.numContainers);
+  assert.ok(model.usedMemoryMB);
+  assert.ok(model.availMemoryMB);
+  assert.ok(model.usedVirtualCores);
+  assert.ok(model.availableVirtualCores);
+  assert.ok(model.version);
+  assert.ok(model.nodeLabels);
+  assert.ok(model.nodeLabelsAsString);
+  assert.ok(model.nodeStateStyle);
+  assert.ok(model.isDummyNode);
+  assert.ok(model.getMemoryDataForDonutChart);
+  assert.ok(model.getVCoreDataForDonutChart);
+});
+
+test('test fields', function(assert) {
+  let model = this.subject();
+
+  Ember.run(function () {
+    model.set("rack", "/default-rack");
+    model.set("state", "RUNNING");
+    model.set("nodeHostName", "localhost");
+    model.set("id", "localhost:64318");
+    model.set("nodeHTTPAddress", "localhost:8042");
+    model.set("usedMemoryMB", 1024);
+    model.set("availMemoryMB", 7168);
+    model.set("usedVirtualCores", 1);
+    model.set("availableVirtualCores", 7);
+    model.set("nodeLabels", ["x"]);
+    assert.equal(model.get("rack"), "/default-rack");
+    assert.equal(model.get("state"), "RUNNING");
+    assert.equal(model.get("nodeHostName"), "localhost");
+    assert.equal(model.get("id"), "localhost:64318");
+    assert.equal(model.get("nodeHTTPAddress"), "localhost:8042");
+    assert.equal(model.get("usedMemoryMB"), 1024);
+    assert.equal(model.get("availMemoryMB"), 7168);
+    assert.equal(model.get("usedVirtualCores"), 1);
+    assert.equal(model.get("availableVirtualCores"), 7);
+    assert.equal(model.get("isDummyNode"), false);
+    assert.deepEqual(model.get("nodeLabels"), ["x"]);
+    assert.equal(model.get("nodeLabelsAsString"), "x");
+    assert.deepEqual(model.get("nodeStateStyle"), "label label-success");
+    assert.deepEqual(model.get("getMemoryDataForDonutChart"),
+        [{label: "Used", value: 1024}, {label: "Available", value: 7168}]);
+    assert.deepEqual(model.get("getVCoreDataForDonutChart"),
+        [{label: "Used", value: 1}, {label: "Available", value: 7}]);
+    model.set("state", "SHUTDOWN");
+    assert.deepEqual(model.get("nodeStateStyle"), "label label-danger");
+    model.set("state", "REBOOTED");
+    assert.deepEqual(model.get("nodeStateStyle"), "label label-warning");
+    model.set("state", "NEW");
+    assert.deepEqual(model.get("nodeStateStyle"), "label label-default");
+    model.set("nodeLabels", ["x","y"]);
+    assert.equal(model.get("nodeLabelsAsString"), "x");
+    model.set("nodeLabels", undefined);
+    assert.equal(model.get("nodeLabelsAsString"), "");
+  });
+});
+

+ 120 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-container-log-test.js

@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+import Constants from 'yarn-ui/constants';
+
+moduleFor('route:yarn-container-log', 'Unit | Route | ContainerLog', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting container log', function(assert) {
+  var response = {
+      logs: "This is syslog",
+      containerID: "container_e32_1456000363780_0002_01_000001",
+      logFileName: "syslog"};
+  var store = {
+    findRecord: function(type) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response);
+      }
+    )}
+  };
+  assert.expect(6);
+  var route = this.subject();
+  route.set('store', store);
+  var model = route.model({node_id: "localhost:64318",
+      node_addr: "localhost:8042",
+      container_id: "container_e32_1456000363780_0002_01_000001",
+      filename: "syslog"});
+   model.then(function(value) {
+     assert.ok(value);
+     assert.ok(value.containerLog);
+     assert.deepEqual(value.containerLog, response);
+     assert.ok(value.nodeInfo);
+     assert.equal(value.nodeInfo.addr, 'localhost:8042');
+     assert.equal(value.nodeInfo.id, 'localhost:64318');
+   });
+});
+
+/**
+ * This can happen when an empty response is sent from server
+ */
+test('Test non HTTP error while getting container log', function(assert) {
+  var error = {};
+  var response = {
+      logs: "",
+      containerID: "container_e32_1456000363780_0002_01_000001",
+      logFileName: "syslog"};
+  var store = {
+    findRecord: function(type) {
+      return new Ember.RSVP.Promise(function(resolve, reject) {
+        reject(error);
+      }
+    )}
+  };
+  assert.expect(6);
+  var route = this.subject();
+  route.set('store', store);
+  var model = route.model({node_id: "localhost:64318",
+      node_addr: "localhost:8042",
+      container_id: "container_e32_1456000363780_0002_01_000001",
+      filename: "syslog"});
+   model.then(function(value) {
+     assert.ok(value);
+     assert.ok(value.containerLog);
+     assert.deepEqual(value.containerLog, response);
+     assert.ok(value.nodeInfo);
+     assert.equal(value.nodeInfo.addr, 'localhost:8042');
+     assert.equal(value.nodeInfo.id, 'localhost:64318');
+   });
+});
+
+test('Test HTTP error while getting container log', function(assert) {
+  var error = {errors: [{status: 404, responseText: 'Not Found'}]};
+  var response = {
+      logs: "",
+      containerID: "container_e32_1456000363780_0002_01_000001",
+      logFileName: "syslog"};
+  var store = {
+    findRecord: function(type) {
+      return new Ember.RSVP.Promise(function(resolve, reject) {
+        reject(error);
+      }
+    )}
+  };
+  assert.expect(5);
+  var route = this.subject();
+  route.set('store', store);
+  var model = route.model({node_id: "localhost:64318",
+      node_addr: "localhost:8042",
+      container_id: "container_e32_1456000363780_0002_01_000001",
+      filename: "syslog"});
+   model.then(function(value) {
+     assert.ok(value);
+     assert.ok(value.errors);
+     assert.equal(value.errors.length, 1);
+     assert.equal(value.errors[0].status, 404);
+     assert.equal(value.errors[0].responseText, 'Not Found');
+   });
+});

+ 56 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-app-test.js

@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-node-app', 'Unit | Route | NodeApp', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting specific app on a node', function(assert) {
+  var response =
+      {id:"application_1456251210105_0001", state:"FINISHED", user:"root"};
+  var store = {
+    queryRecord: function(type, query) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response);
+      });
+    }
+  };
+  assert.expect(6);
+  var route = this.subject();
+  route.set('store', store);
+  var model =
+      route.model({node_id:"localhost:64318", node_addr:"localhost:8042",
+          app_id:"application_1456251210105_0001"}).
+      then(
+        function(value){
+          assert.ok(value);
+          assert.ok(value.nodeApp);
+          assert.deepEqual(value.nodeApp, response);
+          assert.ok(value.nodeInfo);
+          assert.equal(value.nodeInfo.addr, 'localhost:8042');
+          assert.equal(value.nodeInfo.id, 'localhost:64318');
+        }
+      );
+});

+ 60 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-apps-test.js

@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-node-apps', 'Unit | Route | NodeApps', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting apps on a node', function(assert) {
+  var response = [
+      {id:"application_1456251210105_0001", state:"FINISHED", user:"root"},
+      {id:"application_1456251210105_0002", state:"RUNNING",user:"root",
+      containerids:["container_e38_1456251210105_0002_01_000001",
+      "container_e38_1456251210105_0002_01_000002"]}];
+  var store = {
+    query: function(type, query) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response.slice());
+      });
+    }
+  };
+  assert.expect(8);
+  var route = this.subject();
+  route.set('store', store);
+  var model =
+      route.model({node_id:"localhost:64318", node_addr:"localhost:8042"}).
+      then(
+        function(value){
+          assert.ok(value);
+          assert.ok(value.apps);
+          assert.equal(value.apps.length, 2);
+          assert.deepEqual(response[0], value.apps[0]);
+          assert.deepEqual(response[1], value.apps[1]);
+          assert.ok(value.nodeInfo);
+          assert.equal(value.nodeInfo.addr, 'localhost:8042');
+          assert.equal(value.nodeInfo.id, 'localhost:64318');
+        }
+      );
+});

+ 61 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-container-test.js

@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-node-container', 'Unit | Route | NodeContainer', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting specific container on a node', function(assert) {
+  var response =
+      {id: "container_e32_1456000363780_0002_01_000001", state: "RUNNING",
+      exitCode:-1000,diagnostics:"",user:"root",totalMemoryNeededMB:2048,
+      totalVCoresNeeded:1,containerLogsLink: "http://localhost:8042/node/" +
+      "containerlogs/container_e32_1456000363780_0002_01_000001/root",
+      nodeId: "localhost:64318", containerLogFiles:["syslog","stderr",
+      "stdout"]};
+  var store = {
+    queryRecord: function(type, query) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response);
+      });
+    }
+  };
+  assert.expect(6);
+  var route = this.subject();
+  route.set('store', store);
+  var model =
+      route.model({node_id:"localhost:64318", node_addr:"localhost:8042",
+          container_id:"container_e32_1456000363780_0002_01_000001"}).
+      then(
+        function(value){
+          assert.ok(value);
+          assert.ok(value.nodeContainer);
+          assert.deepEqual(value.nodeContainer, response);
+          assert.ok(value.nodeInfo);
+          assert.equal(value.nodeInfo.addr, 'localhost:8042');
+          assert.equal(value.nodeInfo.id, 'localhost:64318');
+        }
+      );
+});

+ 68 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-containers-test.js

@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-node-containers', 'Unit | Route | NodeContainers', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting apps on a node', function(assert) {
+  var response =
+      [{id: "container_e32_1456000363780_0002_01_000001", state: "RUNNING",
+      exitCode:-1000,diagnostics:"",user:"root",totalMemoryNeededMB:2048,
+      totalVCoresNeeded:1,containerLogsLink: "http://localhost:8042/node/" +
+      "containerlogs/container_e32_1456000363780_0002_01_000001/root",
+      nodeId: "localhost:64318", containerLogFiles:["syslog","stderr",
+      "stdout"]},
+      {id:"container_e32_1456000363780_0002_01_000003", state:"RUNNING",
+      exitCode:-1000, diagnostics:"", user:"root", totalMemoryNeededMB:1024,
+      totalVCoresNeeded:1,containerLogsLink:"http://localhost:8042/node" +
+      "/containerlogs/container_e32_1456000363780_0002_01_000003/root",
+      nodeId:"localhost:64318",containerLogFiles:["syslog","stderr",
+      "syslog.shuffle","stdout"]}];
+  var store = {
+    query: function(type, query) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response.slice());
+      });
+    }
+  };
+  assert.expect(8);
+  var route = this.subject();
+  route.set('store', store);
+  var model =
+      route.model({node_id:"localhost:64318", node_addr:"localhost:8042"}).
+      then(
+        function(value){
+          assert.ok(value);
+          assert.ok(value.containers);
+          assert.equal(value.containers.length, 2);
+          assert.deepEqual(value.containers[0], response[0]);
+          assert.deepEqual(value.containers[1], response[1]);
+          assert.ok(value.nodeInfo);
+          assert.equal(value.nodeInfo.addr, 'localhost:8042');
+          assert.equal(value.nodeInfo.id, 'localhost:64318');
+        }
+      );
+});

+ 84 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-node-test.js

@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+import Ember from 'ember';
+
+moduleFor('route:yarn-node', 'Unit | Route | Node', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting a node', function(assert) {
+  var nodeResponse =
+      {healthReport: "Healthy", totalVmemAllocatedContainersMB: 344064,
+      totalPmemAllocatedContainersMB: 163840,
+      totalVCoresAllocatedContainers: 160,
+      vmemCheckEnabled: true, pmemCheckEnabled: true,
+      lastNodeUpdateTime: 1456250210310, nodeHealthy: true,
+      nodeManagerVersion: "3.0.0-SNAPSHOT",
+      nodeManagerBuildVersion: "3.0.0-SNAPSHOT",
+      nodeManagerVersionBuiltOn: "2000-01-01T00:00Z",
+      hadoopVersion: "3.0.0-SNAPSHOT",
+      hadoopBuildVersion: "3.0.0-SNAPSHOT",
+      hadoopVersionBuiltOn: "2000-01-01T00:00Z",
+      id: "localhost:64318", nodeHostName: "192.168.0.102",
+      nmStartupTime: 1456250208231};
+  var rmNodeResponse =
+      {rack: "/default-rack", state: "RUNNING", id: "localhost:64318",
+      nodeHostName: "localhost", nodeHTTPAddress: "localhost:8042",
+      lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+      healthReport: "", numContainers: 0, usedMemoryMB: 0,
+      availMemoryMB: 163840, usedVirtualCores: 0,
+      availableVirtualCores: 160,
+      resourceUtilization: {
+      nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+      nodeCPUUsage: 0.14995001256465912,
+      aggregatedContainersPhysicalMemoryMB: 0,
+      aggregatedContainersVirtualMemoryMB: 0,
+      containersCPUUsage: 0
+      }};
+
+  // Create store which returns appropriate responses.
+  var store = {
+    findRecord: function(type) {
+      if (type == 'yarnNode') {
+        return new Ember.RSVP.Promise(function(resolve) {
+          resolve(nodeResponse);
+        });
+      } else if (type == 'yarnRmNode') {
+        return new Ember.RSVP.Promise(function(resolve) {
+          resolve(rmNodeResponse);
+        });
+      }
+    }
+  };
+  var route = this.subject();
+  assert.expect(4);
+  route.set('store', store);
+  var model = route.model(
+      {node_addr:"localhost:8042", node_id:"localhost:64318"})._result;
+  assert.ok(model.node);
+  assert.deepEqual(model.node, nodeResponse);
+  assert.ok(model.rmNode);
+  assert.deepEqual(model.rmNode, rmNodeResponse);
+});

+ 74 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/routes/yarn-nodes-test.js

@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+import Ember from 'ember';
+
+moduleFor('route:yarn-nodes', 'Unit | Route | Nodes', {
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+  assert.ok(route.model);
+});
+
+test('Test getting nodes', function(assert) {
+  var response = [{
+      rack: "/default-rack", state: "RUNNING", id: "192.168.1.1:64318",
+      nodeHostName: "192.168.1.1", nodeHTTPAddress: "192.168.1.1:8042",
+      lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+      healthReport: "", numContainers: 0, usedMemoryMB: 0,
+      availMemoryMB: 163840, usedVirtualCores: 0,
+      availableVirtualCores: 160,
+      resourceUtilization: {
+        nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+        nodeCPUUsage: 0.14995001256465912,
+        aggregatedContainersPhysicalMemoryMB: 0,
+        aggregatedContainersVirtualMemoryMB: 0,
+        containersCPUUsage: 0
+      }},
+      {rack: "/default-rack", state: "RUNNING", id: "192.168.1.2:64318",
+      nodeHostName: "192.168.1.2", nodeHTTPAddress: "192.168.1.2:8042",
+      lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+      healthReport: "", numContainers: 0, usedMemoryMB: 0,
+      availMemoryMB: 163840, usedVirtualCores: 0,
+      availableVirtualCores: 160,
+      resourceUtilization: {
+        nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+        nodeCPUUsage: 0.14995001256465912,
+        aggregatedContainersPhysicalMemoryMB: 0,
+        aggregatedContainersVirtualMemoryMB: 0,
+        containersCPUUsage: 0
+      }}];
+  var store = {
+    findAll: function(type) {
+      return new Ember.RSVP.Promise(function(resolve) {
+        resolve(response);
+      });
+    }
+  };
+  var route = this.subject();
+  route.set('store', store);
+  var model = route.model()._result;
+  assert.expect(4);
+  assert.ok(model);
+  assert.equal(model.length, 2);
+  assert.deepEqual(response[0], model[0]);
+  assert.deepEqual(response[1], model[1]);
+});

+ 49 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-container-log-test.js

@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('serializer:yarn-container-log', 'Unit | Serializer | ContainerLog', {
+});
+
+test('Basic creation test', function(assert) {
+  let serializer = this.subject();
+
+  assert.ok(serializer);
+  assert.ok(serializer.normalizeSingleResponse);
+});
+
+test('normalizeSingleResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-container-log"
+  },
+  payload = "This is syslog";
+  var id = "localhost:64318!container_e32_1456000363780_0002_01_000001!syslog";
+  assert.expect(6);
+  var response =
+      serializer.normalizeSingleResponse({}, modelClass, payload, id, null);
+  assert.ok(response.data);
+  assert.equal(response.data.id, id);
+  assert.equal(response.data.type, modelClass.modelName);
+  assert.equal(response.data.attributes.logs, payload);
+  assert.equal(response.data.attributes.containerID,
+      "container_e32_1456000363780_0002_01_000001");
+  assert.equal(response.data.attributes.logFileName, "syslog");
+});
+

+ 102 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-app-test.js

@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('serializer:yarn-node-app', 'Unit | Serializer | NodeApp', {
+});
+
+test('Basic creation test', function(assert) {
+  let serializer = this.subject();
+
+  assert.ok(serializer);
+  assert.ok(serializer.normalizeSingleResponse);
+  assert.ok(serializer.normalizeArrayResponse);
+  assert.ok(serializer.internalNormalizeSingleResponse);
+});
+
+test('normalizeArrayResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-app"
+  },
+  payload = {
+    apps: {
+      app: [{
+        id:"application_1456251210105_0001", state:"FINISHED", user:"root"
+      },{
+        id:"application_1456251210105_0002", state:"RUNNING",user:"root",
+        containerids:["container_e38_1456251210105_0002_01_000001",
+        "container_e38_1456251210105_0002_01_000002"]
+      }]
+    }
+  };
+  assert.expect(15);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 2);
+  assert.equal(response.data[0].attributes.containers, undefined);
+  assert.equal(response.data[1].attributes.containers.length, 2);
+  assert.deepEqual(response.data[1].attributes.containers,
+      payload.apps.app[1].containerids);
+  for (var i = 0; i < 2; i++) {
+    assert.equal(response.data[i].type, modelClass.modelName);
+    assert.equal(response.data[i].id, payload.apps.app[i].id);
+    assert.equal(response.data[i].attributes.appId, payload.apps.app[i].id);
+    assert.equal(response.data[i].attributes.state, payload.apps.app[i].state);
+    assert.equal(response.data[i].attributes.user, payload.apps.app[i].user);
+  }
+});
+
+test('normalizeArrayResponse no apps test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-app"
+  },
+  payload = { apps: null };
+  assert.expect(5);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 1);
+  assert.equal(response.data[0].type, modelClass.modelName);
+  assert.equal(response.data[0].id, "dummy");
+  assert.equal(response.data[0].attributes.appId, undefined);
+});
+
+test('normalizeSingleResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-app"
+  },
+  payload = {
+    app: {id:"application_1456251210105_0001", state:"FINISHED", user:"root"}
+  };
+  assert.expect(7);
+  var response =
+      serializer.normalizeSingleResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(payload.app.id, response.data.id);
+  assert.equal(modelClass.modelName, response.data.type);
+  assert.equal(payload.app.id, response.data.attributes.appId);
+  assert.equal(payload.app.state, response.data.attributes.state);
+  assert.equal(payload.app.user, response.data.attributes.user);
+  assert.equal(response.data.attributes.containers, undefined);
+});
+

+ 128 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-container-test.js

@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('serializer:yarn-node-container', 'Unit | Serializer | NodeContainer', {
+});
+
+test('Basic creation test', function(assert) {
+  let serializer = this.subject();
+
+  assert.ok(serializer);
+  assert.ok(serializer.normalizeSingleResponse);
+  assert.ok(serializer.normalizeArrayResponse);
+  assert.ok(serializer.internalNormalizeSingleResponse);
+});
+
+test('normalizeArrayResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-container"
+  },
+  payload = {
+    containers: {
+      container: [{
+        id: "container_e32_1456000363780_0002_01_000001", state: "RUNNING",
+        exitCode:-1000,diagnostics:"",user:"root",totalMemoryNeededMB:2048,
+        totalVCoresNeeded:1,containerLogsLink: "http://localhost:8042/node/" +
+        "containerlogs/container_e32_1456000363780_0002_01_000001/root",
+        nodeId: "localhost:64318", containerLogFiles:["syslog","stderr",
+        "stdout"]
+      },{
+        id:"container_e32_1456000363780_0002_01_000003", state:"RUNNING",
+        exitCode:-1000, diagnostics:"", user:"root", totalMemoryNeededMB:1024,
+        totalVCoresNeeded:1,containerLogsLink:"http://localhost:8042/node" +
+        "/containerlogs/container_e32_1456000363780_0002_01_000003/root",
+        nodeId:"localhost:64318",containerLogFiles:["syslog","stderr",
+        "syslog.shuffle","stdout"]
+      }]
+    }
+  };
+  assert.expect(14);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 2);
+  assert.equal(response.data[0].id,
+      "container_e32_1456000363780_0002_01_000001");
+  assert.equal(response.data[1].id,
+      "container_e32_1456000363780_0002_01_000003");
+  assert.equal(response.data[0].attributes.containerLogFiles.length, 3);
+  assert.equal(response.data[1].attributes.containerLogFiles.length, 4);
+  for (var i = 0; i < 2; i++) {
+    assert.equal(response.data[i].type, modelClass.modelName);
+    assert.deepEqual(response.data[i].attributes.containerLogFiles,
+        payload.containers.container[i].containerLogFiles);
+    assert.equal(response.data[i].attributes.state,
+        payload.containers.container[i].state);
+    assert.equal(response.data[i].attributes.user,
+        payload.containers.container[i].user);
+  }
+});
+
+test('normalizeArrayResponse no containers test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-container"
+  },
+  payload = { containers: null };
+  assert.expect(5);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 1);
+  assert.equal(response.data[0].type, modelClass.modelName);
+  assert.equal(response.data[0].id, "dummy");
+  assert.equal(response.data[0].attributes.containerId, undefined);
+});
+
+test('normalizeSingleResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node-container"
+  },
+  payload = {
+    container: {
+      id: "container_e32_1456000363780_0002_01_000001", state: "RUNNING",
+      exitCode:-1000,diagnostics:"",user:"root",totalMemoryNeededMB:2048,
+      totalVCoresNeeded:1,containerLogsLink: "http://localhost:8042/node/" +
+      "containerlogs/container_e32_1456000363780_0002_01_000001/root",
+      nodeId: "localhost:64318", containerLogFiles:["syslog","stderr",
+      "stdout"]
+    }
+  };
+  assert.expect(11);
+  var response =
+      serializer.normalizeSingleResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.id, payload.container.id);
+  assert.equal(response.data.type, modelClass.modelName);
+  assert.equal(response.data.attributes.containerId, payload.container.id);
+  assert.equal(response.data.attributes.state, payload.container.state);
+  assert.equal(response.data.attributes.user, payload.container.user);
+  assert.equal(response.data.attributes.exitCode, payload.container.exitCode);
+  assert.equal(response.data.attributes.totalMemoryNeededMB,
+      payload.container.totalMemoryNeeded);
+  assert.equal(response.data.attributes.totalVCoresNeeded,
+      payload.container.totalVCoresNeeded);
+  assert.equal(response.data.attributes.containerLogFiles.length, 3);
+  assert.deepEqual(response.data.attributes.containerLogFiles,
+      payload.container.containerLogFiles);
+});
+

+ 69 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-node-test.js

@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+import Converter from 'yarn-ui/utils/converter';
+
+moduleFor('serializer:yarn-node', 'Unit | Serializer | Node', {
+});
+
+test('Basic creation test', function(assert) {
+  let serializer = this.subject();
+
+  assert.ok(serializer);
+  assert.ok(serializer.normalizeSingleResponse);
+  assert.ok(serializer.internalNormalizeSingleResponse);
+});
+
+test('normalizeSingleResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-node"
+  },
+  payload = {
+    nodeInfo: {
+      healthReport: "Healthy", totalVmemAllocatedContainersMB: 344064,
+      totalPmemAllocatedContainersMB: 163840,
+      totalVCoresAllocatedContainers: 160,
+      vmemCheckEnabled: true, pmemCheckEnabled: true,
+      lastNodeUpdateTime: 1456250210310, nodeHealthy: true,
+      nodeManagerVersion: "3.0.0-SNAPSHOT",
+      nodeManagerBuildVersion: "3.0.0-SNAPSHOT",
+      nodeManagerVersionBuiltOn: "2000-01-01T00:00Z",
+      hadoopVersion: "3.0.0-SNAPSHOT",
+      hadoopBuildVersion: "3.0.0-SNAPSHOT",
+      hadoopVersionBuiltOn: "2000-01-01T00:00Z",
+      id: "localhost:64318", nodeHostName: "192.168.0.102",
+      nmStartupTime: 1456250208231
+    }
+  };
+  assert.expect(6);
+  var id = "localhost:64318";
+  var response = serializer.normalizeSingleResponse({}, modelClass, payload, id, null);
+  assert.equal(response.data.id, id);
+  assert.equal(response.data.type, modelClass.modelName);
+  assert.equal(response.data.attributes.totalVmemAllocatedContainersMB,
+      payload.nodeInfo.totalVmemAllocatedContainersMB);
+  assert.equal(response.data.attributes.totalPmemAllocatedContainersMB,
+      payload.nodeInfo.totalPmemAllocatedContainersMB);
+  assert.equal(response.data.attributes.totalVCoresAllocatedContainers,
+      payload.nodeInfo.totalVCoresAllocatedContainers);
+  assert.equal(response.data.attributes.nmStartupTime,
+      Converter.timeStampToDate(payload.nodeInfo.nmStartupTime));
+});
+

+ 153 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/serializers/yarn-rm-node-test.js

@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('serializer:yarn-rm-node', 'Unit | Serializer | RMNode', {
+});
+
+test('Basic creation test', function(assert) {
+  let serializer = this.subject();
+
+  assert.ok(serializer);
+  assert.ok(serializer.normalizeSingleResponse);
+  assert.ok(serializer.normalizeArrayResponse);
+  assert.ok(serializer.internalNormalizeSingleResponse);
+});
+
+test('normalizeArrayResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-rm-node"
+  },
+  payload = {
+    nodes: {
+      node: [{
+        rack: "/default-rack", state: "RUNNING", id: "192.168.1.1:64318",
+        nodeHostName: "192.168.1.1", nodeHTTPAddress: "192.168.1.1:8042",
+        lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+        healthReport: "", numContainers: 0, usedMemoryMB: 2048,
+        availMemoryMB: 161792, usedVirtualCores: 2,
+        availableVirtualCores: 158, nodeLabels: ["x"],
+        resourceUtilization: {
+          nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+          nodeCPUUsage: 0.14995001256465912,
+          aggregatedContainersPhysicalMemoryMB: 0,
+          aggregatedContainersVirtualMemoryMB: 0,
+          containersCPUUsage: 0
+        }
+      },{
+        rack: "/default-rack", state: "RUNNING", id: "192.168.1.2:64318",
+        nodeHostName: "192.168.1.2", nodeHTTPAddress: "192.168.1.2:8042",
+        lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+        healthReport: "", numContainers: 0, usedMemoryMB: 0,
+        availMemoryMB: 163840, usedVirtualCores: 0,
+        availableVirtualCores: 160, nodeLabels: ["y"],
+        resourceUtilization: {
+          nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+          nodeCPUUsage: 0.14995001256465912,
+          aggregatedContainersPhysicalMemoryMB: 0,
+          aggregatedContainersVirtualMemoryMB: 0,
+          containersCPUUsage: 0
+        }
+      }]
+    }
+  };
+  assert.expect(12);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 2);
+  assert.equal(response.data[0].id, "192.168.1.1:64318");
+  assert.equal(response.data[1].id, "192.168.1.2:64318");
+  for (var i = 0; i < 2; i++) {
+    assert.equal(response.data[i].type, modelClass.modelName);
+    assert.equal(response.data[i].attributes.nodeHostName,
+        payload.nodes.node[i].nodeHostName);
+    assert.equal(response.data[i].attributes.nodeHTTPAddress,
+        payload.nodes.node[i].nodeHTTPAddress);
+    assert.deepEqual(response.data[i].attributes.nodeLabels,
+        payload.nodes.node[i].nodeLabels);
+  }
+});
+
+test('normalizeArrayResponse no nodes test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-rm-node"
+  },
+  payload = { nodes: null };
+  assert.expect(5);
+  var response =
+      serializer.normalizeArrayResponse({}, modelClass, payload, null, null);
+  console.log(response);
+  assert.ok(response.data);
+  assert.equal(response.data.length, 1);
+  assert.equal(response.data[0].type, modelClass.modelName);
+  assert.equal(response.data[0].id, "dummy");
+  assert.equal(response.data[0].attributes.nodeHostName, undefined);
+});
+
+test('normalizeSingleResponse test', function(assert) {
+  let serializer = this.subject(),
+  modelClass = {
+    modelName: "yarn-rm-node"
+  },
+  payload = {
+    node: {
+      rack: "/default-rack", state: "RUNNING", id: "192.168.1.1:64318",
+      nodeHostName: "192.168.1.1", nodeHTTPAddress: "192.168.1.1:8042",
+      lastHealthUpdate: 1456251290905, version: "3.0.0-SNAPSHOT",
+      healthReport: "", numContainers: 0, usedMemoryMB: 2048,
+      availMemoryMB: 161792, usedVirtualCores: 2,
+      availableVirtualCores: 158, nodeLabels: ["x"],
+      resourceUtilization: {
+        nodePhysicalMemoryMB: 4549, nodeVirtualMemoryMB: 4549,
+        nodeCPUUsage: 0.14995001256465912,
+        aggregatedContainersPhysicalMemoryMB: 0,
+        aggregatedContainersVirtualMemoryMB: 0,
+        containersCPUUsage: 0
+      }
+    }
+  };
+  assert.expect(13);
+  var id = "localhost:64318";
+  var response =
+      serializer.normalizeSingleResponse({}, modelClass, payload, id, null);
+  assert.ok(response.data);
+  assert.equal(response.data.id, id);
+  assert.equal(response.data.type, modelClass.modelName);
+  assert.equal(response.data.attributes.rack, payload.node.rack);
+  assert.equal(response.data.attributes.state, payload.node.state);
+  assert.equal(response.data.attributes.nodeHostName,
+      payload.node.nodeHostName);
+  assert.equal(response.data.attributes.nodeHTTPAddress,
+      payload.node.nodeHTTPAddress);
+  assert.equal(response.data.attributes.version, payload.node.version);
+  assert.equal(response.data.attributes.availMemoryMB,
+      payload.node.availMemoryMB);
+  assert.equal(response.data.attributes.usedMemoryMB,
+      payload.node.usedMemoryMB);
+  assert.equal(response.data.attributes.availableVirtualCores,
+      payload.node.availableVirtualCores);
+  assert.equal(response.data.attributes.usedVirtualCores,
+      payload.node.usedVirtualCores);
+  assert.deepEqual(response.data.attributes.nodeLabels,
+      payload.node.nodeLabels);
+});
+

+ 45 - 3
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/utils/converter-test.js

@@ -1,10 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 import converter from '../../../utils/converter';
 import { module, test } from 'qunit';
 
-module('Unit | Utility | converter');
+module('Unit | Utility | Converter');
 
 // Replace this with your real tests.
 test('it works', function(assert) {
-  var result = converter();
-  assert.ok(result);
+  assert.ok(converter);
+  assert.ok(converter.splitForContainerLogs);
+});
+
+test('split for container logs', function(assert) {
+  var id = "localhost:64318!container_e32_1456000363780_0002_01_000001!" +
+      "syslog";
+  var arr = converter.splitForContainerLogs(id);
+  assert.ok(arr);
+  assert.deepEqual(arr, ["localhost:64318",
+      "container_e32_1456000363780_0002_01_000001", "syslog"]);
+  id = "localhost:64318!container_e32_1456000363780_0002_01_000001!" +
+      "syslog!logs";
+  arr = converter.splitForContainerLogs(id);
+  assert.ok(arr);
+  assert.deepEqual(arr, ["localhost:64318",
+      "container_e32_1456000363780_0002_01_000001", "syslog!logs"]);
+  id = "localhost:64318!container_e32_1456000363780_0002_01_000001";
+  arr = converter.splitForContainerLogs(id);
+  assert.notOk(arr);
+  id = null;
+  arr = converter.splitForContainerLogs(id);
+  assert.notOk(arr);
+  id = undefined;
+  arr = converter.splitForContainerLogs(id);
+  assert.notOk(arr);
 });

+ 26 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/tests/unit/utils/sorter-test.js

@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Sorter from 'yarn-ui/utils/sorter';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | Sorter');
+
+test('Basic creation test', function(assert) {
+  assert.ok(Sorter);
+});