Parcourir la source

AMBARI-13393. Express Upgrade: UX changes for upgrade method selections window.(xiwang)

Xi Wang il y a 9 ans
Parent
commit
92915952ab

+ 267 - 15
ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js

@@ -45,6 +45,18 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
    */
   upgradeVersion: null,
 
+  /**
+   * @type {string}
+   * @default null
+   */
+  upgradeTypeDisplayName: null,
+
+  /**
+   * @type {object}
+   * @default null
+   */
+  failuresTolerance: null,
+
   /**
    * @type {boolean}
    * @default false
@@ -64,6 +76,30 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
    */
   targetVersions: [],
 
+  /**
+   * methods through which cluster could be upgraded, "allowed" indicated if the method is allowed
+   * by stack upgrade path
+   * @type {Array}
+   */
+  upgradeMethods: [
+    Em.Object.create({
+      displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.RU.title'),
+      type: 'ROLLING',
+      icon: "icon-dashboard",
+      description: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.RU.description'),
+      selected: false,
+      allowed: true
+    }),
+    Em.Object.create({
+      displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.EU.title'),
+      type: 'NON-ROLLING',
+      icon: "icon-bolt",
+      description: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.EU.description'),
+      selected: false,
+      allowed: true
+    })
+  ],
+
   /**
    * @type {boolean} true if some request that should disable actions is in progress
    */
@@ -75,7 +111,7 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   /**
    * properties that stored to localStorage to resume wizard progress
    */
-  wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'isDowngrade', 'isSuspended'],
+  wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'upgradeTypeDisplayName', 'failuresTolerance', 'isDowngrade', 'isSuspended'],
 
   /**
    * mutable properties of Upgrade Task
@@ -472,11 +508,23 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
     this.set('upgradeId', data.resources[0].Upgrade.request_id);
     this.set('upgradeVersion', params.label);
     this.set('isDowngrade', !!params.isDowngrade);
+    var upgradeMethod = this.get('upgradeMethods').findProperty('type', params.type);
+    var upgradeTypeDisplayName  = upgradeMethod ? upgradeMethod.get('displayName') : null;
+    this.set('upgradeTypeDisplayName', upgradeTypeDisplayName);
+    this.set('failuresTolerance', Em.Object.create({
+      skipComponentFailures: params.skipComponentFailures,
+      skipSCFailures: params.skipSCFailures
+    }));
     this.setDBProperties({
       upgradeVersion: params.label,
       upgradeId: data.resources[0].Upgrade.request_id,
       upgradeState: 'PENDING',
-      isDowngrade: !!params.isDowngrade
+      isDowngrade: !!params.isDowngrade,
+      upgradeTypeDisplayName: upgradeTypeDisplayName,
+      failuresTolerance: Em.Object.create({
+        skipComponentFailures: params.skipComponentFailures,
+        skipSCFailures: params.skipSCFailures
+      })
     });
     App.set('upgradeState', 'PENDING');
     App.clusterStatus.setClusterStatus({
@@ -488,21 +536,221 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   },
 
   /**
-   * upgrade confirmation popup
+   * success callback of updating upgrade options including failures tolerance. etc
+   * @param {object} data
+   */
+  updateOptionsSuccessCallback: function (data, opt, params) {
+    this.set('failuresTolerance', Em.Object.create({
+      skipComponentFailures: params.skipComponentFailures,
+      skipSCFailures: params.skipSCFailures
+    }));
+  },
+
+  /**
+   * Open upgrade options window: upgrade type and failures tolerance
+   * @param {boolean} isInUpgradeWizard
+   * @param {object} version
+   * @return App.ModalPopup
+   */
+  upgradeOptions: function(isInUpgradeWizard, version) {
+    var self = this;
+    return App.ModalPopup.show({
+      encodeBody: false,
+      primary: isInUpgradeWizard? Em.I18n.t('ok') : Em.I18n.t('common.proceed'),
+      primaryClass: 'btn-success',
+      classNames: ['upgrade-options-popup'],
+      header: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.header'),
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/admin/stack_upgrade/upgrade_options'),
+        didInsertElement: function() {
+          //add pre-upgrade check results to each method object and set selected method
+          var view = this;
+          self.get('upgradeMethods').forEach(function(method){
+            if (!isInUpgradeWizard && method.get('allowed')) {
+              self.runPreUpgradeCheckOnly.call(self, {
+                value: version.get('repositoryVersion'),
+                label: version.get('displayName'),
+                type: method.get('type')
+              });
+            }
+            if (method.get('selected')) {
+              view.set('parentView.selectedMethod', method);
+            }
+          });
+          App.tooltip($(".not-allowed.thumbnail"), {
+            placement: "bottom",
+            title: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.notAllowed')
+          });
+        },
+        parentView: this.get('parentView'),
+        isInUpgradeWizard: isInUpgradeWizard,
+        versionText: isInUpgradeWizard? '' : Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.version').format(version.get('displayName')),
+        upgradeMethods: function () {
+          if (isInUpgradeWizard) {
+            self.get('upgradeMethods').forEach(function(method){
+              if (method.get('displayName') == self.get('upgradeTypeDisplayName')) {
+                method.set('selected', true);
+              } else {
+                method.set('selected', false);
+              }
+            });
+          } else {
+            var ruMethod = self.get('upgradeMethods').findProperty('type', 'ROLLING');
+            var ssuMethod = self.get('upgradeMethods').findProperty('type', 'NON-ROLLING');
+            ruMethod.set('selected', ruMethod.get('allowed'));
+            ssuMethod.set('selected', !ruMethod.get('allowed') && ssuMethod.get('allowed'));
+          }
+          return self.get('upgradeMethods');
+        }.property('self.upgradeMethods', 'upgradeTypeDisplayName'),
+        selectMethod: function(event) {
+          if (isInUpgradeWizard || !event.context.get('allowed')) return;
+          var selectedMethod = event.context;
+          this.get('upgradeMethods').forEach(function(method){
+            method.set('selected', false);
+          });
+          selectedMethod.set('selected', true);
+          this.set('parentView.selectedMethod', selectedMethod);
+        },
+        openMessage: function(event) {
+          if (isInUpgradeWizard || !event.context.get('allowed')) return;
+          var data = event.context.get('precheckResultsData');
+          var header = Em.I18n.t('popup.clusterCheck.Upgrade.header').format(version.get('displayName')),
+            failTitle = Em.I18n.t('popup.clusterCheck.Upgrade.fail.title'),
+            failAlert = new Em.Handlebars.SafeString(Em.I18n.t('popup.clusterCheck.Upgrade.fail.alert')),
+            warningTitle = Em.I18n.t('popup.clusterCheck.Upgrade.warning.title'),
+            warningAlert = new Em.Handlebars.SafeString(Em.I18n.t('popup.clusterCheck.Upgrade.warning.alert')),
+            configsMergeWarning = data.items.findProperty('UpgradeChecks.id', "CONFIG_MERGE"),
+            configs = [];
+          if (configsMergeWarning && Em.get(configsMergeWarning, 'UpgradeChecks.status') === 'WARNING') {
+            data.items = data.items.rejectProperty('UpgradeChecks.id', 'CONFIG_MERGE');
+            var configsMergeCheckData = Em.get(configsMergeWarning, 'UpgradeChecks.failed_detail');
+            if (configsMergeCheckData) {
+              configs = configsMergeCheckData.map(function (item) {
+                var isDeprecated = Em.isNone(item.new_stack_value),
+                  willBeRemoved = Em.isNone(item.result_value);
+                return {
+                  type: item.type,
+                  name: item.property,
+                  currentValue: item.current,
+                  recommendedValue: isDeprecated ? Em.I18n.t('popup.clusterCheck.Upgrade.configsMerge.deprecated') : item.new_stack_value,
+                  isDeprecated: isDeprecated,
+                  resultingValue: willBeRemoved ? Em.I18n.t('popup.clusterCheck.Upgrade.configsMerge.willBeRemoved') : item.result_value,
+                  willBeRemoved: willBeRemoved
+                };
+              });
+            }
+          }
+          App.showPreUpgradeCheckPopup(data, header, failTitle, failAlert, warningTitle, warningAlert, function () {
+            self.runPreUpgradeCheckOnly.call(self, {
+              value: version.get('repositoryVersion'),
+              label: version.get('displayName'),
+              type: event.context.get('type')
+            });
+          }, configs, version.get('displayName'));
+        }
+      }),
+      selectedMethod: '',
+      skipComponentFailures: self.get('failuresTolerance.skipComponentFailures'),
+      skipSCFailures: self.get('failuresTolerance.skipSCFailures'),
+      disablePrimary: function() {
+        if (isInUpgradeWizard) return false;
+        var selectedMethod = self.get('upgradeMethods').findProperty('selected', true);
+        return selectedMethod ? selectedMethod.get('precheckResultsMessageClass') == 'RED' : true;
+      }.property('selectedMethod', 'selectedMethod.precheckResultsMessageClass'),
+      onPrimary: function () {
+        this.hide();
+        if (isInUpgradeWizard) {
+          return App.ajax.send({
+            name: 'admin.upgrade.update.options',
+            sender: self,
+            data: {
+              upgradeId: self.get('upgradeId'),
+              skipComponentFailures: this.get('skipComponentFailures') || false,
+              skipSCFailures: this.get('skipSCFailures') || false
+            },
+            success: 'updateOptionsSuccessCallback'
+          });
+        } else {
+          version.upgradeType = self.get('upgradeMethods').findProperty('selected', true).get('type');
+          version.upgradeTypeDisplayName = self.get('upgradeMethods').findProperty('selected', true).get('displayName');
+          version.skipComponentFailures = this.get('skipComponentFailures');
+          version.skipSCFailures = this.get('skipSCFailures');
+          self.runPreUpgradeCheck.call(self, version);
+        }
+      },
+      onSecondary: function () {
+        this.hide();
+      },
+      onClose:  function () {
+        this.hide();
+      }
+    });
+  },
+
+  /**
+   * open upgrade options from upgrade wizard
+   * @return App.ModalPopup
+   */
+  openUpgradeOptions: function () {
+    this.upgradeOptions(true, null);
+  },
+
+  /**
+   * upgrade confirmation popup including upgrade options: upgrade type and failures tolerance
    * @param {object} version
    * @return App.ModalPopup
    */
   confirmUpgrade: function (version) {
+    this.upgradeOptions(false, version);
+  },
+
+  /**
+   * send request for pre upgrade check only
+   */
+  runPreUpgradeCheckOnly: function(data) {
+    if (App.get('supports.preUpgradeCheck')) {
+      App.ajax.send({
+        name: "admin.upgrade.pre_upgrade_check",
+        sender: this,
+        data: data,
+        success: "runPreUpgradeCheckOnlySuccess",
+        error: "runPreUpgradeCheckError"
+      });
+    }
+  },
+
+  /**
+   * success callback of <code>runPreUpgradeCheckOnly()</code>
+   * Show a message how many fails/warnings/passed
+   * on clicking that message a popup window show up
+   * @param data {object}
+   * @param opt {object}
+   * @param params {object}
+   */
+  runPreUpgradeCheckOnlySuccess: function (data, opt, params) {
     var self = this;
+    var message = '';
+    var messageClass = 'GREEN';
+    var messageIconClass = 'icon-ok';
+    if (data.items.someProperty('UpgradeChecks.status', 'WARNING')) {
+      message = message + data.items.filterProperty('UpgradeChecks.status', 'WARNING').length + ' Warning ';
+      messageClass = 'ORANGE';
+      messageIconClass = 'icon-warning-sign';
+    }
+    if (data.items.someProperty('UpgradeChecks.status', 'FAIL')) {
+      message = data.items.filterProperty('UpgradeChecks.status', 'FAIL').length + ' Required ' + message;
+      messageClass = 'RED';
+      messageIconClass = 'icon-remove';
+    }
 
-    return App.showConfirmationPopup(
-      function () {
-        self.runPreUpgradeCheck.call(self, version);
-      },
-      Em.I18n.t('admin.stackUpgrade.upgrade.confirm.body').format(version.get('displayName')),
-      null,
-      Em.I18n.t('admin.stackUpgrade.dialog.header').format(version.get('displayName'))
-    );
+    if (!message) {
+      message = Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.preCheck.allPassed');
+    }
+    var method = self.get('upgradeMethods').findProperty('type', params.type);
+    method.set('precheckResultsMessage', message);
+    method.set('precheckResultsMessageClass', messageClass);
+    method.set('precheckResultsMessageIconClass', messageIconClass);
+    method.set('precheckResultsData', data);
   },
 
   /**
@@ -512,13 +760,15 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   runPreUpgradeCheck: function(version) {
     var params = {
       value: version.get('repositoryVersion'),
-      label: version.get('displayName')
+      label: version.get('displayName'),
+      type: version.get('upgradeType'),
+      skipComponentFailures: version.get('skipComponentFailures'),
+      skipSCFailures: version.get('skipSCFailures')
     };
-
     if (App.get('supports.preUpgradeCheck')) {
       this.set('requestInProgress', true);
       App.ajax.send({
-        name: "admin.rolling_upgrade.pre_upgrade_check",
+        name: "admin.upgrade.pre_upgrade_check",
         sender: this,
         data: params,
         success: "runPreUpgradeCheckSuccess",
@@ -587,7 +837,7 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
       },
       Em.I18n.t('admin.stackUpgrade.upgrade.retry.confirm.body').format(version.get('displayName')),
       null,
-      Em.I18n.t('admin.stackUpgrade.dialog.header').format(version.get('displayName'))
+      Em.I18n.t('admin.stackUpgrade.dialog.header').format(version.get('upgradeTypeDislayName'), version.get('displayName'))
     );
   },
 
@@ -814,6 +1064,8 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
         upgradeState: 'INIT',
         upgradeVersion: undefined,
         currentVersion: undefined,
+        upgradeTypeDisplayName: undefined,
+        failuresTolerance: undefined,
         isDowngrade: undefined
       });
       App.clusterStatus.setClusterStatus({

+ 16 - 1
ambari-web/app/messages.js

@@ -270,6 +270,7 @@ Em.I18n.translations = {
   'common.dataSet': 'Data Set',
   'common.label': 'Label',
   'common.preview': 'Preview',
+  'common.options': 'Options',
   'common.scope': 'Scope',
   'common.clone': 'Clone',
   'common.removed': 'Removed',
@@ -1441,6 +1442,20 @@ Em.I18n.translations = {
   'admin.stackVersions.version.downgrade.running': "Downgrade: In Process",
   'admin.stackVersions.version.downgrade.aborted': "Downgrade: Aborted",
 
+  'admin.stackVersions.version.upgrade.upgradeOptions.header': "Upgrade Options",
+  'admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.version': "You are about to perform an upgrade to <b>{0}</b>.",
+  'admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.method': "Please choose the method to perform this upgrade:",
+  'admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.tolerance': "Please choose the tolerance with failures. (Can be modified later)",
+  'admin.stackVersions.version.upgrade.upgradeOptions.tolerance.option1': "Skip all component failures",
+  'admin.stackVersions.version.upgrade.upgradeOptions.tolerance.option2': "Skip all service check failures",
+  'admin.stackVersions.version.upgrade.upgradeOptions.RU.title': "Rolling Upgrade",
+  'admin.stackVersions.version.upgrade.upgradeOptions.RU.description': "Services remain up while the upgrade is performed. <br/>No downtime, but slower.",
+  'admin.stackVersions.version.upgrade.upgradeOptions.EU.title': "Express Upgrade",
+  'admin.stackVersions.version.upgrade.upgradeOptions.EU.description': "Services are shut down while the upgrade is performed. <br/>Incurs downtime, but faster.",
+  'admin.stackVersions.version.upgrade.upgradeOptions.preCheck.rerun':'Rerun Checks',
+  'admin.stackVersions.version.upgrade.upgradeOptions.preCheck.allPassed':'Pre-Upgrade checks passed',
+  'admin.stackVersions.version.upgrade.upgradeOptions.notAllowed':'Not allowed by the current version',
+
   'admin.stackVersions.hosts.popup.header.current': "Current",
   'admin.stackVersions.hosts.popup.header.installed': "Installed",
   'admin.stackVersions.hosts.popup.header.not_installed': "Not installed",
@@ -1478,7 +1493,7 @@ Em.I18n.translations = {
   'admin.stackUpgrade.state.paused.downgrade': "Downgrade Paused",
   'admin.stackUpgrade.state.aborted.downgrade': "Downgrade Aborted",
   'admin.stackUpgrade.state.completed.downgrade': "Downgrade Finished",
-  'admin.stackUpgrade.dialog.header': "Rolling Upgrade to {0}",
+  'admin.stackUpgrade.dialog.header': "{0} to {1}",
   'admin.stackUpgrade.dialog.downgrade.header': "Downgrade to {0}",
   'admin.stackUpgrade.dialog.operationFailed': "This operation failed.",
   'admin.stackUpgrade.dialog.stop': "Stop Upgrade",

+ 1 - 1
ambari-web/app/routes/stack_upgrade_routes.js

@@ -39,7 +39,7 @@ module.exports = App.WizardRoute.extend({
           if (controller.get('isDowngrade')) {
             return Em.I18n.t('admin.stackUpgrade.dialog.downgrade.header').format(controller.get('upgradeVersion'));
           } else {
-            return Em.I18n.t('admin.stackUpgrade.dialog.header').format(controller.get('upgradeVersion'));
+            return Em.I18n.t('admin.stackUpgrade.dialog.header').format(controller.get('upgradeTypeDisplayName'), controller.get('upgradeVersion'));
           }
         }.property('App.router.mainAdminStackAndUpgradeController.upgradeVersion', 'App.router.mainAdminStackAndUpgradeController.isDowngrade'),
         bodyClass: App.upgradeWizardView,

+ 127 - 0
ambari-web/app/styles/stack_versions.less

@@ -267,6 +267,12 @@
   .task-list-main-warp i {
     font-size: 16px;
   }
+  .upgrade-options-button {
+    position: absolute;
+    right: 10%;
+    top: 10px;
+    width: 100px;
+  }
 }
 
 .repository-list {
@@ -279,6 +285,127 @@
   }
 }
 
+#upgrade-options-popup-content {
+  .method-options {
+    margin: 10px;
+    .method-option {
+      width: 43.5%;
+    }
+    .thumbnail {
+      height: 140px;
+      border: solid 2px white;
+      cursor: pointer;
+      width: 99%;
+      .method-icon {
+        font-size: 40px;
+      }
+      .method-name {
+        font-size: 16px;
+        font-weight: bold;
+        text-align: center;
+        margin-top: 2px;
+      }
+      .method-description {
+        margin-top: 5px;
+        text-align: center;
+      }
+    }
+    .ROLLING {
+      background-color: #d3e7ca;
+      .method-icon {
+        margin-left: 88px;
+      }
+    }
+    .selected.ROLLING {
+      border: solid 2px #a3c197;
+      box-shadow: 0 8px 6px -6px #91ac86;
+    }
+    .ROLLING:hover {
+      background-color: #c4dfb9;
+    }
+    .NON-ROLLING {
+      background-color: #d8e7fb;
+      .method-icon {
+        margin-left: 94px;
+      }
+    }
+    .selected.NON-ROLLING {
+      border: solid 2px #7fb2f4;
+      box-shadow: 0 8px 6px -6px #72a0db;
+    }
+    .NON-ROLLING:hover {
+      background-color: #c9defa;
+    }
+    .ROLLING.not-allowed:hover, .NON-ROLLING.not-allowed:hover {
+      background-color: #f3f3f3;
+    }
+    .thumbnail.not-allowed {
+      cursor: not-allowed;
+      background-color: #f3f3f3;
+      .method-name,.method-icon,.method-description {
+        color: #a9a9a9;
+      }
+    }
+  }
+
+  .method-options.disabled {
+    .thumbnail {
+      cursor: not-allowed;
+      .method-name,.method-icon,.method-description {
+        color: #a9a9a9;
+      }
+    }
+    .ROLLING,.NON-ROLLING,
+    .ROLLING:hover,.NON-ROLLING:hover {
+      background-color: #f3f3f3;
+    }
+    .selected.ROLLING,.selected.NON-ROLLING,
+    .selected.ROLLING:hover,.selected.NON-ROLLING:hover {
+      border: solid 2px #a9a9a9;
+      box-shadow: 0 8px 6px -6px #a9a9a9;
+    }
+  }
+
+  .method-precheck-message {
+    cursor: pointer;
+    text-align: center;
+    padding-top: 10px;
+  }
+  .method-precheck-message.GREEN {
+    a {
+      font-size: 13px;
+    }
+    .icon-ok {
+      color: @health-status-green;
+      font-size: 16px;
+    }
+  }
+  .method-precheck-message.RED {
+    a {
+      font-size: 13px;
+    }
+    .icon-remove {
+      color: #cf2a27;
+      font-size: 16px;
+    }
+  }
+  .method-precheck-message.ORANGE {
+    a {
+      font-size: 13px;
+    }
+    .icon-warning-sign {
+      color: @health-status-orange;
+      font-size: 16px;
+    }
+  }
+  .tolerance-text .tolerance-option {
+    margin: 5px 15px;
+  }
+  .text {
+    margin-left: 10px;
+  }
+}
+
 .admin-cluster {
   .repositories-table {
     margin-bottom: 10px;

+ 3 - 0
ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_wizard.hbs

@@ -17,6 +17,9 @@
 }}
 
 <div id="stack-upgrade-dialog">
+  <button class="btn btn-primary upgrade-options-button" {{bindAttr disabled="controller.isDowngrade"}} {{action openUpgradeOptions target="controller"}}>
+    <i class="icon-cogs"></i>&nbsp;{{t common.options}}
+  </button>
   <div {{bindAttr class="view.isLoaded::hidden :row-fluid"}}>
     <div class="span3 task-list-main-warp">{{statusIcon controller.requestStatus}}
       &nbsp;{{view.upgradeStatusLabel}}</div>

+ 49 - 0
ambari-web/app/templates/main/admin/stack_upgrade/upgrade_options.hbs

@@ -0,0 +1,49 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div id="upgrade-options-popup-content">
+  <div class="text version-text">{{{view.versionText}}}</div>
+  <div class="text method-text">{{t admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.method}}</div>
+  <div {{bindAttr class=":row-fluid :method-options view.isInUpgradeWizard:disabled"}}>
+    {{#each method in view.upgradeMethods}}
+      <div class="method-option span5">
+        <div {{bindAttr class="method.allowed::not-allowed method.selected:selected method.type :thumbnail"}}
+          {{action selectMethod method target="view"}}>
+            <div {{bindAttr class="method.icon :method-icon"}}></div>
+            <div class="method-name">{{method.displayName}}</div>
+            <div class="method-description">{{{method.description}}}</div>
+        </div>
+        {{#unless view.isInUpgradeWizard}}
+          <div {{bindAttr class=":method-precheck-message method.precheckResultsMessageClass"}}>
+            <i {{bindAttr class="method.precheckResultsMessageIconClass"}}></i>&nbsp;
+            <a {{action openMessage method target="view"}}>
+              {{method.precheckResultsMessage}}
+            </a>
+          </div>
+        {{/unless}}
+      </div>
+    {{/each}}
+  </div>
+
+  <div class="text tolerance-text">{{t admin.stackVersions.version.upgrade.upgradeOptions.bodyMsg.tolerance}}
+    <div class="tolerance-option">{{view Ember.Checkbox checkedBinding="view.parentView.skipComponentFailures"}}
+      {{t admin.stackVersions.version.upgrade.upgradeOptions.tolerance.option1}}</div>
+    <div class="tolerance-option">{{view Ember.Checkbox checkedBinding="view.parentView.skipSCFailures"}}
+      {{t admin.stackVersions.version.upgrade.upgradeOptions.tolerance.option2}}</div>
+  </div>
+</div>

+ 21 - 3
ambari-web/app/utils/ajax/ajax.js

@@ -1573,6 +1573,21 @@ var urls = {
   'admin.upgrade.service_checks': {
     'real': '/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups?upgrade_items/UpgradeItem/status=COMPLETED&upgrade_items/tasks/Tasks/status.in(FAILED,ABORTED,TIMEDOUT)&upgrade_items/tasks/Tasks/command=SERVICE_CHECK&fields=upgrade_items/tasks/Tasks/command_detail,upgrade_items/tasks/Tasks/status&minimal_response=true'
   },
+  'admin.upgrade.update.options': {
+    'real': '/clusters/{clusterName}/upgrades/{upgradeId}',
+    'mock': '/data/stack_versions/start_upgrade.json',
+    'type': 'PUT',
+    'format': function (data) {
+      return {
+        data: JSON.stringify({
+          "Upgrade": {
+            "skip_failures": data.skipComponentFailures,
+            "skip_service_check_failures": data.skipSCFailures
+          }
+        })
+      }
+    }
+  },
   'admin.upgrade.start': {
     'real': '/clusters/{clusterName}/upgrades',
     'mock': '/data/stack_versions/start_upgrade.json',
@@ -1581,7 +1596,10 @@ var urls = {
       return {
         data: JSON.stringify({
           "Upgrade": {
-            "repository_version": data.value
+            "repository_version": data.value,
+            "type": data.type,
+            "skip_failures": data.skipComponentFailures,
+            "skip_service_check_failures": data.skipSCFailures
           }
         })
       }
@@ -1689,8 +1707,8 @@ var urls = {
     }
   },
 
-  'admin.rolling_upgrade.pre_upgrade_check': {
-    'real': '/clusters/{clusterName}/rolling_upgrades_check?fields=*&UpgradeChecks/repository_version={value}',
+  'admin.upgrade.pre_upgrade_check': {
+    'real': '/clusters/{clusterName}/rolling_upgrades_check?fields=*&UpgradeChecks/repository_version={value}&UpgradeChecks/upgrade_type={type}',
     'mock': '/data/stack_versions/pre_upgrade_check.json'
   },
 

+ 55 - 0
ambari-web/app/views/common/modal_popups/cluster_check_popup.js

@@ -71,3 +71,58 @@ App.showClusterCheckPopup = function (data, header, failTitle, failAlert, warnin
     }
   });
 };
+
+
+/**
+ * popup to display requirements that are not met
+ * for current action
+ * @param data
+ * @param header
+ * @param failTitle
+ * @param failAlert
+ * @param warningTitle
+ * @param warningAlert
+ * @param callback
+ * @param configs
+ * @param upgradeVersion
+ * @returns {*|void}
+ */
+App.showPreUpgradeCheckPopup = function (data, header, failTitle, failAlert, warningTitle, warningAlert, callback, configs, upgradeVersion) {
+  var fails = data.items.filterProperty('UpgradeChecks.status', 'FAIL'),
+    warnings = data.items.filterProperty('UpgradeChecks.status', 'WARNING'),
+    hasConfigsMergeConflicts = !!(configs && configs.length),
+    popupBody = {
+      failTitle: failTitle,
+      failAlert: failAlert,
+      warningTitle: warningTitle,
+      warningAlert: warningAlert,
+      templateName: require('templates/common/modal_popups/cluster_check_dialog'),
+      fails: fails,
+      warnings: warnings,
+      hasConfigsMergeConflicts: hasConfigsMergeConflicts
+    };
+  if (hasConfigsMergeConflicts) {
+    popupBody.configsMergeTable = Em.View.extend({
+      templateName: require('templates/main/admin/stack_upgrade/upgrade_configs_merge_table'),
+      configs: configs,
+      didInsertElement: function () {
+        App.tooltip($('.recommended-value'), {
+          title: upgradeVersion
+        });
+      }
+    });
+  }
+  return App.ModalPopup.show({
+    primary: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.preCheck.rerun'),
+    secondary: Em.I18n.t('common.cancel'),
+    header: header,
+    classNames: ['cluster-check-popup'],
+    bodyClass: Em.View.extend(popupBody),
+    onPrimary: function () {
+      this._super();
+      if (callback) {
+        callback();
+      }
+    }
+  });
+};

+ 56 - 8
ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js

@@ -299,14 +299,20 @@ describe('App.MainAdminStackAndUpgradeController', function() {
     it("make ajax call", function() {
       controller.runPreUpgradeCheck(Em.Object.create({
         repositoryVersion: '2.2',
-        displayName: 'HDP-2.2'
+        displayName: 'HDP-2.2',
+        upgradeType: 'ROLLING',
+        skipComponentFailures: false,
+        skipSCFailures: false
       }));
       expect(App.ajax.send.getCall(0).args[0]).to.eql({
-        name: "admin.rolling_upgrade.pre_upgrade_check",
+        name: "admin.upgrade.pre_upgrade_check",
         sender: controller,
         data: {
           value: '2.2',
-          label: 'HDP-2.2'
+          label: 'HDP-2.2',
+          type: 'ROLLING',
+          skipComponentFailures: false,
+          skipSCFailures: false
         },
         success: "runPreUpgradeCheckSuccess",
         error: "runPreUpgradeCheckError"
@@ -520,6 +526,16 @@ describe('App.MainAdminStackAndUpgradeController', function() {
           }
         ]
       };
+      controller.upgradeMethods = [
+        Em.Object.create({
+          displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.RU.title'),
+          type: 'ROLLING'
+        }),
+        Em.Object.create({
+          displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.EU.title'),
+          type: 'NON-ROLLING'
+        })
+      ];
       controller.upgradeSuccessCallback(data, {}, {label: 'HDP-2.2.1', isDowngrade: true});
       expect(controller.load.calledOnce).to.be.true;
       expect(controller.get('upgradeVersion')).to.equal('HDP-2.2.1');
@@ -748,24 +764,56 @@ describe('App.MainAdminStackAndUpgradeController', function() {
     });
   });
 
-  describe("#confirmUpgrade()", function() {
+  describe("#upgradeOptions()", function() {
     before(function () {
-      sinon.spy(App, 'showConfirmationPopup');
+      sinon.spy(App, 'ModalPopup');
       sinon.stub(controller, 'runPreUpgradeCheck', Em.K);
     });
     after(function () {
-      App.showConfirmationPopup.restore();
+      App.ModalPopup.restore();
       controller.runPreUpgradeCheck.restore();
     });
     it("show confirmation popup", function() {
       var version = Em.Object.create({displayName: 'HDP-2.2'});
-      var popup = controller.confirmUpgrade(version);
-      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      controller.upgradeMethods = [
+        Em.Object.create({
+          displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.RU.title'),
+          type: 'ROLLING',
+          icon: "icon-dashboard",
+          description: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.RU.description'),
+          selected: true,
+          allowed: true
+        }),
+        Em.Object.create({
+          displayName: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.EU.title'),
+          type: 'NON-ROLLING',
+          icon: "icon-bolt",
+          description: Em.I18n.t('admin.stackVersions.version.upgrade.upgradeOptions.EU.description'),
+          selected: false,
+          allowed: true
+        })
+      ];
+      var popup = controller.upgradeOptions(false, version);
+      expect(App.ModalPopup.calledOnce).to.be.true;
       popup.onPrimary();
       expect(controller.runPreUpgradeCheck.calledWith(version)).to.be.true;
     });
   });
 
+  describe("#confirmUpgrade()", function() {
+    before(function () {
+      sinon.stub(controller, 'upgradeOptions', Em.K);
+    });
+    after(function () {
+      controller.upgradeOptions.restore();
+    });
+    it("show show upgrade options popup window", function() {
+      var version = Em.Object.create({displayName: 'HDP-2.2'});
+      controller.confirmUpgrade(version);
+      expect(controller.upgradeOptions.calledWith(false, version)).to.be.true;
+    });
+  });
+
   describe("#downgrade()", function() {
     before(function () {
       sinon.stub(App.ajax, 'send', Em.K);