You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by je...@apache.org on 2014/12/10 04:33:51 UTC

[28/53] tez git commit: TEZ-1741. App view. (Sreenath Somarajapuram via hitesh)

TEZ-1741. App view. (Sreenath Somarajapuram via hitesh)


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

Branch: refs/heads/master
Commit: 28ef6ec8f134552f2030cd8b507fe50fc7dee019
Parents: b4be9ca
Author: Hitesh Shah <hi...@apache.org>
Authored: Mon Nov 10 22:01:29 2014 -0800
Committer: Hitesh Shah <hi...@apache.org>
Committed: Mon Nov 10 22:01:29 2014 -0800

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 tez-ui/src/main/webapp/app/scripts/app.js       |  12 +-
 .../app/scripts/controllers/dags_controller.js  |  12 +-
 .../controllers/tez-app-configs-controller.js   |  89 ++++++++++++
 .../scripts/controllers/tez-app-controller.js   |  41 ++++++
 .../controllers/tez-app-dags-controller.js      | 138 +++++++++++++++++++
 .../app/scripts/models/TimelineRestAdapter.js   |  95 ++++++++++++-
 .../src/main/webapp/app/scripts/models/dag.js   |  39 ++++++
 tez-ui/src/main/webapp/app/scripts/router.js    |  61 +++++++-
 .../src/main/webapp/app/templates/tez-app.hbs   |  43 ++++++
 .../webapp/app/templates/tez-app/configs.hbs    |  46 +++++++
 .../main/webapp/app/templates/tez-app/dags.hbs  |  46 +++++++
 .../main/webapp/app/templates/tez-app/index.hbs |  80 +++++++++++
 13 files changed, 693 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 9073a5e..9cc4e82 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -13,6 +13,7 @@ ALL CHANGES:
   TEZ-1720. Allow filters in all tables and also to pass in filters using url params.
   TEZ-1708. Make UI part of TEZ build process.
   TEZ-1617. Shim layer for Tez UI for use within Ambari.
+  TEZ-1741. App view.
 
 Release 0.5.2: Unreleased
 

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/app.js b/tez-ui/src/main/webapp/app/scripts/app.js
index 2db916b..d735bcc 100644
--- a/tez-ui/src/main/webapp/app/scripts/app.js
+++ b/tez-ui/src/main/webapp/app/scripts/app.js
@@ -28,7 +28,8 @@ var App = window.App = Em.Application.createWithMixins(Bootstrap, {
 
   env: {
     standalone: window.parent === window,
-    timelineBaseUrl: "http://localhost:8188"
+    timelineBaseUrl: "http://localhost:8188",
+    RMWebUrl: "http://localhost:8088"
   }
 });
 
@@ -46,6 +47,15 @@ Ember.Application.initializer({
       host: App.env.timelineBaseUrl
     });
     application.ApplicationSerializer = App.TimelineSerializer.extend();
+
+    application.AppDetailAdapter = DS.RESTAdapter.extend({
+      namespace: 'ws/v1/applicationhistory',
+      host: App.env.timelineBaseUrl,
+      pathForType: function() {
+        return "apps";
+      },
+    });
+
   }
 });
 

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
index 896db7f..6887bce 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
@@ -144,7 +144,17 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
         }
       }
     });
-    var appIdCol = columnHelper('Application ID', 'applicationId');
+    var appIdCol = App.ExTable.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Application ID',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          "{{#link-to 'tez-app' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+      }),
+      getCellContent: function(row) {
+        return  row.get('applicationId')
+      }
+    });
     return [nameCol, idCol, userCol, statusCol, submittedTimeCol, runTimeCol, appIdCol];
   }.property(),
 

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-configs-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-configs-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-configs-controller.js
new file mode 100644
index 0000000..6843733
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-configs-controller.js
@@ -0,0 +1,89 @@
+/**
+ * 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.
+ */
+
+App.TezAppConfigsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+  needs: "tezApp",
+  count: 10,
+
+  key: null,
+  value: null,
+
+  loadEntities: function() {
+    var that = this,
+    count = 0,
+    filter = this.getFilterProperties(),
+    configs = configs = this.get('configs').content,
+    filtered = [],
+    i = 0;
+
+    if(filter.fromId) {
+      while(i < configs.length && configs[i].id != filter.fromId) {
+        i++;
+      }
+    }
+
+    // Filter the available data
+    for(; i < configs.length && filtered.length < filter.limit; i++){
+      if((that.key === null || configs[i].get('key').indexOf(that.key) !=-1) && (that.value === null || configs[i].get('value').indexOf(that.value) != -1)) {
+        filtered.push(configs[i]);
+      }
+    }
+
+    this.set('entities', filtered);
+    this.set('loading', false);
+  },
+
+  loadData: function() {
+    var filters = {
+      primary: {
+        key: this.key,
+        value: this.value
+      },
+    };
+    this.setFiltersAndLoadEntities(filters);
+  },
+
+  actions : {
+    filterUpdated: function(filterID, value) {
+      // any validations required goes here.
+      if (!!value) {
+        this.set(filterID, value);
+      } else {
+        this.set(filterID, null);
+      }
+      this.loadData();
+    }
+  },
+
+  columns: function() {
+    var columnHelper = function(displayText, columnId) {
+      return App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
+        textAlign: 'text-align-left',
+        headerCellName: displayText,
+        filterID: columnId,
+        contentPath: columnId
+      });
+    }
+
+    return [
+        columnHelper("Conf key", 'key'),
+        columnHelper("Conf value", 'value'),
+    ];
+  }.property(),
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-controller.js
new file mode 100644
index 0000000..74e985c
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-controller.js
@@ -0,0 +1,41 @@
+/**
+ * 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.
+ */
+
+App.TezAppController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
+  controllerName: 'AppController',
+
+  pageTitle: 'App',
+
+  loading: true,
+
+  RMWebUrl: App.env.RMWebUrl,
+
+  updateLoading: function() {
+    this.set('loading', false);
+  }.observes('content'),
+
+  pageSubTitle: function() {
+    return this.get('id');
+  }.property('id'),
+
+  childDisplayViews: [
+    Ember.Object.create({title: 'Details', linkTo: 'tez-app.index'}),
+    Ember.Object.create({title: 'Dags', linkTo: 'tez-app.dags'}),
+    Ember.Object.create({title: 'Configs', linkTo: 'tez-app.configs'}),
+  ],
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
new file mode 100644
index 0000000..d07478c
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
@@ -0,0 +1,138 @@
+/**
+ * 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.
+ */
+
+App.TezAppDagsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+  needs: "tezApp",
+
+  // required by the PaginatedContentMixin
+  childEntityType: 'dag',
+
+  queryParams: {
+    status_filter: 'status',
+    user_filter: 'user'
+  },
+  status_filter: null,
+  user_filter: null,
+
+  loadData: function() {
+    console.log(new Error().stack);
+    var filters = {
+      primary: {
+        applicationId: this.get('appId'),
+        user: this.user_filter
+      },
+      secondary: {
+        status: this.status_filter
+      }
+    };
+    this.setFiltersAndLoadEntities(filters);
+  },
+
+  actions : {
+    filterUpdated: function(filterID, value) {
+      // any validations required goes here.
+      if (!!value) {
+        this.set(filterID, value);
+      } else {
+        this.set(filterID, null);
+      }
+      this.loadData();
+    }
+  },
+
+  /* table view for dags */
+  columns: function() {
+    var columnHelper = function(columnName, valName) {
+      return App.ExTable.ColumnDefinition.create({
+        textAlign: 'text-align-left',
+        headerCellName: columnName,
+        contentPath: valName
+      });
+    }
+
+    var nameCol = App.ExTable.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Dag Name',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          "{{#link-to 'dag' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
+      }),
+      getCellContent: function(row) {
+        return {
+          id: row.get('id'),
+          name: row.get('name')
+        };
+      }
+    });
+    var idCol = App.ExTable.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Dag ID',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          "{{#link-to 'dag' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+      }),
+      getCellContent: function(row) {
+        return row.get('id')
+      }
+    });
+    var userCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
+      textAlign: 'text-align-left',
+      headerCellName: 'Submitter',
+      filterID: 'user_filter',
+      contentPath: 'user'
+    }); 
+    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
+      textAlign: 'text-align-left',
+      headerCellName: 'Status',
+      filterID: 'status_filter',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          '<span class="ember-table-content">&nbsp;\
+          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+      }),
+      getCellContent: function(row) {
+        return { 
+          status: row.get('status'),
+          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+        };
+      }
+    });
+    var submittedTimeCol = App.ExTable.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Submitted Time',
+      getCellContent: function(row) {
+        return App.Helpers.date.dateFormat(row.get('submittedTime'));
+      }
+    });
+    var runTimeCol = App.ExTable.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Run Time',
+      getCellContent: function(row) {
+        var st = row.get('startTime');
+        var et = row.get('endTime');
+        console.log(st, et);
+        if (st && et) {
+          return App.Helpers.date.durationSummary(st, et);
+        }
+      }
+    });
+    return [nameCol, idCol, userCol, statusCol, submittedTimeCol, runTimeCol];
+  }.property(),
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
index f29b467..468b906 100644
--- a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
+++ b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
@@ -16,10 +16,11 @@
  */
 
 var typeToPathMap = {
-	dag: 'TEZ_DAG_ID',
-	vertex: 'TEZ_VERTEX_ID',
+  dag: 'TEZ_DAG_ID',
+  vertex: 'TEZ_VERTEX_ID',
   task: 'TEZ_TASK_ID',
-	taskAttempt: 'TEZ_TASK_ATTEMPT_ID'
+  taskAttempt: 'TEZ_TASK_ATTEMPT_ID',
+  tezApp: 'TEZ_APPLICATION'
 };
 
 App.TimelineRESTAdapter = DS.RESTAdapter.extend({
@@ -330,3 +331,91 @@ App.VertexSerializer = App.TimelineSerializer.extend({
     return Em.JsonMapper.map(hash, timelineJsonToVertexMap);
   },
 });
+
+var timelineJsonToAppDetailMap = {
+  id: 'appId',
+  attemptId: 'currentAppAttemptId',
+
+  name: 'name',
+  queue: 'queue',
+  user: 'user',
+  type: 'type',
+
+  startedTime: 'startedTime',
+  elapsedTime: 'elapsedTime',
+  finishedTime: 'finishedTime',
+  submittedTime: 'submittedTime',
+
+  appState: 'appState',
+
+  finalAppStatus: 'finalAppStatus',
+  diagnostics: 'otherinfo.diagnostics',
+};
+
+App.AppDetailSerializer = App.TimelineSerializer.extend({
+  normalize: function(type, hash, prop) {
+    return Em.JsonMapper.map(hash, timelineJsonToAppDetailMap);
+  },
+});
+
+var timelineJsonToTezAppMap = {
+  id: 'entity',
+
+  appId: 'appId',
+
+  entityType: 'entitytype',
+
+  startedTime: 'startedTime',
+  domain: 'domain',
+
+  dags: 'relatedentities.TEZ_DAG_ID',
+  configs: 'configs'
+};
+
+App.TezAppSerializer = App.TimelineSerializer.extend({
+  _normalizeSinglePayload: function(rawPayload){
+    var configs = rawPayload.otherinfo.config,
+    appId = rawPayload.entity.substr(4),
+    kVData = [],
+    id;
+
+    rawPayload.appId = appId;
+    rawPayload.configs = [];
+
+    for(var key in configs) {
+      id = appId + key;
+      rawPayload.configs.push(id);
+      kVData.push({
+        id: id,
+        key: key,
+        value: configs[key]
+      });
+    }
+
+    return {
+      tezApp: rawPayload,
+      kVData: kVData
+    };
+  },
+  normalizePayload: function(rawPayload) {
+    if (!!rawPayload.tezApps) {
+      var normalizedPayload = {
+        tezApps: [],
+        kVData: []
+      },
+      push = Array.prototype.push;
+      rawPayload.tezApps.forEach(function(app){
+        var n = this._normalizeSinglePayload(app);
+        normalizedPayload.tezApps.push(n.tezApp);
+        push.apply(normalizedPayload.kVData,n.kVData);
+      });
+      return normalizedPayload;
+    }
+    else {
+      return this._normalizeSinglePayload(rawPayload.tezApp)
+    }
+  },
+  normalize: function(type, hash, prop) {
+    return Em.JsonMapper.map(hash, timelineJsonToTezAppMap);
+  },
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/models/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/models/dag.js b/tez-ui/src/main/webapp/app/scripts/models/dag.js
index 1bd3182..2dbb0d7 100644
--- a/tez-ui/src/main/webapp/app/scripts/models/dag.js
+++ b/tez-ui/src/main/webapp/app/scripts/models/dag.js
@@ -217,6 +217,40 @@ App.Vertex = DS.Model.extend({
   }.property('duration')
 });
 
+App.AppDetail = DS.Model.extend({
+  attemptId: DS.attr('string'),
+
+  user: DS.attr('string'),
+  name: DS.attr('string'),
+  queue: DS.attr('string'),
+  type: DS.attr('string'),
+
+  appState: DS.attr('string'),
+  finalAppStatus: DS.attr('string'),
+  progress: DS.attr('string'),
+
+  startedTime: DS.attr('number'),
+  elapsedTime: DS.attr('number'),
+  finishedTime: DS.attr('number'),
+  submittedTime: DS.attr('number'),
+
+  diagnostics: DS.attr('string'),
+});
+
+App.TezApp = DS.Model.extend({
+  appId: DS.attr('string'),
+  entityType: DS.attr('string'),
+  domain: DS.attr('string'),
+
+  startedTime: DS.attr('number'),
+
+  appDetail: DS.belongsTo('appDetail', { async: true }),
+  dags: DS.hasMany('dag', { async: true }),
+
+  configs: DS.hasMany('kVData', { async: false })
+});
+
+
 App.Task = App.AbstractEntity.extend({
   status: DS.attr('status'),
 
@@ -235,6 +269,11 @@ App.Task = App.AbstractEntity.extend({
   counterGroups: DS.hasMany('counterGroup', { inverse: 'parent' })
 });
 
+App.KVDatum = DS.Model.extend({
+  key: DS.attr('string'),
+  value: DS.attr('string'),
+});
+
 App.VertexState = {
   NEW: "NEW",
   INITIALIZING: "INITIALIZING",

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/scripts/router.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/router.js b/tez-ui/src/main/webapp/app/scripts/router.js
index c2bcca8..5da59db 100644
--- a/tez-ui/src/main/webapp/app/scripts/router.js
+++ b/tez-ui/src/main/webapp/app/scripts/router.js
@@ -19,12 +19,17 @@
 App.Router.map(function() {
 	this.resource('dags', { path: '/' });
 	this.resource('dag', { path: '/dag/:dag_id'}, function() {
-		this.route('vertices'),
-		this.route('tasks'),
-		this.route('counters'),
-		this.route('swimlane')
+		this.route('vertices');
+		this.route('tasks');
+		this.route('counters');
+		this.route('swimlane');
 	});
 
+  this.resource('tez-app', {path: '/tez-app/:app_id'}, function(){
+    this.route('dags');
+    this.route('configs');
+  });
+
   this.resource('vertex', {path: '/vertex/:vertex_id'}, function(){
     this.route('tasks');
     this.route('counters');
@@ -226,4 +231,50 @@ App.TaskAttemptsRoute = Em.Route.extend({
     this._super(controller, model);
     controller.loadData();
   }
-});
\ No newline at end of file
+});
+
+App.TezAppRoute = Em.Route.extend({
+  model: function(params) {
+    var store = this.store;
+    return store.find('tezApp', 'tez_' + params.app_id).then(function(tezApp){
+      return store.find('appDetail', tezApp.get('appId')).then(function(appDetails){
+        tezApp.set('appDetail', appDetails);
+        return tezApp;
+      });
+    });
+  },
+});
+
+App.TezAppDagsRoute = Em.Route.extend({
+  queryParams:  {
+    count: {
+      refreshModel: true,
+      replace: true
+    },    
+    fromID: {
+      refreshModel: true,
+      replace: true
+    },
+    user: {
+      refreshModel: true,
+      replace: true
+    },
+    status: {
+      refreshModel: true,
+      replace: true
+    }
+  },
+
+  setupController: function(controller, model) {
+    this._super(controller, model);
+    controller.loadData();
+  }
+});
+
+App.TezAppConfigsRoute = Em.Route.extend({
+  setupController: function(controller, model) {
+    this._super(controller, model);
+    controller.loadData();
+  }
+});
+

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/templates/tez-app.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/tez-app.hbs b/tez-ui/src/main/webapp/app/templates/tez-app.hbs
new file mode 100644
index 0000000..e4fba4e
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/tez-app.hbs
@@ -0,0 +1,43 @@
+{{!
+* 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.
+}}
+
+{{partial "utils/pageHeader"}}
+{{#unless loading}}
+  <div class='type-table fill-full '>
+    <div class='align-left'>
+      <table class='detail-list'>
+        <tbody>
+          <tr>
+            <td>App Id</td>
+            <td><a href="{{unbound RMWebUrl}}/cluster/app/tez_{{unbound id}}">{{unbound id}}</a></td>
+          </tr>
+          <tr>
+            <td>Attempt Id</td>
+            <td>{{appDetail.attemptId}}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div class='pill-container align-right'>
+      {{bs-pills contentBinding='childDisplayViews' selectedBinding='childDisplayViewSelected' size='lg'}}
+    </div>
+  </div>
+  {{outlet}}
+{{else}}
+    {{partial 'utils/loadingSpinner'}}
+{{/unless}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/templates/tez-app/configs.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/tez-app/configs.hbs b/tez-ui/src/main/webapp/app/templates/tez-app/configs.hbs
new file mode 100644
index 0000000..4662279
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/tez-app/configs.hbs
@@ -0,0 +1,46 @@
+{{!
+* 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.
+}}
+
+{{#unless loading}}
+  <div class='margin-small'>
+    <span class='align-right'>
+      {{page-nav-component
+        hasPrev=hasPrev
+        hasNext=hasNext
+        navNext='navigateNext'
+        navPrev='navigatePrev'
+        navFirst='navigateFirst'
+      }}
+    </span>
+    <div class='align-clear'> </div>
+  </div>
+
+  <div class='table-container'>
+  {{extended-table-component 
+    hasFooter=false
+    enableContentSelection=true
+    columnsBinding="columns"
+    contentBinding="sortedContent"
+    forceFillColumns=true
+    hasFilter=true
+    onFilterUpdated='filterUpdated'
+  }}
+  </div>
+{{else}}
+  {{partial 'utils/loadingSpinner'}}
+{{/unless}}

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/templates/tez-app/dags.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/tez-app/dags.hbs b/tez-ui/src/main/webapp/app/templates/tez-app/dags.hbs
new file mode 100644
index 0000000..4662279
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/tez-app/dags.hbs
@@ -0,0 +1,46 @@
+{{!
+* 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.
+}}
+
+{{#unless loading}}
+  <div class='margin-small'>
+    <span class='align-right'>
+      {{page-nav-component
+        hasPrev=hasPrev
+        hasNext=hasNext
+        navNext='navigateNext'
+        navPrev='navigatePrev'
+        navFirst='navigateFirst'
+      }}
+    </span>
+    <div class='align-clear'> </div>
+  </div>
+
+  <div class='table-container'>
+  {{extended-table-component 
+    hasFooter=false
+    enableContentSelection=true
+    columnsBinding="columns"
+    contentBinding="sortedContent"
+    forceFillColumns=true
+    hasFilter=true
+    onFilterUpdated='filterUpdated'
+  }}
+  </div>
+{{else}}
+  {{partial 'utils/loadingSpinner'}}
+{{/unless}}

http://git-wip-us.apache.org/repos/asf/tez/blob/28ef6ec8/tez-ui/src/main/webapp/app/templates/tez-app/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/tez-app/index.hbs b/tez-ui/src/main/webapp/app/templates/tez-app/index.hbs
new file mode 100644
index 0000000..90a335b
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/tez-app/index.hbs
@@ -0,0 +1,80 @@
+{{!
+* 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='type-table fill-full margin-small-horizontal'>
+  <div class='align-left'>
+    <table class='detail-list'>
+      <tbody>
+        <tr>
+          <td>App Name</td>
+          <td>
+            {{appDetail.name}}
+          </td>
+        </tr>
+        <tr>
+          <td>Queue</td>
+          <td>
+            {{appDetail.queue}}
+          </td>
+        </tr>
+        <tr>
+          <td>User</td>
+          <td>{{appDetail.user}}</td>
+        </tr>
+        <tr>
+          <td>Type</td>
+          <td>{{appDetail.type}}</td>
+        </tr>
+        <tr>
+          <td>App State</td>
+          <td>
+            {{appDetail.appState}}
+          </td>
+        </tr>
+        <tr>
+          <td>Final App Status</td>
+          <td>{{appDetail.finalAppStatus}}</td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+  <div class='align-right'>
+    <table class='detail-list'>
+      <tbody>
+        <tr>
+          <td>{{t common.time.start}}</td>
+          <td>{{formatUnixTimestamp appDetail.startedTime}}</td>
+        </tr>
+        <tr>
+          <td>{{t common.time.end}}</td>
+          <td>{{formatUnixTimestamp appDetail.finishedTime}}</td>
+        </tr>
+        <tr>
+          <td>{{t common.time.duration}}</td>
+          <td>{{formatDuration appDetail.startedTime appDetail.finishedTime}}</td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+
+{{#if diagnostics}}
+  {{#bs-panel heading="Diagnostics" collapsible=false dismiss=false type='danger'}}
+    {{formatDiagnostics diagnostics}}
+  {{/bs-panel}}
+{{/if}}
\ No newline at end of file