You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2014/01/30 13:27:02 UTC

git commit: AMBARI-4469. Bulk Ops: Integrate maintenance mode. (onechiporenko)

Updated Branches:
  refs/heads/trunk 8a415ee1f -> d0338adf3


AMBARI-4469. Bulk Ops: Integrate maintenance mode. (onechiporenko)


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

Branch: refs/heads/trunk
Commit: d0338adf31d51a7b250b6a079254e791fbb23e68
Parents: 8a415ee
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Thu Jan 30 14:17:28 2014 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Thu Jan 30 14:26:54 2014 +0200

----------------------------------------------------------------------
 .../app/controllers/global/update_controller.js |   4 +-
 ambari-web/app/controllers/main/host.js         | 109 +++++++++-
 ambari-web/app/data/host/categories.js          |  10 +-
 ambari-web/app/mappers/hosts_mapper.js          |   5 +-
 ambari-web/app/messages.js                      |  15 +-
 ambari-web/app/models/host.js                   |  11 +-
 ambari-web/app/styles/application.less          |   2 +-
 ambari-web/app/templates/main/host.hbs          |   6 +-
 ambari-web/app/utils/ajax.js                    |  42 ++++
 ambari-web/app/views/main/host.js               |  32 +--
 .../views/main/host/hosts_table_menu_view.js    |  45 ++--
 ambari-web/test/controllers/main/host_test.js   | 204 ++++++++++++++++++-
 ambari-web/test/installer/step9_test.js         |  53 +++--
 13 files changed, 440 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/controllers/global/update_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/update_controller.js b/ambari-web/app/controllers/global/update_controller.js
index 196a39a..95d0aaa 100644
--- a/ambari-web/app/controllers/global/update_controller.js
+++ b/ambari-web/app/controllers/global/update_controller.js
@@ -80,7 +80,7 @@ App.UpdateController = Em.Controller.extend({
 
   updateHost:function(callback) {
     var testUrl = App.get('isHadoop2Stack') ? '/data/hosts/HDP2/hosts.json' : '/data/hosts/hosts.json';
-    var hostsUrl = this.getUrl(testUrl, '/hosts?fields=Hosts/host_name,Hosts/host_status,Hosts/last_heartbeat_time,Hosts/disk_info,' +
+    var hostsUrl = this.getUrl(testUrl, '/hosts?fields=Hosts/host_name,Hosts/host_status,Hosts/last_heartbeat_time,Hosts/disk_info,Hosts/passive_state,' +
       'metrics/disk,metrics/load/load_one,metrics/cpu/cpu_system,metrics/cpu/cpu_user,metrics/memory/mem_total,metrics/memory/mem_free,'+
       'alerts/summary&minimal_response=true');
     App.HttpClient.get(hostsUrl, App.hostsMapper, {
@@ -172,7 +172,7 @@ App.UpdateController = Em.Controller.extend({
       conditionalFieldsString;
 
     var servicesUrl = isInitialLoad ? this.getUrl(testUrl, realUrl + initialFieldsString) : this.getUrl(testUrl, realUrl);
-    var callback = callback || function (jqXHR, textStatus) {
+    callback = callback || function () {
       self.set('isUpdated', true);
     };
     App.HttpClient.get(servicesUrl, App.serviceMetricsMapper, {

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/controllers/main/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host.js b/ambari-web/app/controllers/main/host.js
index 97c9358..56ec00f 100644
--- a/ambari-web/app/controllers/main/host.js
+++ b/ambari-web/app/controllers/main/host.js
@@ -148,11 +148,16 @@ App.MainHostController = Em.ArrayController.extend({
         this.bulkOperationForHostComponentsRestart(operationData, hosts);
       }
       else {
-        if (operationData.action.indexOf('DECOMMISSION') != -1) {
-          this.bulkOperationForHostComponentsDecommission(operationData, hosts);
+        if (operationData.action === 'PASSIVE_STATE') {
+          this.bulkOperationForHostComponentsPassiveState(operationData, hosts);
         }
         else {
-          this.bulkOperationForHostComponents(operationData, hosts);
+          if (operationData.action.indexOf('DECOMMISSION') != -1) {
+            this.bulkOperationForHostComponentsDecommission(operationData, hosts);
+          }
+          else {
+            this.bulkOperationForHostComponents(operationData, hosts);
+          }
         }
       }
     }
@@ -161,13 +166,18 @@ App.MainHostController = Em.ArrayController.extend({
         this.bulkOperationForHostsRestart(operationData, hosts);
       }
       else {
-        this.bulkOperationForHosts(operationData, hosts);
+        if (operationData.action === 'PASSIVE_STATE') {
+          this.bulkOperationForHostsPassiveState(operationData, hosts);
+        }
+        else {
+          this.bulkOperationForHosts(operationData, hosts);
+        }
       }
     }
   },
 
   /**
-   * Do bulk operation for selected hosts
+   * Bulk operation (start/stop all) for selected hosts
    * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
    * @param {Array} hosts - list of affected hosts
    */
@@ -223,18 +233,43 @@ App.MainHostController = Em.ArrayController.extend({
   },
 
   /**
-   * Do bulk operation for selected hostComponents
+   * Bulk turn on/off passive state for selected hosts
    * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
    * @param {Array} hosts - list of affected hosts
    */
-  bulkOperationForHostComponents: function(operationData, hosts) {
-    var hostsWithSelectedComponent = [];
+  bulkOperationForHostsPassiveState: function(operationData, hosts) {
+    var affectedHosts = [];
     hosts.forEach(function(host) {
-      var components = host.get('hostComponents');
-      if (components.findProperty('componentName', operationData.componentName)) {
-        hostsWithSelectedComponent.push(host);
+      if (host.get('passiveState') !== operationData.state) {
+        affectedHosts.push(host.get('hostName'));
       }
     });
+    if (affectedHosts.length) {
+      App.ajax.send({
+        name: 'bulk_request.hosts.passive_state',
+        sender: this,
+        data: {
+          hostNames: affectedHosts.join(','),
+          passive_state: operationData.state,
+          requestInfo: operationData.message
+        }
+      });
+    }
+    else {
+      App.ModalPopup.show({
+        header: Em.I18n.t('rolling.nothingToDo.header'),
+        body: Em.I18n.t('hosts.bulkOperation.passiveState.nothingToDo.body'),
+        secondary: false
+      });
+    }
+  },
+
+  /**
+   * Bulk operation for selected hostComponents
+   * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
+   * @param {Array} hosts - list of affected hosts
+   */
+  bulkOperationForHostComponents: function(operationData, hosts) {
     var service = App.Service.find(operationData.serviceName);
     var components = service.get('hostComponents').filter(function(hc) {
       if (hc.get('componentName') != operationData.componentName) {
@@ -343,6 +378,58 @@ App.MainHostController = Em.ArrayController.extend({
   },
 
   /**
+   * Bulk turn on/off passive state for selected hostComponents
+   * @param {Object} operationData
+   * @param {Array} hosts
+   */
+  bulkOperationForHostComponentsPassiveState: function(operationData, hosts) {
+    var affectedHosts = [];
+    hosts.forEach(function(host) {
+      host.get('hostComponents').forEach(function(hostComponent) {
+        if (hostComponent.get('componentName') == operationData.componentName) {
+          if (hostComponent.get('passiveState') !== operationData.state) {
+            if (hostComponent.get('passiveState') === 'IMPLIED') {
+              if (operationData.state === 'ACTIVE') {
+                affectedHosts.push(host.get('hostName'));
+              }
+            }
+            else {
+              if (hostComponent.get('passiveState') === 'PASSIVE') {
+                if (operationData.state === 'ACTIVE') {
+                  affectedHosts.push(host.get('hostName'));
+                }
+              }
+              else {
+                if (operationData.state === 'PASSIVE') {
+                  affectedHosts.push(host.get('hostName'));
+                }
+              }
+            }
+          }
+        }
+      });
+    });
+    if (affectedHosts.length) {
+      App.ajax.send({
+        name: 'bulk_request.hosts.all_components.passive_state',
+        sender: this,
+        data: {
+          query: 'HostRoles/component_name=' + operationData.componentName + '&HostRoles/host_name.in(' + affectedHosts.join(',') + ')',
+          passive_state: operationData.state,
+          requestInfo: operationData.message
+        }
+      });
+    }
+    else {
+      App.ModalPopup.show({
+        header: Em.I18n.t('rolling.nothingToDo.header'),
+        body: Em.I18n.t('hosts.bulkOperation.host_components.passiveState.nothingToDo.body'),
+        secondary: false
+      });
+    }
+  },
+
+  /**
    * Show BO popup after bulk request
    */
   bulkOperationForHostComponentsSuccessCallback: function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/data/host/categories.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/data/host/categories.js b/ambari-web/app/data/host/categories.js
index 94a0622..6eebbe2 100644
--- a/ambari-web/app/data/host/categories.js
+++ b/ambari-web/app/data/host/categories.js
@@ -85,14 +85,14 @@ module.exports = [
     observes: 'view.content.@each.selected'
   },
   {
-    value: Em.I18n.t('common.maintenance'),
-    hostProperty: 'componentsInMaintenanceCount',
-    class: 'maintenance icon-medkit',
+    value: Em.I18n.t('common.passive_state'),
+    hostProperty: 'componentsInPassiveStateCount',
+    class: 'passive-state icon-medkit',
     isHealthStatus: false,
-    healthStatusValue: 'health-status-MAINTENANCE',
+    healthStatusValue: 'health-status-PASSIVE_STATE',
     column: 9,
     type: 'number',
     filterValue: '>0',
-    observes: 'view.content.@each.componentsInMaintenanceCount'
+    observes: 'view.content.@each.componentsInPassiveStateCount'
   }
 ];

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/mappers/hosts_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/hosts_mapper.js b/ambari-web/app/mappers/hosts_mapper.js
index ac23b4a..8d98e7d 100644
--- a/ambari-web/app/mappers/hosts_mapper.js
+++ b/ambari-web/app/mappers/hosts_mapper.js
@@ -56,7 +56,8 @@ App.hostsMapper = App.QuickDataMapper.create({
     last_heart_beat_time: "Hosts.last_heartbeat_time",
     os_arch: 'Hosts.os_arch',
     os_type: 'Hosts.os_type',
-    ip: 'Hosts.ip'
+    ip: 'Hosts.ip',
+    passive_state: 'Hosts.passive_state'
   },
   map: function (json) {
     console.time('App.hostsMapper execution time');
@@ -175,7 +176,7 @@ App.hostsMapper = App.QuickDataMapper.create({
    */
   getDiscrepancies: function (current, previous) {
     var result = {};
-    var fields = ['disk_total', 'disk_free', 'health_status', 'load_one', 'cpu_system', 'cpu_user', 'mem_total', 'mem_free', 'critical_alerts_count'];
+    var fields = ['disk_total', 'disk_free', 'health_status', 'load_one', 'cpu_system', 'cpu_user', 'mem_total', 'mem_free', 'critical_alerts_count', 'passive_state'];
     if (previous) {
       fields.forEach(function (field) {
         if (current[field] != previous[field]) result[field] = current[field];

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 6ed4070..c2c052b 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -168,6 +168,7 @@ Em.I18n.translations = {
   'common.discard': 'Discard',
   'common.actions': 'Actions',
   'common.maintenance': 'Maintenance',
+  'common.passive_state': 'Passive State',
   'common.selected': 'Selected',
   'common.password': 'Password',
   'common.url': 'URL',
@@ -219,7 +220,7 @@ Em.I18n.translations = {
   'login.username':'Username',
   'login.loginButton':'Sign in',
   'login.error':'Invalid username/password combination.',
-  
+
   'graphs.noData.title': 'No Data',
   'graphs.noData.message': 'There was no data available. Possible reasons include inaccessible Ganglia service.',
   'graphs.noDataAtTime.message': 'No available data for the time period.',
@@ -614,7 +615,7 @@ Em.I18n.translations = {
   'addHost.step4.header':'Configurations',
   'addHost.step4.title':'Select the configuration groups to which the added hosts will belong to.',
 
-  
+
   'installer.stackUpgrade.header':'Stack Upgrade Wizard',
   'installer.stackUpgrade.step1.newVersion':'New Version',
   'installer.stackUpgrade.step1.installedVersion':'Installed Version',
@@ -1332,8 +1333,8 @@ Em.I18n.translations = {
   'hosts.table.restartComponents.withNames':'Restart {0}',
   'hosts.table.restartComponents.withoutNames':'{0} components should be restarted',
 
-  'hosts.table.componentsInMaintenance.withNames':'{0} in maintenance mode',
-  'hosts.table.componentsInMaintenance.withoutNames':'{0} components in maintenance mode',
+  'hosts.table.componentsInPassiveState.withNames':'{0} in passive state',
+  'hosts.table.componentsInPassiveState.withoutNames':'{0} components in passive state',
 
   'hosts.table.menu.l1.selectedHosts':'Selected Hosts',
   'hosts.table.menu.l1.filteredHosts':'Filtered Hosts',
@@ -1344,6 +1345,8 @@ Em.I18n.translations = {
   'hosts.bulkOperation.confirmation.header':'Confirm Bulk Operation',
   'hosts.bulkOperation.confirmation.hosts':'Are you sure you want to <strong>{0}</strong> on the following {1} hosts?',
   'hosts.bulkOperation.confirmation.hostComponents':'Are you sure you want to <strong>{0} {1}</strong> on the following {2} hosts?',
+  'hosts.bulkOperation.passiveState.nothingToDo.body':'All hosts are already in Passive State that you selected',
+  'hosts.bulkOperation.host_components.passiveState.nothingToDo.body':'All host components are already in Passive State that you selected',
 
   'hosts.selectHostsDialog.title': 'Select Configuration Group Hosts',
   'hosts.selectHostsDialog.message': 'Select hosts that should belong to this {0} Configuration Group.  All hosts belonging to this group will have the same set of {0} configurations.',
@@ -1643,8 +1646,8 @@ Em.I18n.translations = {
   'dashboard.services.flume.channels': 'Channels',
   'dashboard.services.flume.sources': 'Sources',
   'dashboard.services.flume.sinks': 'Sinks',
-  
-  
+
+
   'dashboard.services.hbase.summary':'{0} region servers with {1} average load',
   'dashboard.services.hbase.masterServer':'HBase Master',
   'dashboard.services.hbase.masterServer.active':'Active HBase Master',

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/models/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/host.js b/ambari-web/app/models/host.js
index ccec977..7319cc2 100644
--- a/ambari-web/app/models/host.js
+++ b/ambari-web/app/models/host.js
@@ -45,6 +45,7 @@ App.Host = DS.Model.extend({
   cpuSystem:DS.attr('number'),
   cpuUser:DS.attr('number'),
   criticalAlertsCount: DS.attr('number'),
+  passiveState: DS.attr('string'),
 
   /**
    * Is host checked at the main Hosts page
@@ -87,12 +88,14 @@ App.Host = DS.Model.extend({
   }.property('componentsWithStaleConfigs.length'),
 
   /**
-   * Get count of host components in maintenance mode
+   * Get count of host components in passive state
    * @returns {Number}
    */
-  componentsInMaintenanceCount: function() {
-    return this.get('hostComponents').filterProperty('workStatus', App.HostComponentStatus.maintenance).length;
-  }.property('hostComponents.@each.workStatus'),
+  componentsInPassiveStateCount: function() {
+    return this.get('hostComponents').filter(function(component) {
+      return component.get('passiveState') !== 'ACTIVE';
+    }).length;
+  }.property('hostComponents.@each.passiveState'),
 
   /**
    * Truncate hostName if it longer than 43 symbols

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index 56d77b5..1419840 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -2758,7 +2758,7 @@ table.graphs {
   }
   .health-status-DEAD-YELLOW {
   }
-  .maintenance {
+  .passive-state {
     color: #000;
   }
   .alerts-status {

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/templates/main/host.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host.hbs b/ambari-web/app/templates/main/host.hbs
index 4c93212..9d29b22 100644
--- a/ambari-web/app/templates/main/host.hbs
+++ b/ambari-web/app/templates/main/host.hbs
@@ -105,9 +105,9 @@
               <span class="muted icon-refresh" rel="ComponentsTooltip" {{bindAttr title="view.restartRequiredComponentsMessage"}}></span>
             {{/if}}
           </td>
-          <td class="maintenance">
-            {{#if host.componentsInMaintenanceCount}}
-              <span class="icon-medkit" rel="ComponentsTooltip" {{bindAttr title="view.componentsInMaintenanceMessage"}}></span>
+          <td class="passive-state">
+            {{#if host.componentsInPassiveStateCount}}
+              <span class="icon-medkit" rel="ComponentsTooltip" {{bindAttr title="view.componentsInPassiveStateMessage"}}></span>
             {{/if}}
           </td>
           <td>{{host.ip}}</td>

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index 0aec4ea..a83a42b 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -1500,6 +1500,48 @@ var urls = {
     }
   },
 
+  'bulk_request.hosts.passive_state': {
+    'real': '/clusters/{clusterName}/hosts',
+    'mock': '',
+    'format': function(data) {
+      return {
+        type: 'PUT',
+        data: JSON.stringify({
+          RequestInfo: {
+            context: data.requestInfo,
+            query: 'Hosts/host_name.in(' + data.hostNames + ')'
+          },
+          Body: {
+            Hosts: {
+              passive_state: data.passive_state
+            }
+          }
+        })
+      }
+    }
+  },
+
+  'bulk_request.hosts.all_components.passive_state': {
+    'real': '/clusters/{clusterName}/host_components',
+    'mock': '',
+    'format': function(data) {
+      return {
+        type: 'PUT',
+        data: JSON.stringify({
+          RequestInfo: {
+            context: data.requestInfo,
+            query: data.query
+          },
+          Body: {
+            HostRoles: {
+              passive_state: data.passive_state
+            }
+          }
+        })
+      }
+    }
+  },
+
   'mirroring.create_new_dataset': {
     'real': '/falcon/entities/submitAndSchedule/feed',
     'mock': '/data/mirroring/succeeded.json',

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/views/main/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js
index 5089f9a..82da4de 100644
--- a/ambari-web/app/views/main/host.js
+++ b/ambari-web/app/views/main/host.js
@@ -65,7 +65,7 @@ App.MainHostView = App.TableView.extend({
 
   /**
    * Select/deselect all visible hosts flag
-   * @property {Boolean}
+   * @property {bool}
    */
   selectAllHosts: false,
 
@@ -231,14 +231,16 @@ App.MainHostView = App.TableView.extend({
      * Tooltip message for "Maintenance" icon
      * @returns {String}
      */
-    componentsInMaintenanceMessage: function() {
-      var componentsInMaintenance = this.get('content.hostComponents').filterProperty('workStatus', App.HostComponentStatus.maintenance);
-      var count = componentsInMaintenance.length;
+    componentsInPassiveStateMessage: function() {
+      var componentsInPassiveState = this.get('content.hostComponents').filter(function(component) {
+        return component.get('passiveState') !== 'ACTIVE';
+      });
+      var count = componentsInPassiveState.length;
       if (count <= 5) {
-        return Em.I18n.t('hosts.table.componentsInMaintenance.withNames').format(componentsInMaintenance.getEach('displayName').join(', '));
+        return Em.I18n.t('hosts.table.componentsInPassiveState.withNames').format(componentsInPassiveState.getEach('displayName').join(', '));
       }
-      return Em.I18n.t('hosts.table.componentsInMaintenance.withoutNames').format(count);
-    }.property('content.hostComponents.@each.workStatus'),
+      return Em.I18n.t('hosts.table.componentsInPassiveState.withoutNames').format(count);
+    }.property('content.hostComponents.@each.passiveState'),
 
     /**
      * String with list of host components <code>displayName</code>
@@ -272,7 +274,7 @@ App.MainHostView = App.TableView.extend({
     value: null,
     /**
      * Is category based on host health status
-     * @type {Boolean}
+     * @type {bool}
      */
     isHealthStatus: true,
     /**
@@ -282,12 +284,12 @@ App.MainHostView = App.TableView.extend({
     healthStatusValue: '',
     /**
      * Should category be displayed on the top of the hosts table
-     * @type {Boolean}
+     * @type {bool}
      */
     isVisible: true,
     /**
      * Is category selected now
-     * @type {Boolean}
+     * @type {bool}
      */
     isActive: false,
     /**
@@ -311,7 +313,7 @@ App.MainHostView = App.TableView.extend({
      */
     type: null,
     /**
-     * @type {String|Number|Boolean}
+     * @type {String|Number|bool}
      */
     filterValue: null,
     /**
@@ -328,7 +330,7 @@ App.MainHostView = App.TableView.extend({
 
     /**
      * Determine if category has hosts
-     * @type {Boolean}
+     * @type {bool}
      */
     hasHosts: false,
 
@@ -502,13 +504,13 @@ App.MainHostView = App.TableView.extend({
   /**
    * view of the maintenance filter implemented as a category of host statuses
    */
-  maintenanceFilter: Em.View.extend({
+  passiveStateFilter: Em.View.extend({
     column: 9,
     value: null,
     classNames: ['noDisplay'],
     showClearFilter: function(){
       var mockEvent = {
-        context: this.get('parentView.categories').findProperty('healthStatusValue', 'health-status-MAINTENANCE')
+        context: this.get('parentView.categories').findProperty('healthStatusValue', 'health-status-PASSIVE_STATE')
       };
       if(this.get('value')) {
         this.get('parentView.childViews').findProperty('column', 0).selectCategory(mockEvent);
@@ -729,7 +731,7 @@ App.MainHostView = App.TableView.extend({
     associations[6] = 'hostComponents';
     associations[7] = 'criticalAlertsCount';
     associations[8] = 'componentsWithStaleConfigsCount';
-    associations[9] = 'componentsInMaintenanceCount';
+    associations[9] = 'componentsInPassiveStateCount';
     associations[10] = 'selected';
     return associations;
   }.property()

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/app/views/main/host/hosts_table_menu_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/hosts_table_menu_view.js b/ambari-web/app/views/main/host/hosts_table_menu_view.js
index dc682a5..d9c9df0 100644
--- a/ambari-web/app/views/main/host/hosts_table_menu_view.js
+++ b/ambari-web/app/views/main/host/hosts_table_menu_view.js
@@ -83,6 +83,24 @@ App.HostTableMenuView = Em.View.extend({
           componentName: componentNameForDecommission,
           realComponentName: componentNameForOtherActions
         })
+      }),
+      Em.Object.create({
+        label: Em.I18n.t('passiveState.turnOn'),
+        operationData: Em.Object.create({
+          state: 'PASSIVE',
+          action: 'PASSIVE_STATE',
+          message: Em.I18n.t('passiveState.turnOnFor'),
+          componentName: componentNameForOtherActions
+        })
+      }),
+      Em.Object.create({
+        label: Em.I18n.t('passiveState.turnOff'),
+        operationData: Em.Object.create({
+          state: 'ACTIVE',
+          action: 'PASSIVE_STATE',
+          message: Em.I18n.t('passiveState.turnOffFor'),
+          componentName: componentNameForOtherActions
+        })
       })
     ]);
   },
@@ -92,8 +110,8 @@ App.HostTableMenuView = Em.View.extend({
    * operationData format:
    * <code>
    *  {
-   *    action: 'STARTED|INSTALLED|RESTART', // action for selected hosts (will be applied for each host component in selected hosts)
-   *    actionToCheck: 'INSTALLED|STARTED' // state to filter host components should be processed
+   *    action: 'STARTED|INSTALLED|RESTART..', // action for selected hosts (will be applied for each host component in selected hosts)
+   *    actionToCheck: 'INSTALLED|STARTED..' // state to filter host components should be processed
    *    message: 'some text', // just text to BG popup
    *  }
    *  </code>
@@ -123,22 +141,23 @@ App.HostTableMenuView = Em.View.extend({
           action: 'RESTART',
           message: Em.I18n.t('hosts.table.menu.l2.restartAllComponents')
         })
-      })
-      //@todo uncomment when API will be ready
-      /*Em.Object.create({
-        label: Em.I18n.t('maintenance.turnOn'),
+      }),
+      Em.Object.create({
+        label: Em.I18n.t('passiveState.turnOn'),
         operationData: Em.Object.create({
-          action: 'turn_on_maintenance',
-          message: Em.I18n.t('maintenance.turnOn')
+          state: 'PASSIVE',
+          action: 'PASSIVE_STATE',
+          message: Em.I18n.t('passiveState.turnOn')
         })
       }),
       Em.Object.create({
-        label: Em.I18n.t('maintenance.turnOff'),
+        label: Em.I18n.t('passiveState.turnOff'),
         operationData: Em.Object.create({
-          action: 'turn_off_maintenance',
-          message: Em.I18n.t('maintenance.turnOff')
+          state: 'ACTIVE',
+          action: 'PASSIVE_STATE',
+          message: Em.I18n.t('passiveState.turnOff')
         })
-      })*/
+      })
     ]);
   },
 
@@ -153,7 +172,7 @@ App.HostTableMenuView = Em.View.extend({
    * @returns {Array}
    */
   getSubMenuItemsTemplate: function(selection) {
-    var submenu = [{label: Em.I18n.t('hosts.table.menu.l2.allComponents'), submenu: this.getHostItemsTemplate()}];
+    var submenu = Em.A([{label: Em.I18n.t('hosts.table.menu.l2.allComponents'), submenu: this.getHostItemsTemplate()}]);
 
     if (!!App.HDFSService.find().content.length) {
       var slaveItemsForHdfs = this.getSlaveItemsTemplate('NAMENODE', 'DATANODE');

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/test/controllers/main/host_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host_test.js b/ambari-web/test/controllers/main/host_test.js
index f18e92a..a60a605 100644
--- a/ambari-web/test/controllers/main/host_test.js
+++ b/ambari-web/test/controllers/main/host_test.js
@@ -34,13 +34,17 @@ describe('MainHostController', function () {
         bulkOperationForHosts: function(){},
         bulkOperationForHostComponentsRestart: function(){},
         bulkOperationForHostComponentsDecommission: function(){},
-        bulkOperationForHostComponents: function(){}
+        bulkOperationForHostComponents: function(){},
+        bulkOperationForHostComponentsPassiveState: function(){},
+        bulkOperationForHostsPassiveState: function(){}
       });
       sinon.spy(hostController, 'bulkOperationForHostsRestart');
       sinon.spy(hostController, 'bulkOperationForHosts');
       sinon.spy(hostController, 'bulkOperationForHostComponentsRestart');
       sinon.spy(hostController, 'bulkOperationForHostComponentsDecommission');
       sinon.spy(hostController, 'bulkOperationForHostComponents');
+      sinon.spy(hostController, 'bulkOperationForHostComponentsPassiveState');
+      sinon.spy(hostController, 'bulkOperationForHostsPassiveState');
     });
 
     afterEach(function() {
@@ -49,6 +53,9 @@ describe('MainHostController', function () {
       hostController.bulkOperationForHostComponentsRestart.restore();
       hostController.bulkOperationForHostComponentsDecommission.restore();
       hostController.bulkOperationForHostComponents.restore();
+      hostController.bulkOperationForHostComponentsPassiveState.restore();
+      hostController.bulkOperationForHostsPassiveState.restore();
+
     });
 
     it('RESTART for hosts', function() {
@@ -56,7 +63,7 @@ describe('MainHostController', function () {
         action: 'RESTART'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostsRestart.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostsRestart.calledOnce).to.equal(true);
     });
 
     it('START for hosts', function() {
@@ -64,7 +71,7 @@ describe('MainHostController', function () {
         action: 'STARTED'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHosts.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHosts.calledOnce).to.equal(true);
     });
 
     it('STOP for hosts', function() {
@@ -72,7 +79,15 @@ describe('MainHostController', function () {
         action: 'INSTALLED'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHosts.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHosts.calledOnce).to.equal(true);
+    });
+
+    it('PASSIVE_STATE for hosts', function() {
+      var operationData = {
+        action: 'PASSIVE_STATE'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostsPassiveState.calledOnce).to.equal(true);
     });
 
     it('RESTART for hostComponents', function() {
@@ -81,7 +96,7 @@ describe('MainHostController', function () {
         componentNameFormatted: 'DataNodes'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostComponentsRestart.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostComponentsRestart.calledOnce).to.equal(true);
     });
 
     it('START for hostComponents', function() {
@@ -90,7 +105,7 @@ describe('MainHostController', function () {
         componentNameFormatted: 'DataNodes'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostComponents.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostComponents.calledOnce).to.equal(true);
     });
 
     it('STOP for hostComponents', function() {
@@ -99,7 +114,7 @@ describe('MainHostController', function () {
         componentNameFormatted: 'DataNodes'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostComponents.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostComponents.calledOnce).to.equal(true);
     });
 
     it('DECOMMISSION for hostComponents', function() {
@@ -108,7 +123,7 @@ describe('MainHostController', function () {
         componentNameFormatted: 'DataNodes'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.equal(true);
     });
 
     it('RECOMMISSION for hostComponents', function() {
@@ -117,7 +132,16 @@ describe('MainHostController', function () {
         componentNameFormatted: 'DataNodes'
       };
       hostController.bulkOperation(operationData, []);
-      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.be.true;
+      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.equal(true);
+    });
+
+    it('PASSIVE_STATE for hostComponents', function() {
+      var operationData = {
+        action: 'PASSIVE_STATE',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponentsPassiveState.calledOnce).to.equal(true);
     });
 
   });
@@ -233,4 +257,166 @@ describe('MainHostController', function () {
 
   });
 
+  describe('#bulkOperationForHostsPassiveState', function() {
+
+    beforeEach(function(){
+      hostController = App.MainHostController.create({});
+      sinon.spy($, 'ajax');
+    });
+
+    afterEach(function() {
+      $.ajax.restore();
+    });
+
+    var tests = [
+      {
+        hosts: Em.A([]),
+        operationData: {},
+        m: 'No hosts',
+        e: false
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            passiveState: 'ACTIVE'
+          })
+        ]),
+        operationData: {
+          state: 'ACTIVE'
+        },
+        m: 'One host, but in state that should get',
+        e: false
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            passiveState: 'ACTIVE'
+          })
+        ]),
+        operationData: {
+          state: 'PASSIVE'
+        },
+        m: 'One host with proper state',
+        e: true
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        hostController.bulkOperationForHostsPassiveState(test.operationData, test.hosts);
+        expect($.ajax.calledOnce).to.equal(test.e)
+      });
+    });
+
+  });
+
+  describe('#bulkOperationForHostComponentsPassiveState', function() {
+
+    beforeEach(function(){
+      hostController = App.MainHostController.create({});
+      sinon.spy($, 'ajax');
+    });
+
+    afterEach(function() {
+      $.ajax.restore();
+    });
+
+    var tests = [
+      {
+        hosts: Em.A([]),
+        operationData: {},
+        m: 'No hosts',
+        e: false
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          }),
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          })
+        ]),
+        operationData: {
+          componentName: 'CN',
+          state: 'ACTIVE'
+        },
+        m: 'Two hosts with components in state that they should get',
+        e: false
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          }),
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'PASSIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          })
+        ]),
+        operationData: {
+          componentName: 'CN',
+          state: 'PASSIVE'
+        },
+        m: 'One host with component in proper state (ACTIVE)',
+        e: true
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          }),
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'PASSIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          })
+        ]),
+        operationData: {
+          componentName: 'CN',
+          state: 'PASSIVE'
+        },
+        m: 'One host with component in proper state (PASSIVE)',
+        e: true
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          }),
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'IMPLIED'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          })
+        ]),
+        operationData: {
+          componentName: 'CN',
+          state: 'PASSIVE'
+        },
+        m: 'One host with component in proper state (ACTIVE)',
+        e: true
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'ACTIVE'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          }),
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({componentName:'CN',passiveState:'IMPLIED'}), Em.Object.create({componentName:'ACN',passiveState:'ACTIVE'})])
+          })
+        ]),
+        operationData: {
+          componentName: 'CN',
+          state: 'ACTIVE'
+        },
+        m: 'One host with component in proper state (PASSIVE)',
+        e: true
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        hostController.bulkOperationForHostComponentsPassiveState(test.operationData, test.hosts);
+        expect($.ajax.calledOnce).to.equal(test.e)
+      });
+    });
+
+  });
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/d0338adf/ambari-web/test/installer/step9_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/installer/step9_test.js b/ambari-web/test/installer/step9_test.js
index 1b6fb9e..a3b4370 100644
--- a/ambari-web/test/installer/step9_test.js
+++ b/ambari-web/test/installer/step9_test.js
@@ -150,8 +150,10 @@ describe('App.InstallerStep9Controller', function () {
     it('All should have status "pending" and message "Waiting"', function() {
       controller.resetHostsForRetry();
       for (var name in hosts) {
-        expect(controller.get('content.hosts')[name].get('status','pending')).to.equal('pending');
-        expect(controller.get('content.hosts')[name].get('message','Waiting')).to.equal('Waiting');
+        if (hosts.hasOwnProperty(name)) {
+          expect(controller.get('content.hosts')[name].get('status','pending')).to.equal('pending');
+          expect(controller.get('content.hosts')[name].get('message','Waiting')).to.equal('Waiting');
+        }
       }
     });
   });
@@ -662,9 +664,9 @@ describe('App.InstallerStep9Controller', function () {
     it('check requestId priority', function() {
       cluster.set('content.cluster.requestId', 123);
       var url = cluster.getUrl(321);
-      expect(url).to.equal(App.apiPrefix + '/clusters/' + clusterName + '/requests/' + '321' + '?fields=tasks/Tasks/command,tasks/Tasks/exit_code,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status');
+      expect(url).to.equal(App.apiPrefix + '/clusters/' + clusterName + '/requests/' + '321' + '?fields=tasks/Tasks/command,tasks/Tasks/exit_code,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true');
       url = cluster.getUrl();
-      expect(url).to.equal(App.apiPrefix + '/clusters/' + clusterName + '/requests/' + '123' + '?fields=tasks/Tasks/command,tasks/Tasks/exit_code,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status');
+      expect(url).to.equal(App.apiPrefix + '/clusters/' + clusterName + '/requests/' + '123' + '?fields=tasks/Tasks/command,tasks/Tasks/exit_code,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true');
     });
   });
 
@@ -749,7 +751,7 @@ describe('App.InstallerStep9Controller', function () {
         m: 'Some hosts without tasks'
       },
       {
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({
             name: 'host1',
             tasks: [],
@@ -765,7 +767,7 @@ describe('App.InstallerStep9Controller', function () {
             tasks: [],
             bootStatus: 'REGISTERED'
           })
-        ],
+        ]),
         polledData: [],
         e: {
           host1: {count: 0},
@@ -780,7 +782,9 @@ describe('App.InstallerStep9Controller', function () {
         var controller = App.WizardStep9Controller.create({polledData: test.polledData, hosts: test.hosts});
         controller.setTasksPerHost();
         for(var name in test.e.hosts) {
-          expect(controller.get('hosts').findProperty('name', name).get('tasks.length')).to.equal(test.e[name].count);
+          if (test.e.hosts.hasOwnProperty(name)) {
+            expect(controller.get('hosts').findProperty('name', name).get('tasks.length')).to.equal(test.e[name].count);
+          }
         }
       });
     });
@@ -824,21 +828,14 @@ describe('App.InstallerStep9Controller', function () {
   });
 
   describe('#parseHostInfo', function() {
-    var requestId = 1;
-    var polledData = {Requests:{id:2}};
-    it('Invalid requestId. Should return false', function() {
-      var controller = App.WizardStep9Controller.create({content: {cluster:{requestId: requestId}}});
-      var res = controller.parseHostInfo(polledData);
-      expect(res).to.equal(false);
-    });
 
     var tests = [
       {
         cluster: {status: 'PENDING'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host2',status: 'COMPLETED'}},
@@ -856,10 +853,10 @@ describe('App.InstallerStep9Controller', function () {
       },
       {
         cluster: {status: 'PENDING'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host1',status: 'IN_PROGRESS'}},
@@ -871,10 +868,10 @@ describe('App.InstallerStep9Controller', function () {
       },
       {
         cluster: {status: 'PENDING'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host1',status: 'QUEUED'}},
@@ -892,10 +889,10 @@ describe('App.InstallerStep9Controller', function () {
       },
       {
         cluster: {status: 'INSTALLED'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host2',status: 'COMPLETED'}},
@@ -913,10 +910,10 @@ describe('App.InstallerStep9Controller', function () {
       },
       {
         cluster: {status: 'INSTALLED'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host1',status: 'IN_PROGRESS'}},
@@ -934,10 +931,10 @@ describe('App.InstallerStep9Controller', function () {
       },
       {
         cluster: {status: 'INSTALLED'},
-        hosts: [
+        hosts: Em.A([
           Em.Object.create({name: 'host1',status: '',message: '',progress: '',logTasks: []}),
           Em.Object.create({name: 'host2',status: '',message: '',progress: '',logTasks: []})
-        ],
+        ]),
         polledData: {
           tasks:[
             {Tasks: {host_name: 'host1',status: 'QUEUED'}},
@@ -961,7 +958,9 @@ describe('App.InstallerStep9Controller', function () {
         controller.parseHostInfo(test.polledData);
         expect(controller.get('logTasksChangesCounter')).to.equal(logTasksChangesCounter + 1);
         for (var name in test.e.hosts) {
-          expect(controller.get('hosts').findProperty('name', name).get('progress')).to.equal(test.e.hosts[name].progress);
+          if (test.e.hosts.hasOwnProperty(name)) {
+            expect(controller.get('hosts').findProperty('name', name).get('progress')).to.equal(test.e.hosts[name].progress);
+          }
         }
         expect(controller.get('progress')).to.equal(test.e.progress);
       });