You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/02/24 23:35:02 UTC

[25/45] tez git commit: TEZ-3050. Tez UI 2: Add counter columns (sree)

TEZ-3050. Tez UI 2: Add counter columns (sree)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/8a4f4276
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/8a4f4276
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/8a4f4276

Branch: refs/heads/TEZ-2980
Commit: 8a4f4276c5b38416be36ed1124e1ebd97d16a8f9
Parents: 0798c90
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Wed Jan 20 20:55:06 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Thu Feb 25 03:32:16 2016 +0530

----------------------------------------------------------------------
 TEZ-2980-CHANGES.txt                            |   1 +
 .../webapp/app/components/column-selector.js    |  13 +-
 .../src/main/webapp/app/controllers/app/dags.js |   3 +
 tez-ui2/src/main/webapp/app/controllers/dags.js |   3 +
 .../main/webapp/app/controllers/table-page.js   |  21 +-
 .../webapp/app/controllers/task/attempts.js     |   4 +-
 .../webapp/app/controllers/vertex/attempts.js   |   4 +-
 .../main/webapp/app/controllers/vertex/tasks.js |   4 +-
 .../webapp/app/mixins/auto-counter-column.js    |  69 +++++
 .../main/webapp/app/styles/column-selector.less |  28 +-
 .../templates/components/column-selector.hbs    |  30 ++-
 .../app/utils/counter-column-definition.js      |  97 +++++++
 tez-ui2/src/main/webapp/app/utils/misc.js       |   3 +-
 .../src/main/webapp/config/default-app-conf.js  | 266 +++++++++++++++++++
 .../tests/unit/controllers/app/dags-test.js     |   1 +
 .../webapp/tests/unit/controllers/dags-test.js  |   3 +-
 .../tests/unit/controllers/table-page-test.js   |  17 ++
 .../unit/mixins/auto-counter-column-test.js     |  64 +++++
 .../utils/counter-column-definition-test.js     | 124 +++++++++
 19 files changed, 720 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/TEZ-2980-CHANGES.txt
----------------------------------------------------------------------
diff --git a/TEZ-2980-CHANGES.txt b/TEZ-2980-CHANGES.txt
index f92341a..6a2b343 100644
--- a/TEZ-2980-CHANGES.txt
+++ b/TEZ-2980-CHANGES.txt
@@ -22,3 +22,4 @@ ALL CHANGES:
   TEZ-3042. Tez UI 2: Create Counters pages
   TEZ-3043. Tez UI 2: Create configurations page
   TEZ-3049. Tez UI 2: Add column selector
+  TEZ-3050. Tez UI 2: Add counter columns

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/components/column-selector.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/column-selector.js b/tez-ui2/src/main/webapp/app/components/column-selector.js
index 9c9d4d2..8f9ac13 100644
--- a/tez-ui2/src/main/webapp/app/components/column-selector.js
+++ b/tez-ui2/src/main/webapp/app/components/column-selector.js
@@ -34,11 +34,11 @@ export default Ember.Component.extend({
         highlight = false,
         visibleColumnIDs = this.get('content.visibleColumnIDs') || {};
 
-    return this.get('content.columns').map(function (config) {
+    return this.get('content.columns').map(function (definition) {
       var css = '';
 
-      highlight = highlight ^ (config.get("counterGroupName") !== group);
-      group = config.counterGroupName;
+      highlight = highlight ^ (Ember.get(definition, "counterGroupName") !== group);
+      group = Ember.get(definition, "counterGroupName");
 
       if(highlight) {
         css += ' highlight';
@@ -48,10 +48,10 @@ export default Ember.Component.extend({
       }
 
       return Ember.Object.create({
-        id: config.get("id"),
-        displayText: config.get("headerTitle"),
+        id: Ember.get(definition, "id"),
+        displayText: Ember.get(definition, "headerTitle"),
         css: css,
-        selected: visibleColumnIDs[config.id]
+        selected: visibleColumnIDs[Ember.get(definition, "id")]
       });
     });
   }),
@@ -71,7 +71,6 @@ export default Ember.Component.extend({
 
   selectedColumnIDs: Ember.computed("options", function () {
     var columnIds = {};
-
     this.get('options').forEach(function (option) {
       columnIds[option.get("id")] = option.get('selected');
     });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/app/dags.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/app/dags.js b/tez-ui2/src/main/webapp/app/controllers/app/dags.js
index bcb4db8..04f8f43 100644
--- a/tez-ui2/src/main/webapp/app/controllers/app/dags.js
+++ b/tez-ui2/src/main/webapp/app/controllers/app/dags.js
@@ -95,4 +95,7 @@ export default TablePageController.extend({
     }
   }]),
 
+  getCounterColumns: function () {
+    return this._super().concat(this.get('env.app.tables.defaultColumns.dagCounters'));
+  }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/dags.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/dags.js b/tez-ui2/src/main/webapp/app/controllers/dags.js
index fced7d8..7d5f1d9 100644
--- a/tez-ui2/src/main/webapp/app/controllers/dags.js
+++ b/tez-ui2/src/main/webapp/app/controllers/dags.js
@@ -105,4 +105,7 @@ export default TablePageController.extend({
     }
   }]),
 
+  getCounterColumns: function () {
+    return this._super().concat(this.get('env.app.tables.defaultColumns.dagCounters'));
+  }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/table-page.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/table-page.js b/tez-ui2/src/main/webapp/app/controllers/table-page.js
index 09ae8ab..12f4715 100644
--- a/tez-ui2/src/main/webapp/app/controllers/table-page.js
+++ b/tez-ui2/src/main/webapp/app/controllers/table-page.js
@@ -23,6 +23,8 @@ import PageController from './page';
 import TableDefinition from 'em-table/utils/table-definition';
 import isIOCounter from '../utils/misc';
 
+import CounterColumnDefinition from '../utils/counter-column-definition';
+
 var MoreObject = more.Object;
 
 export default PageController.extend({
@@ -33,6 +35,8 @@ export default PageController.extend({
   sortOrder: "",
   pageNo: 1,
 
+  columns: [],
+
   headerComponentNames: ['em-table-search-ui', 'table-controls', 'em-table-pagination-ui'],
 
   visibleColumnIDs: {},
@@ -65,13 +69,24 @@ export default PageController.extend({
     this.set('visibleColumnIDs', visibleColumnIDs);
   })),
 
-  visibleColumns: Ember.computed('visibleColumnIDs', 'columns', function() {
+  allColumns: Ember.computed("columns", function () {
+    var columns = this.get("columns"),
+        counters = this.getCounterColumns();
+
+    return columns.concat(CounterColumnDefinition.make(counters));
+  }),
+
+  visibleColumns: Ember.computed('visibleColumnIDs', 'allColumns', function() {
     var visibleColumnIDs = this.visibleColumnIDs;
-    return this.get('columns').filter(function (column) {
+    return this.get('allColumns').filter(function (column) {
       return visibleColumnIDs[column.get("id")];
     });
   }),
 
+  getCounterColumns: function () {
+    return this.get('env.app.tables.defaultColumns.counters');
+  },
+
   actions: {
     searchChanged: function (searchText) {
       this.set("searchText", searchText);
@@ -97,7 +112,7 @@ export default PageController.extend({
         targetObject: this,
         content: {
           message: this.get('columnSelectorMessage'),
-          columns: this.get('columns'),
+          columns: this.get('allColumns'),
           visibleColumnIDs: this.get('visibleColumnIDs')
         }
       });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/task/attempts.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/task/attempts.js b/tez-ui2/src/main/webapp/app/controllers/task/attempts.js
index 5b97033..d7bec55 100644
--- a/tez-ui2/src/main/webapp/app/controllers/task/attempts.js
+++ b/tez-ui2/src/main/webapp/app/controllers/task/attempts.js
@@ -19,7 +19,9 @@
 import TablePageController from '../table-page';
 import ColumnDefinition from 'em-table/utils/column-definition';
 
-export default TablePageController.extend({
+import AutoCounterColumn from '../../mixins/auto-counter-column';
+
+export default TablePageController.extend(AutoCounterColumn, {
   breadcrumbs: [{
     text: "Task Attempts",
     routeName: "task.attempts",

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/vertex/attempts.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/vertex/attempts.js b/tez-ui2/src/main/webapp/app/controllers/vertex/attempts.js
index b6a4389..b0fb983 100644
--- a/tez-ui2/src/main/webapp/app/controllers/vertex/attempts.js
+++ b/tez-ui2/src/main/webapp/app/controllers/vertex/attempts.js
@@ -19,7 +19,9 @@
 import TablePageController from '../table-page';
 import ColumnDefinition from 'em-table/utils/column-definition';
 
-export default TablePageController.extend({
+import AutoCounterColumn from '../../mixins/auto-counter-column';
+
+export default TablePageController.extend(AutoCounterColumn, {
   breadcrumbs: [{
     text: "Task Attempts",
     routeName: "vertex.attempts",

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/controllers/vertex/tasks.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/vertex/tasks.js b/tez-ui2/src/main/webapp/app/controllers/vertex/tasks.js
index f35a022..350d246 100644
--- a/tez-ui2/src/main/webapp/app/controllers/vertex/tasks.js
+++ b/tez-ui2/src/main/webapp/app/controllers/vertex/tasks.js
@@ -19,7 +19,9 @@
 import TablePageController from '../table-page';
 import ColumnDefinition from 'em-table/utils/column-definition';
 
-export default TablePageController.extend({
+import AutoCounterColumn from '../../mixins/auto-counter-column';
+
+export default TablePageController.extend(AutoCounterColumn, {
   breadcrumbs: [{
     text: "Tasks",
     routeName: "vertex.tasks",

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/mixins/auto-counter-column.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/mixins/auto-counter-column.js b/tez-ui2/src/main/webapp/app/mixins/auto-counter-column.js
new file mode 100644
index 0000000..7820e4a
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/mixins/auto-counter-column.js
@@ -0,0 +1,69 @@
+/*global more*/
+
+/**
+ * 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';
+
+var MoreObject = more.Object;
+
+export default Ember.Mixin.create({
+  columnSelectorMessage: "<span class='per-io'>Per-IO counter</span> selection wouldn't persist.",
+
+  getCounterColumns: function () {
+    var columns = [],
+        records = this.get("model"),
+        counterHash = {};
+
+    this._super().forEach(function (column) {
+      var groupHash =
+          counterHash[column.counterGroupName] =
+          counterHash[column.counterGroupName] || {};
+      groupHash[column.counterName] = column.counterName;
+    });
+
+    if(records) {
+      records.forEach(function (record) {
+        let counterGroups = Ember.get(record, 'counterGroups');
+
+        if(counterGroups) {
+          counterGroups.forEach(function (group) {
+            var groupHash =
+                counterHash[group.counterGroupName] =
+                counterHash[group.counterGroupName] || {};
+
+            group.counters.forEach(function (counter) {
+              groupHash[counter.counterName] = counter.counterName;
+            });
+          });
+        }
+      });
+    }
+
+    MoreObject.forEach(counterHash, function (groupName, counters) {
+      MoreObject.forEach(counters, function (counterName) {
+        columns.push({
+          counterName: counterName,
+          counterGroupName: groupName
+        });
+      });
+    });
+
+    return columns;
+  }
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/styles/column-selector.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/column-selector.less b/tez-ui2/src/main/webapp/app/styles/column-selector.less
index 2e39995..3e061e1 100644
--- a/tez-ui2/src/main/webapp/app/styles/column-selector.less
+++ b/tez-ui2/src/main/webapp/app/styles/column-selector.less
@@ -16,19 +16,35 @@
  * limitations under the License.
  */
 
+@import "../../bower_components/snippet-ss/less/force";
+@import "../../bower_components/snippet-ss/less/effects";
+
 .column-selector {
   .message {
-    text-align: right;
-    font-size: 10px;
+    position: absolute;
+    font-size: 12px;
+    padding: 1px;
+    left: 15px;
+  }
+
+  .highlight {
+    background-color: @bg-lite;
+  }
+  .per-io {
+    color: @text-green;
   }
 
   .selection-list {
     border-bottom: 1px solid @border-color;
 
-    .highlight {
-      background-color: @bg-lite;
+    .options {
+      .force-scrollbar;
+
+      max-height: 500px;
+      overflow: auto;
     }
-    .select-option, .search-option {
+
+    .select-option, .filter-option {
       border-top: 1px dotted @border-color;
       padding: 5px 15px;
 
@@ -40,7 +56,7 @@
         vertical-align: middle;
       }
     }
-    .search-option {
+    .filter-option {
       border: none;
 
       .form-group {

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/templates/components/column-selector.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/column-selector.hbs b/tez-ui2/src/main/webapp/app/templates/components/column-selector.hbs
index d9be633..a5dcf9c 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/column-selector.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/column-selector.hbs
@@ -16,11 +16,8 @@
  * limitations under the License.
 }}
 
-<div class="message">
-  {{{content.message}}}
-</div>
 <div class="selection-list">
-  <div class="search-option highlight">
+  <div class="filter-option highlight">
     <div class="form-group">
       {{input class="form-control" placeholder="Filter..." value=searchText}}
     </div>
@@ -31,18 +28,23 @@
       &nbsp;Select All
     </div>
   </div>
-  {{#if filteredOptions.length}}
-    {{#each filteredOptions as |option|}}
-      <div class="select-option {{option.css}}">
-        {{input type="checkbox" classNames='checkbox' checked=option.selected}}
-        {{option.displayText}}
-      </div>
-    {{/each}}
-  {{else}}
-    <h4>&nbsp;No options available...</h4>
-  {{/if}}
+  <div class="options">
+    {{#if filteredOptions.length}}
+      {{#each filteredOptions as |option|}}
+        <div class="select-option {{option.css}}">
+          {{input type="checkbox" classNames='checkbox' checked=option.selected}}
+          {{option.displayText}}
+        </div>
+      {{/each}}
+    {{else}}
+      <h4>&nbsp;No options available...</h4>
+    {{/if}}
+  </div>
 </div>
 <div class="form-actions">
+  <span class="message">
+    {{{content.message}}}
+  </span>
   <button type="button" class="btn btn-primary" {{action "ok"}} data-dismiss="modal" aria-label="Close">Ok</button>
   <button type="button" class="btn" data-dismiss="modal" aria-label="Close">Cancel</button>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/utils/counter-column-definition.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/counter-column-definition.js b/tez-ui2/src/main/webapp/app/utils/counter-column-definition.js
new file mode 100644
index 0000000..be36053
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/utils/counter-column-definition.js
@@ -0,0 +1,97 @@
+/**
+ * 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 isIOCounter from '../utils/misc';
+import ColumnDefinition from 'em-table/utils/column-definition';
+
+/*
+ * Returns a counter value from for a row
+ * @param row
+ * @return value
+ */
+function getCounterContent(row) {
+  var counter = Ember.get(row, this.get("contentPath"));
+
+  if(counter) {
+    counter = counter[this.get("counterGroupName")];
+    if(counter) {
+      return counter[this.get("counterName")] || null;
+    }
+    return null;
+  }
+}
+
+var CounterColumnDefinition = ColumnDefinition.extend({
+  counterName: "",
+  counterGroupName: "",
+
+  observePath: true,
+  contentPath: "counterHash",
+
+  getCellContent: getCounterContent,
+  getSearchValue: getCounterContent,
+  getSortValue: getCounterContent,
+
+  id: Ember.computed("counterName", "counterGroupName", function () {
+    var groupName = this.get("counterGroupName"),
+        counterName = this.get("counterName");
+    return `${groupName}/${counterName}`;
+  }),
+
+  groupDisplayName: Ember.computed("counterGroupName", function () {
+    var displayName = this.get("counterGroupName");
+
+    // Prune dotted path
+    displayName = displayName.substr(displayName.lastIndexOf('.') + 1);
+
+    if(isIOCounter(displayName)) {
+      displayName = displayName.replace("_INPUT_", " to Input-");
+      displayName = displayName.replace("_OUTPUT_", " to Output-");
+    }
+
+    // Prune counter text
+    displayName = displayName.replace("Counter_", " - ");
+    displayName = displayName.replace("Counter", "");
+
+    return displayName;
+  }),
+
+  headerTitle: Ember.computed("groupDisplayName", "counterName", function () {
+    var groupName = this.get("groupDisplayName"),
+        counterName = this.get("counterName");
+    return `${groupName} - ${counterName}`;
+  }),
+});
+
+CounterColumnDefinition.make = function (rawDefinition) {
+  if(Array.isArray(rawDefinition)) {
+    return rawDefinition.map(function (def) {
+      return CounterColumnDefinition.create(def);
+    });
+  }
+  else if(typeof rawDefinition === 'object') {
+    return CounterColumnDefinition.create(rawDefinition);
+  }
+  else {
+    throw new Error("rawDefinition must be an Array or an Object.");
+  }
+};
+
+export default CounterColumnDefinition;

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/app/utils/misc.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/misc.js b/tez-ui2/src/main/webapp/app/utils/misc.js
index 5e2ecf1..93e2f0b 100644
--- a/tez-ui2/src/main/webapp/app/utils/misc.js
+++ b/tez-ui2/src/main/webapp/app/utils/misc.js
@@ -18,5 +18,6 @@
 
 export default function isIOCounter(name) {
   name = name.split('/')[0];
-  return name.match('_INPUT_') || name.match('_OUTPUT_');
+  name = name.substr(name.indexOf('_') + 1);
+  return !!(name.match('_INPUT_') || name.match('_OUTPUT_'));
 }

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/config/default-app-conf.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/config/default-app-conf.js b/tez-ui2/src/main/webapp/config/default-app-conf.js
index 400290a..200dc14 100644
--- a/tez-ui2/src/main/webapp/config/default-app-conf.js
+++ b/tez-ui2/src/main/webapp/config/default-app-conf.js
@@ -50,5 +50,271 @@ module.exports = { // Tez App configurations
   hrefs: {
     help: "https://tez.apache.org/tez_ui_user_data.html",
     license: "http://www.apache.org/licenses/LICENSE-2.0"
+  },
+
+  tables: {
+    defaultColumns: {
+      counters: [
+        // File System Counters
+        {
+          counterName: 'FILE_BYTES_READ',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'FILE_BYTES_WRITTEN',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'FILE_READ_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'FILE_LARGE_READ_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'FILE_WRITE_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'HDFS_BYTES_READ',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'HDFS_BYTES_WRITTEN',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'HDFS_READ_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'HDFS_LARGE_READ_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+        {
+          counterName: 'HDFS_WRITE_OPS',
+          counterGroupName: 'org.apache.tez.common.counters.FileSystemCounter',
+        },
+
+        // Task Counters
+        {
+          counterName: "NUM_SPECULATIONS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "REDUCE_INPUT_GROUPS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "REDUCE_INPUT_RECORDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SPLIT_RAW_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "COMBINE_INPUT_RECORDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SPILLED_RECORDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "NUM_SHUFFLED_INPUTS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "NUM_SKIPPED_INPUTS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "NUM_FAILED_SHUFFLE_INPUTS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "MERGED_MAP_OUTPUTS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "GC_TIME_MILLIS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "CPU_MILLISECONDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "PHYSICAL_MEMORY_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "VIRTUAL_MEMORY_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "COMMITTED_HEAP_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "INPUT_RECORDS_PROCESSED",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "OUTPUT_RECORDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "OUTPUT_LARGE_RECORDS",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "OUTPUT_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "OUTPUT_BYTES_WITH_OVERHEAD",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "OUTPUT_BYTES_PHYSICAL",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "ADDITIONAL_SPILLS_BYTES_WRITTEN",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "ADDITIONAL_SPILLS_BYTES_READ",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "ADDITIONAL_SPILL_COUNT",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_BYTES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_BYTES_DECOMPRESSED",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_BYTES_TO_MEM",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_BYTES_TO_DISK",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_BYTES_DISK_DIRECT",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "NUM_MEM_TO_DISK_MERGES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "NUM_DISK_TO_DISK_MERGES",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "SHUFFLE_PHASE_TIME",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "MERGE_PHASE_TIME",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "FIRST_EVENT_RECEIVED",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+        {
+          counterName: "LAST_EVENT_RECEIVED",
+          counterGroupName: "org.apache.tez.common.counters.TaskCounter",
+        },
+      ],
+
+      dagCounters: [
+        {
+          counterName :"NUM_FAILED_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"NUM_KILLED_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"NUM_SUCCEEDED_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"TOTAL_LAUNCHED_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"OTHER_LOCAL_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"DATA_LOCAL_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"RACK_LOCAL_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"SLOTS_MILLIS_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"FALLOW_SLOTS_MILLIS_TASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"TOTAL_LAUNCHED_UBERTASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"NUM_UBER_SUBTASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+        {
+          counterName :"NUM_FAILED_UBERTASKS",
+          counterGroupName :"org.apache.tez.common.counters.DAGCounter",
+        },
+
+        {
+          counterName: "REDUCE_OUTPUT_RECORDS",
+          counterGroupName: "REDUCE_OUTPUT_RECORDS",
+        },
+        {
+          counterName: "REDUCE_SKIPPED_GROUPS",
+          counterGroupName: "REDUCE_SKIPPED_GROUPS",
+        },
+        {
+          counterName: "REDUCE_SKIPPED_RECORDS",
+          counterGroupName: "REDUCE_SKIPPED_RECORDS",
+        },
+        {
+          counterName: "COMBINE_OUTPUT_RECORDS",
+          counterGroupName: "COMBINE_OUTPUT_RECORDS",
+        },
+        {
+          counterName: "SKIPPED_RECORDS",
+          counterGroupName: "SKIPPED_RECORDS",
+        },
+        {
+          counterName: "INPUT_GROUPS",
+          counterGroupName: "INPUT_GROUPS",
+        }
+      ]
+    }
   }
 };

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/tests/unit/controllers/app/dags-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/controllers/app/dags-test.js b/tez-ui2/src/main/webapp/tests/unit/controllers/app/dags-test.js
index c60fbb5..25afc63 100644
--- a/tez-ui2/src/main/webapp/tests/unit/controllers/app/dags-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/controllers/app/dags-test.js
@@ -34,4 +34,5 @@ test('Basic creation test', function(assert) {
   assert.ok(controller);
   assert.ok(controller.breadcrumbs);
   assert.ok(controller.columns);
+  assert.ok(controller.getCounterColumns);
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/tests/unit/controllers/dags-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/controllers/dags-test.js b/tez-ui2/src/main/webapp/tests/unit/controllers/dags-test.js
index 944f813..21dbc96 100644
--- a/tez-ui2/src/main/webapp/tests/unit/controllers/dags-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/controllers/dags-test.js
@@ -26,7 +26,7 @@ moduleFor('controller:dags', 'Unit | Controller | dags', {
 });
 
 test('Basic creation test', function(assert) {
-  assert.expect(2 + 2);
+  assert.expect(2 + 3);
 
   let controller = this.subject({
     initVisibleColumns: Ember.K,
@@ -38,4 +38,5 @@ test('Basic creation test', function(assert) {
 
   assert.ok(controller);
   assert.ok(controller.columns);
+  assert.ok(controller.getCounterColumns);
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/tests/unit/controllers/table-page-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/controllers/table-page-test.js b/tez-ui2/src/main/webapp/tests/unit/controllers/table-page-test.js
index 8aee8d7..07941b9 100644
--- a/tez-ui2/src/main/webapp/tests/unit/controllers/table-page-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/controllers/table-page-test.js
@@ -40,8 +40,25 @@ test('Basic creation test', function(assert) {
   assert.equal(controller.sortOrder, "");
   assert.equal(controller.pageNo, 1);
 
+  assert.ok(controller.headerComponentNames);
+  assert.ok(controller.visibleColumnIDs);
+  assert.ok(controller.columnSelectorTitle);
+  assert.ok(controller.definition);
+
+  assert.ok(controller.storageID);
+  assert.ok(controller.initVisibleColumns);
+
+  assert.ok(controller.columns);
+  assert.ok(controller.allColumns);
+  assert.ok(controller.visibleColumns);
+
+  assert.ok(controller.getCounterColumns);
+
   assert.ok(controller.actions.searchChanged);
   assert.ok(controller.actions.sortChanged);
   assert.ok(controller.actions.rowsChanged);
   assert.ok(controller.actions.pageChanged);
+
+  assert.ok(controller.actions.openColumnSelector);
+  assert.ok(controller.actions.columnsSelected);
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/tests/unit/mixins/auto-counter-column-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/mixins/auto-counter-column-test.js b/tez-ui2/src/main/webapp/tests/unit/mixins/auto-counter-column-test.js
new file mode 100644
index 0000000..191eb67
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/mixins/auto-counter-column-test.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 Ember from 'ember';
+import AutoCounterColumnMixin from '../../../mixins/auto-counter-column';
+import { module, test } from 'qunit';
+
+module('Unit | Mixin | auto counter column');
+
+test('Basic creation test', function(assert) {
+  let AutoCounterColumnObject = Ember.Object.extend(AutoCounterColumnMixin);
+  let subject = AutoCounterColumnObject.create();
+
+  assert.ok(subject);
+  assert.ok(subject.columnSelectorMessage);
+  assert.ok(subject.getCounterColumns);
+});
+
+test('getCounterColumns test', function(assert) {
+  let TestParent = Ember.Object.extend({
+    getCounterColumns: function () { return []; }
+  });
+
+  let AutoCounterColumnObject = TestParent.extend(AutoCounterColumnMixin);
+  let subject = AutoCounterColumnObject.create({
+    model: [{
+      counterGroups: [{
+        counterGroupName: "gp1",
+        counters: [{counterName: "c11"}, {counterName: "c12"}]
+      }]
+    }, {
+      counterGroups: [{
+        counterGroupName: "gp2",
+        counters: [{counterName: "c21"}, {counterName: "c22"}]
+      }]
+    }]
+  });
+
+  let columns = subject.getCounterColumns();
+  assert.equal(columns.length, 4);
+  assert.equal(columns[0].counterGroupName, "gp1");
+  assert.equal(columns[0].counterName, "c11");
+  assert.equal(columns[1].counterGroupName, "gp1");
+  assert.equal(columns[1].counterName, "c12");
+  assert.equal(columns[2].counterGroupName, "gp2");
+  assert.equal(columns[2].counterName, "c21");
+  assert.equal(columns[3].counterGroupName, "gp2");
+  assert.equal(columns[3].counterName, "c22");
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/8a4f4276/tez-ui2/src/main/webapp/tests/unit/utils/counter-column-definition-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/utils/counter-column-definition-test.js b/tez-ui2/src/main/webapp/tests/unit/utils/counter-column-definition-test.js
new file mode 100644
index 0000000..64df664
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/utils/counter-column-definition-test.js
@@ -0,0 +1,124 @@
+/**
+ * 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 CounterColumnDefinition from '../../../utils/counter-column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | counter column definition');
+
+test('Basic creation test', function(assert) {
+  let definition = CounterColumnDefinition.create();
+
+  assert.ok(definition);
+
+  assert.ok(definition.getCellContent);
+  assert.ok(definition.getSearchValue);
+  assert.ok(definition.getSortValue);
+
+  assert.ok(definition.id);
+  assert.ok(definition.groupDisplayName);
+  assert.ok(definition.headerTitle);
+
+  assert.ok(CounterColumnDefinition.make);
+
+  assert.equal(definition.observePath, true);
+  assert.equal(definition.contentPath, "counterHash");
+});
+
+test('getCellContent, getSearchValue & getSortValue test', function(assert) {
+  let testGroupName = "t.gn",
+      testCounterName = "cn",
+      testCounterValue = "val",
+      testContent = {},
+      testRow = {
+        counterHash: testContent
+      };
+
+  testContent[testGroupName] = {};
+  testContent[testGroupName][testCounterName] = testCounterValue;
+  testContent[testGroupName]["anotherName"] = "anotherValue";
+
+  let definition = CounterColumnDefinition.create({
+    counterGroupName: testGroupName,
+    counterName: testCounterName,
+  });
+
+  assert.equal(definition.getCellContent(testRow), testCounterValue);
+  assert.equal(definition.getSearchValue(testRow), testCounterValue);
+  assert.equal(definition.getSortValue(testRow), testCounterValue);
+});
+
+test('id test', function(assert) {
+  let testGroupName = "t.gn",
+      testCounterName = "cn";
+
+  let definition = CounterColumnDefinition.create({
+    counterGroupName: testGroupName,
+    counterName: testCounterName,
+  });
+
+  assert.equal(definition.get("id"), `${testGroupName}/${testCounterName}`);
+});
+
+test('groupDisplayName test', function(assert) {
+  let definition = CounterColumnDefinition.create();
+
+  definition.set("counterGroupName", "foo");
+  assert.equal(definition.get("groupDisplayName"), "foo");
+
+  definition.set("counterGroupName", "foo.bar");
+  assert.equal(definition.get("groupDisplayName"), "bar");
+
+  definition.set("counterGroupName", "org.apache.tez.common.counters.DAGCounter");
+  assert.equal(definition.get("groupDisplayName"), "DAG");
+
+  definition.set("counterGroupName", "org.apache.tez.common.counters.FileSystemCounter");
+  assert.equal(definition.get("groupDisplayName"), "FileSystem");
+
+  definition.set("counterGroupName", "TaskCounter_ireduce1_INPUT_map");
+  assert.equal(definition.get("groupDisplayName"), "Task - ireduce1 to Input-map");
+
+  definition.set("counterGroupName", "TaskCounter_ireduce1_OUTPUT_reduce");
+  assert.equal(definition.get("groupDisplayName"), "Task - ireduce1 to Output-reduce");
+});
+
+test('headerTitle test', function(assert) {
+  let testGroupName = "t.gn",
+      testCounterName = "cn";
+
+  let definition = CounterColumnDefinition.create({
+    counterGroupName: testGroupName,
+    counterName: testCounterName,
+  });
+
+  assert.equal(definition.get("headerTitle"), "gn - cn");
+});
+
+test('CounterColumnDefinition.make test', function(assert) {
+  var definitions = CounterColumnDefinition.make([{
+    counterGroupName: "gn1",
+    counterName: "nm1",
+  }, {
+    counterGroupName: "gn2",
+    counterName: "nm2",
+  }]);
+
+  assert.equal(definitions.length, 2);
+  assert.equal(definitions[0].get("headerTitle"), "gn1 - nm1");
+  assert.equal(definitions[1].get("headerTitle"), "gn2 - nm2");
+});