瀏覽代碼

AMBARI-13218 Web Client Expose Ability To Suspend and Resume Upgrade. (atkach)

Andrii Tkach 9 年之前
父節點
當前提交
0a0e29d0ec

+ 13 - 7
ambari-web/app/app.js

@@ -69,9 +69,9 @@ module.exports = Em.Application.create({
    * flag is true when upgrade process is aborted
    * @returns {boolean}
    */
-  upgradeAborted: function() {
-    return this.get('upgradeState') === "ABORTED";
-  }.property('upgradeState'),
+  upgradeAborted: function () {
+    return this.get('upgradeState') === "ABORTED" && !App.router.get('mainAdminStackAndUpgradeController.isSuspended');
+  }.property('upgradeState', 'router.mainAdminStackAndUpgradeController.isSuspended'),
 
   /**
    * RU is running
@@ -87,8 +87,11 @@ module.exports = Em.Application.create({
    * @returns {boolean}
    */
   wizardIsNotFinished: function () {
-    return this.get('upgradeIsRunning') || this.get('upgradeAborted') || App.router.get('wizardWatcherController.isNonWizardUser');
-  }.property('upgradeIsRunning', 'upgradeAborted', 'router.wizardWatcherController.isNonWizardUser'),
+    return this.get('upgradeIsRunning') ||
+           this.get('upgradeAborted') ||
+           App.router.get('wizardWatcherController.isNonWizardUser') ||
+           App.router.get('mainAdminStackAndUpgradeController.isSuspended');
+  }.property('upgradeIsRunning', 'upgradeAborted', 'router.wizardWatcherController.isNonWizardUser', 'router.mainAdminStackAndUpgradeController.isSuspended'),
 
   /**
    * compute user access rights by permission type
@@ -102,11 +105,14 @@ module.exports = Em.Application.create({
    * @return {boolean}
    */
   isAccessible: function (type) {
-    if (!App.get('supports.opsDuringRollingUpgrade') && !['INIT', 'COMPLETED'].contains(this.get('upgradeState')) && !type.contains('upgrade_')) {
+    if (!App.router.get('mainAdminStackAndUpgradeController.isSuspended') &&
+        !App.get('supports.opsDuringRollingUpgrade') &&
+        !['INIT', 'COMPLETED'].contains(this.get('upgradeState')) &&
+        !type.contains('upgrade_')) {
       return false;
     }
 
-    if (App.router.get('wizardWatcherController').get('isNonWizardUser')) {
+    if (App.router.get('wizardWatcherController.isNonWizardUser')) {
       return false;
     }
 

+ 48 - 1
ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js

@@ -75,7 +75,7 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   /**
    * properties that stored to localStorage to resume wizard progress
    */
-  wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'isDowngrade'],
+  wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'isDowngrade', 'isSuspended'],
 
   /**
    * mutable properties of Upgrade Task
@@ -153,6 +153,18 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
    */
   nonActiveStates: ['PENDING', 'ABORTED'],
 
+  /**
+   * status of Upgrade request
+   * @type {string}
+   */
+  requestStatus: function () {
+    if (this.get('isSuspended')) {
+      return 'SUSPENDED';
+    } else {
+      return App.get('upgradeState');
+    }
+  }.property('isSuspended', 'App.upgradeState'),
+
   init: function () {
     this.initDBProperties();
   },
@@ -987,5 +999,40 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
 
   loadRepositoriesErrorCallback: function (request, ajaxOptions, error) {
     console.log('Error message is: ' + request.responseText);
+  },
+
+  /**
+   * @returns {$.ajax}
+   */
+  suspendUpgrade: function () {
+    var self = this;
+    return this.abortUpgrade().done(function () {
+      App.set('upgradeState', 'ABORTED');
+      self.set('isSuspended', true);
+      self.setDBProperty('upgradeState', 'ABORTED');
+      self.setDBProperty('isSuspended', true);
+      App.clusterStatus.setClusterStatus({
+        wizardControllerName: self.get('name'),
+        localdb: App.db.data
+      });
+    });
+  },
+
+  /**
+   * @returns {$.ajax}
+   */
+  resumeUpgrade: function() {
+    var self = this;
+    this.retryUpgrade().done(function () {
+      App.set('upgradeState', 'PENDING');
+      App.propertyDidChange('upgradeAborted');
+      self.set('isSuspended', false);
+      self.setDBProperty('upgradeState', 'PENDING');
+      self.setDBProperty('isSuspended', false);
+      App.clusterStatus.setClusterStatus({
+        wizardControllerName: self.get('name'),
+        localdb: App.db.data
+      });
+    });
   }
 });

+ 3 - 0
ambari-web/app/messages.js

@@ -1415,6 +1415,7 @@ Em.I18n.translations = {
   'admin.stackVersions.version.upgrade.notFinalized.warning': "The upgrade has not been finalized yet. After the cluster is verified to be functional, do not forget to finalize the upgrade as soon as possible (within a couple of days is highly recommended) as running the cluster in unfinalized state causes extra resource requirements on HDFS.",
   'admin.stackVersions.version.upgrade.running': "Upgrade: In Process",
   'admin.stackVersions.version.upgrade.aborted': "Upgrade: Aborted",
+  'admin.stackVersions.version.upgrade.suspended': "Upgrade: Paused",
   'admin.stackVersions.version.downgrade.pause': "Downgrade: Action Required",
   'admin.stackVersions.version.downgrade.running': "Downgrade: In Process",
   'admin.stackVersions.version.downgrade.aborted': "Downgrade: Aborted",
@@ -1471,6 +1472,8 @@ Em.I18n.translations = {
   'admin.stackUpgrade.dialog.closePause': "Upgrade is paused. \n If you dismiss this window, you can resume Upgrade later.",
   'admin.stackUpgrade.dialog.aborted': "Upgrade is aborted. \n You can either downgrade or retry upgrade.",
   'admin.stackUpgrade.dialog.downgrade.aborted': "Downgrade is aborted. \n You can retry downgrade.",
+  'admin.stackUpgrade.dialog.suspended': "Upgrade is Paused",
+  'admin.stackUpgrade.dialog.resume': "Resume Upgrade",
   'admin.stackUpgrade.dialog.details.open': "show details",
   'admin.stackUpgrade.dialog.details.hide': "hide details",
   'admin.stackUpgrade.dialog.notActive': "Waiting to execute the next task...",

+ 4 - 1
ambari-web/app/models/upgrade_entity.js

@@ -83,8 +83,11 @@ App.upgradeEntity = Em.Object.extend({
   }.property('isActive', 'hasExpandableItems'),
 
   upgradeGroupStatus: function () {
+    if (this.get('status') === 'ABORTED' && App.router.get('mainAdminStackAndUpgradeController.isSuspended')) {
+      return 'SUSPENDED';
+    }
     if (this.get('type') === 'GROUP') {
       return !this.get('isActive') && this.get('hasExpandableItems') ? 'SUBITEM_FAILED' : this.get('status');
     }
-  }.property('isExpandableGroup')
+  }.property('isExpandableGroup', 'App.router.mainAdminStackAndUpgradeController.isSuspended')
 });

+ 5 - 0
ambari-web/app/templates/application.hbs

@@ -144,6 +144,11 @@
                           <i class="icon-remove"></i>&nbsp;{{t admin.stackVersions.version.upgrade.aborted}}</span>
                     {{/if}}
                   {{/if}}
+                  {{#if App.router.mainAdminStackAndUpgradeController.isSuspended}}
+                      <span class="label upgrade-holding" {{action "openUpgradeDialog" target="App.router.mainAdminStackAndUpgradeController"}}>
+                          <i class="icon-pause"></i>&nbsp;{{t admin.stackVersions.version.upgrade.suspended}}
+                      </span>
+                  {{/if}}
                 </a>
                 </p>
                 {{#if App.router.mainAdminStackAndUpgradeController.isFinalizeItem}}

+ 38 - 25
ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_wizard.hbs

@@ -18,12 +18,12 @@
 
 <div id="stack-upgrade-dialog">
   <div {{bindAttr class="view.isLoaded::hidden :row-fluid"}}>
-    <div class="span3 task-list-main-warp">{{statusIcon controller.upgradeData.Upgrade.request_status}}
+    <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.upgradeData.Upgrade.request_status"
+        statusBinding="controller.requestStatus"
       }}
     </div>
     <div class="span1">
@@ -115,7 +115,7 @@
                 {{#if view.isDowngradeAvailable}}
                   <button class="btn btn-danger" {{bindAttr disabled="controller.requestInProgress"}} {{action confirmDowngrade view.manualItem target="controller"}}>{{t common.downgrade}}</button>
                 {{/if}}
-                <button class="btn" {{action closeWizard target="view.parentView"}}>{{t admin.stackUpgrade.finalize.later}}</button>
+                <button class="btn" {{action pauseUpgrade target="view"}}>{{t admin.stackUpgrade.finalize.later}}</button>
                 <button class="btn btn-success" {{bindAttr disabled="view.isManualProceedDisabled"}} {{action complete view.manualItem target="view"}}>
                   {{t common.finalize}}
                 </button>
@@ -153,30 +153,43 @@
             </div>
           </div>
         {{/if}}
-        {{#if App.upgradeAborted}}
-          {{#unless requestInProgress}}
-            <div class="box details-box">
-              <label class="message">
-                {{#if view.isDowngradeAvailable}}
-                  {{t admin.stackUpgrade.dialog.aborted}}
-                {{else}}
-                  {{t admin.stackUpgrade.dialog.downgrade.aborted}}
-                {{/if}}
-              </label>
-              <div class="button-row">
-                {{#if view.isDowngradeAvailable}}
+        {{#if isSuspended}}
+          <div class="box details-box">
+            <label class="message">
+              {{t admin.stackUpgrade.dialog.suspended}}
+            </label>
+            <div class="button-row">
+              <button class="btn btn-primary" {{action resumeUpgrade target="controller"}}>
+                {{t admin.stackUpgrade.dialog.resume}}
+              </button>
+            </div>
+          </div>
+        {{else}}
+          {{#if App.upgradeAborted}}
+            {{#unless requestInProgress}}
+              <div class="box details-box">
+                <label class="message">
+                    {{#if view.isDowngradeAvailable}}
+                      {{t admin.stackUpgrade.dialog.aborted}}
+                    {{else}}
+                      {{t admin.stackUpgrade.dialog.downgrade.aborted}}
+                    {{/if}}
+                </label>
+                <div class="button-row">
+                    {{#if view.isDowngradeAvailable}}
                   <button class="btn btn-danger" {{bindAttr disabled="controller.requestInProgress"}} {{action confirmDowngrade view.manualItem target="controller"}}>{{t common.downgrade}}</button>
-                {{/if}}
-                <button class="btn btn-success" {{action retryUpgrade target="controller"}}>
-                  {{#if view.isDowngradeAvailable}}
-                    {{t common.reUpgrade}}
-                  {{else}}
-                    {{t common.reDowngrade}}
-                  {{/if}}
-                </button>
+                    {{/if}}
+                    <button class="btn btn-success" {{action retryUpgrade target="controller"}}>
+                      {{#if view.isDowngradeAvailable}}
+                        {{t common.reUpgrade}}
+                      {{else}}
+                        {{t common.reDowngrade}}
+                      {{/if}}
+                    </button>
+                </div>
               </div>
-            </div>
-          {{/unless}}
+            {{/unless}}
+          {{/if}}
         {{/if}}
       </div>
       {{#each group in view.upgradeGroups}}

+ 0 - 1
ambari-web/app/utils/db.js

@@ -36,7 +36,6 @@ var InitialData =  {
   'AddService' : {},
   'WidgetWizard' : {},
   'KerberosWizard': {},
-  'StackUpgrade' : {},
   'ReassignMaster' : {},
   'AddSecurity': {},
   'AddAlertDefinition': {

+ 1 - 0
ambari-web/app/utils/helper.js

@@ -820,6 +820,7 @@ App.registerBoundHelper('statusIcon', Em.View.extend({
     'QUEUED': 'icon-cog queued',
     'IN_PROGRESS': 'icon-cogs in_progress',
     'HOLDING': 'icon-pause',
+    'SUSPENDED': 'icon-pause',
     'ABORTED': 'icon-minus aborted',
     'TIMEDOUT': 'icon-time timedout',
     'HOLDING_TIMEDOUT': 'icon-time timedout',

+ 15 - 5
ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js

@@ -125,6 +125,7 @@ App.UpgradeVersionBoxView = Em.View.extend({
     });
     var isInstalling = this.get('parentView.repoVersions').someProperty('status', 'INSTALLING');
     var isAborted = App.get('upgradeState') === 'ABORTED';
+    var isSuspended = App.router.get('mainAdminStackAndUpgradeController.isSuspended');
 
     if (status === 'CURRENT') {
       element.set('isLabel', true);
@@ -187,10 +188,17 @@ App.UpgradeVersionBoxView = Em.View.extend({
       }
     }
     else if (isAborted) {
-      element.set('isButton', true);
-      element.set('text', this.get('controller.isDowngrade') ? Em.I18n.t('common.reDowngrade') : Em.I18n.t('common.reUpgrade'));
-      element.set('action', this.get('controller.isDowngrade') ? 'confirmRetryDowngrade' : 'confirmRetryUpgrade');
-      element.set('isDisabled', this.get('controller.requestInProgress'));
+      if (isSuspended) {
+        element.set('isButton', true);
+        element.set('text', Em.I18n.t('admin.stackUpgrade.dialog.resume'));
+        element.set('action', 'resumeUpgrade');
+        element.set('isDisabled', this.get('controller.requestInProgress'));
+      } else {
+        element.set('isButton', true);
+        element.set('text', this.get('controller.isDowngrade') ? Em.I18n.t('common.reDowngrade') : Em.I18n.t('common.reUpgrade'));
+        element.set('action', this.get('controller.isDowngrade') ? 'confirmRetryDowngrade' : 'confirmRetryUpgrade');
+        element.set('isDisabled', this.get('controller.requestInProgress'));
+      }
     }
     return element;
   }.property(
@@ -199,7 +207,9 @@ App.UpgradeVersionBoxView = Em.View.extend({
     'isUpgrading',
     'controller.requestInProgress',
     'parentView.repoVersions.@each.status',
-    'isUpgradeAvailable'),
+    'isUpgradeAvailable',
+    'App.router.mainAdminStackAndUpgradeController.isSuspended'
+  ),
 
   didInsertElement: function () {
     this.checkUpgradeAvailability();

+ 14 - 3
ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js

@@ -183,7 +183,7 @@ App.upgradeWizardView = Em.View.extend({
    */
   upgradeStatusLabel: function() {
     var labelKey = null;
-    switch (this.get('controller.upgradeData.Upgrade.request_status')) {
+    switch (App.get('upgradeState')) {
       case 'QUEUED':
       case 'PENDING':
       case 'IN_PROGRESS':
@@ -193,7 +193,11 @@ App.upgradeWizardView = Em.View.extend({
         labelKey = 'admin.stackUpgrade.state.completed';
         break;
       case 'ABORTED':
-        labelKey = 'admin.stackUpgrade.state.aborted';
+        if (this.get('controller.isSuspended')) {
+          labelKey = 'admin.stackUpgrade.state.paused';
+        } else {
+          labelKey = 'admin.stackUpgrade.state.aborted';
+        }
         break;
       case 'TIMEDOUT':
       case 'FAILED':
@@ -209,7 +213,7 @@ App.upgradeWizardView = Em.View.extend({
     } else {
       return "";
     }
-  }.property('controller.upgradeData.Upgrade.request_status', 'controller.isDowngrade'),
+  }.property('App.upgradeState', 'controller.isDowngrade', 'controller.isSuspended'),
 
   /**
    * toggle details box
@@ -353,5 +357,12 @@ App.upgradeWizardView = Em.View.extend({
   complete: function (event) {
     this.get('controller').setUpgradeItemStatus(event.context, 'COMPLETED');
     this.set('isManualDone', false);
+  },
+
+  pauseUpgrade: function() {
+    if (this.get('isFinalizeItem')) {
+      this.get('controller').suspendUpgrade();
+    }
+    this.get('parentView').closeWizard();
   }
 });

+ 78 - 4
ambari-web/test/app_test.js

@@ -399,82 +399,119 @@ describe('App', function () {
   });
 
   describe("#isAccessible()", function() {
+
+    beforeEach(function () {
+      this.mock = sinon.stub(App.router, 'get');
+    });
+    afterEach(function () {
+      this.mock.restore();
+    });
+
     it("Upgrade running, element should be blocked", function() {
       App.set('upgradeState', "IN_PROGRESS");
       App.set('isAdmin', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ADMIN')).to.be.false;
     });
     it("Upgrade running, upgrade element should not be blocked", function() {
       App.set('upgradeState', "IN_PROGRESS");
       App.set('isAdmin', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('upgrade_ADMIN')).to.be.true;
     });
     it("Upgrade running, upgrade element should not be blocked", function() {
       App.set('upgradeState', "IN_PROGRESS");
       App.set('isAdmin', true);
       App.set('supports.opsDuringRollingUpgrade', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ADMIN')).to.be.true;
       App.set('supports.opsDuringRollingUpgrade', false);
     });
     it("ADMIN type, isAdmin true", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ADMIN')).to.be.true;
     });
     it("ADMIN type, isAdmin false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ADMIN')).to.be.false;
     });
     it("MANAGER type, isOperator false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', true);
       App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('MANAGER')).to.be.true;
     });
     it("MANAGER type, isAdmin false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', false);
       App.set('isOperator', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('MANAGER')).to.be.true;
     });
     it("MANAGER type, isAdmin and isOperator false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', false);
       App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('MANAGER')).to.be.false;
     });
     it("OPERATOR type, isOperator false", function() {
       App.set('upgradeState', "INIT");
       App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('OPERATOR')).to.be.false;
     });
     it("OPERATOR type, isOperator false", function() {
       App.set('upgradeState', "INIT");
       App.set('isOperator', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('OPERATOR')).to.be.true;
     });
     it("ONLY_ADMIN type, isAdmin false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ONLY_ADMIN')).to.be.false;
     });
     it("ONLY_ADMIN type, isAdmin true, isOperator false", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', true);
       App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ONLY_ADMIN')).to.be.true;
     });
     it("ONLY_ADMIN type, isAdmin true, isOperator true", function() {
       App.set('upgradeState', "INIT");
       App.set('isAdmin', true);
       App.set('isOperator', true);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('ONLY_ADMIN')).to.be.false;
     });
     it("unknown type", function() {
       App.set('upgradeState', "INIT");
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
       expect(App.isAccessible('')).to.be.false;
     });
+    it("ONLY_ADMIN type, isAdmin true, isOperator true, isSuspended true", function() {
+      App.set('upgradeState', "ABORTED");
+      App.set('isAdmin', true);
+      App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(false);
+      this.mock.withArgs('mainAdminStackAndUpgradeController.isSuspended').returns(true);
+      expect(App.isAccessible('ONLY_ADMIN')).to.be.true;
+    });
+    it("ONLY_ADMIN type, isNonWizardUser true", function() {
+      App.set('upgradeState', "ABORTED");
+      App.set('isAdmin', true);
+      App.set('isOperator', false);
+      this.mock.withArgs('wizardWatcherController.isNonWizardUser').returns(true);
+      expect(App.isAccessible('ONLY_ADMIN')).to.be.false;
+    });
   });
 
   describe('#isHadoop20Stack', function () {
@@ -561,44 +598,81 @@ describe('App', function () {
     var cases = [
       {
         upgradeState: 'INIT',
+        isSuspended: false,
+        upgradeAborted: false
+      },
+      {
+        upgradeState: 'INIT',
+        isSuspended: true,
+        upgradeAborted: false
+      },
+      {
+        upgradeState: 'ABORTED',
+        isSuspended: true,
         upgradeAborted: false
       },
       {
         upgradeState: 'ABORTED',
+        isSuspended: false,
         upgradeAborted: true
       }
     ];
 
+    beforeEach(function () {
+      this.mock = sinon.stub(App.router, 'get');
+    });
+    afterEach(function () {
+      this.mock.restore();
+    });
+
     cases.forEach(function (item) {
-      it(item.upgradeState, function () {
+      it(item.upgradeState + ", " + item.isSuspended, function () {
+        this.mock.returns(item.isSuspended);
         App.set('upgradeState', item.upgradeState);
+        App.propertyDidChange('upgradeAborted');
         expect(App.get('upgradeAborted')).to.equal(item.upgradeAborted);
       });
     });
-
   });
 
   describe('#wizardIsNotFinished', function () {
 
+    beforeEach(function () {
+      this.mock = sinon.stub(App.router, 'get');
+    });
+    afterEach(function () {
+      this.mock.restore();
+    });
+
     var cases = [
       {
         upgradeState: 'INIT',
+        isSuspended: false,
         wizardIsNotFinished: false
       },
       {
         upgradeState: 'IN_PROGRESS',
+        isSuspended: false,
         wizardIsNotFinished: true
       },
       {
         upgradeState: 'HOLDING',
+        isSuspended: false,
         wizardIsNotFinished: true
       },
       {
         upgradeState: 'HOLDING_TIMEDOUT',
+        isSuspended: false,
+        wizardIsNotFinished: true
+      },
+      {
+        upgradeState: 'ABORTED',
+        isSuspended: false,
         wizardIsNotFinished: true
       },
       {
         upgradeState: 'ABORTED',
+        isSuspended: true,
         wizardIsNotFinished: true
       }
     ];
@@ -606,10 +680,10 @@ describe('App', function () {
     cases.forEach(function (item) {
       it(item.upgradeState, function () {
         App.set('upgradeState', item.upgradeState);
+        this.mock.returns(item.isSuspended);
+        App.propertyDidChange('wizardIsNotFinished');
         expect(App.get('wizardIsNotFinished')).to.equal(item.wizardIsNotFinished);
       });
     });
-
   });
-
 });

+ 66 - 0
ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js

@@ -73,6 +73,21 @@ describe('App.MainAdminStackAndUpgradeController', function() {
     });
   });
 
+  describe("#requestStatus", function() {
+    it("isSuspended false", function() {
+      App.set('upgradeState', 'ABORTED');
+      controller.set('isSuspended', false);
+      controller.propertyDidChange('requestStatus');
+      expect(controller.get('requestStatus')).to.equal('ABORTED');
+    });
+    it("isSuspended true", function() {
+      App.set('upgradeState', 'ABORTED');
+      controller.set('isSuspended', true);
+      controller.propertyDidChange('requestStatus');
+      expect(controller.get('requestStatus')).to.equal('SUSPENDED');
+    });
+  });
+
   describe("#load()", function() {
     before(function(){
       sinon.stub(controller, 'loadUpgradeData').returns({
@@ -1211,4 +1226,55 @@ describe('App.MainAdminStackAndUpgradeController', function() {
 
   });
 
+  describe("#suspendUpgrade()", function() {
+    beforeEach(function () {
+      sinon.stub(controller, 'abortUpgrade').returns({
+        done: function (callback) {
+          callback();
+        }
+      });
+      sinon.stub(controller, 'setDBProperty', Em.K);
+      sinon.stub(App.clusterStatus, 'setClusterStatus', Em.K);
+    });
+    afterEach(function () {
+      controller.abortUpgrade.restore();
+      controller.setDBProperty.restore();
+      App.clusterStatus.setClusterStatus.restore();
+    });
+    it("", function() {
+      controller.suspendUpgrade();
+      expect(controller.abortUpgrade.calledOnce).to.be.true;
+      expect(App.get('upgradeState')).to.equal('ABORTED');
+      expect(controller.get('isSuspended')).to.be.true;
+      expect(controller.setDBProperty.calledWith('upgradeState', 'ABORTED')).to.be.true;
+      expect(controller.setDBProperty.calledWith('isSuspended', true)).to.be.true;
+      expect(App.clusterStatus.setClusterStatus.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#resumeUpgrade()", function() {
+    beforeEach(function () {
+      sinon.stub(controller, 'retryUpgrade').returns({
+        done: function (callback) {
+          callback();
+        }
+      });
+      sinon.stub(controller, 'setDBProperty', Em.K);
+      sinon.stub(App.clusterStatus, 'setClusterStatus', Em.K);
+    });
+    afterEach(function () {
+      controller.retryUpgrade.restore();
+      controller.setDBProperty.restore();
+      App.clusterStatus.setClusterStatus.restore();
+    });
+    it("", function() {
+      controller.resumeUpgrade();
+      expect(controller.retryUpgrade.calledOnce).to.be.true;
+      expect(App.get('upgradeState')).to.equal('PENDING');
+      expect(controller.get('isSuspended')).to.be.false;
+      expect(controller.setDBProperty.calledWith('upgradeState', 'PENDING')).to.be.true;
+      expect(controller.setDBProperty.calledWith('isSuspended', false)).to.be.true;
+      expect(App.clusterStatus.setClusterStatus.calledOnce).to.be.true;
+    });
+  });
 });

+ 16 - 1
ambari-web/test/views/main/admin/stack_upgrade/upgrade_wizard_view_test.js

@@ -448,6 +448,12 @@ describe('App.upgradeWizardView', function () {
   });
 
   describe("#upgradeStatusLabel", function () {
+    beforeEach(function () {
+      this.mock = sinon.stub(App, 'get');
+    });
+    afterEach(function () {
+      this.mock.restore();
+    });
     var testCases = [
       {
         data: {
@@ -561,6 +567,14 @@ describe('App.upgradeWizardView', function () {
         },
         result: Em.I18n.t('admin.stackUpgrade.state.aborted.downgrade')
       },
+      {
+        data: {
+          status: 'ABORTED',
+          isDowngrade: false,
+          isSuspended: true
+        },
+        result: Em.I18n.t('admin.stackUpgrade.state.paused')
+      },
       {
         data: {
           status: 'TIMEDOUT',
@@ -598,8 +612,9 @@ describe('App.upgradeWizardView', function () {
       }
     ].forEach(function (test) {
         it('status = ' + test.data.status + ", isDowngrade = " + test.data.isDowngrade, function () {
-          view.set('controller.upgradeData.Upgrade.request_status', test.data.status);
           view.set('controller.isDowngrade', test.data.isDowngrade);
+          view.set('controller.isSuspended', test.data.isSuspended);
+          this.mock.returns(test.data.status);
           view.propertyDidChange('upgradeStatusLabel');
           expect(view.get('upgradeStatusLabel')).to.equal(test.result);
         });