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

ambari git commit: AMBARI-8552. Batch the call to create and install hostComponents in the wizard progress page controller mixin. (jaimin)

Repository: ambari
Updated Branches:
  refs/heads/trunk 356309d16 -> 96c9e2045


AMBARI-8552. Batch the call to create and install hostComponents in the wizard progress page controller mixin. (jaimin)


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

Branch: refs/heads/trunk
Commit: 96c9e2045f5b30b1289846c60528deaddbc6a423
Parents: 356309d
Author: Jaimin Jetly <ja...@hortonworks.com>
Authored: Thu Dec 4 17:01:26 2014 -0800
Committer: Jaimin Jetly <ja...@hortonworks.com>
Committed: Thu Dec 4 17:01:33 2014 -0800

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   1 +
 .../wizard/wizardProgressPageController.js      | 139 ++++++++++++-------
 .../wizard/wizardProgressPageController_test.js | 138 ++++++++++++++++++
 3 files changed, 225 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/96c9e204/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 77f2869..35a80cf 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -128,6 +128,7 @@ var files = ['test/init_model_test',
   'test/mixins/common/table_server_view_mixin_test',
   'test/mixins/main/host/details/host_components/decommissionable_test',
   'test/mixins/wizard/wizard_menu_view_test',
+  'test/mixins/wizard/wizardProgressPageController_test',
   'test/utils/ajax/ajax_test',
   'test/utils/ajax/ajax_queue_test',
   'test/utils/batch_scheduled_requests_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/96c9e204/ambari-web/app/mixins/wizard/wizardProgressPageController.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/wizard/wizardProgressPageController.js b/ambari-web/app/mixins/wizard/wizardProgressPageController.js
index 074d249..63a5fc7 100644
--- a/ambari-web/app/mixins/wizard/wizardProgressPageController.js
+++ b/ambari-web/app/mixins/wizard/wizardProgressPageController.js
@@ -177,8 +177,8 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create({
 
   /**
    * check whether component installed on specified hosts
-   * @param componentName
-   * @param hostNames
+   * @param {string} componentName
+   * @param {string[]} hostNames
    * @return {$.ajax}
    */
   checkInstalledComponents: function (componentName, hostNames) {
@@ -188,49 +188,73 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create({
       data: {
         componentName: componentName,
         hostNames: hostNames.join(',')
-      },
-      success: 'checkInstalledComponentsSuccessCallback'
+      }
     });
   },
 
-  checkInstalledComponentsSuccessCallback: function (data, opt, params) {
-    installedComponents = data.items;
-  },
-
+  
+  /**
+   * Create component on single or multiple hosts.
+   *
+   * @method createComponent
+   * @param {string} componentName - name of the component
+   * @param {(string|string[])} hostName - host/hosts where components should be installed
+   * @param {string} serviceName - name of the services
+   */
   createComponent: function (componentName, hostName, serviceName) {
     var hostNames = (Array.isArray(hostName)) ? hostName : [hostName];
     var self = this;
 
-    this.checkInstalledComponents(componentName, hostNames).complete(function () {
-      var result = [];
-
-      hostNames.forEach(function (hostName) {
-        result.push({
+    this.checkInstalledComponents(componentName, hostNames).then(function (data) {
+      var hostsWithComponents = data.items.mapProperty('HostRoles.host_name');
+      var result = hostNames.map(function(item) {
+        return {
           componentName: componentName,
-          hostName: hostName,
-          hasComponent: installedComponents.someProperty('HostRoles.host_name', hostName)
-        });
+          hostName: item,
+          hasComponent: hostsWithComponents.contains(item)
+        };
       });
-
-      result.forEach(function (host, index, array) {
-        if (!host.hasComponent) {
-          App.ajax.send({
-            name: 'admin.high_availability.create_component',
-            sender: this,
-            data: {
-              hostName: host.hostName,
-              componentName: host.componentName,
-              serviceName: serviceName,
-              taskNum: array.length
-            },
-            success: 'onCreateComponent',
-            error: 'onCreateComponentError'
-          });
-        } else {
-          // Simulates format returned from ajax.send
-          this.onCreateComponent(null, null, {hostName: host.hostName, componentName: host.componentName, taskNum: array.length});
+      var hostsWithoutComponents = result.filterProperty('hasComponent', false).mapProperty('hostName');
+      var taskNum = 1;
+      var requestData = {
+        "RequestInfo": {
+          "query": hostsWithoutComponents.map(function(item) {
+            return 'Hosts/host_name=' + item;
+          }).join('|')
+        },
+        "Body": {
+          "host_components": [
+            {
+              "HostRoles": {
+                "component_name": componentName
+              }
+            }
+          ]
         }
-      }, self)
+      };
+      if (!!hostsWithoutComponents.length) {
+        App.ajax.send({
+          name: 'wizard.step8.register_host_to_component',
+          sender: self,
+          data: {
+            data: JSON.stringify(requestData),
+            hostName: result.mapProperty('hostName'),
+            componentName: componentName,
+            serviceName: serviceName,
+            taskNum: taskNum,
+            cluster: App.get('clusterName')
+          },
+          success: 'onCreateComponent',
+          error: 'onCreateComponent'
+        });        
+      } else {
+        self.onCreateComponent(null, null, {
+          hostName: result.mapProperty('hostName'),
+          componentName: componentName,
+          serviceName: serviceName,
+          taskNum: taskNum
+        }, self);
+      }
     });
   },
 
@@ -250,29 +274,38 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create({
     }
   },
 
+  /**
+   * Update component status on selected hosts.
+   * 
+   * @param {string} componentName
+   * @param {(string|string[])} hostName
+   * @param {string} serviceName
+   * @param {string} context
+   * @param {number} taskNum
+   * @returns {$.ajax}
+   */
   updateComponent: function (componentName, hostName, serviceName, context, taskNum) {
     if (!(hostName instanceof Array)) {
       hostName = [hostName];
     }
     var state = context.toLowerCase() == "start" ? "STARTED" : "INSTALLED";
-    for (var i = 0; i < hostName.length; i++) {
-      App.ajax.send({
-        name: 'common.host.host_component.update',
-        sender: this,
-        data: {
-          context: context + " " + App.format.role(componentName),
-          hostName: hostName[i],
-          serviceName: serviceName,
-          componentName: componentName,
-          taskNum: taskNum || hostName.length,
-          HostRoles: {
-            state: state
-          }
+    return App.ajax.send({
+      name: 'common.host_components.update',
+      sender: this,
+      data: {
+        HostRoles: {
+          state: state
         },
-        success: 'startPolling',
-        error: 'onTaskError'
-      });
-    }
+        query: 'HostRoles/component_name=' + componentName + '&HostRoles/host_name.in(' + hostName.join(',') + ')&HostRoles/maintenance_state=OFF',
+        context: context + " " + App.format.role(componentName),
+        hostName: hostName,
+        taskNum: taskNum || 1,
+        componentName: componentName,
+        serviceName: serviceName
+      },
+      success: 'startPolling',
+      error: 'onTaskError'
+    });
   },
 
   startPolling: function (data) {
@@ -332,7 +365,7 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create({
         var progress = Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / actionsPerHost * 100);
         this.get('tasks').findProperty('id', currentTaskId).set('progress', progress);
         window.setTimeout(function () {
-          self.doPolling()
+          self.doPolling();
         }, self.POLL_INTERVAL);
       }
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/96c9e204/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js b/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
new file mode 100644
index 0000000..d72b3f6
--- /dev/null
+++ b/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+describe('App.wizardProgressPageControllerMixin', function() {
+  var mixedObject = Em.Object.extend(App.wizardProgressPageControllerMixin, {});
+  
+  describe('#createComponent', function() {
+    var mixedObjectInstance;
+    beforeEach(function() {
+      mixedObjectInstance = mixedObject.create({});
+      sinon.stub(App.ajax, 'send', function(params) {
+        return params;
+      });
+      sinon.spy(mixedObjectInstance, 'onCreateComponent');
+      sinon.spy(mixedObjectInstance, 'updateComponent');
+      sinon.stub(mixedObjectInstance, 'checkInstalledComponents', function(componentName, hostNames) {
+        var def = $.Deferred();
+        var data = {
+          'ZOOKEEPER_SERVER': {
+            items: []
+          },
+          'ZOOKEEPER_CLIENT': {
+            items: [
+              { HostRoles: { host_name: 'host1' } }
+            ]
+          }
+        };
+        def.resolve(data[componentName]);
+        return def.promise();
+      });
+    });
+    
+    afterEach(function() {
+      App.ajax.send.restore();
+      mixedObjectInstance.onCreateComponent.restore();
+      mixedObjectInstance.updateComponent.restore();
+      mixedObjectInstance.checkInstalledComponents.restore();
+    });
+    
+    it('should call `checkInstalledComponents` method', function() {
+      mixedObjectInstance.createComponent('ZOOKEEPER_SERVER', 'host1', 'ZOOKEEPER');
+      expect(mixedObjectInstance.checkInstalledComponents.called).to.be.true;
+    });
+
+    it('should call `checkInstalledComponents` method with host name converted to Array', function() {
+      mixedObjectInstance.createComponent('ZOOKEEPER_SERVER', 'host1', 'ZOOKEEPER');
+      expect(mixedObjectInstance.checkInstalledComponents.calledWith('ZOOKEEPER_SERVER', ['host1'])).to.be.true;
+    });
+
+    it('no ZooKeeper Servers installed. install on host1, host2. ajax request should be called with appropriate params', function() {
+      mixedObjectInstance.createComponent('ZOOKEEPER_SERVER', ['host1', 'host2'], 'ZOOKEEPER');
+      var args = App.ajax.send.args[0][0];
+      var queryObject = JSON.parse(args.data.data);
+      expect(args.data.hostName).to.be.eql(['host1', 'host2']);
+      expect(queryObject.RequestInfo.query).to.be.eql('Hosts/host_name=host1|Hosts/host_name=host2');
+      expect(queryObject.Body.host_components[0].HostRoles.component_name).to.be.eql('ZOOKEEPER_SERVER');
+      expect(args.data.taskNum).to.be.eql(1);
+      // invoke callback
+      args.sender[args.success].apply(args.sender, [null, null, args.data]);
+      expect(mixedObjectInstance.updateComponent.called).to.be.true;
+    });
+
+    it('ZooKeeper Client installed on host1. install on host1, host2. ajax request should be called with appropriate params', function() {
+      mixedObjectInstance.createComponent('ZOOKEEPER_CLIENT', ['host1', 'host2'], 'ZOOKEEPER');
+      var args = App.ajax.send.args[0][0];
+      var queryObject = JSON.parse(args.data.data);
+      expect(args.data.hostName).to.be.eql(['host1', 'host2']);
+      expect(queryObject.RequestInfo.query).to.be.eql('Hosts/host_name=host2');
+      expect(queryObject.Body.host_components[0].HostRoles.component_name).to.be.eql('ZOOKEEPER_CLIENT');
+      expect(mixedObjectInstance.onCreateComponent.called).to.be.false;
+      // invoke callback
+      args.sender[args.success].apply(args.sender, [null, null, args.data]);
+      expect(mixedObjectInstance.updateComponent.called).to.be.true;
+    });
+  });
+
+  describe('#updateComponent', function() {
+    var testsAjax = [
+      {
+        callParams: ['ZOOKEEPER_SERVER', 'host1', 'ZOOKEEPER', 'Install', 1],
+        e: [
+          { key: 'data.HostRoles.state', value: 'INSTALLED'},
+          { key: 'data.hostName[0]', value: 'host1'},
+          { key: 'data.query', value: 'HostRoles/component_name=ZOOKEEPER_SERVER&HostRoles/host_name.in(host1)&HostRoles/maintenance_state=OFF'}
+        ]
+      },
+      {
+        callParams: ['ZOOKEEPER_SERVER', ['host1', 'host2'], 'ZOOKEEPER', 'start', 1],
+        e: [
+          { key: 'data.HostRoles.state', value: 'STARTED'},
+          { key: 'data.hostName[0]', value: 'host1'},
+          { key: 'data.hostName[1]', value: 'host2'},
+          { key: 'data.query', value: 'HostRoles/component_name=ZOOKEEPER_SERVER&HostRoles/host_name.in(host1,host2)&HostRoles/maintenance_state=OFF'}
+        ]
+      }
+    ];
+    
+    testsAjax.forEach(function(test) {
+      describe('called with params: ' + JSON.stringify(test.callParams), function() {
+        before(function() {
+          sinon.stub(App.ajax, 'send', Em.K);
+          var mixedObjectInstance = mixedObject.create({});
+          mixedObjectInstance.updateComponent.apply(mixedObjectInstance, test.callParams);
+        });
+
+        after(function() {
+          App.ajax.send.restore();
+        });
+        
+        test.e.forEach(function(eKey) {
+          it('key: {0} should have value: {1}'.format(eKey.key, eKey.value), function() {
+            var args = App.ajax.send.args[0][0];
+            expect(args).to.have.deep.property(eKey.key, eKey.value);
+          });
+        });
+      });
+    });
+  });
+
+
+});