Selaa lähdekoodia

AMBARI-10108. Provide ability to configure rack awareness. (akovalenko)

Aleksandr Kovalenko 10 vuotta sitten
vanhempi
commit
9d5ff60d7d

+ 2 - 1
ambari-web/app/config.js

@@ -73,7 +73,8 @@ App.supports = {
   installGanglia: false,
   opsDuringRollingUpgrade: false,
   customizedWidgets: false,
-  enhancedConfigs: false
+  enhancedConfigs: false,
+  setRackId: false
 };
 
 if (App.enableExperimental) {

+ 1 - 1
ambari-web/app/controllers/global/update_controller.js

@@ -168,7 +168,7 @@ App.UpdateController = Em.Controller.extend({
     var testUrl = '/data/hosts/HDP2/hosts.json',
       self = this,
       hostDetailsFilter = '';
-    var realUrl = '/hosts?<parameters>fields=Hosts/host_name,Hosts/maintenance_state,Hosts/public_host_name,Hosts/cpu_count,Hosts/ph_cpu_count,' +
+    var realUrl = '/hosts?<parameters>fields=Hosts/rack_info,Hosts/host_name,Hosts/maintenance_state,Hosts/public_host_name,Hosts/cpu_count,Hosts/ph_cpu_count,' +
       'alerts_summary,Hosts/host_status,Hosts/last_heartbeat_time,Hosts/ip,host_components/HostRoles/state,host_components/HostRoles/maintenance_state,' +
       'host_components/HostRoles/stale_configs,host_components/HostRoles/service_name,host_components/HostRoles/desired_admin_state,' +
         'metrics/disk,metrics/load/load_one,Hosts/total_mem<hostAuxiliaryInfo><stackVersions>&minimal_response=true';

+ 95 - 9
ambari-web/app/controllers/main/charts/heatmap.js

@@ -19,18 +19,89 @@ var App = require('app');
 
 App.MainChartsHeatmapController = Em.Controller.extend({
   name: 'mainChartsHeatmapController',
-  modelRacks: App.Rack.find(),
+  rackMap: [],
+  modelRacks: [],
+  rackViews: [],
+
   racks: function () {
-    var racks = [];
-    this.get('modelRacks').forEach(function (rack) {
-      racks.push(Em.Object.create({
-        name: rack.get('name'),
-        hosts: rack.get('hosts'),
-        isLoaded: false
-      }));
+    return this.get('modelRacks');
+  }.property('modelRacks.@each.isLoaded'),
+
+  /**
+   * get hosts from server
+   */
+  loadRacks: function () {
+    this.get('modelRacks').clear();
+    this.get('rackMap').clear();
+    App.ajax.send({
+      name: 'hosts.heatmaps',
+      sender: this,
+      data: {},
+      success: 'getHostsSuccessCallback'
+    });
+  },
+
+  getHostsSuccessCallback: function (data, opt, params) {
+    var hosts = [];
+    data.items.forEach(function (item) {
+      hosts.push({
+        hostName: item.Hosts.host_name,
+        publicHostName: item.Hosts.public_host_name,
+        osType: item.Hosts.os_type,
+        ip: item.Hosts.ip,
+        rack: item.Hosts.rack_info,
+        diskTotal: item.metrics ? item.metrics.disk.disk_total : 0,
+        diskFree: item.metrics ? item.metrics.disk.disk_free : 0,
+        cpuSystem: item.metrics ? item.metrics.cpu.cpu_system : 0,
+        cpuUser: item.metrics ? item.metrics.cpu.cpu_user : 0,
+        memTotal: item.metrics ? item.metrics.memory.mem_total : 0,
+        memFree: item.metrics ? item.metrics.memory.mem_free : 0,
+        hostComponents: item.host_components.mapProperty('HostRoles.component_name')
+      });
+    });
+    var rackMap = this.indexByRackId(hosts);
+    var modelRacks = this.toList(rackMap);
+    //this list has an empty host array property
+    this.set('rackMap', rackMap);
+    this.set('modelRacks', modelRacks);
+  },
+
+  indexByRackId: function (hosts) {
+    var rackMap = [];
+    hosts.forEach(function (host) {
+      var rackId = host.rack;
+      if(!rackMap[rackId]) {
+        rackMap[rackId] =
+          Em.Object.create({
+            name: rackId,
+            rackId: rackId,
+            hosts: [host]
+          });
+      } else {
+        rackMap[rackId].hosts.push(host);
+      }
     });
+    return rackMap;
+  },
+
+  toList: function (rackMap) {
+    var racks = [];
+    var i = 0;
+    for (var rackKey in rackMap) {
+      if (rackMap.hasOwnProperty(rackKey)) {
+        racks.push(
+          Em.Object.create({
+            name: rackKey,
+            rackId: rackKey,
+            hosts: [],
+            isLoaded: false,
+            index: i++
+          })
+        );
+      }
+    }
     return racks;
-  }.property('modelRacks.@each.isLoaded'),
+  },
 
   allMetrics: function () {
     var metrics = [];
@@ -113,6 +184,21 @@ App.MainChartsHeatmapController = Em.Controller.extend({
     }
   }.observes('inputMaximum'),
 
+
+  addRackView: function (view) {
+    this.get('rackViews').push(view);
+    if (this.get('rackViews').length == this.get('modelRacks').length) {
+      this.displayAllRacks();
+    }
+  },
+
+  displayAllRacks: function () {
+    if (this.get('rackViews').length) {
+      this.get('rackViews').pop().displayHosts();
+      this.displayAllRacks();
+    }
+  },
+
   showHeatMapMetric: function (event) {
     var metricItem = event.context;
     if (metricItem) {

+ 19 - 2
ambari-web/app/controllers/main/host.js

@@ -19,6 +19,7 @@
 var App = require('app');
 var validator = require('utils/validator');
 var batchUtils = require('utils/batch_scheduled_requests');
+var hostsManagement = require('utils/hosts');
 
 App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
   name: 'mainHostController',
@@ -97,6 +98,11 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
       key: 'metrics/load/load_one',
       type: 'EQUAL'
     },
+    {
+      name: 'rack',
+      key: 'Hosts/rack_info',
+      type: 'MATCH'
+    },
     {
       name: 'hostComponents',
       key: 'host_components/HostRoles/component_name',
@@ -156,6 +162,10 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
       //TODO disk_usage is relative property and need support from API, metrics/disk/disk_free used temporarily
       key: 'metrics/disk/disk_free'
     },
+    {
+      name: 'rack',
+      key: 'Hosts/rack_info'
+    },
     {
       name: 'loadAvg',
       key: 'metrics/load/load_one'
@@ -549,7 +559,9 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
       }
     }
     else {
-      if (operationData.action === 'RESTART') {
+      if (operationData.action === 'SET_RACK_INFO') {
+        this.bulkOperationForHostsSetRackInfo(operationData, hosts);
+      } else if (operationData.action === 'RESTART') {
         this.bulkOperationForHostsRestart(operationData, hosts);
       }
       else {
@@ -648,7 +660,11 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
     }
   },
 
-  /**
+  bulkOperationForHostsSetRackInfo: function (operationData, hosts) {
+    hostsManagement.setRackInfo(operationData, hosts);
+  },
+
+   /**
    * Bulk restart for selected hosts
    * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
    * @param {Ember.Enumerable} hosts - list of affected hosts
@@ -950,6 +966,7 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
     associations[9] = 'componentsInPassiveStateCount';
     associations[10] = 'selected';
     associations[11] = 'hostStackVersion';
+    associations[12] = 'rack';
     return associations;
   }.property()
 

+ 15 - 0
ambari-web/app/controllers/main/host/details.js

@@ -19,6 +19,7 @@
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
 var componentsUtils = require('utils/components');
+var hostsManagement = require('utils/hosts');
 var stringUtils = require('utils/string_utils');
 
 App.MainHostDetailsController = Em.Controller.extend({
@@ -1477,6 +1478,9 @@ App.MainHostDetailsController = Em.Controller.extend({
       case "onOffPassiveModeForHost":
         this.onOffPassiveModeForHost(option.context);
         break;
+      case "setRackId":
+        this.setRackIdForHost();
+        break;
     }
   },
 
@@ -1496,6 +1500,17 @@ App.MainHostDetailsController = Em.Controller.extend({
     );
   },
 
+  /**
+   * Set rack id for host
+   * @method setRackIdForHost
+   */
+  setRackIdForHost: function () {
+    var hostNames = [{hostName: this.get('content.hostName')}];
+    var rack = this.get('content.rack');
+    var operationData = {message: Em.I18n.t('hosts.host.details.setRackId')};
+    hostsManagement.setRackInfo(operationData, hostNames, rack);
+  },
+
   /**
    * Send request to get passive state for host
    * @param {string} state

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

@@ -114,6 +114,7 @@ Em.I18n.translations = {
   'common.key':'Key',
   'common.value':'Value',
   'common.ipAddress':'IP Address',
+  'common.rack':'Rack',
   'common.cpu':'CPU',
   'common.cores': 'Cores (CPU)',
   'common.ram':'RAM',
@@ -302,6 +303,11 @@ Em.I18n.translations = {
   'hostPopup.bgop.sourceRequestSchedule.aborted': 'Future operations of this batch request have been aborted',
   'hostPopup.bgop.abort.rollingRestart': 'Abort Rolling Restart',
   'hostPopup.warning.alertsTimeOut': 'Maintenance Mode has been turned {0}. It may take a few minutes for the alerts to be {1}.',
+  'hostPopup.reccomendation.beforeDecommission': '{0} Maintenance Mode is pre required for decommissioning.',
+  'hostPopup.setRackId.success': 'Updating rack id to \"{0}\". It may take a few moments for it to get refreshed.',
+  'hostPopup.setRackId.error': 'Updating the rack id failed.',
+  'hostPopup.setRackId.invalid': 'Should start with a forward slash it may include alphanumeric chars, dots, dashes and forward slashes.',
+  'hostPopup.RackId': 'Rack Id',
   'hostPopup.recommendation.beforeDecommission': '{0} Maintenance Mode is pre required for decommissioning.',
 
   'question.sure':'Are you sure?',
@@ -2049,6 +2055,7 @@ Em.I18n.translations = {
   'hosts.host.details.restartAllComponents':'Restart All Components',
   'hosts.host.details.refreshConfigs':'Refresh configs',
   'hosts.host.details.for.postfix':'{0} for host',
+  'hosts.host.details.setRackId':'Set Rack Id',
   'host.host.details.installClients': 'Install clients',
 
   'host.host.componentFilter.master':'Master Components',

+ 4 - 1
ambari-web/app/routes/main.js

@@ -142,7 +142,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       heatmap: Em.Route.extend({
         route: '/heatmap',
         connectOutlets: function (router, context) {
-          router.get('mainChartsController').connectOutlet('mainChartsHeatmap');
+          router.get('mainController').dataLoading().done(function () {
+            router.get('mainChartsHeatmapController').loadRacks();
+            router.get('mainChartsController').connectOutlet('mainChartsHeatmap');
+          });
         }
       }),
       horizon_chart: Em.Route.extend({

+ 6 - 0
ambari-web/app/styles/application.less

@@ -2310,6 +2310,12 @@ a:focus {
     }
   }
 }
+.host-rack-id-popup {
+  .control-label {
+    width: 100px;
+    margin-right: 10px;
+  }
+}
 
 .service-content {
   #summary-info {

+ 4 - 1
ambari-web/app/templates/main/charts/heatmap/heatmap_host_detail.hbs

@@ -19,6 +19,9 @@
 <b>{{view.details.publicHostName}}</b><br/>
 {{t common.os}}: {{view.details.osType}}<br/>
 {{t common.ipAddress}}: {{view.details.ip}}<br/>
+{{#if App.supports.setRackId}}
+  {{t common.rack}}: {{view.details.rack}}<br/>
+{{/if}}
 {{#if view.details.metricName}}
   {{view.details.metricName}}: {{view.details.metricValue}}<br/>
 {{/if}}
@@ -33,4 +36,4 @@
 {{/if}}
 {{#if view.details.hostComponents}}
   {{t common.components}}: {{view.details.hostComponents}}<br/>
-{{/if}}
+{{/if}}

+ 10 - 1
ambari-web/app/templates/main/host.hbs

@@ -64,6 +64,9 @@
         <th> </th>
         <th> </th>
         {{view view.parentView.ipSort}}
+        {{#if App.supports.setRackId}}
+          {{view view.parentView.rackSort}}
+        {{/if}}
         {{view view.parentView.cpuSort}}
         {{view view.parentView.memorySort}}
         {{view view.parentView.diskUsageSort}}
@@ -80,6 +83,9 @@
         <th> </th>
         <th> </th>
         <th>{{view view.ipFilterView}}</th>
+        {{#if App.supports.setRackId}}
+          <th>{{view view.rackFilterView}}</th>
+        {{/if}}
         <th>{{view view.cpuFilterView}}</th>
         <th>{{view view.ramFilterView}}</th>
         <th> </th>
@@ -122,6 +128,9 @@
               <span rel="ComponentsTooltip" {{bindAttr data-original-title="view.componentsInPassiveStateMessage" class="host.componentsInPassiveStateCount:icon-medkit"}}></span>
           </td>
           <td class="host-ip">{{host.ip}}</td>
+          {{#if App.supports.setRackId}}
+            <td>{{host.rack}}</td>
+          {{/if}}
           <td class="cores-formatted">{{host.coresFormatted}}</td>
           <td class="memory-formatted">{{host.memoryFormatted}}</td>
 
@@ -154,7 +163,7 @@
       {{/each}}
     {{else}}
       <tr>
-        <td class="first"> </td>
+        <td class="first"></td>
         <td {{bindAttr colspan="view.colspan"}}>
           {{t hosts.table.noHosts}}
         </td>

+ 25 - 0
ambari-web/app/templates/main/host/rack_id_popup.hbs

@@ -0,0 +1,25 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div id="host-rack-id-popup">
+  <div {{bindAttr class=":control-group :inline view.isValid::error"}}>
+    <span>{{t hostPopup.RackId}}:&nbsp;</span>
+    {{view Em.TextField valueBinding="view.parentView.rackId" class="input-xlarge"}}
+    <span class="help-inline">{{view.errorMessage}}</span>
+  </div>
+</div>

+ 5 - 0
ambari-web/app/templates/main/host/summary.hbs

@@ -134,6 +134,11 @@
                         <dt class="summary-ipaddress-label">{{t common.ipAddress}}:</dt>
                           <dd class="summary-ipaddress-value">&nbsp;{{view.content.ip}}</dd>
 
+                        {{#if App.supports.setRackId}}
+                          <dt>{{t common.rack}}:</dt>
+                          <dd>&nbsp;{{view.content.rack}}</dd>
+                        {{/if}}
+
                         <dt class="summary-os-label">{{t common.os}}:</dt>
                           <dd class="summary-os-value">&nbsp;{{view.content.osType}}&nbsp;({{view.content.osArch}})</dd>
 

+ 23 - 1
ambari-web/app/utils/ajax/ajax.js

@@ -2057,6 +2057,28 @@ var urls = {
     }
   },
 
+
+  'bulk_request.hosts.update_rack_id': {
+    '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: {
+              rack_info: data.rackId
+            }
+          }
+        })
+      }
+    }
+  },
+
   'bulk_request.hosts.all_components.passive_state': {
     'real': '/clusters/{clusterName}/host_components',
     'mock': '',
@@ -2177,7 +2199,7 @@ var urls = {
     'mock': ''
   },
   'hosts.heatmaps': {
-    'real': '/clusters/{clusterName}/hosts?fields=Hosts/host_name,Hosts/public_host_name,Hosts/os_type,Hosts/ip,host_components,metrics/disk,metrics/cpu/cpu_system,metrics/cpu/cpu_user,metrics/memory/mem_total,metrics/memory/mem_free&minimal_response=true',
+    'real': '/clusters/{clusterName}/hosts?fields=Hosts/rack_info,Hosts/host_name,Hosts/public_host_name,Hosts/os_type,Hosts/ip,host_components,metrics/disk,metrics/cpu/cpu_system,metrics/cpu/cpu_user,metrics/memory/mem_total,metrics/memory/mem_free&minimal_response=true',
     'mock': '/data/hosts/HDP2/hosts.json'
   },
   'namenode.cpu_wio': {

+ 53 - 2
ambari-web/app/utils/hosts.js

@@ -18,7 +18,7 @@
 require('views/common/table_view');
 
 var App = require('app');
-var lazyloading = require('utils/lazy_loading');
+var validator = require('utils/validator');
 
 module.exports = {
 
@@ -178,5 +178,56 @@ module.exports = {
         }
       })
     });
+  },
+
+   /**
+   * Bulk setting of for rack id
+   * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
+   * @param {Ember.Enumerable} hosts - list of affected hosts
+   */
+  setRackInfo: function (operationData, hosts, rackId) {
+    var self = this;
+    var hostNames = hosts.mapProperty('hostName');
+    return App.ModalPopup.show({
+      header: Em.I18n.t('hosts.host.details.setRackId'),
+      disablePrimary: false,
+      rackId: rackId,
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/rack_id_popup'),
+        errorMessage: null,
+        isValid: true,
+        validation: function () {
+          this.set('isValid', validator.isValidRackId(this.get('parentView.rackId')));
+          this.set('errorMessage', this.get('isValid') ? '' : Em.I18n.t('hostPopup.setRackId.invalid'));
+          this.set('parentView.disablePrimary', !this.get('isValid'));
+        }.observes('parentView.rackId')
+      }),
+      onPrimary: function() {
+        var rackId = this.get('rackId');
+        if (hostNames.length) {
+          App.ajax.send({
+            name: 'bulk_request.hosts.update_rack_id',
+            sender: self,
+            data: {
+              hostNames: hostNames.join(','),
+              requestInfo: operationData.message,
+              rackId: rackId
+            },
+            error: 'errorRackId'
+          });
+        }
+        this.hide();
+      },
+      onSecondary: function() {
+        this.hide();
+      }
+    });
+  },
+
+  /**
+   * Warn user that the rack id will not be updated
+   */
+  errorRackId: function () {
+    App.showAlertPopup(Em.I18n.t('common.error'), Em.I18n.t('hostPopup.setRackId.error'));
   }
-};
+};

+ 5 - 0
ambari-web/app/utils/validator.js

@@ -218,5 +218,10 @@ module.exports = {
       // true is there is no host with this component
       return hostComponents.filterProperty("componentName", item["component-name"]).filterProperty("hostName", item.host).length === 0;
     });
+  }, 
+
+  isValidRackId: function(path) {
+    // See app/message.js:hostPopup.setRackId.invalid
+    return /^\/[/.\w-]+$/.test(path);
   }
 };

+ 2 - 1
ambari-web/app/views/main/charts/heatmap/heatmap_host_detail.js

@@ -27,6 +27,7 @@ App.MainChartsHeatmapHostDetailView = Em.View.extend({
     publicHostName:'test node',
     osType: 'OS',
     ip: '192.168.0.0',
+    rack: '/default_rack',
     metricName: 'metric-name',
     metricValue: 'metric-value',
     diskUsage: '10',
@@ -34,4 +35,4 @@ App.MainChartsHeatmapHostDetailView = Em.View.extend({
     memoryUsage: '10',
     hostComponents: 'host components'
   }
-});
+});

+ 10 - 27
ambari-web/app/views/main/charts/heatmap/heatmap_rack.js

@@ -34,16 +34,13 @@ App.MainChartsHeatmapRackView = Em.View.extend({
   },
 
   /**
-   * get hosts from server
+   * get hosts from the root controller
    */
   getHosts: function () {
-    App.ajax.send({
-      name: 'hosts.heatmaps',
-      sender: this,
-      data: {},
-      success: 'getHostsSuccessCallback',
-      error: 'getHostsErrorCallback'
-    });
+    var controller = App.router.get('mainChartsHeatmapController');
+    var rackId = this.get('rack.rackId');
+    var rackMap = controller.get('rackMap');
+    this.pushHostsToRack(rackMap[rackId].hosts);
   },
 
   getHostsSuccessCallback: function (data, opt, params) {
@@ -56,9 +53,10 @@ App.MainChartsHeatmapRackView = Em.View.extend({
    */
   displayHosts: function () {
     var rackHosts = this.get('rack.hosts');
+    var rackCount = App.router.get('mainChartsHeatmapController.modelRacks.length');
 
     if (this.get('hosts.length') === 0) {
-      if (rackHosts.length > 100) {
+      if (rackHosts.length > 100 && rackCount == 1) {
         lazyloading.run({
           initSize: 100,
           chunkSize: 200,
@@ -81,26 +79,10 @@ App.MainChartsHeatmapRackView = Em.View.extend({
    * push hosts to rack
    * @param data
    */
-  pushHostsToRack: function (data) {
-    var newHostsData = [];
+  pushHostsToRack: function (hosts) {
+    var newHostsData = hosts;
     var rackHosts = this.get('rack.hosts');
 
-    data.items.forEach(function (item) {
-      newHostsData.push({
-        hostName: item.Hosts.host_name,
-        publicHostName: item.Hosts.public_host_name,
-        osType: item.Hosts.os_type,
-        ip: item.Hosts.ip,
-        diskTotal: item.metrics ? item.metrics.disk.disk_total : 0,
-        diskFree: item.metrics ? item.metrics.disk.disk_free : 0,
-        cpuSystem: item.metrics ? item.metrics.cpu.cpu_system : 0,
-        cpuUser: item.metrics ? item.metrics.cpu.cpu_user : 0,
-        memTotal: item.metrics ? item.metrics.memory.mem_total : 0,
-        memFree: item.metrics ? item.metrics.memory.mem_free : 0,
-        hostComponents: item.host_components.mapProperty('HostRoles.component_name')
-      })
-    });
-
     if (rackHosts.length > 0) {
       this.updateLoadedHosts(rackHosts, newHostsData);
     } else {
@@ -161,6 +143,7 @@ App.MainChartsHeatmapRackView = Em.View.extend({
       this.displayHosts();
     }
     this.getHosts();
+    App.router.get('mainChartsHeatmapController').addRackView(this);
   },
   /**
    * Provides the CSS style for an individual host.

+ 20 - 2
ambari-web/app/views/main/host.js

@@ -45,8 +45,8 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
   requestError: null,
 
   colspan: function () {
-    return App.get('supports.stackUpgrade') ? 11 : 10;
-  }.property("App.supports.stackUpgrade"),
+    return 10 + +App.get('supports.stackUpgrade') + +App.get('supports.setRackId');
+  }.property("App.supports.stackUpgrade", 'App.supports.setRackId'),
 
   /**
    * List of hosts in cluster
@@ -498,6 +498,12 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
     displayName: Em.I18n.t('common.ipAddress'),
     type: 'ip'
   }),
+  rackSort: sort.fieldView.extend({
+    column: 12,
+    name:'rack',
+    displayName: Em.I18n.t('common.rack'),
+    type: 'rack'
+  }),
   cpuSort: sort.fieldView.extend({
     column: 3,
     name:'cpu',
@@ -841,6 +847,18 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
     }
   }),
 
+   /**
+   * Filter view for rack column
+   * Based on <code>filters</code> library
+   */
+  rackFilterView: filters.createTextView({
+    column: 12,
+    fieldType: 'filter-input-width',
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
+    }
+  }),
+
   /**
    * Filter view for Cpu column
    * Based on <code>filters</code> library

+ 5 - 1
ambari-web/app/views/main/host/details.js

@@ -47,12 +47,16 @@ App.MainHostDetailsView = Em.View.extend({
 
   maintenance: function(){
     var onOff = this.get('isActive') ? "On" : "Off";
-    return [
+    var options = [
       {action: 'startAllComponents', liClass: (this.get('controller.content.isNotHeartBeating')?'disabled':'enabled'), cssClass: 'icon-play', 'label': this.t('hosts.host.details.startAllComponents')},
       {action: 'stopAllComponents', liClass: (this.get('controller.content.isNotHeartBeating')?'disabled':'enabled'), cssClass: 'icon-stop', 'label': this.t('hosts.host.details.stopAllComponents')},
       {action: 'restartAllComponents', liClass: (this.get('controller.content.isNotHeartBeating')?'disabled':'enabled'), cssClass: 'icon-repeat', 'label': this.t('hosts.host.details.restartAllComponents')},
       {action: 'onOffPassiveModeForHost', liClass:'', cssClass: 'icon-medkit', active:this.get('isActive'), 'label': this.t('passiveState.turn' + onOff)},
       {action: 'deleteHost', liClass:'', cssClass: 'icon-remove', 'label': this.t('hosts.host.details.deleteHost')}];
+      if(App.get('supports.setRackId')) {
+        options.push({action: 'setRackId', liClass:'', cssClass: 'icon-gear', 'label': this.t('hosts.host.details.setRackId')});
+      }
+    return options;
   }.property('controller.content','isActive', 'controller.content.isNotHeartBeating'),
   didInsertElement: function() {
     var self = this;

+ 14 - 2
ambari-web/app/views/main/host/hosts_table_menu_view.js

@@ -237,7 +237,7 @@ App.HostTableMenuView = Em.View.extend({
      * @returns {Array}
      */
     operationsInfo: function () {
-      return Em.A([
+      var options = [
         Em.Object.create({
           label: Em.I18n.t('hosts.host.details.startAllComponents'),
           operationData: Em.Object.create({
@@ -277,7 +277,19 @@ App.HostTableMenuView = Em.View.extend({
             message: Em.I18n.t('passiveState.turnOffFor').format('hosts')
           })
         })
-      ]);
+      ];
+      if(App.get('supports.setRackId')) {
+        options.push(
+          Em.Object.create({
+            label: Em.I18n.t('hosts.host.details.setRackId'),
+            operationData: Em.Object.create({
+              action: 'SET_RACK_INFO',
+              message: Em.I18n.t('hosts.host.details.setRackId').format('hosts')
+            })
+          })
+        );
+      }
+      return options;
     }.property(),
 
     /**