AMBARI-12697 Rolling upgrade: Ambari UI should be able to display rolling 
upgrade history (dili via atkach)


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

Branch: refs/heads/branch-feature-AMBARI-18901
Commit: ef41837712fee03772aca344b2edc337396974a0
Parents: 502cffb
Author: Andrii Tkach <atk...@apache.org>
Authored: Wed Nov 30 20:32:16 2016 +0200
Committer: Andrii Tkach <atk...@apache.org>
Committed: Wed Nov 30 20:32:16 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   5 +
 ambari-web/app/controllers.js                   |   1 +
 .../admin/stack_upgrade_history_controller.js   | 217 +++++++++++
 ambari-web/app/mappers.js                       |   3 +-
 .../app/mappers/stack_upgrade_history_mapper.js |  54 +++
 ambari-web/app/messages.js                      |  28 ++
 ambari-web/app/models.js                        |   2 +
 .../app/models/finished_upgrade_entity.js       |  92 +++++
 .../stack_version/stack_upgrade_history.js      |  37 ++
 ambari-web/app/routes/main.js                   |   7 +
 ambari-web/app/styles/stack_versions.less       |  69 ++++
 .../admin/stack_upgrade/upgrade_history.hbs     | 105 ++++++
 .../stack_upgrade/upgrade_history_details.hbs   |  46 +++
 ambari-web/app/views.js                         |   2 +
 .../views/main/admin/stack_upgrade/menu_view.js |   6 +
 .../upgrade_history_details_view.js             |  85 +++++
 .../admin/stack_upgrade/upgrade_history_view.js | 303 +++++++++++++++
 .../stack_upgrade_history_controller_test.js    | 125 +++++++
 .../stack_upgrade_history_mapper_test.js        | 372 +++++++++++++++++++
 .../test/models/finished_upgrade_entity_test.js | 197 ++++++++++
 .../upgrade_history_details_view_test.js        | 248 +++++++++++++
 .../stack_upgrade/upgrade_history_view_test.js  | 173 +++++++++
 22 files changed, 2176 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js 
b/ambari-web/app/assets/test/tests.js
index 8571fc4..7440819 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -70,6 +70,7 @@ var files = [
   'test/controllers/main/admin/kerberos/step8_controller_test',
 
   'test/controllers/main/admin/stack_and_upgrade_controller_test',
+  'test/controllers/main/admin/stack_upgrade_history_controller_test',
   'test/controllers/main/admin/serviceAccounts_controller_test',
   'test/controllers/main/admin/highAvailability_controller_test',
   'test/controllers/main/admin/highAvailability/progress_controller_test',
@@ -145,6 +146,7 @@ var files = [
   'test/mappers/users_mapper_test',
   'test/mappers/stack_mapper_test',
   'test/mappers/stack_service_mapper_test',
+  'test/mappers/stack_upgrade_history_mapper_test',
   'test/mappers/repository_version_mapper_test',
   'test/mappers/configs/config_groups_mapper_test',
   'test/mappers/configs/service_config_version_mapper_test',
@@ -251,6 +253,8 @@ var files = [
   'test/views/main/admin/stack_upgrade/upgrade_group_view_test',
   'test/views/main/admin/stack_upgrade/upgrade_task_view_test',
   'test/views/main/admin/stack_upgrade/upgrade_wizard_view_test',
+  'test/views/main/admin/stack_upgrade/upgrade_history_view_test',
+  'test/views/main/admin/stack_upgrade/upgrade_history_details_view_test',
   'test/views/main/admin/stack_upgrade/version_view_test',
   'test/views/main/admin/stack_upgrade/services_view_test',
   'test/views/main/admin/stack_upgrade/menu_view_test',
@@ -384,6 +388,7 @@ var files = [
   'test/models/widget_property_test',
   'test/models/host_stack_version_test',
   'test/models/upgrade_entity_test',
+  'test/models/finished_upgrade_entity_test',
   'test/models/configs/sub_section_test',
   'test/models/configs/section_test',
   'test/models/configs/service_config_version_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/controllers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js
index 5664029..81e5eb7 100644
--- a/ambari-web/app/controllers.js
+++ b/ambari-web/app/controllers.js
@@ -85,6 +85,7 @@ 
require('controllers/main/admin/highAvailability/journalNode/step6_controller');
 
require('controllers/main/admin/highAvailability/journalNode/step7_controller');
 
require('controllers/main/admin/highAvailability/journalNode/step8_controller');
 require('controllers/main/admin/stack_and_upgrade_controller');
+require('controllers/main/admin/stack_upgrade_history_controller');
 require('controllers/main/admin/serviceAccounts_controller');
 require('utils/polling');
 require('controllers/main/admin/kerberos');

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/controllers/main/admin/stack_upgrade_history_controller.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/controllers/main/admin/stack_upgrade_history_controller.js 
b/ambari-web/app/controllers/main/admin/stack_upgrade_history_controller.js
new file mode 100644
index 0000000..9bec825
--- /dev/null
+++ b/ambari-web/app/controllers/main/admin/stack_upgrade_history_controller.js
@@ -0,0 +1,217 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+var stringUtils = require('utils/string_utils');
+
+App.MainAdminStackUpgradeHistoryController = Em.ArrayController.extend({
+  name: 'mainAdminStackUpgradeHistoryController',
+
+  startIndex: 1,
+
+  resetStartIndex: false,
+
+  /**
+   * status of tasks/items/groups which should be grayed out and disabled
+   * @type {Array}
+   */
+  nonActiveStates: ['PENDING'],
+
+  /**
+   * mutable properties of Upgrade Task
+   * @type {Array}
+   */
+  taskDetailsProperties: ['status', 'stdout', 'stderr', 'error_log', 
'host_name', 'output_log'],
+
+  /**
+   * Current upgrade record clicked on the UI
+   * @type {App.StackUpgradeHistory|null}
+   * */
+  currentUpgradeRecord: null,
+
+  isDowngrade: false,
+
+  upgradeData: null,
+
+  /**
+   * List of all <code>App.StackUpgradeHistory</code>. Latest one at the 
beginning
+   * @type {App.StackUpgradeHistory[]}
+   */
+  content: App.StackUpgradeHistory.find(),
+
+  upgradeHistoryUrl: function() {
+    return App.get('apiPrefix') + '/clusters/' + App.get('clusterName') + 
'/upgrades?fields=Upgrade';
+  }.property('App.clusterName'),
+
+  upgradeUpgradeRecordUrl: function() {
+    var record = this.get('currentUpgradeRecord');
+    return App.get('apiPrefix') + '/clusters/' + App.get('clusterName') + 
'/upgrades/' + record.get('requestId');
+  }.property('App.clusterName', 'currentUpgradeRecord'),
+
+  loadStackUpgradeHistoryToModel: function () {
+    console.log('Load stack upgrade history');
+    var dfd = $.Deferred();
+    App.HttpClient.get(this.get('upgradeHistoryUrl'), 
App.stackUpgradeHistoryMapper, {
+      complete: function () {
+        dfd.resolve();
+      }
+    });
+    return dfd.promise();
+  },
+
+  loadStackUpgradeRecord: function () {
+    var record = this.get('currentUpgradeRecord');
+    this.set('isDowngrade', ('DOWNGRADE' == record.get('direction')))
+    var dfd = $.Deferred();
+    var self = this;
+    if (record != null) {
+      App.ajax.send({
+        name: 'admin.upgrade.data',
+        sender: this,
+        data: {
+          id: record.get('requestId')
+        },
+        success: 'loadUpgradeRecordSuccessCallback'
+      }).then(dfd.resolve).complete(function () {
+      });
+    } else {
+      dfd.resolve();
+    }
+    return dfd.promise();
+  },
+
+  loadUpgradeRecordSuccessCallback: function(newData){
+    if (Em.isNone(newData)) {
+      var record = this.get('currentUpgradeRecord');
+      console.debug('No data returned for upgrad record ' + 
record.get('requestId'))
+      return;
+    }
+    var upgradeGroups = [];
+    if (newData.upgrade_groups) {
+      nonActiveStates = this.get('nonActiveStates'),
+      //wrap all entities into App.finishedUpgradeEntity
+      newData.upgrade_groups.forEach(function (newGroup) {
+      var hasExpandableItems = newGroup.upgrade_items.some(function (item) {
+            return !nonActiveStates.contains(item.UpgradeItem.status);
+          }),
+          oldGroup = App.finishedUpgradeEntity.create({type: 'GROUP', 
hasExpandableItems: hasExpandableItems}, newGroup.UpgradeGroup),
+          upgradeItems = [];
+        newGroup.upgrade_items.forEach(function (item) {
+          var oldItem = App.finishedUpgradeEntity.create({type: 'ITEM'}, 
item.UpgradeItem);
+          this.formatMessages(oldItem);
+          oldItem.set('tasks', []);
+          upgradeItems.pushObject(oldItem);
+        }, this);
+        upgradeItems.reverse();
+        oldGroup.set('upgradeItems', upgradeItems);
+        upgradeGroups.pushObject(oldGroup);
+      }, this);
+      upgradeGroups.reverse();
+      this.set('upgradeData', Em.Object.create({
+        upgradeGroups: upgradeGroups,
+        upgrade_groups: newData.upgrade_groups,
+        Upgrade: newData.Upgrade
+      }));
+    }
+  },
+
+  /**
+   * format upgrade item text
+   * @param {App.finishedUpgradeEntity} oldItem
+   */
+  formatMessages: function (oldItem) {
+    var text = oldItem.get('text');
+    var messages = [];
+
+    try {
+      var messageArray = JSON.parse(text);
+      for (var i = 0; i < messageArray.length; i++) {
+        messages.push(messageArray[i].message);
+      }
+      oldItem.set('text', messages.join(' '));
+    } catch (err) {
+      console.warn('Upgrade Item has malformed text');
+    }
+    oldItem.set('messages', messages);
+  },
+
+  /**
+   * request Upgrade Item and its tasks from server
+   * @param {Em.Object} item
+   * @param {Function} customCallback
+   * @return {$.ajax}
+   */
+  getUpgradeItem: function (item) {
+    return App.ajax.send({
+      name: 'admin.upgrade.upgrade_item',
+      sender: this,
+      data: {
+        upgradeId: item.get('request_id'),
+        groupId: item.get('group_id'),
+        stageId: item.get('stage_id')
+      },
+      success: 'getUpgradeItemSuccessCallback'
+    });
+  },
+
+  /**
+   * success callback of <code>getTasks</code>
+   * @param {object} data
+   */
+  getUpgradeItemSuccessCallback: function (data) {
+    this.get('upgradeData.upgradeGroups').forEach(function (group) {
+      if (group.get('group_id') === data.UpgradeItem.group_id) {
+        group.get('upgradeItems').forEach(function (item) {
+          if (item.get('stage_id') === data.UpgradeItem.stage_id) {
+            if (item.get('tasks.length')) {
+              data.tasks.forEach(function (task) {
+                var currentTask = item.get('tasks').findProperty('id', 
task.Tasks.id);
+                this.get('taskDetailsProperties').forEach(function (property) {
+                  currentTask.set(property, task.Tasks[property]);
+                }, this);
+              }, this);
+            } else {
+              var tasks = [];
+              data.tasks.forEach(function (task) {
+                tasks.pushObject(App.finishedUpgradeEntity.create({type: 
'TASK'}, task.Tasks));
+              });
+              item.set('tasks', tasks);
+            }
+            item.set('isTasksLoaded', true);
+          }
+        }, this);
+      }
+    }, this);
+  },
+
+  /**
+   * status of Upgrade request
+   * @type {string}
+   */
+  requestStatus: function () {
+    if (this.get('upgradeData')){
+      if (this.get('upgradeData.Upgrade')) {
+        return this.get('upgradeData.Upgrade.request_status');
+      } else {
+        return '';
+      }
+    } else {
+      return ''
+    }
+  }.property('upgradeData.Upgrade.request_status'),
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index 96193bc..cde9bcc 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -43,4 +43,5 @@ require('mappers/alert_groups_mapper');
 require('mappers/alert_notification_mapper');
 require('mappers/root_service_mapper');
 require('mappers/widget_mapper');
-require('mappers/widget_layout_mapper');
\ No newline at end of file
+require('mappers/widget_layout_mapper');
+require('mappers/stack_upgrade_history_mapper');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/mappers/stack_upgrade_history_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/stack_upgrade_history_mapper.js 
b/ambari-web/app/mappers/stack_upgrade_history_mapper.js
new file mode 100644
index 0000000..63088a7
--- /dev/null
+++ b/ambari-web/app/mappers/stack_upgrade_history_mapper.js
@@ -0,0 +1,54 @@
+/**
+ * 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.
+ */
+var App = require('app');
+var stringUtils = require('utils/string_utils');
+
+App.stackUpgradeHistoryMapper = App.QuickDataMapper.create({
+  model: App.StackUpgradeHistory,
+
+  config: {
+    "id": "Upgrade.request_id",
+    "request_id": "Upgrade.request_id",
+    "cluster_name": "Upgrade.cluster_name",
+    "direction": "Upgrade.direction",
+    "from_version": "Upgrade.from_version",
+    "to_version": "Upgrade.to_version",
+    "end_time":"Upgrade.end_time",
+    "start_time":"Upgrade.start_time",
+    "create_time": "Upgrade.create_time",
+    "request_status": "Upgrade.request_status",
+    "upgrade_type": "Upgrade.upgrade_type",
+    "downgrade_allowed": "Upgrade.downgrade_allowed",
+    "skip_failures": "Upgrade.skip_failures",
+    "skip_service_check_failures": "Upgrade.skip_service_check_failures"
+  },
+
+  map: function (json) {
+    App.set('isStackUpgradeHistoryLoaded',false);
+    var model = this.get('model');
+    var result = [];
+    json.items.forEach(function(item) {
+      var parseResult = this.parseIt(item, this.get('config'));
+      result.push(parseResult);
+    }, this);
+
+    App.store.loadMany(this.get('model'), result);
+    App.store.commit();
+    App.set('isStackUpgradeHistoryLoaded',true);
+  },
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 7c3ccd3..5c7e0e8 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -130,6 +130,7 @@ Em.I18n.translations = {
   'common.open':'Open',
   'common.copy':'Copy',
   'common.complete':'Complete',
+  'common.completed':'Completed',
   'common.metrics':'Metrics',
   'common.timeRange':'Time Range',
   'common.name':'Name',
@@ -172,6 +173,7 @@ Em.I18n.translations = {
   'common.recommission':'Recommission',
   'common.failure': 'Failure',
   'common.type': 'Type',
+  'common.direction': 'Direction',
   'common.close': 'Close',
   'common.warning': 'Warning',
   'common.critical': 'Critical',
@@ -192,6 +194,7 @@ Em.I18n.translations = {
   'common.repositories':'Repositories',
   'common.stack.versions':'Stack Versions',
   'common.versions':'Versions',
+  'common.upgrade.history':'Upgrade History',
   'common.serviceAccounts': 'Service Accounts',
   'common.add': 'Add',
   'common.edit': 'Edit',
@@ -203,6 +206,7 @@ Em.I18n.translations = {
   'common.details':'Details',
   'common.stats':'Stats',
   'common.abort': 'Abort',
+  'common.aborted': 'Aborted',
   'common.misc': 'Misc',
   'common.userSettings': 'User Settings',
   'common.aboutAmbari': 'About',
@@ -334,6 +338,14 @@ Em.I18n.translations = {
   'common.logs': 'Logs',
   'common.warn.message': '<div class="alert alert-warn">{0}</div>',
   'common.link': 'Link',
+  'common.from.version': 'From Version',
+  'common.to.version': 'To Version',
+  'common.start.time': 'Start Time',
+  'common.end.time': 'End Time',
+  'common.rolling': 'Rolling',
+  'common.express': 'Express',
+  'common.rolling.downgrade': 'Rolling Downgrade',
+  'common.express.downgrade': 'Express Downgrade',
 
   'models.alert_instance.tiggered.verbose': "Occurred on {0} <br> Checked on 
{1}",
   'models.alert_definition.triggered.verbose': "Occurred on {0}",
@@ -1728,6 +1740,22 @@ Em.I18n.translations = {
   'admin.stackVersions.hosts.popup.primary': "Go to Hosts",
 
   'admin.stackVersions.details.install.hosts.popup.title': "Install {0} 
version",
+  'admin.stackVersions.upgradeHistory.upgrade': 'Upgrades',
+  'admin.stackVersions.upgradeHistory.downgrade': 'Downgrades',
+  'admin.stackVersions.upgradeHistory.show.details': 'Click to show more 
details on {0}',
+  'admin.stackVersions.upgradeHistory.success': 'Sucessful {0}',
+  'admin.stackVersions.upgradeHistory.aborted': 'Aborted {0}',
+  'admin.stackVersions.upgradeHistory.summary': 'Summary',
+  'admin.stackVersions.upgradeHistory.history': 'History',
+  'admin.stackVersions.upgradeHistory.filter.all': 'All ({0})',
+  'admin.stackVersions.upgradeHistory.filter.upgrade': 'Upgrade ({0})',
+  'admin.stackVersions.upgradeHistory.filter.downgrade': 'Downgrade ({0})',
+  'admin.stackVersions.upgradeHistory.filter.successful.upgrade': 'Successful 
Upgrade ({0})',
+  'admin.stackVersions.upgradeHistory.filter.successful.downgrade': 
'Successful Downgrade ({0})',
+  'admin.stackVersions.upgradeHistory.filter.aborted.upgrade': 'Aborted 
Upgrade ({0})',
+  'admin.stackVersions.upgradeHistory.filter.aborted.downgrade': 'Aborted 
Downgrade ({0})',
+  'admin.stackVersions.upgradeHistory.no.history': 'No upgrade/downgrade 
history available',
+  'admin.stackVersions.upgradeHistory.record.title': '{0} {1} to {2}',
 
   'admin.stackUpgrade.preCheck.warning.message': "{0} Warning {1}",
   'admin.stackUpgrade.preCheck.bypass.message': "{0} Error {1}",

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index b877255..e0168a2 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -30,6 +30,7 @@ require('models/stack_version/repository_version');
 require('models/stack_version/os');
 require('models/stack_version/service_simple');
 require('models/stack_version/repository');
+require('models/stack_version/stack_upgrade_history');
 require('models/operating_system');
 require('models/repository');
 require('models/stack_service');
@@ -64,6 +65,7 @@ require('models/master_component');
 require('models/host_stack_version');
 require('models/root_service');
 require('models/upgrade_entity');
+require('models/finished_upgrade_entity');
 require('models/configs/theme/theme_condition');
 require('models/configs/service_config_version');
 require('models/configs/stack_config_property');

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/models/finished_upgrade_entity.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/finished_upgrade_entity.js 
b/ambari-web/app/models/finished_upgrade_entity.js
new file mode 100644
index 0000000..c08ef45
--- /dev/null
+++ b/ambari-web/app/models/finished_upgrade_entity.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.
+ */
+
+
+/**
+ * @type {Ember.Object}
+ * @class
+ */
+App.finishedUpgradeEntity = Em.Object.extend({
+
+  /**
+   * type of entity "GROUP", "ITEM", "TASK"
+   * @type {string}
+   */
+  type: null,
+
+  /**
+   * @type {boolean}
+   */
+  isExpanded: false,
+
+  /**
+   * @type {boolean}
+   */
+  hasExpandableItems: false,
+
+  /**
+   * @type {boolean}
+   */
+  isVisible: Em.computed.notEqual('status', 'PENDING'),
+
+  /**
+   * status of tasks/items/groups which should be grayed out and disabled
+   * @type {Array}
+   */
+  nonActiveStates: ['PENDING'],
+
+  /**
+   * @type {boolean}
+   */
+  isRunning: Em.computed.existsIn('status', ['IN_PROGRESS']),
+
+  /**
+   * @type {number}
+   */
+  progress: function () {
+    return Math.floor(this.get('progress_percent'));
+  }.property('progress_percent'),
+
+  /**
+   * indicate whether entity has active link
+   * @type {boolean}
+   */
+  isActive: function () {
+    return !this.get('nonActiveStates').contains(this.get('status'));
+  }.property('status'),
+
+  /**
+   * indicate whether upgrade group should be expanded
+   * @type {boolean}
+   */
+  isExpandableGroup: function () {
+    return this.get('type') === 'GROUP' && (this.get('isActive') || 
this.get('hasExpandableItems'));
+  }.property('isActive', 'hasExpandableItems'),
+
+  upgradeItemStatus: Em.computed.firstNotBlank('display_status', 'status'),
+
+  /**
+   * @type {string}
+   */
+  upgradeGroupStatus: function () {
+    if (this.get('type') === 'GROUP' && !this.get('isActive') && 
this.get('hasExpandableItems')) {
+      return 'SUBITEM_FAILED';
+    }
+    return this.get('display_status') || this.get('status');
+  }.property('isExpandableGroup', 'display_status', 'status')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/models/stack_version/stack_upgrade_history.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack_version/stack_upgrade_history.js 
b/ambari-web/app/models/stack_version/stack_upgrade_history.js
new file mode 100644
index 0000000..d4a89dc
--- /dev/null
+++ b/ambari-web/app/models/stack_version/stack_upgrade_history.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.
+ */
+
+var App = require('app');
+
+App.StackUpgradeHistory = DS.Model.extend({
+  requestId: DS.attr('number'),
+  clusterName: DS.attr('string'),
+  direction: DS.attr('string'),
+  fromVersion: DS.attr('string'),
+  toVersion: DS.attr('string'),
+  requestStatus: DS.attr('string'),
+  upgradeType: DS.attr('string'),
+  downgradeAllowed: DS.attr('boolean'),
+  skipFailures: DS.attr('boolean'),
+  skipServiceCheckFailures: DS.attr('boolean'),
+  endTime: DS.attr('number'),
+  startTime: DS.attr('number'),
+  createTime: DS.attr('number'),
+});
+
+App.StackUpgradeHistory.FIXTURES = [];

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/routes/main.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js
index f0fe5d7..7e7b622 100644
--- a/ambari-web/app/routes/main.js
+++ b/ambari-web/app/routes/main.js
@@ -519,6 +519,13 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         }
       }),
 
+      upgradeHistory: Em.Route.extend({
+        route: '/history',
+        connectOutlets: function (router, context) {
+          
router.get('mainAdminStackAndUpgradeController').connectOutlet('mainAdminStackUpgradeHistory');
+        },
+      }),
+
       stackNavigate: function (router, event) {
         var parent = event.view._parentView;
         parent.deactivateChildViews();

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/styles/stack_versions.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/stack_versions.less 
b/ambari-web/app/styles/stack_versions.less
index 921b418..715bbe3 100644
--- a/ambari-web/app/styles/stack_versions.less
+++ b/ambari-web/app/styles/stack_versions.less
@@ -420,6 +420,75 @@
   }
 }
 
+#stack-upgrade-record-dialog {
+  .details-box {
+    padding: 5px;
+    margin-left: 15px;
+    margin-right: 95px;
+    .button-row {
+      text-align: right;
+      padding: 5px;
+    }
+    input[type="checkbox"] {
+      margin: 0;
+    }
+    .message {
+      line-height: 30px;
+    }
+  }
+  .task-details {
+    .manage-controls a {
+      cursor: pointer;
+      margin-right: 12px;
+    }
+    textarea {
+      width: 100%;
+      min-height: 100px;
+      box-sizing: border-box;
+    }
+  }
+  .task-list {
+    overflow-x: hidden;
+    .progress {
+      margin-bottom: 0;
+    }
+    padding-left: 20px;
+    i {
+      margin-right: 5px;
+    }
+  }
+  .task-list-main-warp i {
+    font-size: 16px;
+  }
+  ul.failed-info-list {
+    max-height: 500px;
+    margin-top: 5px;
+  }
+  .upgrade-options-link {
+    position: absolute;
+    cursor: pointer;
+    right: 10%;
+    top: 13px;
+    width: 100px;
+    a {
+      font-size: 13px;
+    }
+    .icon-cogs {
+      color: #0088cc;
+      margin-right: 3px;
+    }
+  }
+  .upgrade-options-link.disabled {
+    cursor: not-allowed;
+    a, .icon-cogs {
+      color: #808080;
+    }
+    a:hover {
+      text-decoration: none;
+    }
+  }
+}
+
 .repository-list {
   .os-block {
     border-top: 1px solid #dddddd;

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history.hbs 
b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history.hbs
new file mode 100644
index 0000000..6d4a32d
--- /dev/null
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history.hbs
@@ -0,0 +1,105 @@
+{{!
+* 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 id="upgrade-history-section">
+  <div {{bindAttr class="view.isReady:hide:show :screensaver :no-borders "}}>
+    {{view App.SpinnerView}}
+  </div>
+  <div {{bindAttr class="view.isReady::hidden"}}>
+         <div class="btn-group display-inline-block">
+           <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+             <span class="filters-label">{{t common.filter}}: </span>
+               <span>
+                 {{view.selectedCategory.label}}
+                 <span class="caret"></span>
+               </span>
+           </a>
+           <ul class="dropdown-menu">
+             {{#each category in view.categories}}
+               <li>
+                 <a {{action selectCategory category target="view"}} href="#">
+                   {{category.label}}
+                 </a>
+               </li>
+             {{/each}}
+           </ul>
+         </div>
+         <br/>
+         <br/>
+         <table class="table advanced-header-table table-striped" 
id="upgrade-summary-table">
+           <thead>
+             <th>{{t common.direction}}</th>
+             <th>{{t common.type}}</th>
+             <th>{{t common.from.version}}</th>
+             <th>{{t common.to.version}}</th>
+             <th>{{t common.start.time}}</th>
+             <th>{{t common.duration}}</th>
+             <th>{{t common.end.time}}</th>
+             <th>{{t common.status}}</th>
+           </thead>
+           <tbody>
+                   {{#if view.pageContent}}
+                     {{#each item in view.pageContent}}
+                       <tr>
+                         <td class='name'>
+                           <span class="trim_hostname">
+                             <a href="#" class="black" {{action 
"showUpgradeHistoryRecord" item target="view"}}>
+                               {{unbound item.directionLabel}}
+                             </a>
+                           </span>
+                         </td>
+                         <td>
+                           <span>{{item.upgradeTypeLabel}}</span>
+                         </td>
+                         <td>
+                           <span>{{item.fromVersion}}</span>
+                         </td>
+                         <td>
+                           <span>{{item.toVersion}}</span>
+                         </td>
+                         <td>
+                           <span>{{item.startTimeLabel}}</span>
+                         </td>
+                         <td>
+                     <span>{{item.duration}}</span>
+                   </td>
+                         <td>
+                           <span>{{item.endTimeLabel}}</span>
+                         </td>
+                         <td>
+                           <span>{{item.requestStatus}}</span>
+                         </td>
+                       </tr>
+                     {{/each}}
+                   {{/if}}
+           </tbody>
+         </table>
+         <div class="page-bar">
+           <div class="items-on-page">
+             <label>{{t common.show}}: {{view view.rowsPerPageSelectView 
selectionBinding="view.displayLength"}}</label>
+           </div>
+           <div class="info">{{view.paginationInfo}}</div>
+           <div class="paging_two_button">
+             <a {{bindAttr class="view.paginationLeftClass"}}{{action 
previousPage target="view"}}><i
+                     class="icon-arrow-left"></i></a>
+             <a {{bindAttr class="view.paginationRightClass"}}{{action 
nextPage target="view"}}><i
+                     class="icon-arrow-right"></i></a>
+           </div>
+         </div>
+  </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history_details.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history_details.hbs 
b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history_details.hbs
new file mode 100644
index 0000000..2d431f9
--- /dev/null
+++ 
b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_history_details.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.
+}}
+
+<div id="stack-upgrade-record-dialog">
+  <div {{bindAttr class="view.isLoaded::hidden :row-fluid"}}>
+    <div class="span3 task-list-main-warp">{{statusIcon 
controller.requestStatus}}
+      &nbsp;{{view.upgradeStatusLabel}}</div>
+    <div class="span8">
+      {{view App.ProgressBarView
+        progressBinding="view.overallProgress"
+        statusBinding="controller.requestStatus"
+      }}
+    </div>
+    <div class="span1">
+      {{view.overallProgress}}%
+    </div>
+  </div>
+
+  <div class="task-list scrollable-block task-list-main-warp">
+    {{#if view.isReady}}
+      {{#each group in controller.upgradeData.upgradeGroups}}
+        {{#if group.isVisible}}
+          {{view App.upgradeGroupView contentBinding="group"}}
+        {{/if}}
+      {{/each}}
+    {{/if}}
+  </div>
+  {{#unless view.isReady}}
+    {{view App.SpinnerView}}
+  {{/unless}}
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 0d9dc3f..0dac227 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -213,6 +213,8 @@ 
require('views/main/admin/stack_upgrade/upgrade_version_box_view');
 require('views/main/admin/stack_upgrade/upgrade_version_column_view');
 require('views/main/admin/stack_upgrade/upgrade_group_view');
 require('views/main/admin/stack_upgrade/upgrade_task_view');
+require('views/main/admin/stack_upgrade/upgrade_history_view');
+require('views/main/admin/stack_upgrade/upgrade_history_details_view');
 require('views/main/admin/stack_upgrade/services_view');
 require('views/main/admin/stack_upgrade/versions_view');
 require('views/main/admin/stack_upgrade/menu_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/views/main/admin/stack_upgrade/menu_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/menu_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/menu_view.js
index 1e84f1c..db5f946 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/menu_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/menu_view.js
@@ -35,6 +35,12 @@ App.MainAdminStackMenuView = Em.CollectionView.extend({
         label: Em.I18n.t('common.versions'),
         routing: 'versions',
         hidden: !App.get('stackVersionsAvailable')
+      }),
+      Em.Object.create({
+        name: 'upgradeHistory',
+        label: Em.I18n.t('common.upgrade.history'),
+        routing: 'upgradeHistory',
+        hidden: !App.get('stackVersionsAvailable')
       })
     ]
   }.property('App.stackVersionsAvailable'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_details_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_details_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_details_view.js
new file mode 100644
index 0000000..983c26a
--- /dev/null
+++ 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_details_view.js
@@ -0,0 +1,85 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+var date = require('utils/date/date');
+
+App.MainAdminStackUpgradeHistoryDetailsView = Em.View.extend({
+  controllerBinding: 'App.router.mainAdminStackUpgradeHistoryController',
+  templateName: 
require('templates/main/admin/stack_upgrade/upgrade_history_details'),
+  isReady: false,
+
+  willInsertElement: function(){
+    var self = this;
+    this.get('controller').loadStackUpgradeRecord().done(function(){
+      self.populateUpgradeHistoryRecord();
+    });
+  },
+
+  willDestroyElement: function () {
+    this.set('isReady', false);
+  },
+
+  populateUpgradeHistoryRecord: function(){
+    var upgradeData = this.get('controller').get('upgradeData')
+    this.set('isReady', (upgradeData != null))
+  },
+
+  /**
+   * progress value is rounded to floor
+   * @type {number}
+   */
+  overallProgress: function () {
+    return 
Math.floor(this.get('controller.upgradeData.Upgrade.progress_percent'));
+  }.property('controller.upgradeData.Upgrade.progress_percent'),
+
+  /**
+   * label of Upgrade status
+   * @type {string}
+   */
+  upgradeStatusLabel: function() {
+    var labelKey = null;
+    switch (this.get('controller.upgradeData.Upgrade.request_status')) {
+      case 'QUEUED':
+      case 'PENDING':
+      case 'IN_PROGRESS':
+        labelKey = 'admin.stackUpgrade.state.inProgress';
+        break;
+      case 'COMPLETED':
+        labelKey = 'admin.stackUpgrade.state.completed';
+        break;
+      case 'ABORTED':
+        labelKey = 'admin.stackUpgrade.state.paused';
+        break;
+      case 'TIMEDOUT':
+      case 'FAILED':
+      case 'HOLDING_FAILED':
+      case 'HOLDING_TIMEDOUT':
+      case 'HOLDING':
+        labelKey = 'admin.stackUpgrade.state.paused';
+        break;
+    }
+    if (labelKey) {
+      labelKey += (this.get('controller.isDowngrade')) ? '.downgrade' : "";
+      return Em.I18n.t(labelKey);
+    } else {
+      return "";
+    }
+  }.property('controller.upgradeData.Upgrade.request_status', 
'controller.isDowngrade'),
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_view.js
new file mode 100644
index 0000000..ef5f46b
--- /dev/null
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_history_view.js
@@ -0,0 +1,303 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+var date = require('utils/date/date');
+
+App.MainAdminStackUpgradeHistoryView = 
App.TableView.extend(App.TableServerViewMixin, {
+
+  controllerBinding: 'App.router.mainAdminStackUpgradeHistoryController',
+
+  templateName: require('templates/main/admin/stack_upgrade/upgrade_history'),
+
+  summary: [],
+
+  isReady: false,
+
+  categories: [
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.all',
+      value: 'ALL',
+      isSelected: true
+    }),
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.upgrade',
+      value: 'UPGRADE',
+      isSelected: false
+    }),
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.downgrade',
+      value: 'DOWNGRADE',
+      isSelected: false
+    }),
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.successful.upgrade',
+      value: 'UPGRADE_COMPLETED',
+      isSelected: false
+    }),
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.aborted.upgrade',
+      value: 'UPGRADE_ABORTED',
+      isSelected: false
+    }),
+    Em.Object.create({
+      labelKey: 
'admin.stackVersions.upgradeHistory.filter.successful.downgrade',
+      value: 'DOWNGRADE_COMPLETED',
+      isSelected: false
+    }),
+    Em.Object.create({
+      labelKey: 'admin.stackVersions.upgradeHistory.filter.aborted.downgrade',
+      value: 'DOWNGRADE_ABORTED',
+      isSelected: false
+    }),
+  ],
+
+  /**
+   * @type {object}
+   */
+  selectedCategory: Em.computed.findBy('categories', 'isSelected', true),
+
+  filteredCount: function(){
+    var filteredContent = this.get('filteredContent').toArray();
+    return filteredContent.length;
+  }.property('filteredContent'),
+
+  /**
+   * displaying content filtered by upgrade type and upgrade status.
+   */
+  filteredContent: function () {
+    var result = [];
+    var filterValue = 'ALL';
+    var category = this.get('selectedCategory');
+    if (category)
+      filterValue = category.get('value');
+    var result = this.filterBy(filterValue);
+    return result.reverse();
+  }.property('selectedCategory'),
+
+  /**
+   * sort and slice recieved content by pagination parameters
+   */
+  pageContent: function () {
+    var content = this.get('filteredContent').toArray();
+    content = this.processForDisplay(content);
+    content = content.slice(this.get('startIndex') - 1, this.get('endIndex'))
+    return content;
+  }.property('filteredContent', 'startIndex', 'endIndex'),
+
+  processForDisplay: function(content){
+    var processedContent = [];
+    content.forEach(function(item){
+      if('UPGRADE' == item.get('direction'))
+        item.set('directionLabel', Em.I18n.t('common.upgrade'));
+      else
+        item.set('directionLabel', Em.I18n.t('common.downgrade'));
+
+      if('NON_ROLLING' == item.get('upgradeType'))
+        item.set('upgradeTypeLabel', Em.I18n.t('common.express'));
+      else
+        item.set('upgradeTypeLabel', Em.I18n.t('common.rolling'));
+
+      item.set('startTimeLabel', date.startTime(item.get('startTime')));
+      item.set('endTimeLabel', date.startTime(item.get('endTime')));
+      item.set('duration', date.durationSummary(item.get('startTime'), 
item.get('endTime')));
+      processedContent.push(item);
+    },this);
+    return processedContent;
+  },
+
+  paginationLeftClass: function () {
+    if (this.get("startIndex") > 1) {
+      return "paginate_previous";
+    }
+    return "paginate_disabled_previous";
+  }.property("startIndex", 'filteredCount'),
+
+  /**
+   * Determines how display "next"-link - as link or text
+   * @type {string}
+   */
+  paginationRightClass: function () {
+    if (this.get("endIndex") < this.get("filteredCount")) {
+      return "paginate_next";
+    }
+    return "paginate_disabled_next";
+  }.property("endIndex", 'filteredCount'),
+
+  /**
+   * Show previous-page if user not in the first page
+   * @method previousPage
+   */
+  previousPage: function () {
+    if (this.get('paginationLeftClass') === 'paginate_previous') {
+      this._super();
+    }
+  },
+
+  /**
+   * Show next-page if user not in the last page
+   * @method nextPage
+   */
+  nextPage: function () {
+    if (this.get('paginationRightClass') === 'paginate_next') {
+      this._super();
+    }
+  },
+
+  willInsertElement: function(){
+    var self = this;
+    this.get('controller').loadStackUpgradeHistoryToModel().done(function(){
+      self.populateUpgradeHistorySummary();
+    });
+  },
+
+  didInsertElement: function () {
+    this.observesCategories();
+  },
+
+  observesCategories: function(){
+    this.get('categories').forEach(function (category) {
+      var label = 
Em.I18n.t(category.labelKey).format(this.filterBy(category.value).length);
+      category.set('label', label)
+    }, this);
+  }.observes('isReady'),
+
+  filterBy: function(filterValue){
+    if ('ALL' == filterValue) {
+      var all_records = App.StackUpgradeHistory.find();
+      return all_records.toArray();
+    } else {
+      var tokens = filterValue.split('_');
+      var direction_token = null
+      var status_token = null
+
+      if (tokens.length == 1) {
+        direction_token = tokens[0]
+      } else if (tokens.length > 1) {
+        direction_token = tokens[0]
+        status_token = tokens[1]
+      }
+
+      var result = []
+      App.StackUpgradeHistory.find().forEach(function(item){
+        var direction = item.get('direction');
+        if (direction == direction_token) {
+          if (status_token != null) {
+            //only for the given status
+            var status = item.get('requestStatus');
+            if (status == status_token) {
+              result.push(item)
+            }
+          } else {
+            //regardless status
+            result.push(item)
+          }
+        }
+      }, this);
+      return result
+    }
+  },
+
+  selectCategory: function(event){
+    this.get('categories').filterProperty('isSelected').setEach('isSelected', 
false);
+    event.context.set('isSelected', true);
+  },
+
+  populateUpgradeHistorySummary: function(){
+    this.set('isReady', false);
+    var result = [
+      Em.Object.create({
+        direction: 'UPGRADE',
+        label:Em.I18n.t('common.upgrade'),
+        hasSuccess: false,
+        success:0,
+        hasAbort: false,
+        abort:0,
+      }),
+      Em.Object.create({
+        direction: 'DOWNGRADE',
+        label:Em.I18n.t('common.downgrade'),
+        hasSuccess: false,
+        success:0,
+        hasAbort: false,
+        abort:0,
+      })
+    ];
+
+    App.StackUpgradeHistory.find().forEach(function(item){
+      var direction = item.get('direction');
+      var status = item.get('requestStatus');
+      if('UPGRADE' == direction){
+        if('COMPLETED' == status){
+          result[0].set('success', result[0].get('success') + 1);
+        } else if ('ABORTED' == status) {
+          result[0].set('abort', result[0].get('abort') + 1);
+        }
+      } else if('DOWNGRADE' == direction){
+        if('COMPLETED' == status){
+          result[1].set('success', result[1].get('success')+1);
+        } else if ('ABORTED' == status){
+          result[1].set('abort', result[1].get('abort')+1);
+        }
+      }
+    }, this);
+
+    result[0].set('hasSuccess', result[0].get('success') > 0);
+    result[1].set('hasSuccess', result[1].get('success') > 0);
+    result[0].set('hasAbort', result[0].get('abort') > 0);
+    result[1].set('hasAbort', result[1].get('abort') > 0);
+
+    this.set('summary', result);
+    this.set('isReady', true);
+  },
+
+  showUpgradeHistoryRecord: function(event) {
+    var record = event.context
+    var title = '';
+    var direction = App.format.normalizeName(record.get('direction'));
+    var type = record.get('upgradeType')
+    if ('ROLLING' == type)
+      type = App.format.normalizeName(type);
+    else if ('NON_ROLLING' == type)
+      type = 'Express'
+
+    title = 
Em.I18n.t('admin.stackVersions.upgradeHistory.record.title').format(type, 
direction, record.get('fromVersion'));
+
+    this.get('controller').set('currentUpgradeRecord', record)
+
+    App.ModalPopup.show({
+      classNames: ['full-width-modal'],
+      header: title,
+      bodyClass: App.MainAdminStackUpgradeHistoryDetailsView,
+      primary: Em.I18n.t('common.dismiss'),
+      secondary: null,
+      didInsertElement: function () {
+        this._super();
+        this.fitHeight();
+        this.fitInnerHeight();
+      },
+      fitInnerHeight: function () {
+        var block = this.$().find('#modal > .modal-body');
+        var scrollable = this.$().find('#modal .scrollable-block');
+        scrollable.css('max-height', Number(block.css('max-height').slice(0, 
-2)) - block.height());
+        block.css('max-height', 'none');
+      },
+    });
+  },
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/test/controllers/main/admin/stack_upgrade_history_controller_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/controllers/main/admin/stack_upgrade_history_controller_test.js
 
b/ambari-web/test/controllers/main/admin/stack_upgrade_history_controller_test.js
new file mode 100644
index 0000000..bbfce19
--- /dev/null
+++ 
b/ambari-web/test/controllers/main/admin/stack_upgrade_history_controller_test.js
@@ -0,0 +1,125 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+require('controllers/main/admin/stack_upgrade_history_controller');
+require('utils/string_utils');
+var testHelpers = require('test/helpers');
+describe('App.MainAdminStackUpgradeHistoryController', function() {
+
+  var controller = App.MainAdminStackUpgradeHistoryController.create({
+
+  });
+
+  describe("#upgradeHistoryUrl", function() {
+    before(function () {
+      this.mock = sinon.stub(App, 'get');
+      this.mock.withArgs('apiPrefix').returns('apiPrefix')
+        .withArgs('clusterName').returns('clusterName');
+    });
+    after(function () {
+      this.mock.restore();
+    });
+    it("should be valid", function() {
+      controller.propertyDidChange('upgradeHistoryUrl');
+      
expect(controller.get('upgradeHistoryUrl')).to.equal('apiPrefix/clusters/clusterName/upgrades?fields=Upgrade');
+    });
+  });
+
+  describe("#requestStatus", function() {
+    beforeEach(function() {
+      this.mock = sinon.stub(App, 'get');
+    });
+    afterEach(function() {
+      this.mock.restore();
+    });
+    it("state should be what the record states", function() {
+      this.mock.returns(false);
+      controller.set('upgradeData', { Upgrade: {request_status: 'COMPLETED'}});
+      controller.propertyDidChange('requestStatus');
+      expect(controller.get('requestStatus')).to.equal('COMPLETED');
+    });
+
+    it("upgradeData is null", function() {
+      this.mock.returns(false);
+      controller.set('upgradeData', null);
+      controller.propertyDidChange('requestStatus');
+      expect(controller.get('requestStatus')).to.be.empty;
+    });
+  });
+
+  describe("#loadStackUpgradeRecord()", function() {
+    it("get upgrade record data", function() {
+      controller.set('currentUpgradeRecord', Em.Object.create({'requestId':1, 
'direction':'DOWNGRADE'}));
+      controller.loadStackUpgradeRecord();
+      var args = testHelpers.findAjaxRequest('name', 'admin.upgrade.data');
+      expect(args[0]).to.exists;
+      expect(args[0].sender).to.be.eql(controller);
+      expect(args[0].data).to.be.eql({
+        id: 1
+      });
+    });
+  });
+
+  describe("#loadUpgradeRecordSuccessCallback()", function() {
+    it("correct data", function() {
+      var data = {
+        "Upgrade": {
+          "request_status": "COMPLETED"
+        },
+        "upgrade_groups": [
+          {
+            "UpgradeGroup": {
+              "id": 1
+            },
+            "upgrade_items": []
+          }
+        ]};
+      controller.loadUpgradeRecordSuccessCallback(data);
+      expect(controller.get('upgradeData') == null).to.be.false;
+    });
+
+    it("data is null", function() {
+      var data = null;
+      controller.set('upgradeData', null)
+      controller.loadUpgradeRecordSuccessCallback(data);
+      expect(controller.get('upgradeData') == null).to.be.true;
+    });
+  });
+
+  describe("#getUpgradeItem()", function() {
+    it("default callback", function() {
+      var item = Em.Object.create({
+        request_id: 1,
+        group_id: 2,
+        stage_id: 3
+      });
+      controller.getUpgradeItem(item);
+      var args = testHelpers.findAjaxRequest('name', 
'admin.upgrade.upgrade_item');
+      expect(args[0]).to.exists;
+      expect(args[0].sender).to.be.eql(controller);
+      expect(args[0].success).to.be.equal('getUpgradeItemSuccessCallback');
+      expect(args[0].data).to.be.eql({
+        upgradeId: 1,
+        groupId: 2,
+        stageId: 3
+      });
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/test/mappers/stack_upgrade_history_mapper_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mappers/stack_upgrade_history_mapper_test.js 
b/ambari-web/test/mappers/stack_upgrade_history_mapper_test.js
new file mode 100644
index 0000000..07027e1
--- /dev/null
+++ b/ambari-web/test/mappers/stack_upgrade_history_mapper_test.js
@@ -0,0 +1,372 @@
+/**
+ * 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.
+ */
+
+/*eslint-disable */
+
+var App = require('app');
+
+require('mappers/stack_upgrade_history_mapper');
+
+describe('App.stackUpgradeHistoryMapper', function () {
+
+  describe('#map', function () {
+
+    var data = {
+        "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades?fields=Upgrade";,
+        "items" : [
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/7";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463779169144,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463779266087,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 7,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463779170159,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/8";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463779266212,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463779299440,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 8,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463779267220,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/9";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463780699654,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463780757685,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 9,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463780700670,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/10";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463780757799,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463780794009,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 10,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463780758807,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/11";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463781287967,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463781341452,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 11,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463781288984,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/12";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1463781341576,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1463781371778,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 12,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1463781342585,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/13";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464120656181,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464120881477,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 13,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464120657198,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/14";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464120881574,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464120918774,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 14,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464120882580,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/15";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464120943986,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464121132856,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 15,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464120945002,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/16";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464121132981,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464121167178,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 16,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464121133988,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/17";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464121207511,
+              "direction" : "UPGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464121301821,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Upgrading to 2.4.0.0-169",
+              "request_id" : 17,
+              "request_status" : "ABORTED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464121208524,
+              "suspended" : false,
+              "to_version" : "2.4.0.0-169",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          },
+          {
+            "href" : 
"http://bdavm079.svl.ibm.com:8080/api/v1/clusters/bi/upgrades/18";,
+            "Upgrade" : {
+              "cluster_name" : "bi",
+              "create_time" : 1464121301933,
+              "direction" : "DOWNGRADE",
+              "downgrade_allowed" : true,
+              "end_time" : 1464121336149,
+              "exclusive" : false,
+              "from_version" : "2.3.6.0-3712",
+              "pack" : "nonrolling-upgrade-2.4",
+              "progress_percent" : 100.0,
+              "request_context" : "Downgrading to 2.3.6.0-3712",
+              "request_id" : 18,
+              "request_status" : "COMPLETED",
+              "skip_failures" : false,
+              "skip_service_check_failures" : false,
+              "start_time" : 1464121302941,
+              "suspended" : false,
+              "to_version" : "2.3.6.0-3712",
+              "type" : "INTERNAL_REQUEST",
+              "upgrade_type" : "NON_ROLLING"
+            }
+          }
+        ]
+      };
+
+    var upgradeParseResult = {
+        'clusterName':'bi',
+        'createTime':1464121301933,
+        "direction" : "DOWNGRADE",
+        "downgradeAllowed" : true,
+        "endTime" : 1464121336149,
+        "fromVersion" : "2.3.6.0-3712",
+        "requestId" : 18,
+        "requestStatus" : "COMPLETED",
+        "skipFailures" : false,
+        "skipServiceCheckFailures" : false,
+        "startTime" : 1464121302941,
+        "toVersion" : "2.3.6.0-3712",
+        "upgradeType" : "NON_ROLLING"
+    };
+
+    beforeEach(function () {
+      App.resetDsStoreTypeMap(App.StackUpgradeHistory);
+      sinon.stub(App.store, 'commit', Em.K);
+    });
+
+    afterEach(function(){
+      App.store.commit.restore();
+    });
+
+    it('Parse upgrade records returned by the Ambari server', function () {
+      App.stackUpgradeHistoryMapper.map(data);
+      var all_records = App.StackUpgradeHistory.find();
+      var upgrades = all_records.toArray();
+      expect(upgrades.length).to.eql(12);
+      var total_downgrades = 0;
+      var total_upgrades = 0;
+      upgrades.forEach(function(upgrade){
+        var direction = upgrade.get('direction')
+        if ('DOWNGRADE' == direction){
+          total_downgrades++;
+        }
+        if ('UPGRADE' == direction){
+          total_upgrades++;
+        }
+      });
+      expect(total_upgrades).to.eql(6);
+      expect(total_downgrades).to.eql(6);
+
+      var record = App.StackUpgradeHistory.find().findProperty('requestId', 
18);
+      Em.keys(upgradeParseResult).forEach(function (key) {
+        expect(record.get(key)).to.eql(upgradeParseResult[key]);
+      });
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/test/models/finished_upgrade_entity_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/finished_upgrade_entity_test.js 
b/ambari-web/test/models/finished_upgrade_entity_test.js
new file mode 100644
index 0000000..a2a9527
--- /dev/null
+++ b/ambari-web/test/models/finished_upgrade_entity_test.js
@@ -0,0 +1,197 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+require('models/finished_upgrade_entity');
+
+function getModel() {
+  return App.finishedUpgradeEntity.create();
+}
+
+describe('App.finishedUpgradeEntity', function () {
+  var model;
+
+  beforeEach(function () {
+    model = getModel();
+  });
+
+  App.TestAliases.testAsComputedNotEqual(getModel(), 'isVisible', 'status', 
'PENDING');
+
+  describe("#progress", function() {
+    it("progress_percent = 1.9", function() {
+      model.set('progress_percent', 1.9);
+      model.propertyDidChange('progress');
+      expect(model.get('progress')).to.equal(1);
+    });
+    it("progress_percent = 1", function() {
+      model.set('progress_percent', 1);
+      model.propertyDidChange('progress');
+      expect(model.get('progress')).to.equal(1);
+    });
+  });
+
+  describe("#isActive", function() {
+    it("status IN_PROGRESS", function() {
+      model.set('status', 'IN_PROGRESS');
+      model.propertyDidChange('isActive');
+      expect(model.get('isActive')).to.be.true;
+    });
+    it("status PENDING", function() {
+      model.set('status', 'PENDING');
+      model.propertyDidChange('isActive');
+      expect(model.get('isActive')).to.be.false;
+    });
+  });
+
+  describe('#isExpandableGroup', function () {
+
+    var cases = [
+      {
+        input: {
+          type: 'ITEM'
+        },
+        isExpandableGroup: false,
+        title: 'not upgrade group'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'PENDING',
+          hasExpandableItems: false
+        },
+        isExpandableGroup: false,
+        title: 'pending upgrade group without expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'ABORTED',
+          hasExpandableItems: false
+        },
+        isExpandableGroup: true,
+        title: 'aborted upgrade group without expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'ABORTED',
+          hasExpandableItems: true
+        },
+        isExpandableGroup: true,
+        title: 'aborted upgrade group with expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'IN_PROGRESS',
+          hasExpandableItems: false
+        },
+        isExpandableGroup: true,
+        title: 'active upgrade group'
+      }
+    ];
+
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        model.setProperties(item.input);
+        
expect(model.get('isExpandableGroup')).to.equal(item.isExpandableGroup);
+      });
+    });
+
+  });
+
+  describe('#upgradeGroupStatus', function () {
+
+    var cases = [
+      {
+        input: {
+          type: 'ITEM',
+          upgradeSuspended: false
+        },
+        upgradeGroupStatus: undefined,
+        title: 'not upgrade group'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'PENDING',
+          hasExpandableItems: false,
+          upgradeSuspended: false
+        },
+        upgradeGroupStatus: 'PENDING',
+        title: 'pending upgrade group'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'PENDING',
+          hasExpandableItems: true,
+          upgradeSuspended: false
+        },
+        upgradeGroupStatus: 'SUBITEM_FAILED',
+        title: 'pending upgrade group with expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'ABORTED',
+          hasExpandableItems: false,
+          upgradeSuspended: false
+        },
+        upgradeGroupStatus: 'ABORTED',
+        title: 'aborted upgrade group with expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'ABORTED',
+          hasExpandableItems: true,
+          upgradeSuspended: true
+        },
+        upgradeGroupStatus: 'ABORTED',
+        title: 'aborted upgrade group with expandable items'
+      },
+      {
+        input: {
+          type: 'GROUP',
+          status: 'IN_PROGRESS',
+          hasExpandableItems: false,
+          upgradeSuspended: false
+        },
+        upgradeGroupStatus: 'IN_PROGRESS',
+        title: 'active upgrade'
+      }
+    ];
+
+    beforeEach(function() {
+      this.mock = sinon.stub(App, 'get');
+    });
+    afterEach(function() {
+      this.mock.restore();
+    });
+
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        this.mock.returns(item.input.upgradeSuspended);
+        model.setProperties(item.input);
+        
expect(model.get('upgradeGroupStatus')).to.equal(item.upgradeGroupStatus);
+      });
+    });
+
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_details_view_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_details_view_test.js
 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_details_view_test.js
new file mode 100644
index 0000000..ab38e34
--- /dev/null
+++ 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_details_view_test.js
@@ -0,0 +1,248 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+require('views/main/admin/stack_upgrade/upgrade_history_details_view');
+var testHelpers = require('test/helpers');
+
+describe('App.MainAdminStackUpgradeHistoryDetailsView', function () {
+  var view;
+
+  beforeEach(function () {
+    view = App.MainAdminStackUpgradeHistoryDetailsView.create({
+      controller: {
+        currentUpgradeRecord: App.StackUpgradeHistory.createRecord({
+          'requestId':1,
+          'direction':'DOWNGRADE',
+        }),
+      }
+    });
+  });
+
+  afterEach(function () {
+    view.destroy();
+  });
+
+  describe("#overallProgress", function () {
+    it("progress is 1.9", function () {
+      view.set('controller.upgradeData', {
+        Upgrade: {
+          progress_percent: 1.9
+        }
+      });
+      expect(view.get('overallProgress')).to.equal(1);
+    });
+    it("progress is 1", function () {
+      view.set('controller.upgradeData', {
+        Upgrade: {
+          progress_percent: 1
+        }
+      });
+      expect(view.get('overallProgress')).to.equal(1);
+    });
+  });
+
+  describe("#willInsertElement()", function() {
+    beforeEach(function () {
+      sinon.spy(view.get('controller'), 'loadStackUpgradeRecord');
+    });
+    afterEach(function () {
+      view.get('controller').loadStackUpgradeRecord.restore();
+    });
+    it("load data by controller is called once", function() {
+      view.set('controller.currentUpgradeRecord', 
App.StackUpgradeHistory.createRecord({
+        'requestId':1,
+        'direction':'DOWNGRADE',
+      }));
+      view.willInsertElement();
+      
expect(view.get('controller').loadStackUpgradeRecord.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#willDestroyElement()", function () {
+    it("reset ready flag", function () {
+      view.set('isReady', true);
+      view.willDestroyElement();
+      expect(view.get('isReady')).to.be.false;
+    });
+  });
+
+  describe("#upgradeStatusLabel", function () {
+    beforeEach(function () {
+      Em.setFullPath(view, 'controller.upgradeData.Upgrade', {});
+    });
+
+    [
+      {
+        data: {
+          status: 'QUEUED',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress')
+      },
+      {
+        data: {
+          status: 'PENDING',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress')
+      },
+      {
+        data: {
+          status: 'IN_PROGRESS',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress')
+      },
+      {
+        data: {
+          status: 'COMPLETED',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.completed')
+      },
+      {
+        data: {
+          status: 'ABORTED',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: 'TIMEDOUT',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: 'FAILED',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: 'HOLDING_FAILED',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: 'HOLDING_TIMEDOUT',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: 'HOLDING',
+          isDowngrade: false
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
+      {
+        data: {
+          status: '',
+          isDowngrade: false
+        },
+        result: ''
+      },
+      {
+        data: {
+          status: 'QUEUED',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress.downgrade')
+      },
+      {
+        data: {
+          status: 'PENDING',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress.downgrade')
+      },
+      {
+        data: {
+          status: 'IN_PROGRESS',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.inProgress.downgrade')
+      },
+      {
+        data: {
+          status: 'COMPLETED',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.completed.downgrade')
+      },
+      {
+        data: {
+          status: 'ABORTED',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      },
+      {
+        data: {
+          status: 'TIMEDOUT',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      },
+      {
+        data: {
+          status: 'FAILED',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      },
+      {
+        data: {
+          status: 'HOLDING_FAILED',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      },
+      {
+        data: {
+          status: 'HOLDING_TIMEDOUT',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      },
+      {
+        data: {
+          status: 'HOLDING',
+          isDowngrade: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused.downgrade')
+      }
+    ].forEach(function (test) {
+        it('status = ' + test.data.status + ", isDowngrade = " + 
test.data.isDowngrade, function () {
+          view.set('controller.isDowngrade', test.data.isDowngrade);
+          view.set('controller.upgradeData.Upgrade.request_status', 
test.data.status);
+          view.propertyDidChange('upgradeStatusLabel');
+          expect(view.get('upgradeStatusLabel')).to.equal(test.result);
+        });
+      });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef418377/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_view_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_view_test.js 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_view_test.js
new file mode 100644
index 0000000..d68a854
--- /dev/null
+++ 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_history_view_test.js
@@ -0,0 +1,173 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+require('views/main/admin/stack_upgrade/upgrade_history_view');
+var testHelpers = require('test/helpers');
+
+describe('App.MainAdminStackUpgradeHistoryView', function () {
+  var view;
+
+  beforeEach(function () {
+    view = App.MainAdminStackUpgradeHistoryView.create();
+  });
+
+  afterEach(function () {
+    view.destroy();
+  });
+
+  describe("#filterBy()", function () {
+    var records = [
+        Em.Object.create({
+          requestStatus: "ABORTED",
+          direction: "UPGRADE"
+        }),
+        Em.Object.create({
+          requestStatus: "ABORTED",
+          direction: "DOWNGRADE"
+        }),
+        Em.Object.create({
+          requestStatus: "COMPLETED",
+          direction: "UPGRADE"
+        }),
+        Em.Object.create({
+          requestStatus: "COMPLETED",
+          direction: "DOWNGRADE"
+        })
+      ];
+
+
+    beforeEach(function () {
+      this.mock = sinon.stub(App.StackUpgradeHistory, 'find');
+    });
+
+    afterEach(function () {
+      this.mock.restore();
+    });
+
+    it('All should return all records', function(){
+      this.mock.returns(records);
+      var filteredResults = view.filterBy('ALL')
+      expect(filteredResults.length == 4).to.be.true
+    });
+
+    it('Filter aborted upgrades', function(){
+      this.mock.returns(records);
+      var filteredResults = view.filterBy('UPGRADE_ABORTED')
+      expect(filteredResults.length == 1).to.be.true
+    });
+
+    it('Filter completed upgrades', function(){
+      this.mock.returns(records);
+      var filteredResults = view.filterBy('UPGRADE_COMPLETED')
+      expect(filteredResults.length == 1).to.be.true
+    });
+
+    it('Filter aborted downgrades', function(){
+      this.mock.returns(records);
+      var filteredResults = view.filterBy('DOWNGRADE_ABORTED')
+      expect(filteredResults.length == 1).to.be.true
+    });
+
+    it('Filter completed downgrades', function(){
+      this.mock.returns(records);
+      var filteredResults = view.filterBy('DOWNGRADE_COMPLETED')
+      expect(filteredResults.length == 1).to.be.true
+    });
+  });
+
+  describe("#didInsertElement()", function() {
+    beforeEach(function () {
+      sinon.stub(view, 'observesCategories', Em.K);
+    });
+    afterEach(function () {
+      view.observesCategories.restore();
+    });
+    it("observesCategories is called once", function() {
+      view.didInsertElement();
+      expect(view.observesCategories.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#observesCategories()", function () {
+    var mock = {format: Em.K};
+    beforeEach(function () {
+      sinon.stub(Em.I18n, 't').returns(mock);
+      sinon.stub(mock, 'format').returns('label');
+      sinon.stub(view, 'filterBy').returns([]);
+      view.set('categories', [
+        Em.Object.create({
+          labelKey: 'labelKey',
+          value: 'value',
+          isSelected: false
+        })
+      ]);
+      view.observesCategories();
+    });
+    afterEach(function () {
+      Em.I18n.t.restore();
+      mock.format.restore();
+      view.filterBy.restore();
+    });
+    it("categories[0].label is updated", function () {
+      expect(view.get('categories')[0].get('label')).to.equal('label');
+    });
+  });
+
+  describe("#selectCategory()", function() {
+    var event;
+    beforeEach(function () {
+      event = {
+        context: Em.Object.create({
+          isSelected: false,
+          value: 'ALL',
+        })
+      };
+      view.set('categories', [
+        Em.Object.create({
+          isSelected: true,
+          value: 'UPGRADE_COMPLETED',
+        }),
+        event.context
+      ]);
+      view.selectCategory(event);
+    });
+    afterEach(function () {
+    });
+    it("categories[0].isSelected false", function() {
+      expect(view.get('categories')[0].get('isSelected')).to.be.false;
+    });
+    it("isSelected is true", function() {
+      expect(event.context.get('isSelected')).to.be.true;
+    });
+  });
+
+  describe("#willInsertElement()", function() {
+    beforeEach(function () {
+      sinon.spy(view.get('controller'), 'loadStackUpgradeHistoryToModel');
+    });
+    afterEach(function () {
+      view.get('controller').loadStackUpgradeHistoryToModel.restore();
+    });
+    it("load data by controller is called once", function() {
+      view.willInsertElement();
+      
expect(view.get('controller').loadStackUpgradeHistoryToModel.calledOnce).to.be.true;
+    });
+  });
+});

Reply via email to