浏览代码

AMBARI-1578. Add host wizard - support assignment of "ZooKeeper Server" and "HBase Master". (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1454160 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 年之前
父节点
当前提交
e26a0d95c8

+ 3 - 0
CHANGES.txt

@@ -105,6 +105,9 @@ Trunk (unreleased changes):
 
  IMPROVEMENTS
 
+ AMBARI-1578. Add host wizard - support assignment of "ZooKeeper Server"
+ and "HBase Master". (yusaku)
+
  AMBARI-1546. Improve Cluster Management loading screen. (Xi Wang via yusaku)
 
  AMBARI-1537. Constrain the width of all wizard popups. (Xi Wang via yusaku)

+ 24 - 57
ambari-web/app/controllers/installer.js

@@ -177,74 +177,41 @@ App.InstallerController = App.WizardController.extend({
   saveSlaveComponentHosts: function (stepController) {
 
     var hosts = stepController.get('hosts');
-    var isMrSelected = stepController.get('isMrSelected');
-    var isHbSelected = stepController.get('isHbSelected');
+    var headers = stepController.get('headers');
 
-    var dataNodeHosts = [];
-    var taskTrackerHosts = [];
-    var regionServerHosts = [];
-    var clientHosts = [];
+    var formattedHosts = Ember.Object.create();
+    headers.forEach(function(header) {
+      formattedHosts.set(header.get('name'), []);
+    });
 
     hosts.forEach(function (host) {
-      if (host.get('isDataNode')) {
-        dataNodeHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: false
-        });
-      }
-      if (isMrSelected && host.get('isTaskTracker')) {
-        taskTrackerHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: false
-        });
-      }
-      if (isHbSelected && host.get('isRegionServer')) {
-        regionServerHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: false
-        });
-      }
-      if (host.get('isClient')) {
-        clientHosts.pushObject({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: false
-        });
-      }
-    }, this);
 
-    var slaveComponentHosts = [];
-    slaveComponentHosts.push({
-      componentName: 'DATANODE',
-      displayName: 'DataNode',
-      hosts: dataNodeHosts
-    });
-    if (isMrSelected) {
-      slaveComponentHosts.push({
-        componentName: 'TASKTRACKER',
-        displayName: 'TaskTracker',
-        hosts: taskTrackerHosts
+      var checkboxes = host.get('checkboxes');
+      headers.forEach(function(header) {
+        var cb = checkboxes.findProperty('title', header.get('label'));
+        if (cb.get('checked')) {
+          formattedHosts.get(header.get('name')).push({
+            hostName: host.hostName,
+            group: 'Default',
+            isInstalled: cb.get('installed')
+          });
+        }
       });
-    }
-    if (isHbSelected) {
+    });
+
+    var slaveComponentHosts = [];
+
+    headers.forEach(function(header) {
       slaveComponentHosts.push({
-        componentName: 'HBASE_REGIONSERVER',
-        displayName: 'RegionServer',
-        hosts: regionServerHosts
+        componentName: header.get('name'),
+        displayName: header.get('label').replace(/\s/g, ''),
+        hosts: formattedHosts.get(header.get('name'))
       });
-    }
-    slaveComponentHosts.pushObject({
-      componentName: 'CLIENT',
-      displayName: 'client',
-      hosts: clientHosts
     });
 
     App.db.setSlaveComponentHosts(slaveComponentHosts);
+    console.log('InstallerController.saveSlaveComponentHosts: saved hosts ', slaveComponentHosts);
     this.set('content.slaveComponentHosts', slaveComponentHosts);
-    console.log("InstallerController.saveSlaveComponentHosts: saved hosts ", slaveComponentHosts);
   },
 
   /**

+ 67 - 59
ambari-web/app/controllers/main/host/add_controller.js

@@ -23,7 +23,7 @@ App.AddHostController = App.WizardController.extend({
 
   name: 'addHostController',
 
-  totalSteps: 6,
+  totalSteps: 7,
 
   /**
    * Used for hiding back button in wizard
@@ -54,6 +54,8 @@ App.AddHostController = App.WizardController.extend({
     isWizard: true
   }),
 
+  components:require('data/service_components'),
+
   /**
    * return new object extended from clusterStatusTemplate
    * @return Object
@@ -162,7 +164,9 @@ App.AddHostController = App.WizardController.extend({
     });
     this.set('content.services', servicesInfo);
     console.log('AddHostController.loadServices: loaded data ', servicesInfo);
-    console.log('selected services ', servicesInfo.filterProperty('isSelected', true).filterProperty('isDisabled', false).mapProperty('serviceName'));
+    var serviceNames = servicesInfo.filterProperty('isSelected', true).mapProperty('serviceName');
+    console.log('selected services ', serviceNames);
+    this.set('content.missMasterStep', !serviceNames.contains('HBASE') && !serviceNames.contains('ZOOKEEPER'));
   },
 
   /**
@@ -187,6 +191,43 @@ App.AddHostController = App.WizardController.extend({
     console.log("AddHostController.loadMasterComponentHosts: loaded hosts ", masterComponentHosts);
   },
 
+  /**
+   * Save HBase and ZooKeeper to main controller
+   * @param stepController
+   */
+  saveHbZk: function(stepController) {
+    var self = this;
+    var hosts = stepController.get('hosts');
+    var headers = stepController.get('headers');
+    var masterComponentHosts = App.db.getMasterComponentHosts();
+
+    headers.forEach(function(header) {
+      var rm = masterComponentHosts.filterProperty('component', header.get('name'));
+      if(rm) {
+        masterComponentHosts.removeObjects(rm);
+      }
+    });
+
+    headers.forEach(function(header) {
+      var component = self.get('components').findProperty('component_name', header.get('name'));
+      hosts.forEach(function(host) {
+        if (host.get('checkboxes').findProperty('title', component.display_name).checked) {
+          masterComponentHosts .push({
+            display_name: component.display_name,
+            component: component.component_name,
+            hostName: host.get('hostName'),
+            serviceId: component.service_name,
+            isInstalled: false
+          });
+        }
+      });
+    });
+
+    console.log("installerController.saveMasterComponentHosts: saved hosts ", masterComponentHosts);
+    App.db.setMasterComponentHosts(masterComponentHosts);
+    this.set('content.masterComponentHosts', masterComponentHosts);
+  },
+
   /**
    * Save slaveHostComponents to main controller
    * @param stepController
@@ -194,70 +235,36 @@ App.AddHostController = App.WizardController.extend({
   saveSlaveComponentHosts: function (stepController) {
 
     var hosts = stepController.get('hosts');
-    var isMrSelected = stepController.get('isMrSelected');
-    var isHbSelected = stepController.get('isHbSelected');
+    var headers = stepController.get('headers');
 
-    var dataNodeHosts = [];
-    var taskTrackerHosts = [];
-    var regionServerHosts = [];
-    var clientHosts = [];
+    var formattedHosts = Ember.Object.create();
+    headers.forEach(function(header) {
+      formattedHosts.set(header.get('name'), []);
+    });
 
     hosts.forEach(function (host) {
 
-      if (host.get('isDataNode')) {
-        dataNodeHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: host.get('isDataNodeInstalled')
-        });
-      }
-      if (isMrSelected && host.get('isTaskTracker')) {
-        taskTrackerHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: host.get('isTaskTrackerInstalled')
-        });
-      }
-      if (isHbSelected && host.get('isRegionServer')) {
-        regionServerHosts.push({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: host.get('isRegionServerInstalled')
-        });
-      }
-      if (host.get('isClient')) {
-        clientHosts.pushObject({
-          hostName: host.hostName,
-          group: 'Default',
-          isInstalled: host.get('isClientInstalled')
-        });
-      }
-    }, this);
+      var checkboxes = host.get('checkboxes');
+      headers.forEach(function(header) {
+        var cb = checkboxes.findProperty('title', header.get('label'));
+        if (cb.get('checked')) {
+          formattedHosts.get(header.get('name')).push({
+            hostName: host.hostName,
+            group: 'Default',
+            isInstalled: cb.get('installed')
+          });
+        }
+      });
+    });
 
     var slaveComponentHosts = [];
-    slaveComponentHosts.push({
-      componentName: 'DATANODE',
-      displayName: 'DataNode',
-      hosts: dataNodeHosts
-    });
-    if (isMrSelected) {
-      slaveComponentHosts.push({
-        componentName: 'TASKTRACKER',
-        displayName: 'TaskTracker',
-        hosts: taskTrackerHosts
-      });
-    }
-    if (isHbSelected) {
+
+    headers.forEach(function(header) {
       slaveComponentHosts.push({
-        componentName: 'HBASE_REGIONSERVER',
-        displayName: 'RegionServer',
-        hosts: regionServerHosts
+        componentName: header.get('name'),
+        displayName: header.get('label').replace(/\s/g, ''),
+        hosts: formattedHosts.get(header.get('name'))
       });
-    }
-    slaveComponentHosts.pushObject({
-      componentName: 'CLIENT',
-      displayName: 'client',
-      hosts: clientHosts
     });
 
     App.db.setSlaveComponentHosts(slaveComponentHosts);
@@ -464,12 +471,13 @@ App.AddHostController = App.WizardController.extend({
   loadAllPriorSteps: function () {
     var step = this.get('currentStep');
     switch (step) {
+      case '9':
       case '8':
       case '7':
       case '6':
       case '5':
-      case '4':
         this.loadServiceConfigProperties();
+      case '4':
       case '3':
         this.loadClients();
         this.loadServices();

+ 254 - 194
ambari-web/app/controllers/wizard/step6_controller.js

@@ -35,82 +35,60 @@ App.WizardStep6Controller = Em.Controller.extend({
 
   hosts: [],
 
-  isAddHostWizard: function(){
-    return this.get('content.controllerName') === 'addHostController';
-  }.property('content.controllerName'),
-
-  isAllDataNodes: function () {
-    return this.get('hosts').everyProperty('isDataNode', true);
-  }.property('hosts.@each.isDataNode'),
-
-  isAllTaskTrackers: function () {
-    return this.get('hosts').everyProperty('isTaskTracker', true);
-  }.property('hosts.@each.isTaskTracker'),
-
-  isAllRegionServers: function () {
-    return this.get('hosts').everyProperty('isRegionServer', true);
-  }.property('hosts.@each.isRegionServer'),
-
-  isAllClients: function () {
-    return this.get('hosts').everyProperty('isClient', true);
-  }.property('hosts.@each.isClient'),
-
-  isNoDataNodes: function () {
-    return this.get('hosts').everyProperty('isDataNode', false);
-  }.property('hosts.@each.isDataNode'),
-
-  isNoTaskTrackers: function () {
-    return this.get('hosts').everyProperty('isTaskTracker', false);
-  }.property('hosts.@each.isTaskTracker'),
-
-  isNoRegionServers: function () {
-    return this.get('hosts').everyProperty('isRegionServer', false);
-  }.property('hosts.@each.isRegionServer'),
-
-  isNoClients: function () {
-    return this.get('hosts').everyProperty('isClient', false);
-  }.property('hosts.@each.isClient'),
+  headers: [],
 
   /**
-   * Return whether Hbase service was selected or not.
-   * Calculate this information on <code>content.services</code> variable
-   * @return Boolean
+   * true - assign ZK, HB
+   * false - slaves and clients
    */
-  isHbSelected: function () {
-    return this.get('content.services').findProperty('serviceName', 'HBASE').get('isSelected');
-  }.property('content.services'),
+  isMasters: false,
 
-  /**
-   * Return whether MapReduce service was selected or not.
-   * Calculate this information on <code>content.services</code> variable
-   * @return Boolean
-   */
-  isMrSelected: function () {
-    return this.get('content.services').findProperty('serviceName', 'MAPREDUCE').get('isSelected');
-  }.property('content.services'),
+  components:require('data/service_components'),
+
+  isAddHostWizard: function(){
+    return this.get('content.controllerName') === 'addHostController';
+  }.property('content.controllerName'),
 
   clearError: function () {
+    var self = this;
     var isError = false;
+    var err = true;
     var hosts = this.get('hosts');
-    if (this.get('isNoDataNodes') === false &&
-      (this.get('isNoTaskTrackers') === false || this.get('isMrSelected') === false) &&
-      (this.get('isNoRegionServers') === false || this.get('isHbSelected') === false) &&
-      this.get('isNoClients') === false) {
+    var headers = this.get('headers');
+    headers.forEach(function(header) {
+      var all_false = true;
+      hosts.forEach(function(host) {
+        var checkboxes = host.get('checkboxes');
+        all_false &= !checkboxes.findProperty('title', header.get('label')).checked;
+      });
+      err &= all_false;
+    });
+
+    if (!err) {
       this.set('errorMessage', '');
     }
-    if(this.get('isAddHostWizard')){
-      for(var i = 0; i < hosts.length; i++){
-        isError = !(hosts[i].get('isDataNode') || hosts[i].get('isClient')
-          || ( this.get('isMrSelected') && hosts[i].get('isTaskTracker'))
-          || ( this.get('isHbSelected') && hosts[i].get('isRegionServer')));
-        if (isError) {
-          break;
-        } else {
-          this.set('errorMessage', '');
-        }
+
+    if(this.get('isAddHostWizard')) {
+      if (this.get('isMasters')) {
+        this.set('errorMessage', '');
+      }
+      else {
+        hosts.forEach(function(host) {
+          isError = false;
+          headers.forEach(function(header) {
+            isError |= host.get('checkboxes').findProperty('title', header.get('label')).checked;
+          });
+          isError = !isError;
+          if (isError) {
+            return;
+          }
+          else {
+            self.set('errorMessage', '');
+          }
+        });
       }
     }
-  }.observes('isNoDataNodes', 'isNoTaskTrackers', 'isNoRegionServers', 'isNoClients'),
+  },
 
   /**
    * Check whether current host is currently selected as master
@@ -121,58 +99,141 @@ App.WizardStep6Controller = Em.Controller.extend({
     return this.get('content.masterComponentHosts').someProperty('hostName', hostName);
   },
 
-  selectAllDataNodes: function () {
-    var forFilter = this.get('hosts').filterProperty('isDataNodeInstalled', false);
-    forFilter.setEach('isDataNode', true);
-  },
-
-  selectAllTaskTrackers: function () {
-    var forFilter = this.get('hosts').filterProperty('isTaskTrackerInstalled', false);
-    forFilter.setEach('isTaskTracker', true);
+  clearStep: function () {
+    this.set('hosts', []);
+    this.set('headers', []);
+    this.clearError();
   },
 
-  selectAllRegionServers: function () {
-    var forFilter = this.get('hosts').filterProperty('isRegionServerInstalled', false);
-    forFilter.setEach('isRegionServer', true);
+  /**
+   * Enable some service for all hosts
+   * @param event
+   */
+  selectAllNodes: function(event) {
+    this.setAllNodes(event.context.label, true);
   },
 
-  selectAllClients: function () {
-    var forFilter = this.get('hosts').filterProperty('isClientInstalled', false);
-    forFilter.setEach('isClient', true);
+  /**
+   * Disable some services for all hosts
+   * @param event
+   */
+  deselectAllNodes: function(event) {
+    this.setAllNodes(event.context.label, false);
   },
 
-  deselectAllDataNodes: function () {
-    var forFilter = this.get('hosts').filterProperty('isDataNodeInstalled', false);
-    forFilter.setEach('isDataNode', false);
+  /**
+   * Enable/disable some service for all hosts
+   * @param {String} label - service name
+   * @param {Boolean} checked - true - enable, false - disable
+   */
+  setAllNodes: function(label, checked) {
+    this.get('hosts').forEach(function(host) {
+      host.get('checkboxes').forEach(function(checkbox) {
+        if (checkbox.get('title') === label) {
+          checkbox.set('checked', checked);
+        }
+      });
+    });
   },
 
-  deselectAllTaskTrackers: function () {
-    var forFilter = this.get('hosts').filterProperty('isTaskTrackerInstalled', false);
-    forFilter.setEach('isTaskTracker', false);
+  /**
+   * Return whether service was selected or not
+   * @param name serviceName
+   * @return {*}
+   */
+  isServiceSelected: function(name) {
+    return this.get('content.services').findProperty('serviceName', name).get('isSelected');
   },
 
-  deselectAllRegionServers: function () {
-    var forFilter = this.get('hosts').filterProperty('isRegionServerInstalled', false);
-    forFilter.setEach('isRegionServer', false);
-  },
+  /**
+   * Checkbox check callback
+   * @param {String} title
+   */
+  checkCallback: function(title) {
 
-  deselectAllClients: function () {
-    var forFilter = this.get('hosts').filterProperty('isClientInstalled', false);
-    forFilter.setEach('isClient', false);
+    var header = this.get('headers').findProperty('label', title);
+    var hosts = this.get('hosts');
+    var allTrue = true;
+    var allFalse = true;
+    hosts.forEach(function(host) {
+      host.get('checkboxes').forEach(function(cb) {
+        if (cb.get('title') === title) {
+          allTrue &= cb.get('checked');
+          allFalse &= !cb.get('checked');
+        }
+      });
+    });
+    header.set('allChecked', allTrue);
+    header.set('noChecked', allFalse);
+    this.clearError();
   },
 
-  clearStep: function () {
-    this.set('hosts', []);
-    this.clearError();
+  getComponentDisplayName: function(componentName) {
+    return this.get('components').findProperty('component_name', componentName).display_name
   },
 
   loadStep: function () {
+
+    var self = this;
+
     console.log("WizardStep6Controller: Loading step6: Assign Slaves");
     this.clearStep();
-    this.renderSlaveHosts();
 
-    if(this.get('content.missSlavesStep')){
-      App.router.send('next');
+    var headers = [];
+
+    if (this.get('isMasters')) {
+      if (this.isServiceSelected('HBASE')) {
+        headers.pushObject(Em.Object.create({
+          name: 'HBASE_MASTER',
+          label: self.getComponentDisplayName('HBASE_MASTER')
+        }));
+      }
+      if (this.isServiceSelected('ZOOKEEPER')) {
+        headers.pushObject(Em.Object.create({
+         name:'ZOOKEEPER_SERVER',
+         label: self.getComponentDisplayName('ZOOKEEPER_SERVER')
+       }));
+      }
+    }
+    else {
+      headers.pushObject(Ember.Object.create({
+        name: 'DATANODE',
+        label: self.getComponentDisplayName('DATANODE')
+      }));
+      if (this.isServiceSelected('MAPREDUCE')) {
+        headers.pushObject(Em.Object.create({
+          name:'TASKTRACKER',
+          label: self.getComponentDisplayName('TASKTRACKER')
+        }));
+      }
+      if (this.isServiceSelected('HBASE')) {
+        headers.pushObject(Em.Object.create({
+          name:'HBASE_REGIONSERVER',
+          label: self.getComponentDisplayName('HBASE_REGIONSERVER')
+        }));
+      }
+      headers.pushObject(Ember.Object.create({
+        name: 'CLIENT',
+        label: self.getComponentDisplayName('CLIENT')
+      }));
+    }
+
+    headers.forEach(function(header) {
+      header.setProperties({ allChecked: false, noChecked: true });
+    });
+
+    this.get('headers').pushObjects(headers);
+
+    this.render();
+    if (this.get('isMasters')) {
+      if(this.get('content.missMasterStep')) {
+        App.router.send('next');
+      }
+    }
+    else {
+      if(this.get('content.missSlavesStep')) {
+        App.router.send('next');
+      }
     }
   },
 
@@ -195,113 +256,101 @@ App.WizardStep6Controller = Em.Controller.extend({
    * Load all data needed for this module. Then it automatically renders in template
    * @return {Ember.Set}
    */
-  renderSlaveHosts: function () {
+  render: function () {
     var hostsObj = Em.Set.create();
     var allHosts = this.getHostNames();
-    // TODO - Hard coding should be removed.
-    var maxNoofHostComponents = 11;
-    var slaveComponents = this.get('content.slaveComponentHosts');
 
+    var self = this;
     allHosts.forEach(function (_hostName) {
-      hostsObj.push(Em.Object.create({
+
+      var obj = Em.Object.create({
         hostName: _hostName,
         isMaster: false,
-        isDataNode: false,
-        isTaskTracker: false,
-        isRegionServer: false,
-        isClient: false,
-        isDataNodeInstalled: false,
-        isTaskTrackerInstalled: false,
-        isRegionServerInstalled: false,
-        isClientInstalled: false
-      }));
-    });
-
-    if (!slaveComponents) { // we are at this page for the first time
-      if (allHosts.length > 3) {             //multiple nodes scenario
-        hostsObj.forEach(function (host) {
-          host.isMaster = this.hasMasterComponents(host.hostName);
-          host.isDataNode = host.isTaskTracker
-            = host.isRegionServer = !host.isMaster;
-        }, this);
-
-        if (hostsObj.someProperty('isDataNode', true)) {
-          hostsObj.findProperty('isDataNode', true).set('isClient', true);
-        }
-      } else {
-        var masterObj = {
-          host: null,
-          masterComponents: maxNoofHostComponents
-        };
-        hostsObj.forEach(function (host) {
-          host.isMaster = this.hasMasterComponents(host.hostName);
-          var countMasterComp = this.getMasterComponentsForHost(host.hostName).length;
-          if (countMasterComp <= masterObj.masterComponents) {
-            masterObj.masterComponents = countMasterComp;
-            masterObj.host = host;
-          }
-        }, this);
-        masterObj.host.set('isClient', true);
-        masterObj.host.set('isDataNode', true);
-        masterObj.host.set('isTaskTracker', true);
-        masterObj.host.set('isRegionServer', true);
-
-      }
-
-    } else {
+        checkboxes: []
+      });
 
-      var dataNodes = slaveComponents.findProperty('componentName', 'DATANODE');
-      dataNodes.hosts.forEach(function (_dataNode) {
-        var dataNode = hostsObj.findProperty('hostName', _dataNode.hostName);
-        if (dataNode) {
-          dataNode.set('isDataNode', true);
-          dataNode.set('isDataNodeInstalled', _dataNode.isInstalled);
-        }
+      self.get('headers').forEach(function(header) {
+        obj.checkboxes.pushObject(Em.Object.create({
+          title: header.label,
+          checked: false,
+          installed: false
+        }));
       });
 
-      if (this.get('isMrSelected')) {
-        var taskTrackers = slaveComponents.findProperty('componentName', 'TASKTRACKER');
-        taskTrackers.hosts.forEach(function (_taskTracker) {
-          var taskTracker = hostsObj.findProperty('hostName', _taskTracker.hostName);
-          if (taskTracker) {
-            taskTracker.set('isTaskTracker', true);
-            taskTracker.set('isTaskTrackerInstalled', _taskTracker.isInstalled);
-          }
-        });
-      }
+      hostsObj.push(obj);
+    });
 
-      if (this.get('isHbSelected')) {
-        var regionServers = slaveComponents.findProperty('componentName', 'HBASE_REGIONSERVER');
-        regionServers.hosts.forEach(function (_regionServer) {
-          var regionServer = hostsObj.findProperty('hostName', _regionServer.hostName);
-          if (regionServer) {
-            regionServer.set('isRegionServer', true);
-            regionServer.set('isRegionServerInstalled', _regionServer.isInstalled);
-          }
-        });
-      }
+    if (this.get('isMasters')) {
+      hostsObj = this.renderMasters(hostsObj);
+    }
+    else {
+      hostsObj = this.renderSlaves(hostsObj);
+    }
 
-      var clients = slaveComponents.findProperty('componentName', 'CLIENT');
-      clients.hosts.forEach(function (_client) {
-        var client = hostsObj.findProperty('hostName', _client.hostName);
-        if (client) {
-          client.set('isClient', true);
-          client.set('isClientInstalled', _client.isInstalled);
-        }
-      }, this);
+    hostsObj.forEach(function (host) {
+      this.get('hosts').pushObject(host);
+    }, this);
+    this.get('headers').forEach(function(header) {
+      self.checkCallback(header.get('label'));
+    });
+  },
 
+  /**
+   *
+   * @param hostsObj
+   * @return {*}
+   */
+  renderSlaves: function(hostsObj) {
+    var allHosts = this.getHostNames();
+    var slaveComponents = this.get('content.slaveComponentHosts');
+    if (!slaveComponents) { // we are at this page for the first time
+      hostsObj.forEach(function(host) {
+        host.isMaster = this.hasMasterComponents(host.hostName);
+      }, this);
+    }
+    else {
+      this.get('headers').forEach(function(header) {
+        var nodes = slaveComponents.findProperty('componentName', header.get('name'));
+        if (nodes) {
+          nodes.hosts.forEach(function (_node) {
+            var node = hostsObj.findProperty('hostName', _node.hostName);
+            if (node) {
+              node.get('checkboxes').findProperty('title', header.get('label')).set('checked', true);
+              node.get('checkboxes').findProperty('title', header.get('label')).set('installed', _node.isInstalled);
+            }
+          });
+        }
+      });
       allHosts.forEach(function (_hostname) {
         var host = hostsObj.findProperty('hostName', _hostname);
         if (host) {
           host.set('isMaster', this.hasMasterComponents(_hostname));
         }
       }, this);
+    }
+    return hostsObj;
+  },
 
+  /**
+   *
+   * @param hostsObj
+   * @return {*}
+   */
+  renderMasters: function(hostsObj) {
+    var self = this;
+    var masterComponentHosts = this.get('content.masterComponentHosts');
+    console.warn('masterComponentHosts', masterComponentHosts);
+
+    if (masterComponentHosts) {
+      masterComponentHosts.forEach(function(item) {
+        var host = hostsObj.findProperty('hostName', item.hostName);
+        if (host) {
+          host.get('checkboxes').findProperty('title', item.display_name).set('checked', true);
+        }
+      });
     }
 
-    hostsObj.forEach(function (host) {
-      this.get('hosts').pushObject(host);
-    }, this);
+    return hostsObj;
   },
 
   /**
@@ -321,22 +370,33 @@ App.WizardStep6Controller = Em.Controller.extend({
   validate: function () {
     var isError = false;
     var hosts = this.get('hosts');
-    if(this.get('isAddHostWizard')){
-      for(var i = 0; i < hosts.length; i++){
-        isError = !(hosts[i].get('isDataNode') || hosts[i].get('isClient')
-          || ( this.get('isMrSelected') && hosts[i].get('isTaskTracker'))
-          || ( this.get('isHbSelected') && hosts[i].get('isRegionServer')));
+    var headers = this.get('headers');
+    if(this.get('isAddHostWizard')) {
+      for(var i = 0; i < hosts.length; i++) {
+        var checkboxes = hosts[i].get('checkboxes');
+        isError = false;
+        headers.forEach(function(header) {
+          isError |= checkboxes.findProperty('title', header.get('label')).checked;
+        });
+        isError = !isError;
         if (isError) {
-          this.set('errorMessage', Ember.I18n.t('installer.step6.error.mustSelectOneForHost'));
+          this.set('errorMessage', Em.I18n.t('installer.step6.error.mustSelectOneForHost'));
           break;
         }
       }
-    } else {
-      isError = this.get('isNoDataNodes') || this.get('isNoClients')
-        || ( this.get('isMrSelected') && this.get('isNoTaskTrackers'))
-        || ( this.get('isHbSelected') && this.get('isNoRegionServers'));
+    }
+    else {
+
+      headers.forEach(function(header) {
+        var all_false = true;
+        hosts.forEach(function(host) {
+          var checkboxes = host.get('checkboxes');
+          all_false = all_false && !checkboxes.findProperty('title', header.get('label')).checked;
+        });
+        isError = isError || all_false;
+      });
       if (isError) {
-        this.set('errorMessage', Ember.I18n.t('installer.step6.error.mustSelectOne'));
+        this.set('errorMessage', Em.I18n.t('installer.step6.error.mustSelectOne'));
       }
     }
 

+ 2 - 2
ambari-web/app/controllers/wizard/step8_controller.js

@@ -494,7 +494,7 @@ App.WizardStep8Controller = Em.Controller.extend({
   },
 
   loadDnValue: function (dnComponent) {
-    var dnHosts = this.get('content.slaveComponentHosts').findProperty('displayName', 'DataNode');
+    var dnHosts = this.get('content.slaveComponentHosts').findProperty('displayName', 'Datanode');
     var totalDnHosts = dnHosts.hosts.length;
     /* var totalGroups = this.get('slaveComponentConfig.components').findProperty('componentName', 'DATANODE').groups.length;
      var groupLabel;
@@ -616,7 +616,7 @@ App.WizardStep8Controller = Em.Controller.extend({
   },
 
   loadRegionServerValue: function (rsComponent) {
-    var rsHosts = this.get('content.slaveComponentHosts').findProperty('displayName', 'RegionServer');
+    var rsHosts = this.get('content.slaveComponentHosts').findProperty('displayName', 'HBaseRegionServer');
     var totalRsHosts = rsHosts.hosts.length;
     /* var totalGroups = this.get('slaveComponentConfig.components').findProperty('componentName', 'HBASE_REGIONSERVER').groups.length;
      var groupLabel;

+ 8 - 0
ambari-web/app/data/service_components.js

@@ -249,5 +249,13 @@ module.exports = new Ember.Set([
     isMaster: false,
     isClient: true,
     description: ''
+  },
+  {
+    service_name: 'CLIENT',
+    component_name: 'CLIENT',
+    display_name: 'Client',
+    isMaster: false,
+    isClient: true,
+    description: ''
   }
 ]);

+ 3 - 4
ambari-web/app/messages.js

@@ -61,6 +61,8 @@ Em.I18n.translations = {
   'common.version':'Version',
   'common.description':'Description',
   'common.client':'Client',
+  'common.zookeeper':'ZooKeeper',
+  'common.hbase':'HBase',
   'common.regionServer':'RegionServer',
   'common.taskTracker':'TaskTracker',
   'common.dataNode':'DataNode',
@@ -300,6 +302,7 @@ Em.I18n.translations = {
   'installer.step6.error.mustSelectOne':'You must assign at least one host to each.',
   'installer.step6.error.mustSelectOneForHost':'You must assign at least one slave/client component to each.',
   'installer.step6.wizardStep6Host.title':'master components hosted on {0}',
+  'installer.step6.addHostWizard.body':'Assign HBase master and ZooKeeper server.',
 
   'installer.step7.header':'Customize Services',
   'installer.step7.body':'We have come up with recommended configurations for the services you selected. Customize them as you see fit.',
@@ -744,10 +747,6 @@ Em.I18n.translations = {
   'hosts.host.healthStatusCategory.red': "Master Down",
   'hosts.host.healthStatusCategory.orange': "Slave Down",
   'hosts.host.healthStatusCategory.yellow': "No Heartbeat",
-  'hosts.decommission.popup.body':'Are you sure?',
-  'hosts.decommission.popup.header':'Confirmation',
-  'hosts.delete.popup.body':'Are you sure?',
-  'hosts.delete.popup.header':'Confirmation',
   'hosts.cant.do.popup.header':'Operation not allowed',
   'hosts.cant.do.popup.masterList.body':'You cannot delete this host because it is hosting following master services: {0}.',
   'hosts.cant.do.popup.workingList.body':'You cannot delete this host because following slave services are not fully stopped or decommissioned: {0}.',

+ 65 - 23
ambari-web/app/routes/add_host_routes.js

@@ -55,16 +55,16 @@ module.exports = Em.Route.extend({
       if (currentClusterStatus) {
         switch (currentClusterStatus.clusterState) {
           case 'ADD_HOSTS_DEPLOY_PREP_2' :
-            addHostController.setCurrentStep('4');
+            addHostController.setCurrentStep('5');
             App.db.data = currentClusterStatus.localdb;
             break;
           case 'ADD_HOSTS_INSTALLING_3' :
           case 'SERVICE_STARTING_3' :
-            addHostController.setCurrentStep('5');
+            addHostController.setCurrentStep('6');
             App.db.data = currentClusterStatus.localdb;
             break;
           case 'ADD_HOSTS_INSTALLED_4' :
-            addHostController.setCurrentStep('6');
+            addHostController.setCurrentStep('7');
             App.db.data = currentClusterStatus.localdb;
             break;
           default:
@@ -161,16 +161,58 @@ module.exports = Em.Route.extend({
 
   step3: Em.Route.extend({
     route: '/step3',
-    connectOutlets: function (router, context) {
+    connectOutlets: function (router) {
       console.log('in addHost.step3:connectOutlets');
       var controller = router.get('addHostController');
       controller.setCurrentStep('3');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         controller.connectOutlet('wizardStep6', controller.get('content'));
+        var wizardStep6Controller = router.get('wizardStep6Controller');
+        wizardStep6Controller.set('isMasters', true);
       })
     },
-    back: Em.Router.transitionTo('step2'),
+    back: function(router) {
+      var controller = router.get('addHostController');
+      if(!controller.get('content.missMasterStep')) {
+        router.transitionTo('step2');
+      }
+      else {
+        router.transitionTo('step1');
+      }
+    },
+    next: function (router, context) {
+      var addHostController = router.get('addHostController');
+      var wizardStep6Controller = router.get('wizardStep6Controller');
+      addHostController.saveHbZk(wizardStep6Controller);
+      App.db.setBootStatus(true);
+      addHostController.loadServicesFromServer();
+      router.transitionTo('step4');
+    }
+  }),
+
+  step4: Em.Route.extend({
+    route: '/step4',
+    connectOutlets: function (router, context) {
+      console.log('in addHost.step4:connectOutlets');
+      var controller = router.get('addHostController');
+      controller.setCurrentStep('4');
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep6', controller.get('content'));
+        var wizardStep6Controller = router.get('wizardStep6Controller');
+        wizardStep6Controller.set('isMasters', false);
+      });
+    },
+    back: function(router) {
+      var controller = router.get('addHostController');
+      if(!controller.get('content.missMasterStep')) {
+        router.transitionTo('step2');
+      }
+      else {
+        router.transitionTo('step1');
+      }
+    },
     next: function (router) {
       var addHostController = router.get('addHostController');
       var wizardStep6Controller = router.get('wizardStep6Controller');
@@ -184,23 +226,23 @@ module.exports = Em.Route.extend({
         wizardStep7Controller.set('content', addHostController.get('content'));
         wizardStep7Controller.loadStep();
         addHostController.saveServiceConfigProperties(wizardStep7Controller);
-        router.transitionTo('step4');
+        router.transitionTo('step5');
       }
     }
   }),
 
-  step4: Em.Route.extend({
-    route: '/step4',
+  step5: Em.Route.extend({
+    route: '/step5',
     connectOutlets: function (router, context) {
-      console.log('in addHost.step4:connectOutlets');
+      console.log('in addHost.step5:connectOutlets');
       var controller = router.get('addHostController');
-      controller.setCurrentStep('4');
+      controller.setCurrentStep('5');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         controller.connectOutlet('wizardStep8', controller.get('content'));
       })
     },
-    back: Em.Router.transitionTo('step3'),
+    back: Em.Router.transitionTo('step4'),
     next: function (router) {
       var addHostController = router.get('addHostController');
       var wizardStep8Controller = router.get('wizardStep8Controller');
@@ -215,25 +257,25 @@ module.exports = Em.Route.extend({
         localdb: App.db.data
       });
       wizardStep8Controller.set('servicesInstalled', true);
-      router.transitionTo('step5');
+      router.transitionTo('step6');
     }
   }),
 
-  step5: Em.Route.extend({
-    route: '/step5',
+  step6: Em.Route.extend({
+    route: '/step6',
     connectOutlets: function (router, context) {
-      console.log('in addHost.step5:connectOutlets');
+      console.log('in addHost.step6:connectOutlets');
       var controller = router.get('addHostController');
-      controller.setCurrentStep('5');
+      controller.setCurrentStep('6');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         if (!App.testMode) {              //if test mode is ON don't disable prior steps link.
-          controller.setLowerStepsDisable(5);
+          controller.setLowerStepsDisable(6);
         }
         controller.connectOutlet('wizardStep9', controller.get('content'));
       })
     },
-    back: Em.Router.transitionTo('step4'),
+    back: Em.Router.transitionTo('step5'),
     retry: function(router,context) {
       var addHostController = router.get('addHostController');
       var wizardStep9Controller = router.get('wizardStep9Controller');
@@ -269,12 +311,12 @@ module.exports = Em.Route.extend({
         localdb: App.db.data
       });
 
-      router.transitionTo('step6');
+      router.transitionTo('step7');
     }
   }),
 
-  step6: Em.Route.extend({
-    route: '/step6',
+  step7: Em.Route.extend({
+    route: '/step7',
     connectOutlets: function (router, context) {
       console.log('in addHost.step6:connectOutlets');
       var controller = router.get('addHostController');
@@ -282,12 +324,12 @@ module.exports = Em.Route.extend({
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         if (!App.testMode) {              //if test mode is ON don't disable prior steps link.
-          controller.setLowerStepsDisable(6);
+          controller.setLowerStepsDisable(7);
         }
         controller.connectOutlet('wizardStep10', controller.get('content'));
       })
     },
-    back: Em.Router.transitionTo('step5'),
+    back: Em.Router.transitionTo('step6'),
     complete: function (router, context) {
       if (true) {   // this function will be moved to installerController where it will validate
         var addHostController = router.get('addHostController');

+ 5 - 4
ambari-web/app/templates/main/host/add.hbs

@@ -30,10 +30,11 @@
               <li class="nav-header">{{t hosts.add.header}}</li>
               <li {{bindAttr class="isStep1:active view.isStep1Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep1 target="controller"}}>{{t installer.step2.header}}</a></li>
               <li {{bindAttr class="isStep2:active view.isStep2Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep2 target="controller"}}>{{t installer.step3.header}}</a></li>
-              <li {{bindAttr class="isStep3:active view.isStep3Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep3 target="controller"}}>{{t installer.step6.header}}</a></li>
-              <li {{bindAttr class="isStep4:active view.isStep4Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep4 target="controller"}}>{{t installer.step8.header}}</a></li>
-              <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t installer.step9.header}}</a></li>
-              <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}>{{t installer.step10.header}}</a></li>
+              <li {{bindAttr class="isStep3:active view.isStep3Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep3 target="controller"}}>{{t installer.step5.header}}</a></li>
+              <li {{bindAttr class="isStep4:active view.isStep4Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep4 target="controller"}}>{{t installer.step6.header}}</a></li>
+              <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t installer.step8.header}}</a></li>
+              <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}>{{t installer.step9.header}}</a></li>
+              <li {{bindAttr class="isStep7:active view.isStep7Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep7 target="controller"}}>{{t installer.step10.header}}</a></li>
             </ul>
           </div>
         </div>

+ 34 - 58
ambari-web/app/templates/wizard/step6.hbs

@@ -17,72 +17,48 @@
 }}
 
 <div id="step6">
-  <h2>{{t installer.step6.header}}</h2>
-
+  <h2>{{view.title}}</h2>
   <div class="alert alert-info">{{{view.label}}}</div>
   {{#if errorMessage}}
-  <div class="alert alert-error">{{errorMessage}}</div>
+    <div class="alert alert-error">{{errorMessage}}</div>
   {{/if}}
 
   <div class="pre-scrollable">
-  <table class="table table-striped">
-    <thead>
-      <tr>
-        <th>{{t common.host}}</th>
-        <th>
-          <a href="#" {{bindAttr class="isAllDataNodes:selected:deselected"}}
-            {{action selectAllDataNodes target="controller"}}>{{t all}}</a> |
-
-          <a href="#" {{bindAttr class="isNoDataNodes:selected:deselected"}}
-            {{action deselectAllDataNodes target="controller"}}>{{t none}}</a>
-        </th>
-      {{#if controller.isMrSelected}}
-        <th>
-          <a href="#" {{bindAttr class="isAllTaskTrackers:selected:deselected"}}
-            {{action selectAllTaskTrackers target="controller"}}>{{t all}}</a> |
+    <table class="table table-striped">
+      <thead>
+        <tr>
+          <th>{{t common.host}}</th>
+          {{#each header in controller.headers}}
 
-          <a href="#" {{bindAttr class="isNoTaskTrackers:selected:deselected"}}
-            {{action deselectAllTaskTrackers target="controller"}}>{{t none}}</a>
-        </th>
-      {{/if}}
-      {{#if controller.isHbSelected}}
-        <th>
-          <a href="#" {{bindAttr class="isAllRegionServers:selected:deselected"}}
-            {{action selectAllRegionServers target="controller"}}>{{t all}}</a> |
+              <th>
+                <a href="#" {{bindAttr class="header.allChecked:selected:deselected"}}
+                  {{action "selectAllNodes" header target="controller"}}>{{t all}}</a> |
 
-          <a href="#" {{bindAttr class="isNoRegionServers:selected:deselected"}}
-            {{action deselectAllRegionServers target="controller"}}>{{t none}}</a>
-        </th>
-      {{/if}}
-        <th>
-          <a href="#" {{bindAttr class="isAllClients:selected:deselected"}} {{action selectAllClients target="controller"}}>{{t all}}</a>
-          |
-          <a href="#" {{bindAttr class="isNoClients:selected:deselected"}} {{action deselectAllClients target="controller"}}>{{t none}}</a>
-        </th>
-      </tr>
-    </thead>
-    <tbody>
+                <a href="#" {{bindAttr class="header.noChecked:selected:deselected"}}
+                  {{action "deselectAllNodes" header target="controller"}}>{{t none}}</a>
+              </th>
 
-    {{#each hosts}}
-    <tr>
-        {{#view App.WizardStep6HostView hostBinding = "this" }}
-          {{hostName}}
-          {{#if isMaster}}
-            <i class=icon-asterisks>&#10037</i>
-          {{/if}}
-        {{/view}}
-      <td><label class="checkbox">{{view Ember.Checkbox disabledBinding="isDataNodeInstalled" checkedBinding="isDataNode"}}{{t common.dataNode}}</label></td>
-      {{#if controller.isMrSelected}}
-      <td><label class="checkbox">{{view Ember.Checkbox disabledBinding="isTaskTrackerInstalled" checkedBinding="isTaskTracker"}}{{t common.taskTracker}}</label></td>
-      {{/if}}
-      {{#if controller.isHbSelected}}
-      <td><label class="checkbox">{{view Ember.Checkbox disabledBinding="isRegionServerInstalled" checkedBinding="isRegionServer"}}{{t common.regionServer}}</label></td>
-      {{/if}}
-      <td><label class="checkbox">{{view Ember.Checkbox disabledBinding="isClientInstalled" checkedBinding="isClient"}}{{t common.client}}</label></td>
-    </tr>
-    {{/each}}
-    </tbody>
-  </table>
+          {{/each}}
+        </tr>
+      </thead>
+      <tbody>
+        {{#each host in hosts}}
+          <tr>
+            {{#view App.WizardStep6HostView hostBinding="host" }}
+              {{host.hostName}}
+              {{#if host.isMaster}}
+                <i class=icon-asterisks>&#10037</i>
+              {{/if}}
+            {{/view}}
+            {{#each checkbox in host.checkboxes}}
+              <td>
+                  <label class="checkbox">{{view App.WizardStep6CheckboxView checkboxBinding="checkbox"}}</label>
+              </td>
+            {{/each}}
+          </tr>
+        {{/each}}
+      </tbody>
+    </table>
   </div>
   <div class="btn-area">
     <a class="btn" {{action back}}>&larr; {{t common.back}}</a>

+ 60 - 20
ambari-web/app/views/wizard/step6_view.js

@@ -23,9 +23,18 @@ App.WizardStep6View = Em.View.extend({
 
   templateName: require('templates/wizard/step6'),
 
+  title: '',
+
   didInsertElement: function () {
     var controller = this.get('controller');
-    this.setLabel();
+    if (controller.get('isMasters')) {
+      this.set('label', Em.I18n.t('installer.step6.addHostWizard.body'));
+      this.set('title', Em.I18n.t('installer.step5.header'));
+    }
+    else {
+      this.set('title', Em.I18n.t('installer.step6.header'));
+      this.setLabel();
+    }
     $('body').tooltip({
       selector: '[rel=tooltip]'
     });
@@ -38,14 +47,17 @@ App.WizardStep6View = Em.View.extend({
     clients.forEach(function (_client) {
       if (clients.length === 1) {
         label = label + ' ' + _client.display_name;
-      } else if (_client !== clients[clients.length - 1]) {           // [clients.length - 1]
-        label = label + ' ' + _client.display_name;
-        if(_client !== clients[clients.length - 2]) {
-          label = label + ',';
-  }
-      } else {
-        label = label + ' ' + Em.I18n.t('and') + ' ' + _client.display_name + '.';
       }
+      else
+        if (_client !== clients[clients.length - 1]) {           // [clients.length - 1]
+          label = label + ' ' + _client.display_name;
+          if(_client !== clients[clients.length - 2]) {
+            label = label + ',';
+          }
+        }
+        else {
+          label = label + ' ' + Em.I18n.t('and') + ' ' + _client.display_name + '.';
+        }
     }, this);
     this.set('label', label);
   }
@@ -57,18 +69,46 @@ App.WizardStep6HostView = Em.View.extend({
   tagName: 'td',
 
   didInsertElement: function () {
-    var components = this.get('controller').getMasterComponentsForHost(this.get('host.hostName'));
-    if (components && components.length > 0) {
-      components = components.map(function(_component) {
-        return App.format.role(_component);
-      });
-      components = components.join(" /\n");
-      this.$().popover({
-        title: Em.I18n.t('installer.step6.wizardStep6Host.title').format(this.get('host.hostName')),
-        content: components,
-        placement: 'right',
-        trigger: 'hover'
-      });
+    if (!this.get('controller.isMasters')) {
+      var components = this.get('controller').getMasterComponentsForHost(this.get('host.hostName'));
+      if (components && components.length > 0) {
+        components = components.map(function(_component) {
+          return App.format.role(_component);
+        });
+        components = components.join(" /\n");
+        this.$().popover({
+          title: Em.I18n.t('installer.step6.wizardStep6Host.title').format(this.get('host.hostName')),
+          content: components,
+          placement: 'right',
+          trigger: 'hover'
+        });
+      }
     }
   }
 });
+
+/**
+ * Binding host property with dynamic name
+ * @type {*}
+ */
+App.WizardStep6CheckboxView = Em.Checkbox.extend({
+  /**
+   * Header object with host property name
+   */
+  checkbox: null,
+
+  checkedBinding: 'checkbox.checked',
+
+  disabledBinding: 'checkbox.disabled',
+
+  checkCallback: function() {
+    var self = this;
+    Ember.run.next(function(){
+      self.get('controller').checkCallback(self.get('checkbox.title'));
+    });
+
+  }.observes('checked'),
+
+  template: Ember.Handlebars.compile('{{checkbox.title}}')
+
+});