This is an automated email from the ASF dual-hosted git repository.

atkach pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 15ec6f5  AMBARI-22940 Service-auto start page update of UI
15ec6f5 is described below

commit 15ec6f5e084f81a21b3a3545dbdf4ed33492bc0f
Author: Andrii Tkach <atk...@apache.org>
AuthorDate: Mon Feb 12 19:26:18 2018 +0200

    AMBARI-22940 Service-auto start page update of UI
---
 ambari-web/app/assets/test/tests.js                |   1 -
 .../controllers/main/admin/service_auto_start.js   | 250 ++++++---------
 ambari-web/app/messages.js                         |   4 +-
 ambari-web/app/routes/main.js                      |   2 +-
 ambari-web/app/styles/application.less             |  45 ++-
 ambari-web/app/styles/bootstrap_overrides.less     |   7 +
 ambari-web/app/styles/circle-toggle-switch.less    |  87 ++++++
 .../templates/main/admin/service_auto_start.hbs    | 131 +++-----
 .../service_auto_start/component_auto_start.hbs    |  19 --
 .../service_auto_start_indicator.hbs               |  19 --
 ambari-web/app/views.js                            |   2 -
 .../app/views/main/admin/service_auto_start.js     |  68 +++--
 .../service_auto_start/component_auto_start.js     |  58 ----
 .../service_auto_start_indicator.js                |  34 ---
 .../main/admin/service_auto_start_test.js          | 335 +++++++++------------
 .../component_auto_start_test.js                   |  64 ----
 .../views/main/admin/service_auto_start_test.js    |  69 ++++-
 17 files changed, 528 insertions(+), 667 deletions(-)

diff --git a/ambari-web/app/assets/test/tests.js 
b/ambari-web/app/assets/test/tests.js
index 51e9f3e..195ba72 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -292,7 +292,6 @@ var files = [
   'test/views/main/admin/stack_upgrade/services_view_test',
   'test/views/main/admin/stack_upgrade/menu_view_test',
   'test/views/main/admin/stack_upgrade/failed_hosts_modal_view_test',
-  'test/views/main/admin/service_auto_start/component_auto_start_test',
   'test/views/main/admin/service_auto_start_test',
   'test/views/main/dashboard/config_history_view_test',
   'test/views/main/dashboard/config_history_search_box_test',
diff --git a/ambari-web/app/controllers/main/admin/service_auto_start.js 
b/ambari-web/app/controllers/main/admin/service_auto_start.js
index df07c94..0d34ef0 100644
--- a/ambari-web/app/controllers/main/admin/service_auto_start.js
+++ b/ambari-web/app/controllers/main/admin/service_auto_start.js
@@ -30,77 +30,105 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
   /**
    * @type {Array}
    */
-  componentsConfigs: [],
+  componentsConfigsCached: [],
+
+  /**
+   * @type {object}
+   */
+  componentsConfigsCachedMap: function() {
+    const map = {};
+    
this.get('componentsConfigsCached').mapProperty('ServiceComponentInfo').forEach((component)
 => {
+      map[component.component_name] = component.recovery_enabled === 'true'
+    });
+    return map;
+  
}.property('componentsConfigsCached.@each.ServiceComponentInfo.recovery_enabled'),
+
+  /**
+   * @type {Array}
+   */
+  componentsConfigsGrouped: [],
 
   /**
    * @type {boolean}
-   * @default false
    */
-  isSaveDisabled: true,
+  isLoaded: false,
 
   /**
-   * Current value of servicesAutoStart
    * @type {boolean}
    */
-  servicesAutoStart: false,
+  isGeneralRecoveryEnabled: false,
 
   /**
-   * Value of servicesAutoStart saved on server
    * @type {boolean}
    */
-  servicesAutoStartSaved: false,
+  isGeneralRecoveryEnabledCached: false,
 
   /**
-   * config version tag,
-   * required to sync cluster configs locally without reload
-   * @type {?object}
+   * @type {boolean}
    */
-  tag: null,
+  saveInProgress: false,
 
   /**
    * @type {boolean}
    */
-  isServicesAutoStartChanged: 
Em.computed.notEqualProperties('servicesAutoStart', 'servicesAutoStartSaved'),
+  isModified: function() {
+    return this.get('isGeneralModified') || this.get('isComponentModified');
+  }.property('isGeneralModified', 'isComponentModified'),
 
-  valueChanged: function() {
-    var isSaveDisabled = true;
+  /**
+   * @type {boolean}
+   */
+  isGeneralModified: function() {
+    return this.get('isGeneralRecoveryEnabled') !== 
this.get('isGeneralRecoveryEnabledCached')
+  }.property('isGeneralRecoveryEnabled', 'isGeneralRecoveryEnabledCached'),
 
-    if (this.get('isServicesAutoStartChanged')) {
-      this.set('clusterConfigs.recovery_enabled', 
this.get('servicesAutoStart') + '');
-      isSaveDisabled = false;
-    }
-    this.get('tabs').forEach(function(tab) {
-      if (isSaveDisabled) {
-        isSaveDisabled = !tab.get('components').someProperty('isChanged');
-      }
-    });
+  /**
+   * @type {boolean}
+   */
+  isComponentModified: function() {
+    const componentsConfigsCachedMap = this.get('componentsConfigsCachedMap');
 
-    this.set('isSaveDisabled', isSaveDisabled);
+    return this.get('componentsConfigsGrouped').some((component) => {
+      return component.get('recoveryEnabled') !== 
componentsConfigsCachedMap[component.get('componentName')];
+    });
+  }.property('componentsConfigsGrouped.@each.recoveryEnabled', 
'componentsConfigsCachedMap'),
+
+  parseComponentConfigs: function(componentsConfigsCached) {
+    const componentsConfigsGrouped = [];
+    const servicesMap = componentsConfigsCached
+      .mapProperty('ServiceComponentInfo.service_name').uniq().toWickMap();
+
+    
componentsConfigsCached.mapProperty('ServiceComponentInfo').forEach((component) 
=> {
+      componentsConfigsGrouped.push(Em.Object.create({
+        serviceDisplayName: App.format.role(component.service_name, true),
+        isFirst: servicesMap[component.service_name],
+        componentName: component.component_name,
+        displayName: App.format.role(component.component_name, false),
+        recoveryEnabled: component.recovery_enabled === 'true'
+      }));
+      servicesMap[component.service_name] = false;
+    });
+    return componentsConfigsGrouped;
   },
 
   load: function() {
-    var self = this;
-    var dfd = $.Deferred();
-
-    return this.loadClusterConfig().done(function (data) {
-      var tag = [
+    this.loadClusterConfig().done((data) => {
+      const tag = [
         {
           siteName: 'cluster-env',
           tagName: data.Clusters.desired_configs['cluster-env'].tag,
           newTagName: null
         }
       ];
-      self.set('tag', tag);
-      
App.router.get('configurationController').getConfigsByTags(tag).done(function 
(data) {
-        var servicesAutoStart = data[0].properties.recovery_enabled === 'true';
-        self.set('clusterConfigs', data[0].properties);
-        self.set('servicesAutoStart', servicesAutoStart);
-        self.set('servicesAutoStartSaved', servicesAutoStart);
-        self.loadComponentsConfigs().done(dfd.resolve).fail(dfd.reject);
-      }).fail(dfd.reject);
-    }).fail(dfd.reject);
-
-    return dfd.promise();
+      
App.router.get('configurationController').getConfigsByTags(tag).done((data) => {
+        this.set('clusterConfigs', data[0].properties);
+        this.set('isGeneralRecoveryEnabled', 
data[0].properties.recovery_enabled === 'true');
+        this.set('isGeneralRecoveryEnabledCached', 
this.get('isGeneralRecoveryEnabled'));
+        this.loadComponentsConfigs().then(() => {
+          this.set('isLoaded', true);
+        });
+      });
+    });
   },
 
   loadClusterConfig: function () {
@@ -122,7 +150,8 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
   },
 
   loadComponentsConfigsSuccess: function (data) {
-    this.set('componentsConfigs', data.items);
+    this.set('componentsConfigsCached', data.items);
+    this.set('componentsConfigsGrouped', 
this.parseComponentConfigs(data.items));
   },
 
   saveClusterConfigs: function (clusterConfigs) {
@@ -149,123 +178,28 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
     });
   },
 
-  tabs: function() {
-    var services = {};
-    var tabs = [];
-
-    this.get('componentsConfigs').forEach(function (component) {
-      var serviceComponentInfo = component.ServiceComponentInfo;
-      if (serviceComponentInfo.total_count) {
-        if (serviceComponentInfo.category === "MASTER" || 
serviceComponentInfo.category === "SLAVE") {
-          var componentRecovery = 
this.createRecoveryComponent(serviceComponentInfo);
-          var service = services[serviceComponentInfo.service_name];
-
-          if (service) {
-            service.get('components').pushObject(componentRecovery);
-            service.set('enabledComponents', 
services[serviceComponentInfo.service_name].get('enabledComponents') + 
(componentRecovery.get('recovery_enabled') ? 1 : 0));
-            service.set('totalComponents', 
services[serviceComponentInfo.service_name].get('totalComponents') + 1);
-          } else {
-            services[serviceComponentInfo.service_name] = 
this.createTab(serviceComponentInfo, componentRecovery);
-          }
-        }
-      }
-    }, this);
-    for (var name in services ) {
-      tabs.push(services[name]);
-    }
-    if (tabs.length) {
-      tabs[0].set('isActive', true);
-    }
-    return tabs;
-  }.property('componentsConfigs'),
-
-  createRecoveryComponent: function(serviceComponentInfo) {
-    return Ember.Object.create({
-      displayName: App.format.role(serviceComponentInfo.component_name, false),
-      componentName: serviceComponentInfo.component_name,
-      recoveryEnabled: serviceComponentInfo.recovery_enabled === 'true',
-      recoveryEnabledSaved: serviceComponentInfo.recovery_enabled === 'true',
-      isChanged: Em.computed.notEqualProperties('recoveryEnabled', 
'recoveryEnabledSaved'),
-      serviceName: serviceComponentInfo.service_name
-    });
-  },
-
-  createTab: function(serviceComponentInfo, componentRecovery) {
-    return Ember.Object.create({
-      service_name: serviceComponentInfo.service_name,
-      display_name: App.format.role(serviceComponentInfo.service_name, true),
-      headingClass: "." + serviceComponentInfo.service_name,
-      isActive: false,
-      tooltip: function () {
-        var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
-        var text = Em.I18n.t('admin.serviceAutoStart.tooltip.text');
-        if (percentage === 1) {
-          return text.format("All");
-        } else if (percentage === 0) {
-          return text.format("No");
-        } else {
-          return text.format(this.get('enabledComponents') + "/" + 
this.get('totalComponents'));
-        }
-      }.property('enabledComponents', 'totalComponents'),
-      components: Em.A([componentRecovery]),
-      enabledComponents: componentRecovery.recoveryEnabled ? 1 : 0,
-      totalComponents: 1,
-      indicator: function () {
-        var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
-        var indicator = "icon-adjust";
-        if (percentage === 1) {
-          indicator = "icon-circle";
-        } else if (percentage === 0) {
-          indicator = "icon-circle-blank";
-        }
-        return indicator;
-      }.property('enabledComponents', 'totalComponents')
-    });
-  },
-
-  filterTabsByRecovery: function(tabs, isRecoveryEnabled) {
-    var components = [];
-
-    tabs.forEach(function (service) {
-      service.get('components').forEach(function (component) {
-        if (component.get('isChanged') && component.get('recoveryEnabled') === 
isRecoveryEnabled) {
-          components.push(component.get('componentName'));
-        }
-      });
-    });
-    return components;
-  },
-
   syncStatus: function () {
-    var servicesAutoStart = this.get('servicesAutoStart');
-    this.set('servicesAutoStartSaved', servicesAutoStart);
-    
App.router.get('configurationController').getConfigsByTags(this.get('tag')).done(function(data)
 {
-      data[0].properties.recovery_enabled = servicesAutoStart + '';
-      App.router.get('configurationController').saveToDB(data);
+    const componentsConfigsGrouped = this.get('componentsConfigsGrouped');
+    this.set('isGeneralRecoveryEnabledCached', 
this.get('isGeneralRecoveryEnabled'));
+    
this.get('componentsConfigsCached').mapProperty('ServiceComponentInfo').forEach((component)
 => {
+      const actualComponent = 
componentsConfigsGrouped.findProperty('componentName', 
component.component_name);
+      component.recovery_enabled = 
String(actualComponent.get('recoveryEnabled'));
     });
-    this.get('tabs').forEach(function(tab) {
-      tab.get('components').forEach(function(component) {
-        component.set('recoveryEnabledSaved', 
component.get('recoveryEnabled'));
-      });
-    });
-    this.valueChanged();
+    this.propertyDidChange('componentsConfigsCached');
   },
 
-  revertStatus: function () {
-    this.set('servicesAutoStart', this.get('servicesAutoStartSaved'));
-    this.get('tabs').forEach(function(tab) {
-      tab.get('components').forEach(function(component) {
-        component.set('recoveryEnabled', 
component.get('recoveryEnabledSaved'));
-      });
-    });
+  restoreCachedValues: function () {
+    this.set('isGeneralRecoveryEnabled', 
this.get('isGeneralRecoveryEnabledCached'));
+    this.set('componentsConfigsGrouped', 
this.parseComponentConfigs(this.get('componentsConfigsCached')));
   },
 
-  enableAll: function (event) {
-    event.context.get('components').setEach('recoveryEnabled', true);
-  },
+  filterComponentsByChange: function(components, value) {
+    const map = this.get('componentsConfigsCachedMap');
 
-  disableAll: function (event) {
-    event.context.get('components').setEach('recoveryEnabled', false);
+    return components.filter((component) => {
+      return component.get('recoveryEnabled') !== 
map[component.get('componentName')]
+          && component.get('recoveryEnabled') === value;
+    }).mapProperty('componentName');
   },
 
   /**
@@ -296,13 +230,14 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
       primary: Em.I18n.t('common.save'),
       secondary: Em.I18n.t('common.cancel'),
       onSave: function () {
-        var clusterConfigsCall, enabledComponentsCall, disabledComponentsCall;
+        let clusterConfigsCall, enabledComponentsCall, disabledComponentsCall;
 
-        if (self.get('isServicesAutoStartChanged')) {
+        if (self.get('isGeneralModified')) {
           clusterConfigsCall = 
self.saveClusterConfigs(self.get('clusterConfigs'));
         }
-        var enabledComponents = self.filterTabsByRecovery(self.get('tabs'), 
true);
-        var disabledComponents = self.filterTabsByRecovery(self.get('tabs'), 
false);
+
+        const enabledComponents = 
self.filterComponentsByChange(self.get('componentsConfigsGrouped'), true);
+        const disabledComponents = 
self.filterComponentsByChange(self.get('componentsConfigsGrouped'), false);
 
         if (enabledComponents.length) {
           enabledComponentsCall = self.saveComponentSettingsCall('true', 
enabledComponents);
@@ -310,16 +245,19 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
         if (disabledComponents.length) {
           disabledComponentsCall = self.saveComponentSettingsCall('false', 
disabledComponents);
         }
+        self.set('saveInProgress', true);
         $.when(clusterConfigsCall, enabledComponentsCall, 
disabledComponentsCall).done(function () {
           if (typeof transitionCallback === 'function') {
             transitionCallback();
           }
           self.syncStatus();
+        }).always(function() {
+          self.set('saveInProgress', false);
         });
         this.hide();
       },
       onDiscard: function () {
-        self.revertStatus();
+        self.restoreCachedValues();
         if (typeof transitionCallback === 'function') {
           transitionCallback();
         }
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index b71cfa3..36185a1 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1739,10 +1739,10 @@ Em.I18n.translations = {
   'admin.misc.nothingToShow': 'No user accounts to display',
 
   'admin.serviceAutoStart.title': "Service Auto Start",
-  'admin.serviceAutoStart.header': "Service Auto Start Configuration",
   'admin.serviceAutoStart.header.text': "Ambari services can be configured to 
start automatically on system boot. Each service can be configured to start all 
components, masters and workers, or selectively.",
-  'admin.serviceAutoStart.body.text': "Auto-Start Services",
+  'admin.serviceAutoStart.general.switcher': "Auto Start Settings",
   'admin.serviceAutoStart.tooltip.text': "{0} components enabled",
+  'admin.serviceAutoStart.column.autoStart': "Auto Start",
 
   'admin.stackVersions.filter.notInstalled': "Not Installed ({0})",
   'admin.stackVersions.filter.all': "All ({0})",
diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js
index 8021043..a5f9345 100644
--- a/ambari-web/app/routes/main.js
+++ b/ambari-web/app/routes/main.js
@@ -664,7 +664,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       },
       exitRoute: function (router, context, callback) {
         var controller = router.get('mainAdminServiceAutoStartController');
-        if (!controller.get('isSaveDisabled')) {
+        if (controller.get('isModified')) {
           controller.showSavePopup(callback);
         } else {
           callback();
diff --git a/ambari-web/app/styles/application.less 
b/ambari-web/app/styles/application.less
index 1d5056b..59b50e4 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -1282,16 +1282,43 @@ table.graphs {
    margin-left: 20px
  }
 
-.admin-auto-start {
-  .nav i {
-    margin-top: 0;
-    color: @green;
+#admin-auto-start {
+  background-color: #fff;
+  padding: 30px;
+  .description {
+    color: @gray-text;
   }
-  .services-heading {
-    padding: 8px;
-    color: #999;
-    font-size: 13px;
-    font-weight: bold;
+  .table {
+    th {
+      font-weight: normal;
+    }
+    th:first-child {
+      color: #666;
+    }
+    th:first-child,
+    td:first-child,
+    th:first-child + th,
+    td:first-child + td {
+      line-height: 24px;
+      min-width: 200px;
+      width: 20%;
+    }
+    th:first-child + th + th,
+    td:first-child + td + td {
+      width: 15%;
+      text-align: center;
+    }
+    .skip-border {
+      border: none;
+    }
+    .all-checkbox {
+      display: inline-block;
+      top: 2px;
+    }
+  }
+  .general-auto-start {
+    display: inline-block;
+    margin-left: 15px;
   }
 }
 
diff --git a/ambari-web/app/styles/bootstrap_overrides.less 
b/ambari-web/app/styles/bootstrap_overrides.less
index b2ed244..db98047 100644
--- a/ambari-web/app/styles/bootstrap_overrides.less
+++ b/ambari-web/app/styles/bootstrap_overrides.less
@@ -458,4 +458,11 @@ select.form-control {
 
 input[type="checkbox"]:checked + label:after {
   line-height: 2em;
+}
+
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+  padding-top: 0;
 }
\ No newline at end of file
diff --git a/ambari-web/app/styles/circle-toggle-switch.less 
b/ambari-web/app/styles/circle-toggle-switch.less
new file mode 100644
index 0000000..f5ec73d
--- /dev/null
+++ b/ambari-web/app/styles/circle-toggle-switch.less
@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+
+
+@bg-color-on: #1EB475;
+@bg-color-off: #EBECF1;
+@text-color-off: #666;
+@circle-size: 20px;
+
+.circle-toggle-switch {
+  .bootstrap-switch {
+    border-radius: 28px;
+    border: none;
+
+    .bootstrap-switch-label {
+      border-radius: @circle-size;
+      width: @circle-size !important;
+      height: @circle-size;
+      line-height: 14px;
+    }
+
+    .bootstrap-switch-container {
+      height: 28px;
+    }
+  }
+
+  .bootstrap-switch-on {
+    background-color: @bg-color-on;
+    .bootstrap-switch-container {
+      background-color: transparent;
+    }
+    .bootstrap-switch-label {
+      box-shadow: 1px 1px 30px 1px @text-color-off;
+      margin-top: 4px;
+      margin-left: -4px;
+      margin-right: 4px;
+    }
+  }
+
+  .bootstrap-switch-off {
+    box-shadow: inset 0 0 5px @text-color-off;
+    background-color: @bg-color-off;
+
+    .bootstrap-switch-container {
+      background-color: transparent;
+    }
+    .bootstrap-switch-label {
+      margin-top: 4px;
+      margin-left: 4px;
+      margin-right: 5px;
+      background-color: #999;
+      border: none;
+    }
+  }
+
+  .bootstrap-switch-handle-on.bootstrap-switch-success,
+  .bootstrap-switch-handle-off.bootstrap-switch-success {
+    background-color: transparent !important;
+  }
+
+  .bootstrap-switch-handle-on.bootstrap-switch-default,
+  .bootstrap-switch-handle-off.bootstrap-switch-default {
+    background-color: transparent !important;
+    color: @text-color-off !important;
+    text-align: left;
+  }
+
+  .bootstrap-switch-handle-off.bootstrap-switch-default:hover {
+    background-color: transparent !important;
+    color: @text-color-off !important;
+  }
+}
diff --git a/ambari-web/app/templates/main/admin/service_auto_start.hbs 
b/ambari-web/app/templates/main/admin/service_auto_start.hbs
index 6dc7dc5..0955d22 100644
--- a/ambari-web/app/templates/main/admin/service_auto_start.hbs
+++ b/ambari-web/app/templates/main/admin/service_auto_start.hbs
@@ -16,90 +16,59 @@
 * limitations under the License.
 }}
 
-<div class="admin-auto-start">
-  <h4>{{t admin.serviceAutoStart.header}}</h4>
-  <p>{{t admin.serviceAutoStart.header.text}}</p>
-  {{#if view.isLoaded}}
-    <div class="cluster-switcher row">
-      <div class="col-md-2">
-        <strong>{{t admin.serviceAutoStart.body.text}}</strong>
-      </div>
-      <div class="col-md-4">
-        {{view App.CheckboxView checkedBinding="controller.servicesAutoStart"}}
-      </div>
-      <div class="col-md-6">
-        <div class="pull-right operations-button">
-          <button class="btn btn-default" {{action revertStatus 
target="controller"}} {{bindAttr disabled="controller.isSaveDisabled"}}>{{t 
common.discard}}</button>
-          <button class="btn btn-success" {{action showSavePopup 
target="controller"}} {{bindAttr disabled="controller.isSaveDisabled"}}>{{t 
common.save}}</button>
-        </div>
-      </div>
+<div id="admin-auto-start">
+  <p class="description">{{t admin.serviceAutoStart.header.text}}</p>
+  {{#if controller.isLoaded}}
+    <div class="cluster-level-switcher">
+      <span class="h2">{{t admin.serviceAutoStart.general.switcher}}</span>
+      {{view App.CheckboxView
+             didInsertElementBinding="view.initSwitcher"
+             classNames="general-auto-start circle-toggle-switch"
+             disabledBinding="view.isDisabled"
+             checkedBinding="controller.isGeneralRecoveryEnabled"}}
     </div>
-    <hr>
-    <div class="row">
-      <div class="col-md-4 col-lg-3">
-        <div class="services-heading">{{t common.service}}</div>
-        <div class="row">
-          <div class="col-md-12">
-            <ul class="nav nav-tabs nav-stacked nav-tabs-left">
-              {{#each tab in controller.tabs}}
-                <li {{bindAttr class="tab.isActive:active"}}>
-                  <a href="#" data-toggle="tab" {{bindAttr 
data-target="tab.headingClass"}}>
-                    {{tab.display_name}}
-                    {{view App.MainAdminServiceAutoStartIndicatorView 
tabBinding="tab"}}
-                  </a>
-                </li>
-              {{/each}}
-            </ul>
-          </div>
-        </div>
-      </div>
-      <div class="col-md-8 col-lg-9 row">
-        <div class="tab-content col-md-12">
-          {{#each tab in controller.tabs}}
-            <div {{bindAttr class=":tab-pane tab.isActive:active 
tab.service_name"}}>
-              <table class="table table-hover">
-                <thead>
-                  <tr>
-                    <th class="col-md-4">
-                      {{t common.component}}
-                    </th>
-                    <th class="col-md-8">
-                      {{t common.status}}
-                    </th>
-                  </tr>
-                </thead>
-                <tbody>
-                  {{#each component in tab.components}}
-                    <tr>
-                      <td class="col-md-4">
-                        {{component.displayName}}
-                      </td>
-                      <td class="col-md-8">
-                        {{view App.MainAdminServiceAutoStartComponentView 
componentBinding="component" tabBinding="tab"}}
-                      </td>
-                    </tr>
-                  {{/each}}
-                </tbody>
-                {{#isAuthorized "SERVICE.MANAGE_AUTO_START"}}
-                  <tfoot>
-                    <tr>
-                      <td class="col-md-4"></td>
-                      <td class="col-md-8">
-                        <a href="#" class="enable-all-link" {{action enableAll 
tab target="controller"}}>
-                            {{t common.enableAll}}
-                        </a> |
-                        <a href="#" {{action disableAll tab 
target="controller"}}>{{t common.disableAll}}</a>
-                      </td>
-                    </tr>
-                  </tfoot>
-                {{/isAuthorized}}
-              </table>
-            </div>
-          {{/each}}
-        </div>
-      </div>
+    <div class="form-horizontal">
+      <table class="table">
+        <thead>
+          <tr>
+            <th>{{t common.service}}</th>
+            <th>{{t common.components}}</th>
+            <th>
+              {{view App.CheckboxView
+                     disabledBinding="view.isDisabled"
+                     classNames="all-checkbox"
+                     checkedBinding="view.allComponentsChecked"}}
+              {{t admin.serviceAutoStart.column.autoStart}}
+            </th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+        {{#each component in componentsConfigsGrouped}}
+          <tr>
+            <td {{bindAttr class="component.isFirst::skip-border"}}>
+              {{#if component.isFirst}}
+                {{component.serviceDisplayName}}
+              {{/if}}
+            </td>
+            <td>{{component.displayName}}</td>
+            <td>{{view App.CheckboxView
+                       disabledBinding="view.isDisabled"
+                       checkedBinding="component.recoveryEnabled"}}</td>
+            <td></td>
+          </tr>
+        {{/each}}
+        </tbody>
+      </table>
     </div>
   {{else}}
     {{view App.SpinnerView}}
   {{/if}}
+  <div {{bindAttr class="view.isDisablad:hidden :configs-save-panel"}}>
+    <div class="pull-right">
+      <button class="btn btn-default" {{action restoreCachedValues 
target="controller"}} {{bindAttr disabled="view.isSaveDisabled"}}>{{t 
common.cancel}}</button>
+      <button class="btn btn-success" {{action showSavePopup 
target="controller"}} {{bindAttr disabled="view.isSaveDisabled"}}>{{t 
common.save}}</button>
+    </div>
+    <div class="clearfix"></div>
+  </div>
 </div>
diff --git 
a/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
 
b/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
deleted file mode 100644
index a52dcfd..0000000
--- 
a/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
+++ /dev/null
@@ -1,19 +0,0 @@
-{{!
-* 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.
-}}
-
-{{view App.CheckboxView checkedBinding="view.component.recoveryEnabled"}}
diff --git 
a/ambari-web/app/templates/main/admin/service_auto_start/service_auto_start_indicator.hbs
 
b/ambari-web/app/templates/main/admin/service_auto_start/service_auto_start_indicator.hbs
deleted file mode 100644
index 4abcc13..0000000
--- 
a/ambari-web/app/templates/main/admin/service_auto_start/service_auto_start_indicator.hbs
+++ /dev/null
@@ -1,19 +0,0 @@
-{{!
-* 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.
-}}
-
-<i {{bindAttr class=":pull-right tab.indicator" 
data-original-title="tab.tooltip"}} rel="tooltip"></i>
\ No newline at end of file
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index fa51bfa..3b1520e 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -169,8 +169,6 @@ require('views/main/host/metrics/processes');
 require('views/main/host/addHost/step4_view');
 require('views/main/admin');
 require('views/main/admin/service_auto_start');
-require('views/main/admin/service_auto_start/component_auto_start');
-require('views/main/admin/service_auto_start/service_auto_start_indicator');
 require('views/main/admin/highAvailability/nameNode/wizard_view');
 require('views/main/admin/highAvailability/progress_view');
 require('views/main/admin/highAvailability/nameNode/rollback_view');
diff --git a/ambari-web/app/views/main/admin/service_auto_start.js 
b/ambari-web/app/views/main/admin/service_auto_start.js
index 0399944..2c7a9a1 100644
--- a/ambari-web/app/views/main/admin/service_auto_start.js
+++ b/ambari-web/app/views/main/admin/service_auto_start.js
@@ -23,25 +23,40 @@ App.MainAdminServiceAutoStartView = Em.View.extend({
 
   /**
    * @type {boolean}
-   * @default false
    */
-  isLoaded: false,
+  isSaveDisabled: Em.computed.or('!controller.isModified', 
'controller.saveInProgress'),
 
+  /**
+   * @type {boolean}
+   * @default false
+   */
   isDisabled: false,
 
+  /**
+   * @type {boolean}
+   */
+  skipCyclicCall: false,
+
+  /**
+   * @type {boolean}
+   */
+  allComponentsChecked: false,
+
+  /**
+   * @type {?object}
+   */
+  switcher: null,
+
   didInsertElement: function () {
     this.set('isDisabled', !App.isAuthorized('CLUSTER.MANAGE_AUTO_START'));
-    this.get('controller').load().then(() => {
-      this.set('isLoaded', true);
-      Em.run.next(() => Em.run.next(() => this.initSwitcher()));
-    });
+    this.get('controller').load();
   },
 
   onValueChange: function () {
     if (this.get('switcher')) {
-      this.get('switcher').bootstrapSwitch('state', 
this.get('controller.servicesAutoStart'));
+      this.get('switcher').bootstrapSwitch('state', 
this.get('controller.isGeneralRecoveryEnabled'));
     }
-  }.observes('controller.servicesAutoStart'),
+  }.observes('controller.isGeneralRecoveryEnabled'),
 
   /**
    * Init switcher plugin.
@@ -49,22 +64,41 @@ App.MainAdminServiceAutoStartView = Em.View.extend({
    * @method initSwitcher
    */
   initSwitcher: function () {
-    var self = this;
-    if (this.$) {
-      this.set('switcher', this.$("input:eq(0)").bootstrapSwitch({
-        state: self.get('controller.servicesAutoStart'),
+    const self = this.get('parentView');
+    if (self.get('controller.isLoaded')) {
+      self.set('switcher', $(".general-auto-start>input").bootstrapSwitch({
+        state: self.get('controller.isGeneralRecoveryEnabled'),
         onText: Em.I18n.t('common.enabled'),
         offText: Em.I18n.t('common.disabled'),
         offColor: 'default',
         onColor: 'success',
-        disabled: this.get('isDisabled'),
+        disabled: self.get('isDisabled'),
         handleWidth: Math.max(Em.I18n.t('common.enabled').length, 
Em.I18n.t('common.disabled').length) * 8,
-        onSwitchChange: function (event, state) {
-          self.set('controller.servicesAutoStart', state);
-          self.get('controller').valueChanged();
+        onSwitchChange: (event, state) => {
+          self.set('controller.isGeneralRecoveryEnabled', state);
         }
       }));
     }
-  }
+  },
+
+  observeAllComponentsChecked: function() {
+    if (this.get('skipCyclicCall')) {
+      this.set('skipCyclicCall', false);
+    } else {
+      
this.get('controller.componentsConfigsGrouped').setEach('recoveryEnabled', 
this.get('allComponentsChecked'));
+    }
+  }.observes('allComponentsChecked'),
+
+  observesEachComponentChecked: function() {
+    const components = this.get('controller.componentsConfigsGrouped');
+    if (this.get('allComponentsChecked') && 
components.someProperty('recoveryEnabled', false)) {
+      this.set('skipCyclicCall', true);
+      this.set('allComponentsChecked', false);
+    } else if (!this.get('allComponentsChecked') && 
components.everyProperty('recoveryEnabled', true)) {
+      this.set('skipCyclicCall', true);
+      this.set('allComponentsChecked', true);
+    }
+  }.observes('controller.componentsConfigsGrouped.@each.recoveryEnabled')
+
 });
 
diff --git 
a/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js 
b/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
deleted file mode 100644
index b8ad812..0000000
--- a/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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.MainAdminServiceAutoStartComponentView = Em.View.extend({
-  templateName: 
require('templates/main/admin/service_auto_start/component_auto_start'),
-
-  tab: null,
-  component: null,
-
-  didInsertElement: function () {
-    this.initSwitcher();
-  },
-
-  onValueChange: function () {
-    this.get('switcher').bootstrapSwitch('state', 
this.get('component.recoveryEnabled'));
-  }.observes('component.recoveryEnabled'),
-
-  /**
-   * Init switcher plugin.
-   *
-   * @method initSwitcher
-   */
-  initSwitcher: function () {
-    var self = this;
-    if (this.$()) {
-      this.set('switcher', this.$("input:eq(0)").bootstrapSwitch({
-        onText: Em.I18n.t('common.enabled'),
-        offText: Em.I18n.t('common.disabled'),
-        offColor: 'default',
-        onColor: 'success',
-        disabled: !App.isAuthorized('SERVICE.MANAGE_AUTO_START'),
-        handleWidth: Math.max(Em.I18n.t('common.enabled').length, 
Em.I18n.t('common.disabled').length) * 8,
-        onSwitchChange: function (event, state) {
-          self.set('tab.enabledComponents', self.get('tab.enabledComponents') 
+ (state ? 1 : -1));
-          self.set('component.recoveryEnabled', state);
-          self.get('controller').valueChanged();
-        }
-      }));
-    }
-  }
-});
\ No newline at end of file
diff --git 
a/ambari-web/app/views/main/admin/service_auto_start/service_auto_start_indicator.js
 
b/ambari-web/app/views/main/admin/service_auto_start/service_auto_start_indicator.js
deleted file mode 100644
index 49a80e3..0000000
--- 
a/ambari-web/app/views/main/admin/service_auto_start/service_auto_start_indicator.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * 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.MainAdminServiceAutoStartIndicatorView = Em.View.extend({
-  templateName: 
require('templates/main/admin/service_auto_start/service_auto_start_indicator'),
-  tagName: 'span',
-
-  tab: null,
-
-  didInsertElement: function () {
-    App.tooltip(this.$("[rel='tooltip']"));
-  },
-
-  willDestroyElement: function () {
-    this.$("[rel='tooltip']").tooltip('destroy');
-  }
-});
\ No newline at end of file
diff --git a/ambari-web/test/controllers/main/admin/service_auto_start_test.js 
b/ambari-web/test/controllers/main/admin/service_auto_start_test.js
index 8cb176e..66d8b70 100644
--- a/ambari-web/test/controllers/main/admin/service_auto_start_test.js
+++ b/ambari-web/test/controllers/main/admin/service_auto_start_test.js
@@ -28,59 +28,100 @@ describe('App.MainAdminServiceAutoStartController', 
function() {
     });
   });
 
+  describe('#parseComponentConfigs', function() {
 
-  describe('#valueChanged()', function() {
+    it('should return parsed components', function() {
+      var components = [
+        {
+          ServiceComponentInfo: {
+            service_name: 'S1',
+            component_name: 'C1',
+            recovery_enabled: 'true'
+          }
+        },
+        {
+          ServiceComponentInfo: {
+            service_name: 'S1',
+            component_name: 'C2',
+            recovery_enabled: 'false'
+          }
+        }
+      ];
+      expect(controller.parseComponentConfigs(components)).to.be.eql([
+        Em.Object.create({
+          "componentName": "C1",
+          "displayName": "C1",
+          "isFirst": true,
+          "recoveryEnabled": true,
+          "serviceDisplayName": "S1"
+        }),
+        Em.Object.create({
+          "componentName": "C2",
+          "displayName": "C2",
+          "isFirst": false,
+          "recoveryEnabled": false,
+          "serviceDisplayName": "S1"
+        })
+      ]);
+    });
+  });
 
-    it('servicesAutoStart is changed', function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        servicesAutoStartSaved: false,
-        tabs: []
+  describe('#load', function() {
+    var mock = {
+      getConfigsByTags: sinon.stub().returns({
+        done: function(callback) {
+          callback([
+            {
+              properties: {
+                recovery_enabled: 'true'
+              }
+            }
+          ]);
+        }
+      })
+    };
+    beforeEach(function() {
+      sinon.stub(controller, 'loadClusterConfig').returns({
+        done: function(callback) {
+          callback({
+            Clusters: {
+              desired_configs: {
+                'cluster-env': {
+                  tag: 1
+                }
+              }
+            }
+          });
+        }
       });
-      controller.valueChanged();
-      expect(controller.get('isSaveDisabled')).to.be.false;
+      sinon.stub(App.router, 'get').returns(mock);
+      sinon.stub(controller, 'loadComponentsConfigs').returns({
+        then: Em.clb
+      });
+      controller.load();
+    });
+    afterEach(function() {
+      controller.loadClusterConfig.restore();
+      App.router.get.restore();
+      controller.loadComponentsConfigs.restore();
     });
 
-    it('servicesAutoStart is not changed', function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        servicesAutoStartSaved: true,
-        tabs: []
+    it('clusterConfigs should be set', function() {
+      expect(controller.get('clusterConfigs')).to.be.eql({
+        recovery_enabled: 'true'
       });
-      controller.valueChanged();
-      expect(controller.get('isSaveDisabled')).to.be.true;
     });
 
-    it('components state is not changed', function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        servicesAutoStartSaved: true,
-        tabs: [Em.Object.create({
-          components: [
-            Em.Object.create({
-              isChanged: false
-            })
-          ]
-        })]
-      });
-      controller.valueChanged();
-      expect(controller.get('isSaveDisabled')).to.be.true;
+    it('isGeneralRecoveryEnabled should be true', function() {
+      expect(controller.get('isGeneralRecoveryEnabled')).to.be.true;
     });
 
-    it('components state is changed', function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        servicesAutoStartSaved: true,
-        tabs: [Em.Object.create({
-          components: [
-            Em.Object.create({
-              isChanged: true
-            })
-          ]
-        })]
-      });
-      controller.valueChanged();
-      expect(controller.get('isSaveDisabled')).to.be.false;
+    it('isGeneralRecoveryEnabledCached should be true', function() {
+      expect(controller.get('isGeneralRecoveryEnabledCached')).to.be.true;
+    });
+
+    it('isLoaded should be true', function() {
+      expect(controller.get('isLoaded')).to.be.true;
     });
   });
 
@@ -114,9 +155,21 @@ describe('App.MainAdminServiceAutoStartController', 
function() {
 
   describe('#loadComponentsConfigsSuccess()', function() {
 
-    it('componentsConfigs should be set', function() {
+    beforeEach(function() {
+      sinon.stub(controller, 'parseComponentConfigs').returns({});
+    });
+    afterEach(function() {
+      controller.parseComponentConfigs.restore();
+    });
+
+    it('componentsConfigsCached should be set', function() {
+      controller.loadComponentsConfigsSuccess({items: [{prop1: 'val1'}]});
+      expect(controller.get('componentsConfigsCached')).to.be.eql([{prop1: 
'val1'}]);
+    });
+
+    it('componentsConfigsGrouped should be set', function() {
       controller.loadComponentsConfigsSuccess({items: {prop1: 'val1'}});
-      expect(controller.get('componentsConfigs')).to.be.eql({prop1: 'val1'});
+      expect(controller.get('componentsConfigsGrouped')).to.be.eql({});
     });
   });
 
@@ -154,173 +207,79 @@ describe('App.MainAdminServiceAutoStartController', 
function() {
     });
   });
 
-  describe('#createRecoveryComponent()', function() {
-
-    it('should return recovery component', function() {
-      var serviceComponentInfo = {
-        component_name: 'c1',
-        recovery_enabled: 'true',
-        service_name: 's1'
-      };
-      
expect(controller.createRecoveryComponent(serviceComponentInfo)).to.be.an.object;
-    });
-  });
-
-  describe('#createTab()', function() {
-
-    it('should return tab', function() {
-      var serviceComponentInfo = {
-        component_name: 'c1',
-        recovery_enabled: 'true',
-        service_name: 's1'
-      };
-      expect(controller.createTab(serviceComponentInfo, {})).to.be.an.object;
-    });
-  });
-
-  describe('#filterTabsByRecovery()', function() {
-
-    it('should return empty when no components were changed', function() {
-      var tabs = [Em.Object.create({
-        components: [
-          Em.Object.create({
-            isChanged: false
-          })
-        ]
-      })];
-      expect(controller.filterTabsByRecovery(tabs, true)).to.be.empty;
-    });
-
-    it('should return enabled components ', function() {
-      var tabs = [Em.Object.create({
-        components: [
-          Em.Object.create({
-            isChanged: true,
-            recoveryEnabled: true
-          })
-        ]
-      })];
-      expect(controller.filterTabsByRecovery(tabs, true)).to.have.length(1);
-    });
-
-    it('should return disabled components ', function() {
-      var tabs = [Em.Object.create({
-        components: [
-          Em.Object.create({
-            isChanged: true,
-            recoveryEnabled: false
-          })
-        ]
-      })];
-      expect(controller.filterTabsByRecovery(tabs, false)).to.have.length(1);
-    });
-  });
-
-  describe('#syncStatus()', function() {
-    var mock = {
-      getConfigsByTags: function() {
-        return {
-          done: function(callback) {
-            callback([{properties: {}}]);
-          }
-        }
-      },
-      saveToDB: Em.K
-    };
-
+  describe('#syncStatus', function() {
     beforeEach(function() {
-      sinon.stub(App.router, 'get').returns(mock);
-      sinon.spy(mock, 'saveToDB');
-      sinon.stub(controller, 'valueChanged');
+      sinon.stub(controller, 'propertyDidChange');
     });
-
     afterEach(function() {
-      App.router.get.restore();
-      mock.saveToDB.restore();
-      controller.valueChanged.restore();
+      controller.propertyDidChange.restore();
     });
 
-    it('should save servicesAutoStart to local DB', function() {
-      controller.setProperties({
-        servicesAutoStart: true
-      });
-      controller.syncStatus();
-      expect(mock.saveToDB.getCall(0).args[0]).to.be.eql([{
-        properties: {
-          recovery_enabled: 'true'
+    it('should apply new values', function() {
+      controller.set('isGeneralRecoveryEnabled', true);
+      controller.set('componentsConfigsCached', [
+        {
+          ServiceComponentInfo: {
+            component_name: 'C1',
+            recovery_enabled: 'false'
+          }
         }
-      }]);
-    });
-
-    it('recoveryEnabledSaved should be synced with recoveryEnabled', 
function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        tabs: [Em.Object.create({
-          components: [
-            Em.Object.create({
-              recoveryEnabled: true,
-              recoveryEnabledSaved: false
-            })
-          ]
-        })]
-      });
-      controller.syncStatus();
-      
expect(controller.get('tabs')[0].get('components')[0].get('recoveryEnabledSaved')).to.be.true;
-    });
-
-    it('valueChanged should be called', function() {
+      ]);
+      controller.set('componentsConfigsGrouped', [
+        Em.Object.create({
+          componentName: 'C1',
+          recoveryEnabled: true
+        })
+      ]);
       controller.syncStatus();
-      expect(controller.valueChanged).to.be.calledonce;
+      
expect(controller.get('componentsConfigsCached')[0].ServiceComponentInfo.recovery_enabled).to.be.equal('true');
+      expect(controller.get('isGeneralRecoveryEnabledCached')).to.be.true;
     });
   });
 
-  describe('#revertStatus()', function() {
+  describe('#restoreCachedValues', function() {
+    beforeEach(function() {
+      sinon.stub(controller, 'parseComponentConfigs').returns([]);
+      controller.set('isGeneralRecoveryEnabledCached', true);
+      controller.restoreCachedValues();
+    });
+    afterEach(function() {
+      controller.parseComponentConfigs.restore();
+    });
 
-    it('component recoveryEnabled should be reverted', function() {
-      controller.reopen({
-        tabs: [Em.Object.create({
-          components: [
-            Em.Object.create({
-              recoveryEnabled: true,
-              recoveryEnabledSaved: false
-            })
-          ]
-        })]
-      });
-      controller.revertStatus();
-      
expect(controller.get('tabs')[0].get('components')[0].get('recoveryEnabled')).to.be.false;
+    it('isGeneralRecoveryEnabled should be true', function() {
+      expect(controller.get('isGeneralRecoveryEnabled')).to.be.true;
     });
 
-    it('servicesAutoStart should be reverted', function() {
-      controller.reopen({
-        servicesAutoStart: true,
-        servicesAutoStartSaved: false
-      });
-      controller.revertStatus();
-      expect(controller.get('servicesAutoStart')).to.be.false;
+    it('componentsConfigsGrouped should be set', function() {
+      expect(controller.get('componentsConfigsGrouped')).to.be.eql([]);
     });
   });
 
-  describe('#enableAll()', function() {
+  describe('#filterComponentsByChange', function() {
 
-    it('should set each recoveryEnabled to true', function() {
+    it('should return checked components', function() {
       var components = [
-        Em.Object.create({recoveryEnabled: false})
+        Em.Object.create({
+          recoveryEnabled: true,
+          componentName: 'C1'
+        })
       ];
-      controller.enableAll({context: Em.Object.create({components: 
components})});
-      expect(components[0].get('recoveryEnabled')).to.be.true;
+      controller.set('componentsConfigsCachedMap', {'C1': false});
+      expect(controller.filterComponentsByChange(components, 
true)).to.be.eql(['C1']);
     });
-  });
 
-  describe('#disableAll()', function() {
-
-    it('should set each recoveryEnabled to false', function() {
+    it('should return unchecked components', function() {
       var components = [
-        Em.Object.create({recoveryEnabled: true})
+        Em.Object.create({
+          recoveryEnabled: false,
+          componentName: 'C1'
+        })
       ];
-      controller.disableAll({context: Em.Object.create({components: 
components})});
-      expect(components[0].get('recoveryEnabled')).to.be.false;
+      controller.set('componentsConfigsCachedMap', {'C1': true});
+      expect(controller.filterComponentsByChange(components, 
false)).to.be.eql(['C1']);
     });
   });
 
+
 });
diff --git 
a/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
 
b/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
deleted file mode 100644
index 70fd61e..0000000
--- 
a/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * 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 view;
-
-describe('App.MainAdminServiceAutoStartComponentView', function () {
-
-  beforeEach(function () {
-    view = App.MainAdminServiceAutoStartComponentView.create();
-  });
-
-
-  describe('#didInsertElement()', function() {
-
-    beforeEach(function() {
-      sinon.stub(view, 'initSwitcher');
-    });
-
-    afterEach(function() {
-      view.initSwitcher.restore();
-    });
-
-    it('initSwitcher should be called', function() {
-      view.didInsertElement();
-      expect(view.initSwitcher).to.be.calledOnce;
-    });
-  });
-
-  describe('#onValueChange()', function() {
-    var mock = {
-      bootstrapSwitch: Em.K
-    };
-
-    beforeEach(function() {
-      sinon.spy(mock, 'bootstrapSwitch');
-    });
-
-    afterEach(function() {
-      mock.bootstrapSwitch.restore();
-    });
-
-    it('bootstrapSwitch should be called', function() {
-      view.set('switcher', mock);
-      view.onValueChange();
-      expect(mock.bootstrapSwitch).to.be.calledOnce;
-    });
-  });
-});
\ No newline at end of file
diff --git a/ambari-web/test/views/main/admin/service_auto_start_test.js 
b/ambari-web/test/views/main/admin/service_auto_start_test.js
index 0edcf28..ec70d9b 100644
--- a/ambari-web/test/views/main/admin/service_auto_start_test.js
+++ b/ambari-web/test/views/main/admin/service_auto_start_test.js
@@ -24,7 +24,8 @@ describe('App.MainAdminServiceAutoStartView', function () {
   beforeEach(function () {
     view = App.MainAdminServiceAutoStartView.create({
       controller: Em.Object.create({
-        load: Em.K
+        load: Em.K,
+        componentsConfigsGrouped: []
       })
     });
   });
@@ -33,27 +34,15 @@ describe('App.MainAdminServiceAutoStartView', function () {
   describe('#didInsertElement()', function() {
 
     beforeEach(function() {
-      sinon.stub(view, 'initSwitcher');
-      sinon.stub(Em.run, 'next', Em.clb);
-      sinon.stub(view.get('controller'), 'load').returns({
-        then: Em.clb
-      })
+      sinon.stub(view.get('controller'), 'load');
     });
-
     afterEach(function() {
-      view.initSwitcher.restore();
       view.get('controller').load.restore();
-      Em.run.next.restore();
-    });
-
-    it('initSwitcher should be called', function() {
-      view.didInsertElement();
-      expect(view.initSwitcher).to.be.calledOnce;
     });
 
-    it('isLoaded should be true', function() {
+    it('load should be called', function() {
       view.didInsertElement();
-      expect(view.get('isLoaded')).to.be.true;
+      expect(view.get('controller').load.calledOnce).to.be.true;
     });
   });
 
@@ -82,4 +71,52 @@ describe('App.MainAdminServiceAutoStartView', function () {
       expect(mock.bootstrapSwitch).to.be.calledOnce;
     });
   });
+
+  describe('#observeAllComponentsChecked', function() {
+
+    it('should skip checking child checkboxes', function() {
+      view.set('skipCyclicCall', true);
+      view.observeAllComponentsChecked();
+      expect(view.get('skipCyclicCall')).to.be.false;
+    });
+
+    it('should check all child checkboxes', function() {
+      view.get('controller').set('componentsConfigsGrouped', [
+        Em.Object.create({
+          recoveryEnabled: false
+        })
+      ]);
+      view.set('skipCyclicCall', false);
+      view.set('allComponentsChecked', true);
+      view.observeAllComponentsChecked();
+      
expect(view.get('controller.componentsConfigsGrouped').mapProperty('recoveryEnabled')).to.be.eql(
+        [true]
+      );
+    });
+  });
+
+  describe('#observesEachComponentChecked', function() {
+
+    it('should be false when at least one checkbox unchecked', function() {
+      view.set('allComponentsChecked', true);
+      view.get('controller').set('componentsConfigsGrouped', [
+        Em.Object.create({
+          recoveryEnabled: false
+        })
+      ]);
+      view.observesEachComponentChecked();
+      expect(view.get('allComponentsChecked')).to.be.false;
+    });
+
+    it('should be true when all checked', function() {
+      view.set('allComponentsChecked', false);
+      view.get('controller').set('componentsConfigsGrouped', [
+        Em.Object.create({
+          recoveryEnabled: true
+        })
+      ]);
+      view.observesEachComponentChecked();
+      expect(view.get('allComponentsChecked')).to.be.true;
+    });
+  });
 });
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
atk...@apache.org.

Reply via email to