Kaynağa Gözat

Revert "AMBARI-6046. Lags on Assign Masters step on big cluster. (onechiporenko)"

This reverts commit 441b02580002b75cbffbf51b1fe777459181d769.
Yusaku Sako 11 yıl önce
ebeveyn
işleme
4cc2dc1b33

+ 92 - 125
ambari-web/app/controllers/wizard/step5_controller.js

@@ -18,6 +18,7 @@
 
 var App = require('app');
 var numberUtils = require('utils/number_utils');
+var lazyLoading = require('utils/lazy_loading');
 
 App.WizardStep5Controller = Em.Controller.extend({
 
@@ -65,18 +66,38 @@ App.WizardStep5Controller = Em.Controller.extend({
    */
   multipleComponents: ['ZOOKEEPER_SERVER', 'HBASE_MASTER'],
 
-  /**
-   * Define state for submit button
-   * @type {bool}
-   */
   submitDisabled: false,
 
   /**
-   * Trigger for executing host names check for components
-   * Should de "triggered" when host changed for some component and when new multiple component is added/removed
+   * Define state for submit button. Return true only for Reassign Master Wizard and if more than one master component was reassigned.
    * @type {bool}
    */
-  hostNameCheckTrigger: false,
+  isSubmitDisabled: function () {
+    if (!this.get('isReassignWizard')) {
+      this.set('submitDisabled', false);
+    } else {
+      App.ajax.send({
+        name: 'host_components.all',
+        sender: this,
+        data: {
+          clusterName: App.get('clusterName')
+        },
+        success: 'isSubmitDisabledSuccessCallBack'
+      });
+    }
+  }.observes('servicesMasters.@each.selectedHost'),
+
+  isSubmitDisabledSuccessCallBack: function (response) {
+    var reassigned = 0;
+    var arr1 = response.items.mapProperty('HostRoles').filterProperty('component_name', this.get('content.reassign.component_name')).mapProperty('host_name');
+    var arr2 = this.get('servicesMasters').mapProperty('selectedHost');
+    arr1.forEach(function (host) {
+      if (!arr2.contains(host)) {
+        reassigned++;
+      }
+    }, this);
+    this.set('submitDisabled', reassigned !== 1);
+  },
 
   /**
    * List of hosts
@@ -85,13 +106,11 @@ App.WizardStep5Controller = Em.Controller.extend({
   hosts: [],
 
   /**
-   * Name of multiple component which host name was changed last
    * @type {Object|null}
    */
   componentToRebalance: null,
 
   /**
-   * Flag for rebalance multiple components
    * @type {number}
    */
   rebalanceComponentHostsCounter: 0,
@@ -106,12 +125,6 @@ App.WizardStep5Controller = Em.Controller.extend({
    */
   selectedServicesMasters: [],
 
-  /**
-   * Is data for current step loaded
-   * @type {bool}
-   */
-  isLoaded: false,
-
   /**
    * Check if HIVE_SERVER component exist (also checks if this is not reassign)
    * @type {bool}
@@ -128,40 +141,69 @@ App.WizardStep5Controller = Em.Controller.extend({
    *     {
    *       host_name: '',
    *       hostInfo: {},
-   *       masterServices: [],
-   *       masterServicesToDisplay: [] // used only in template
+   *       masterServices: []
    *    },
    *    ....
    *   ]
    * </code>
    * @type {Ember.Enumerable}
    */
-  masterHostMapping: function () {
+  masterHostMapping: [],
+
+  isLoaded: false,
+
+  /**
+   * Check if HIVE_SERVER component exist (also checks if this is not reassign)
+   * @type {bool}
+   */
+  hasHiveServer: function () {
+    return this.get('selectedServicesMasters').someProperty('component_name', 'HIVE_SERVER') && !this.get('isReassignWizard');
+  }.property('selectedServicesMasters'),
+
+  masterHostMappingObserver: function () {
+    var requestName = this.get('content.controllerName') == 'installerController' ? 'hosts.confirmed.install' : 'hosts.confirmed'
+    App.ajax.send({
+      name: requestName,
+      sender: this,
+      data: {
+        clusterName: App.get('clusterName')
+      },
+      success: 'masterHostMappingSuccessCallback'
+    });
+  }.observes('selectedServicesMasters', 'selectedServicesMasters.@each.selectedHost'),
+
+  masterHostMappingSuccessCallback: function (response) {
     var mapping = [], mappingObject, mappedHosts, hostObj;
     //get the unique assigned hosts and find the master services assigned to them
-    mappedHosts = this.get("selectedServicesMasters").mapProperty("selectedHost").uniq();
+    mappedHosts = this.get("selectedServicesMasters").mapProperty("selectedHost").uniq().without(undefined);
     mappedHosts.forEach(function (item) {
-      hostObj = this.get("hosts").findProperty("host_name", item);
-      // User may input invalid host name (this is handled in hostname checker). Here we just skip it
-      if (!hostObj) return;
-      var masterServices = this.get("selectedServicesMasters").filterProperty("selectedHost", item),
-        masterServicesToDisplay = [];
-      masterServices.mapProperty('display_name').uniq().forEach(function(n) {
-        masterServicesToDisplay.pushObject(masterServices.findProperty('display_name', n));
-      });
+      var host = response.items.mapProperty('Hosts').findProperty('host_name', item);
+      hostObj = {
+        name: host.host_name,
+        memory: host.total_mem,
+        cpu: host.cpu_count
+      };
+
       mappingObject = Em.Object.create({
         host_name: item,
-        hostInfo: hostObj.host_info,
-        masterServices: masterServices,
-        masterServicesToDisplay: masterServicesToDisplay
+        hostInfo: Em.I18n.t('installer.step5.hostInfo').fmt(hostObj.name, numberUtils.bytesToSize(hostObj.memory, 1, 'parseFloat', 1024), hostObj.cpu),
+        masterServices: this.get("selectedServicesMasters").filterProperty("selectedHost", item)
       });
 
       mapping.pushObject(mappingObject);
     }, this);
 
-    return mapping.sortProperty('host_name');
-  }.property("selectedServicesMasters.@each.selectedHost", 'selectedServicesMasters.@each.isHostNameValid'),
-  
+    this.set('masterHostMapping', []);
+    lazyLoading.run({
+      initSize: 20,
+      chunkSize: 50,
+      delay: 50,
+      destination: this.get('masterHostMapping'),
+      source: mapping.sortProperty('host_name'),
+      context: Em.Object.create()
+    });
+  },
+
   /**
    * Count of hosts without masters
    * @type {number}
@@ -171,48 +213,9 @@ App.WizardStep5Controller = Em.Controller.extend({
      return 0;
     } else {
       return (this.get("hosts.length") - this.get("masterHostMapping.length"));
-    }
+    };
   }.property('masterHostMapping.length', 'selectedServicesMasters.@each.selectedHost'),
 
-  /**
-   * Update submit button status
-   * @metohd getIsSubmitDisabled
-   */
-  getIsSubmitDisabled: function () {
-    if (!this.get('isReassignWizard')) {
-      this.set('submitDisabled', this.get('servicesMasters').someProperty('isHostNameValid', false));
-    }
-    else {
-      App.ajax.send({
-        name: 'host_components.all',
-        sender: this,
-        data: {
-          clusterName: App.get('clusterName')
-        },
-        success: 'getIsSubmitDisabledSuccessCallBack'
-      });
-    }
-  }.observes('servicesMasters.@each.selectedHost', 'servicesMasters.@each.isHostNameValid'),
-
-  /**
-   * Success callback for getIsSubmitDisabled method
-   * Set true for Reassign Master Wizard and if more than one master component was reassigned.
-   * For installer, addHost and addService verify that provided host names for components are valid
-   * @param {object} response
-   * @method getIsSubmitDisabledSuccessCallBack
-   */
-  getIsSubmitDisabledSuccessCallBack: function (response) {
-    var reassigned = 0;
-    var arr1 = response.items.mapProperty('HostRoles').filterProperty('component_name', this.get('content.reassign.component_name')).mapProperty('host_name');
-    var arr2 = this.get('servicesMasters').mapProperty('selectedHost');
-    arr1.forEach(function (host) {
-      if (!arr2.contains(host)) {
-        reassigned++;
-      }
-    }, this);
-    this.set('submitDisabled', reassigned !== 1);
-  },
-
   /**
    * Clear controller data (hosts, masters etc)
    * @method clearStep
@@ -288,7 +291,15 @@ App.WizardStep5Controller = Em.Controller.extend({
         }));
       }
     }
-    this.set("hosts", result);
+    this.set("hosts", []);
+    lazyLoading.run({
+      initSize: 20,
+      chunkSize: 50,
+      delay: 50,
+      destination: this.get('hosts'),
+      source: result,
+      context: Em.Object.create()
+    });
     this.sortHosts(this.get('hosts'));
     this.set('isLoaded', true);
   },
@@ -420,7 +431,6 @@ App.WizardStep5Controller = Em.Controller.extend({
           }
         }
       }
-      componentObj.set('isHostNameValid', true);
       result.push(componentObj);
     }, this);
 
@@ -549,61 +559,18 @@ App.WizardStep5Controller = Em.Controller.extend({
   },
 
   /**
-   * On change callback for inputs
+   * On change callback for selects
    * @param {string} componentName
    * @param {string} selectedHost
    * @param {number} zId
    * @method assignHostToMaster
    */
   assignHostToMaster: function (componentName, selectedHost, zId) {
-    var flag = this.isHostNameValid(componentName, selectedHost);
-    this.updateIsHostNameValidFlag(componentName, zId, flag);
-    if (zId) {
-      this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("zId", zId).set("selectedHost", selectedHost);
-    }
-    else {
-      this.get('selectedServicesMasters').findProperty("component_name", componentName).set("selectedHost", selectedHost);
-    }
-  },
-
-  /**
-   * Determines if hostName is valid for component:
-   * <ul>
-   *  <li>host name shouldn't be empty</li>
-   *  <li>host should exist</li>
-   *  <li>host should have only one component with <code>componentName</code></li>
-   * </ul>
-   * @param {string} componentName
-   * @param {string} selectedHost
-   * @returns {boolean} true - valid, false - invalid
-   * @method isHostNameValid
-   */
-  isHostNameValid: function(componentName, selectedHost) {
-    return (selectedHost.trim() !== '') &&
-      this.get('hosts').mapProperty('host_name').contains(selectedHost) &&
-      (this.get('selectedServicesMasters').
-        filterProperty('component_name', componentName).
-        mapProperty('selectedHost').
-        filter(function(h) {
-          return h === selectedHost;
-        }).length <= 1);
-  },
-
-  /**
-   * Update <code>isHostNameValid</code> property with <code>flag</code> value
-   * for component with name <code>componentName</code> and
-   * <code>zId</code>-property equal to <code>zId</code>-parameter value
-   * @param {string} componentName
-   * @param {number} zId
-   * @param {bool} flag
-   * @method updateIsHostNameValidFlag
-   */
-  updateIsHostNameValidFlag: function (componentName, zId, flag) {
-    if (componentName) {
+    if (selectedHost && componentName) {
       if (zId) {
-        this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("zId", zId).set("isHostNameValid", flag);
+        this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("zId", zId).set("selectedHost", selectedHost);
       } else {
-        this.get('selectedServicesMasters').findProperty("component_name", componentName).set("isHostNameValid", flag);
+        this.get('selectedServicesMasters').findProperty("component_name", componentName).set("selectedHost", selectedHost);
       }
     }
   },
@@ -681,7 +648,7 @@ App.WizardStep5Controller = Em.Controller.extend({
 
       this.set('componentToRebalance', componentName);
       this.incrementProperty('rebalanceComponentHostsCounter');
-      this.toggleProperty('hostNameCheckTrigger');
+
       return true;
     }
     return false;//if no more zookeepers can be added
@@ -715,7 +682,7 @@ App.WizardStep5Controller = Em.Controller.extend({
 
     this.set('componentToRebalance', componentName);
     this.incrementProperty('rebalanceComponentHostsCounter');
-    this.toggleProperty('hostNameCheckTrigger');
+
     return true;
   },
 
@@ -724,7 +691,7 @@ App.WizardStep5Controller = Em.Controller.extend({
    * @metohd submit
    */
   submit: function () {
-    this.getIsSubmitDisabled();
+    this.isSubmitDisabled();
     if (!this.get('submitDisabled')) {
       App.router.send('next');
     }

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

@@ -79,10 +79,6 @@ wbr {
   display: inline-block;
 }
 
-ul.typeahead.dropdown-menu {
-  z-index: 3000000 !important;
-}
-
 #wrapper {
   min-height: 100%;
 }

+ 10 - 4
ambari-web/app/templates/wizard/step5.hbs

@@ -61,15 +61,21 @@
                         {{selectedHost}}<i class="icon-asterisks">&#10037;</i>
                       </div>
                     {{else}}
-                    <div class="control-group">
-                      {{view App.SelectHostView componentBinding="this" disabledBinding="isInstalled" }}
+                      {{view App.SelectHostView
+                      optionValuePath="content.host_name"
+                      optionLabelPath="content.host_info"
+                      selectedHostBinding="selectedHost"
+                      componentNameBinding="component_name"
+                      class="host-select"
+                      zIdBinding="zId"
+                      disabledBinding="isInstalled"
+                      }}
                       {{#if showAddControl}}
                         {{view App.AddControlView componentNameBinding="component_name"}}
                       {{/if}}
                       {{#if showRemoveControl}}
                         {{view App.RemoveControlView componentNameBinding="component_name" zIdBinding="zId"}}
                       {{/if}}
-                      </div>
                     {{/if}}
                   </div>
                 </div>
@@ -84,7 +90,7 @@
       {{#each masterHostMapping}}
         <div class="mapping-box round-corners well">
           <div class="hostString"><span>{{hostInfo}}</span></div>
-          {{#each masterServicesToDisplay}}
+          {{#each masterServices}}
             <span {{bindAttr class="isInstalled:assignedService:newService :round-corners"}}>{{display_name}}</span>
           {{/each}}
         </div>

+ 72 - 90
ambari-web/app/views/wizard/step5_view.js

@@ -18,6 +18,7 @@
 
 
 var App = require('app');
+var lazyloading = require('utils/lazy_loading');
 
 App.WizardStep5View = Em.View.extend({
 
@@ -29,14 +30,7 @@ App.WizardStep5View = Em.View.extend({
 
 });
 
-App.SelectHostView = Em.TextField.extend({
-
-  /**
-   * Element of <code>controller.servicesMasters</code>
-   * Binded from template
-   * @type {object}
-   */
-  component: null,
+App.SelectHostView = Em.Select.extend({
 
   /**
    * List of avaiable host names
@@ -45,87 +39,59 @@ App.SelectHostView = Em.TextField.extend({
   content: [],
 
   /**
-   * Host component name
-   * @type {string}
+   * Index for multiple component (like ZOOKEEPER_SERVER)
+   * @type {number|null}
    */
-  componentName: null,
-
-  attributeBindings: ['disabled'],
+  zId: null,
 
   /**
-   * Saved typeahead component
-   * @type {$}
+   * Selected host name for host component
+   * @type {string}
    */
-  typeahead: null,
+  selectedHost: null,
 
   /**
-   * Handler for selected value change
-   * Triggers <code>changeHandler</code> execution
-   * @method change
+   * Host component name
+   * @type {string}
    */
-  change: function () {
-    if ('destroyed' === this.get('state')) return;
-    this.get('controller').toggleProperty('hostNameCheckTrigger');
-  },
+  componentName: null,
+
+  attributeBindings: ['disabled'],
 
   /**
-   * Add or remove <code>error</code> class from parent div-element
-   * @param {bool} flag true - add class, false - remove
-   * @method updateErrorStatus
+   * Is data loaded
+   * @type {bool}
    */
-  updateErrorStatus: function(flag) {
-    var parentBlock = this.$().parent('div');
-    /* istanbul ignore next */
-    if (flag) {
-      parentBlock.removeClass('error');
-    }
-    else {
-      parentBlock.addClass('error');
-    }
-  },
+  isLoaded: false,
 
   /**
-   * When <code>value</code> (hostname) is changed this method is triggered
-   * If new hostname is valid, this host is assigned to master component
-   * @method changeHandler
+   * Is lazy loading used
+   * @type {bool}
    */
-  changeHandler: function() {
-    if ('destroyed' === this.get('state')) return;
-    var componentIsMultiple = this.get('controller.multipleComponents').contains(this.get("component.component_name"));
-    this.get('controller').assignHostToMaster(this.get("component.component_name"), this.get("value"), this.get("component.zId"));
-    if(componentIsMultiple) {
-      this.get('controller').set('componentToRebalance', this.get("component.component_name"));
-      this.get('controller').incrementProperty('rebalanceComponentHostsCounter');
-    }
-  }.observes('controller.hostNameCheckTrigger'),
+  isLazyLoading: false,
 
   /**
-   * If <code>component.isHostNameValid</code> was changed,
-   * error status should be updated according to new value
-   * @method isHostNameValidObs
+   * Handler for selected value change
+   * @method change
    */
-  isHostNameValidObs: function() {
-    this.updateErrorStatus(this.get('component.isHostNameValid'));
-  }.observes('component.isHostNameValid'),
+  change: function () {
+    this.get('controller').assignHostToMaster(this.get("componentName"), this.get("value"), this.get("zId"));
+    this.set('selectedHost', this.get('value'));
+    this.get('controller').set('componentToRebalance', this.get("componentName"));
+    this.get('controller').incrementProperty('rebalanceComponentHostsCounter');
+  },
 
   /**
    * Recalculate available hosts
-   * This should be done only once per Ember loop
    * @method rebalanceComponentHosts
    */
   rebalanceComponentHosts: function () {
-    Em.run.next(this, 'rebalanceComponentHostsOnce');
-  }.observes('controller.rebalanceComponentHostsCounter'),
-
-  /**
-   * Recalculate available hosts
-   * @method rebalanceComponentHostsOnce
-   */
-  rebalanceComponentHostsOnce: function() {
-    if (this.get('component.component_name') === this.get('controller.componentToRebalance')) {
+    if (this.get('componentName') === this.get('controller.componentToRebalance')) {
+      this.get('content').clear();
+      this.set('isLoaded', false);
       this.initContent();
     }
-  },
+  }.observes('controller.rebalanceComponentHostsCounter'),
 
   /**
    * Get available hosts
@@ -136,12 +102,12 @@ App.SelectHostView = Em.TextField.extend({
    */
   getAvailableHosts: function () {
     var hosts = this.get('controller.hosts').slice(),
-      componentName = this.get('component.component_name'),
+      componentName = this.get('componentName'),
       multipleComponents = this.get('controller.multipleComponents'),
       occupiedHosts = this.get('controller.selectedServicesMasters')
         .filterProperty('component_name', componentName)
         .mapProperty('selectedHost')
-        .without(this.get('component.selectedHost'));
+        .without(this.get('selectedHost'));
 
     if (multipleComponents.contains(componentName)) {
       return hosts.filter(function (host) {
@@ -151,32 +117,39 @@ App.SelectHostView = Em.TextField.extend({
     return hosts;
   },
 
-  didInsertElement: function () {
-    this.initContent();
-    this.set("value", this.get("component.selectedHost"));
-    var content = this.get('content').mapProperty('host_name'),
-      self = this,
-      typeahead = this.$().typeahead({items: 10, source: content});
-    typeahead.on('blur', function() {
-      self.change();
-    }).on('keyup', function(e) {
-        self.set('value', $(e.currentTarget).val());
-        self.change();
-      });
-    this.set('typeahead', typeahead);
-  },
-
   /**
-   * Update <code>source</code> property of <code>typeahead</code> with a new list of hosts
-   * @param {string[]} hosts
-   * @method updateTypeaheadData
+   * On click start lazy loading
+   * @method click
    */
-  updateTypeaheadData: function(hosts) {
-    if (this.get('typeahead')) {
-      this.get('typeahead').data('typeahead').source = hosts;
+  click: function () {
+    var source = [];
+    var availableHosts = this.getAvailableHosts();
+
+    if (!this.get('isLoaded') && this.get('isLazyLoading')) {
+      //filter out hosts, which already pushed in select
+      source = availableHosts.filter(function (_host) {
+        return !this.get('content').someProperty('host_name', _host.host_name);
+      }, this).slice();
+      lazyloading.run({
+        destination: this.get('content'),
+        source: source,
+        context: this,
+        initSize: 30,
+        chunkSize: 200,
+        delay: 50
+      });
     }
   },
 
+  didInsertElement: function () {
+    //The lazy loading for select elements supported only by Firefox and Chrome
+    var isBrowserSupported = $.browser.mozilla || ($.browser.safari && navigator.userAgent.indexOf('Chrome') !== -1);
+    var isLazyLoading = isBrowserSupported && this.get('controller.hosts').length > 100;
+    this.set('isLazyLoading', isLazyLoading);
+    this.initContent();
+    this.set("value", this.get("selectedHost"));
+  },
+
   /**
    * Extract hosts from controller,
    * filter out available to selection and
@@ -185,8 +158,17 @@ App.SelectHostView = Em.TextField.extend({
    */
   initContent: function () {
     var hosts = this.getAvailableHosts();
-    this.set("content", hosts);
-    this.updateTypeaheadData(hosts.mapProperty('host_name'));
+    if (this.get('isLazyLoading')) {
+      //select need at least 30 hosts to have scrollbar
+      var initialHosts = hosts.slice(0, 30);
+      if (!initialHosts.someProperty('host_name', this.get('selectedHost'))) {
+        initialHosts.unshift(hosts.findProperty('host_name', this.get('selectedHost')));
+      }
+      this.set("content", initialHosts);
+    }
+    else {
+      this.set("content", hosts);
+    }
   }
 });
 

+ 67 - 83
ambari-web/test/controllers/wizard/step5_test.js

@@ -19,7 +19,6 @@
 var Ember = require('ember');
 var App = require('app');
 require('controllers/wizard/step5_controller');
-require('utils/ajax/ajax');
 var c;
 describe('App.WizardStep5Controller', function () {
   beforeEach(function() {
@@ -192,14 +191,6 @@ describe('App.WizardStep5Controller', function () {
 
   describe('#isReassignHive', function() {
 
-    beforeEach(function() {
-      sinon.stub(controller, 'getIsSubmitDisabled', Em.K);
-    });
-
-    afterEach(function() {
-      controller.getIsSubmitDisabled.restore();
-    });
-
     var tests = Em.A([
       {
         servicesMasters: Em.A([{component_name: 'HIVE_SERVER'}]),
@@ -512,15 +503,10 @@ describe('App.WizardStep5Controller', function () {
 
   });
 
-  describe('#getIsSubmitDisabled', function() {
-    it('should base on selected host to masters if it\'s not a isReassignWizard', function() {
+  describe('#isSubmitDisabled', function() {
+    it('should be false if it\'s not a isReassignWizard', function() {
       c.set('controllerName', 'addServiceController');
-      c.reopen({servicesMasters: [{isHostNameValid: true}, {isHostNameValid: false}]});
-      c.getIsSubmitDisabled();
-      expect(c.get('submitDisabled')).to.equal(true);
-      c.reopen({servicesMasters: [{isHostNameValid: true}, {isHostNameValid: true}]});
-      c.getIsSubmitDisabled();
-      expect(c.get('submitDisabled')).to.equal(false);
+      expect(c.get('isSubmitDisabled')).to.equal(false);
     });
   });
 
@@ -644,7 +630,6 @@ describe('App.WizardStep5Controller', function () {
   });
 
   describe('#renderComponents', function() {
-
     var tests = Em.A([
       {
         masterComponents: Em.A([
@@ -720,21 +705,19 @@ describe('App.WizardStep5Controller', function () {
       }
     ]);
     tests.forEach(function(test) {
+      beforeEach(function() {
+        App.reopen({isHaEnabled: test.isHaEnabled});
+      });
       it(test.m, function() {
-        sinon.stub(App, 'get', function(k) {
-          if ('isHaEnabled' === k) return test.isHaEnabled;
-          return Em.get(App, k);
-        });
+        App.set('isHaEnabled', test.isHaEnabled);
         c.reopen({
           content: Em.Object.create({
-            getIsSubmitDisabled: Em.K,
             services: test.services,
             controllerName: test.controllerName,
             reassign: {component_name: test.component_name}
           })
         });
         c.renderComponents(test.masterComponents);
-        App.get.restore();
         expect(c.get('selectedServicesMasters').mapProperty('component_name')).to.eql(test.e.selectedServicesMasters);
         expect(c.get('servicesMasters').mapProperty('component_name')).to.eql(test.e.servicesMasters);
         expect(c.get('selectedServicesMasters').mapProperty('showRemoveControl')).to.eql(test.e.showRemoveControl);
@@ -851,21 +834,18 @@ describe('App.WizardStep5Controller', function () {
 
   describe('#submit', function() {
     beforeEach(function() {
-      if(!App.router) {
-        App.router = Em.Object.create({send: Em.K});
-      }
-      sinon.stub(App.router, 'send', Em.K);
+      sinon.spy(App.router, 'send');
     });
     afterEach(function() {
       App.router.send.restore();
     });
     it('should go next if not isSubmitDisabled', function() {
-      c.reopen({servicesMasters: [{isHostNameValid: true}]});
+      c.reopen({isSubmitDisabled: false});
       c.submit();
       expect(App.router.send.calledWith('next')).to.equal(true);
     });
     it('shouldn\'t go next if isSubmitDisabled', function() {
-      c.reopen({servicesMasters: [{isHostNameValid: false}]});
+      c.reopen({isSubmitDisabled: true});
       c.submit();
       expect(App.router.send.called).to.equal(false);
     });
@@ -1068,6 +1048,63 @@ describe('App.WizardStep5Controller', function () {
     });
   });
 
+  describe('#isSubmitDisabled', function() {
+    it('should be false if no isReassignWizard', function() {
+      c.reopen({isReassignWizard: false});
+      expect(c.get('isSubmitDisabled')).to.equal(false);
+    });
+    it('should be true if isReassignWizard', function() {
+      var hostComponents = Em.A([
+        Em.Object.create({componentName: 'c1', host: Em.Object.create({hostName: 'h1'})}),
+        Em.Object.create({componentName: 'c1', host: Em.Object.create({hostName: 'h2'})})
+      ]);
+      sinon.stub(App.HostComponent, 'find', function() {
+        return hostComponents;
+      });
+      c.reopen({
+        isReassignWizard: true,
+        content:{
+          reassign:{
+            component_name: 'c1'
+          }
+        },
+        servicesMasters: [
+          {selectedHost: 'h5'},
+          {selectedHost: 'h4'},
+          {selectedHost: 'h3'}
+        ]
+      });
+      expect(c.get('isSubmitDisabled')).to.equal(true);
+      App.HostComponent.find.restore();
+    });
+
+    it('should be false if isReassignWizard', function() {
+      var hostComponents = Em.A([
+        Em.Object.create({componentName: 'c1', host: Em.Object.create({hostName: 'h1'})}),
+        Em.Object.create({componentName: 'c1', host: Em.Object.create({hostName: 'h2'})}),
+        Em.Object.create({componentName: 'c1', host: Em.Object.create({hostName: 'h3'})})
+      ]);
+      sinon.stub(App.HostComponent, 'find', function() {
+        return hostComponents;
+      });
+      c.reopen({
+        isReassignWizard: true,
+        content:{
+          reassign:{
+            component_name: 'c1'
+          }
+        },
+        servicesMasters: [
+          {selectedHost: 'h1'},
+          {selectedHost: 'h2'}
+        ]
+      });
+      expect(c.get('isSubmitDisabled')).to.equal(false);
+      App.HostComponent.find.restore();
+    });
+
+  });
+
   describe('#masterHostMapping', function() {
     Em.A([
         {
@@ -1316,57 +1353,4 @@ describe('App.WizardStep5Controller', function () {
       });
   });
 
-  describe('#isHostNameValid', function() {
-
-    beforeEach(function() {
-      controller.set('hosts', [{host_name: 'h1'}]);
-      controller.set('selectedServicesMasters', [{component_name: 'c1', selectedHost: 'h2'}]);
-    });
-
-    it('hostname is empty', function() {
-      expect(controller.isHostNameValid('c1', '')).to.be.false;
-    });
-
-    it('hostname not exists', function() {
-      expect(controller.isHostNameValid('c1', 'h2')).to.be.false;
-    });
-
-    it('hostname is assigned to such component', function() {
-      controller.get('selectedServicesMasters').pushObject({component_name: 'c1', selectedHost: 'h2'});
-      expect(controller.isHostNameValid('c1', 'h2')).to.be.false;
-    });
-
-    it('hostname is valid', function() {
-      expect(controller.isHostNameValid('c1', 'h1')).to.be.true;
-    });
-
-  });
-
-  describe('#updateIsHostNameValidFlag', function() {
-
-    beforeEach(function() {
-      controller.set('selectedServicesMasters', [
-        Em.Object.create({component_name: 'ZOOKEEPER_SERVER', zId: 1, isHostNameValid: true}),
-        Em.Object.create({component_name: 'ZOOKEEPER_SERVER', zId: 2, isHostNameValid: true}),
-        Em.Object.create({component_name: 'c1', zId: null, isHostNameValid: true})
-      ]);
-    });
-
-    it('shouldn\'t do nothing componentName not provided', function() {
-      controller.updateIsHostNameValidFlag(null, null, false);
-      expect(controller.get('selectedServicesMasters').everyProperty('isHostNameValid', true)).to.be.true;
-    });
-
-    it('should update one multiple component', function() {
-      controller.updateIsHostNameValidFlag('ZOOKEEPER_SERVER', 2, false);
-      expect(controller.get('selectedServicesMasters').mapProperty('isHostNameValid')).to.eql([true, false, true]);
-    });
-
-    it('should update single component', function() {
-      controller.updateIsHostNameValidFlag('c1', null, false);
-      expect(controller.get('selectedServicesMasters').mapProperty('isHostNameValid')).to.eql([true, true, false]);
-    });
-
-  });
-
 });

+ 103 - 41
ambari-web/test/views/wizard/step5_view_test.js

@@ -45,9 +45,7 @@ describe('App.SelectHostView', function() {
 
   beforeEach(function() {
     view = App.SelectHostView.create({
-      controller: App.WizardStep5Controller.create({}),
-      $: function() {return {typeahead: function(){return {on: Em.K}}}},
-      updateErrorStatus: Em.K
+      controller: App.WizardStep5Controller.create({})
     });
   });
 
@@ -62,18 +60,19 @@ describe('App.SelectHostView', function() {
       view.didInsertElement();
       expect(view.initContent.calledOnce).to.equal(true);
     });
-    it('should set component.selectedHost to value', function() {
-      view.set('component', {selectedHost: 'h1'});
+    it('should set selectedHost to value', function() {
+      view.set('selectedHost', 'h1');
       view.set('value', '');
       view.didInsertElement();
       expect(view.get('value')).to.equal('h1');
     });
   });
 
-  describe('#changeHandler', function() {
+  describe('#change', function() {
     beforeEach(function() {
-      view.set('component', {component_name: 'ZOOKEEPER_SERVER', zId: 1});
+      view.set('componentName', 'ZOOKEEPER_SERVER');
       view.set('value', 'h1');
+      view.set('zId', 1);
       view.set('controller.rebalanceComponentHostsCounter', 0);
       view.set('controller.componentToRebalance', '');
       sinon.stub(view.get('controller'), 'assignHostToMaster', Em.K);
@@ -81,23 +80,16 @@ describe('App.SelectHostView', function() {
     afterEach(function() {
       view.get('controller').assignHostToMaster.restore();
     });
-
-    it('shouldn\'t do nothing if view is destroyed', function() {
-      view.set('state', 'destroyed');
-      expect(view.get('controller').assignHostToMaster.called).to.be.false;
-    });
-
     it('should call assignHostToMaster', function() {
-      view.changeHandler();
+      view.change();
       expect(view.get('controller').assignHostToMaster.calledWith('ZOOKEEPER_SERVER', 'h1', 1));
     });
-    it('should increment rebalanceComponentHostsCounter if component is multiple', function() {
-      view.set('component', {component_name: 'ZOOKEEPER_SERVER'});
-      view.changeHandler();
+    it('should increment rebalanceComponentHostsCounter', function() {
+      view.change();
       expect(view.get('controller.rebalanceComponentHostsCounter')).to.equal(1);
     });
     it('should set componentToRebalance', function() {
-      view.changeHandler();
+      view.change();
       expect(view.get('controller.componentToRebalance')).to.equal('ZOOKEEPER_SERVER');
     });
   });
@@ -157,7 +149,7 @@ describe('App.SelectHostView', function() {
     tests.forEach(function(test) {
       it(test.m, function() {
         view.set('controller.hosts', test.hosts);
-        view.set('component', {component_name: test.componentName});
+        view.set('componentName', test.componentName);
         view.set('controller.selectedServicesMasters', test.selectedServicesMasters);
         var r = view.getAvailableHosts();
         expect(r.mapProperty('host_name')).to.eql(test.e);
@@ -165,36 +157,45 @@ describe('App.SelectHostView', function() {
     });
   });
 
-  describe('#rebalanceComponentHostsOnce', function() {
+  describe('#rebalanceComponentHosts', function() {
     var tests = Em.A([
       {
         componentName: 'c1',
         componentToRebalance: 'c2',
+        isLoaded: true,
         content: [{}],
         m: 'componentName not equal to componentToRebalance',
         e: {
-          initContent: false
+          initContent: false,
+          isLoaded: true,
+          content: 1
         }
       },
       {
         componentName: 'c2',
         componentToRebalance: 'c2',
+        isLoaded: true,
         content: [{}],
         m: 'componentName equal to componentToRebalance',
         e: {
-          initContent: true
+          initContent: true,
+          isLoaded: false,
+          content: 0
         }
       }
     ]);
 
     tests.forEach(function(test) {
       it(test.m, function() {
+        view.set('isLoaded', test.isLoaded);
         view.set('content', test.content);
-        view.set('component', {component_name: test.componentName});
+        view.set('componentName', test.componentName);
         view.set('controller.componentToRebalance', test.componentToRebalance);
         sinon.stub(view, 'initContent', Em.K);
-        view.rebalanceComponentHostsOnce();
+        view.rebalanceComponentHosts();
         expect(view.initContent.calledOnce).to.equal(test.e.initContent);
+        expect(view.get('isLoaded')).to.equal(test.e.isLoaded);
+        expect(view.get('content.length')).to.equal(test.e.content);
         view.initContent.restore();
       });
     });
@@ -203,15 +204,43 @@ describe('App.SelectHostView', function() {
   describe('#initContent', function() {
     var tests = Em.A([
       {
+        isLazyLoading: false,
         hosts: 25,
         m: 'not lazy loading, 25 hosts, no selected host',
         e: 25
       },
       {
+        isLazyLoading: false,
         hosts: 25,
         h: 4,
         m: 'not lazy loading, 25 hosts, one selected host',
         e: 25
+      },
+      {
+        isLazyLoading: true,
+        hosts: 25,
+        h: 4,
+        m: 'lazy loading, 25 hosts, one selected host',
+        e: 25
+      },
+      {
+        isLazyLoading: true,
+        hosts: 25,
+        m: 'lazy loading, 25 hosts, no selected host',
+        e: 26
+      },
+      {
+        isLazyLoading: true,
+        hosts: 100,
+        h: 4,
+        m: 'lazy loading, 100 hosts, one selected host',
+        e: 30
+      },
+      {
+        isLazyLoading: true,
+        hosts: 100,
+        m: 'lazy loading, 100 hosts, no selected host',
+        e: 31
       }
     ]);
     tests.forEach(function(test) {
@@ -220,35 +249,68 @@ describe('App.SelectHostView', function() {
         if (test.h) {
           view.set('selectedHost', test.h);
         }
+        view.set('isLazyLoading', test.isLazyLoading);
         view.initContent();
         expect(view.get('content.length')).to.equal(test.e);
       });
     });
   });
 
-  describe('#change', function() {
-
+  describe('#click', function() {
     beforeEach(function() {
-      sinon.stub(view, 'changeHandler', Em.K);
+      sinon.stub(lazyloading, 'run', Em.K);
     });
-
     afterEach(function() {
-      view.changeHandler.restore();
+      lazyloading.run.restore();
     });
-
-    it('shouldn\'t do nothing if view is destroyed', function() {
-      view.set('controller.hostNameCheckTrigger', false);
-      view.set('state', 'destroyed');
-      view.change();
-      expect(view.get('controller.hostNameCheckTrigger')).to.equal(false);
+    Em.A([
+        {
+          isLoaded: true,
+          isLazyLoading: true,
+          e: false
+        },
+        {
+          isLoaded: true,
+          isLazyLoading: false,
+          e: false
+        },
+        {
+          isLoaded: false,
+          isLazyLoading: true,
+          e: true
+        },
+        {
+          isLoaded: false,
+          isLazyLoading: false,
+          e: false
+        }
+      ]).forEach(function(test) {
+      it('isLoaded = ' + test.isLoaded.toString() + ', isLazyLoading = ' + test.isLazyLoading.toString(), function() {
+        view.reopen({
+          isLazyLoading: test.isLazyLoading,
+          isLoaded: test.isLoaded
+        });
+        view.click();
+        if(test.e) {
+          expect(lazyloading.run.calledOnce).to.equal(true);
+        }
+        else {
+          expect(lazyloading.run.called).to.equal(false);
+        }
+      });
     });
-
-    it('should toggle hostNameCheckTrigger', function() {
-      view.set('controller.hostNameCheckTrigger', false);
-      view.change();
-      expect(view.get('controller.hostNameCheckTrigger')).to.equal(true);
+    it('check lazyLoading parameters', function() {
+      view.reopen({
+        isLoaded: false,
+        isLazyLoading: true,
+        content: [{host_name: 'host1'}, {host_name: 'host2'}]
+      });
+      var availableHosts = d3.range(1, 100).map(function(i) {return {host_name: 'host' + i.toString()};});
+      sinon.stub(view, 'getAvailableHosts', function() {return availableHosts;});
+      view.click();
+      expect(lazyloading.run.args[0][0].source.length).to.equal(97); // 99-2
+      view.getAvailableHosts.restore();
     });
-
   });
 
 });