瀏覽代碼

AMBARI-769. Implement step 9 (Install,start and test) of installer wizard. (Jaimin Jetly via yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1390727 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 年之前
父節點
當前提交
8f367128da

+ 3 - 0
AMBARI-666-CHANGES.txt

@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
   NEW FEATURES
 
+  AMBARI-769. Implement step 9 (Install,start and test) of installer wizard.
+  (Jaimin Jetly via yusaku)
+
   AMBARI-768. Implement step 5 of installer wizard (Assign Masters).
   (Ananya Sen via yusaku)
 

+ 1 - 0
ambari-web/app/controllers.js

@@ -29,6 +29,7 @@ require('controllers/installer/step4_controller');
 require('controllers/installer/step5_controller');
 require('controllers/installer/step6_controller');
 require('controllers/installer/step7_controller');
+require('controllers/installer/step9_controller');
 require('controllers/main');
 require('controllers/main/service');
 require('controllers/main/service/item');

+ 21 - 20
ambari-web/app/controllers/installer/step3_controller.js

@@ -44,13 +44,12 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
     var hostInfo = [];
     hostInfo = App.db.getHosts();
     var hosts = new Ember.Set();
-    for (var index in hostInfo) {
+    for( var index in hostInfo) {
       hostInfo[index].status = "pending";
       hosts.add(hostInfo[index]);
+      console.log("TRACE: host name is: " + hostInfo[index].name);
     }
-    hosts.forEach(function (_host) {
-      console.log("TRACE: host name is: " + _host.name);
-    });
+
     return hosts;
   },
 
@@ -59,11 +58,10 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
     var self = this;
     hostsInfo.forEach(function (_hostInfo) {
       var hostInfo = App.HostInfo.create({
-        hostName: _hostInfo.name,
-        status: _hostInfo.status
+        name: _hostInfo.name
       });
 
-      console.log('pushing ' + hostInfo.hostName);
+      console.log('pushing ' + hostInfo.name);
       self.content.pushObject(hostInfo);
     });
   },
@@ -75,7 +73,7 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
   parseHostInfo: function (hostsFrmServer, hostsFrmContent) {
     var result = true;                    // default value as true implies if the data rendered by REST API has no hosts, polling will stop
     hostsFrmServer.forEach(function (_hostFrmServer) {
-      var host = hostsFrmContent.findProperty('hostName', _hostFrmServer.name);
+      var host = hostsFrmContent.findProperty('name', _hostFrmServer.name);
       if (host !== null && host !== undefined) { // check if hostname extracted from REST API data matches any hostname in content
         host.set('status', _hostFrmServer.status);
       }
@@ -112,7 +110,7 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
     var hosts = this.visibleHosts();
     var selectedHosts = hosts.filterProperty('isChecked', true);
     selectedHosts.forEach(function (_host) {
-      console.log('Retrying:  ' + _host.hostName);
+      console.log('Retrying:  ' + _host.name);
     });
 
     //TODO: uncomment below code to hookup with @GET bootstrap API
@@ -129,24 +127,22 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
     this.parseHostInfo(mockHosts, selectedHosts);
   },
 
+  removeHosts: function (hosts) {
+    App.db.removeHosts(hosts);
+    this.removeObjects(hosts);
+  },
 
-  /* Below function checks the category selected and then collects all the
-   selected hosts in that category and deletes them from localStorage,
-   in-memory data structure and controller content
-   */
-
-  /* Removes set of selected visisble hosts on the remove button click */
-  removeHosts: function () {
+  removeBtn: function() {
     if (this.get('isSubmitDisabled')) {
       return;
     }
     var hostResult = this.visibleHosts();
     var selectedHosts = hostResult.filterProperty('isChecked', true);
     selectedHosts.forEach(function (_hostInfo) {
-      console.log('Removing:  ' + _hostInfo.hostName);
+      console.log('Removing:  ' + _hostInfo.name);
     });
-    App.db.removeHosts(selectedHosts);
-    this.removeObjects(selectedHosts);
+
+    this.removeHosts(selectedHosts);
   },
 
 
@@ -181,7 +177,6 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
       statusCode: {
         404: function () {
           console.log("URI not found.");
-          //alert("URI not found");
         }
       },
 
@@ -197,12 +192,18 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
 
   submit: function () {
     if (!this.get('isSubmitDisabled')) {
+      var erroneousHosts = this.filterProperty('status', 'error');
+      console.log("INFO: Removing erroneous hosts before moving to step4");
+      this.removeHosts(erroneousHosts);
       App.get('router').transitionTo('step4');
     }
   },
   hostLogPopup: function (event) {
     App.ModalPopup.show({
       header: Em.I18n.t('installer.step3.hostLog.popup.header'),
+      onPrimary: function () {
+        this.hide();
+      },
       bodyClass: Ember.View.extend({
         templateName: require('templates/installer/step3HostLogPopup')
       })

+ 283 - 0
ambari-web/app/controllers/installer/step9_controller.js

@@ -0,0 +1,283 @@
+/**
+ * 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');
+
+App.InstallerStep9Controller = Em.ArrayController.extend({
+  name: 'installerStep9Controller',
+  content: [],
+  progress: '0',
+  // result: 'pending', // val = pending or success or failed
+  isStepCompleted: false,
+  isSubmitDisabled: function() {
+    return !this.get('isStepCompleted');
+  }.property('isStepCompleted'),
+
+  status: 'info',
+  mockHostData: require('data/mock/step9_hosts'),
+  pollData_1: require('data/mock/step9_pollData_1'),
+  pollData_2: require('data/mock/step9_pollData_2'),
+  pollDataCounter: 0,
+
+  loadHosts: function () {
+    var hostInfo = [];
+    hostInfo = App.db.getHosts();
+    var hosts = new Ember.Set();
+    for (var index in hostInfo) {
+      hostInfo[index].status = "pending";
+      hosts.add(hostInfo[index]);
+      console.log("TRACE: host name is: " + hostInfo[index].name);
+    }
+    return hosts;
+  },
+
+  renderHosts: function (hostsInfo) {
+    var self = this;
+    hostsInfo.forEach(function (_hostInfo) {
+      var hostInfo = App.HostInfo.create({
+        name: _hostInfo.name,
+        message: _hostInfo.message,
+        progress: '0'
+      });
+
+      console.log('pushing ' + hostInfo.name);
+      self.content.pushObject(hostInfo);
+    });
+  },
+
+  onSuccessPerHost: function (actions, contentHost) {
+    if (actions.everyProperty('status', 'completed')) {
+      contentHost.set('status', 'success');
+    }
+  },
+
+  onWarningPerHost: function (actions, contentHost) {
+    if (actions.findProperty('status', 'failed') || actions.findProperty('status', 'aborted')) {
+      contentHost.set('status', 'warning');
+      this.set('status', 'warning');
+    }
+  },
+
+  onInProgressPerHost: function (actions, contentHost) {
+    var runningAction = actions.findProperty('status', 'inprogress');
+    if (runningAction !== null && runningAction !== undefined) {
+      contentHost.set('message', runningAction.message);
+    }
+  },
+
+  progressPerHost: function (actions, contentHost) {
+    var totalProgress = 0;
+    var actionsPerHost = actions.length;
+    var completedActions = actions.filterProperty('status', 'completed').length
+      + actions.filterProperty('status', 'failed').length +
+      actions.filterProperty('status', 'aborted').length;
+    var progress = (completedActions / actionsPerHost) * 100;
+    console.log('INFO: progressPerHost is: ' + progress);
+    contentHost.set('progress', progress.toString());
+    return progress;
+  },
+
+  isSuccess: function (polledData) {
+    return polledData.everyProperty('status', 'success');
+  },
+
+  isStepFailed: function (polledData) {
+    var self = this;
+    var result = false;
+    polledData.forEach(function (_polledData) {
+      var successFactor = _polledData.sf;
+      var actionsPerRole = polledData.filterProperty('role', _polledData.role);
+      var actionsFailed = actionsPerRole.filterProperty('status', 'failed');
+      var actionsAborted = actionsPerRole.filterProperty('status', 'aborted');
+      if ((((actionsFailed.length + actionsAborted.length) / actionsPerRole.length) * 100) <= successFactor) {
+        console.log('TRACE: Entering success factor and result is failed');
+        result = true;
+      }
+    });
+    return result;
+  },
+
+
+  getFailedHostsForFailedRoles: function(polledData) {
+    var hostArr = new Ember.Set();
+    polledData.forEach(function (_polledData) {
+      var successFactor = _polledData.sf;
+      var actionsPerRole = polledData.filterProperty('role', _polledData.role);
+      var actionsFailed = actionsPerRole.filterProperty('status', 'failed');
+      var actionsAborted = actionsPerRole.filterProperty('status', 'aborted');
+      if ((((actionsFailed.length + actionsAborted.length) / actionsPerRole.length) * 100) <= successFactor) {
+        actionsFailed.forEach(function(_actionFailed) {
+          hostArr.add(_actionFailed.name);
+        });
+        actionsAborted.forEach(function(_actionFailed) {
+          hostArr.add(_actionFailed.name);
+        });
+      }
+    });
+    return hostArr;
+  },
+
+  setHostsStatus: function(hosts,status) {
+    var self = this;
+    hosts.forEach(function(_host){
+      var host = self.findProperty('name',_host);
+      host.set('status',status);
+    });
+  },
+
+  // polling from ui stops only when no action has 'pending', 'queued' or 'inprogress' status
+
+  finishStep: function (polledData) {
+    var self = this;
+    if (!polledData.someProperty('status', 'pending') && !polledData.someProperty('status', 'queued') && !polledData.someProperty('status', 'inprogress')) {
+      this.set('progress', '100');
+      if (this.isSuccess(polledData)) {
+        this.set('status', 'success');
+      } else {
+        if (this.isStepFailed(polledData)) {
+          self.set('status', 'failed');
+          this.setHostsStatus(this.getFailedHostsForFailedRoles(polledData),'failed');
+        }
+      }
+      this.set('isStepCompleted',true);
+    }
+  },
+
+
+  parseHostInfo: function (polledData) {
+    console.log('TRACE: Entering host info function');
+    var self = this;
+    var result = false;
+    var totalProgress = 0;
+    this.forEach(function (_content) {
+      var actions = polledData.filterProperty('name', _content.name);
+      if(actions.length === 0) {
+        alert('For testing with mockData follow the sequence: hit referesh,"mockData btn", "pollData btn", again "pollData btn"');
+        //exit();
+      }
+      if (actions !== null && actions !== undefined && actions.length !== 0) {
+        self.onSuccessPerHost(actions, _content);    // every action should be a success
+        self.onWarningPerHost(actions, _content);    // any action should be a faliure
+        self.onInProgressPerHost(actions, _content); // current running action for a host
+        totalProgress = totalProgress + self.progressPerHost(actions, _content);
+      }
+    });
+    totalProgress = totalProgress / this.content.length;
+    this.set('progress', totalProgress.toString());
+    console.log("INFO: right now the progress is: " + this.get('progress'));
+    this.finishStep(polledData);
+    return this.get('isStepCompleted');
+  },
+
+
+  retry: function () {
+    if (this.get('isSubmitDisabled')) {
+      return;
+    }
+    this.clear();
+    this.renderHosts(this.loadHosts());
+    //this.startPolling();
+  },
+
+  startPolling: function () {
+    this.set('isSubmitDisabled', true);
+    this.doPolling();
+  },
+
+  doPolling: function () {
+    var self = this;
+    $.ajax({
+      type: 'GET',
+      url: '/ambari_server/api/polling',
+      async: false,
+      timeout: 5000,
+      success: function (data) {
+        console.log("TRACE: In success function for the GET bootstrap call");
+        var result = self.parseHostInfo(data);
+        if (result !== true) {
+          window.setTimeout(self.doPolling, 3000);
+        } else {
+          self.stopPolling();
+        }
+      },
+
+      error: function () {
+        console.log("ERROR");
+        self.stopPolling();
+      },
+
+      statusCode: {
+        404: function () {
+          console.log("URI not found.");
+        }
+      },
+
+      dataType: 'application/json'
+    });
+
+  },
+
+  stopPolling: function () {
+    //TODO: uncomment following line after the hook up with the API call
+    // this.set('isStepCompleted',true);
+  },
+
+  submit: function () {
+    if (!this.get('isSubmitDisabled')) {
+      //var erroneousHosts = hostResult.filterProperty('status', 'error');
+
+      App.get('router').transitionTo('step4');
+    }
+  },
+
+  hostLogPopup: function (event) {
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step3.hostLog.popup.header'),
+      onPrimary: function () {
+        this.hide();
+      },
+      bodyClass: Ember.View.extend({
+        templateName: require('templates/installer/step3HostLogPopup')
+      })
+    });
+  },
+  mockBtn: function () {
+    this.set('isSubmitDisabled', false);
+    this.clear();
+    var hostInfo = this.mockHostData;
+    this.renderHosts(hostInfo);
+
+  },
+  pollBtn: function () {
+    this.set('isSubmitDisabled', false);
+    var data1 = this.pollData_1;
+    var data2 = this.pollData_2;
+    if ((this.get('pollDataCounter') / 2) === 0) {
+      console.log("TRACE: In pollBtn function data1");
+      var counter = parseInt(this.get('pollDataCounter')) + 1;
+      this.set('pollDataCounter', counter.toString());
+      this.parseHostInfo(data1);
+    } else {
+      console.log("TRACE: In pollBtn function data2");
+      var counter = parseInt(this.get('pollDataCounter')) + 1;
+      this.set('pollDataCounter', counter.toString());
+      this.parseHostInfo(data2);
+    }
+
+  }
+
+});

+ 82 - 0
ambari-web/app/data/mock/step9_hosts.js

@@ -0,0 +1,82 @@
+/**
+ * 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.
+ */
+
+
+
+module.exports = new Ember.Set([
+  {
+    name: '192.168.1.1',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.2',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.3',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.4',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.5',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.6',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.7',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.8',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.9',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.10',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.11',
+    progress: '0',
+    message: 'starting'
+  },
+  {
+    name: '192.168.1.12',
+    progress: '0',
+    message: 'starting'
+  }
+]);

+ 310 - 0
ambari-web/app/data/mock/step9_pollData_1.js

@@ -0,0 +1,310 @@
+/**
+ * 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.
+ */
+
+
+
+module.exports = new Ember.Set([
+  {
+    actionId: '1',
+    name: '192.168.1.1',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '2',
+    name: '192.168.1.2',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '3',
+    name: '192.168.1.3',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '4',
+    name: '192.168.1.4',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '5',
+    name: '192.168.1.5',
+    status: 'queued',
+    sf: '100',
+    role: 'DataNode',
+    message: 'action is queued'
+  },
+  {
+    actionId: '6',
+    name: '192.168.1.6',
+    status: 'queued',
+    sf: '100',
+    role: 'jobTracker',
+    message: 'action is queued'
+  },
+  {
+    actionId: '7',
+    name: '192.168.1.7',
+    status: 'queued',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is queued'
+  },
+  {
+    actionId: '8',
+    name: '192.168.1.8',
+    status: 'pending',
+    sf: '100',
+    role: 'NameNode',
+    message: 'not started'
+  },
+  {
+    actionId: '9',
+    name: '192.168.1.9',
+    status: 'pending',
+    sf: '100',
+    role: 'RegionServer',
+    message: 'not started'
+  },
+  {
+    actionId: '10',
+    name: '192.168.1.10',
+    status: 'queued',
+    sf: '100',
+    role: 'DataNode',
+    message: 'action is queued'
+  },
+  {
+    actionId: '11',
+    name: '192.168.1.11',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '12',
+    name: '192.168.1.12',
+    status: 'pending',
+    sf: '100',
+    role: 'DataNode',
+    message: 'not started'
+  },
+  {
+    actionId: '13',
+    name: '192.168.1.1',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'taskTracker installation completed'
+  },
+  {
+    actionId: '14',
+    name: '192.168.1.2',
+    status: 'queued',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is queued'
+  },
+  {
+    actionId: '15',
+    name: '192.168.1.3',
+    status: 'inprogress',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'tasktracker is being installed'
+  },
+  {
+    actionId: '16',
+    name: '192.168.1.4',
+    status: 'inprogress',
+    role: 'taskTracker',
+    sf: '100',
+    message: 'tasktracker is being installed'
+  },
+  {
+    actionId: '17',
+    name: '192.168.1.5',
+    status: 'inprogress',
+    role: 'taskTracker',
+    sf: '100',
+    message: 'starting tasktracker'
+  },
+  {
+    actionId: '18',
+    name: '192.168.1.6',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed'
+  },
+  {
+    actionId: '19',
+    name: '192.168.1.7',
+    status: 'inprogress',
+    sf: '100',
+    role: 'DataNode',
+    message: 'starting Datanode'
+  },
+  {
+    actionId: '20',
+    name: '192.168.1.8',
+    status: 'queued',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed%'
+  },
+  {
+    actionId: '21',
+    name: '192.168.1.9',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed'
+  },
+  {
+    actionId: '22',
+    name: '192.168.1.10',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed'
+  },
+  {
+    actionId: '23',
+    name: '192.168.1.11',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed'
+  },
+  {
+    actionId: '24',
+    name: '192.168.1.12',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'action is completed'
+  },
+  {
+    actionId: '25',
+    name: '192.168.1.1',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'Pig client being started'
+  },
+  {
+    actionId: '26',
+    name: '192.168.1.2',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client being installed'
+  },
+  {
+    actionId: '27',
+    name: '192.168.1.3',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client is installed'
+  },
+  {
+    actionId: '28',
+    name: '192.168.1.4',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client is installed'
+  },
+  {
+    actionId: '29',
+    name: '192.168.1.5',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client is installed'
+  },
+  {
+    actionId: '30',
+    name: '192.168.1.6',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client being tested'
+  },
+  {
+    actionId: '31',
+    name: '192.168.1.7',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'pig client is installed'
+  },
+  {
+    actionId: '32',
+    name: '192.168.1.8',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'starting pig client'
+  },
+  {
+    actionId: '33',
+    name: '192.168.1.9',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'starting pig client'
+  },
+  {
+    actionId: '34',
+    name: '192.168.1.10',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'starting pig client'
+  },
+  {
+    actionId: '35',
+    name: '192.168.1.11',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'starting pig client'
+  },
+  {
+    actionId: '36',
+    name: '192.168.1.12',
+    status: 'inprogress',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'starting pig client'
+  }
+]);

+ 310 - 0
ambari-web/app/data/mock/step9_pollData_2.js

@@ -0,0 +1,310 @@
+/**
+ * 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.
+ */
+
+
+
+module.exports = new Ember.Set([
+  {
+    actionId: '1',
+    name: '192.168.1.1',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '2',
+    name: '192.168.1.2',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '3',
+    name: '192.168.1.3',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '4',
+    name: '192.168.1.4',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '5',
+    name: '192.168.1.5',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '6',
+    name: '192.168.1.6',
+    status: 'completed',
+    sf: '100',
+    role: 'jobTracker',
+    message: 'jobTracker installation completed'
+  },
+  {
+    actionId: '7',
+    name: '192.168.1.7',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'tasktracker installation completed'
+  },
+  {
+    actionId: '8',
+    name: '192.168.1.8',
+    status: 'failed',
+    sf: '100',
+    role: 'NameNode',
+    message: 'NameNode installation completed'
+  },
+  {
+    actionId: '9',
+    name: '192.168.1.9',
+    status: 'completed',
+    sf: '100',
+    role: 'RegionServer',
+    message: 'RegionServer installation completed'
+  },
+  {
+    actionId: '10',
+    name: '192.168.1.10',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '11',
+    name: '192.168.1.11',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '12',
+    name: '192.168.1.12',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'Datanode installation completed'
+  },
+  {
+    actionId: '13',
+    name: '192.168.1.1',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'tasktracker installation completed'
+  },
+  {
+    actionId: '14',
+    name: '192.168.1.2',
+    status: 'failed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'tasktracker installation completed'
+  },
+  {
+    actionId: '15',
+    name: '192.168.1.3',
+    status: 'failed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '16',
+    name: '192.168.1.4',
+    status: 'failed',
+    role: 'taskTracker',
+    sf: '100',
+    message: 'completed 40%'
+  },
+  {
+    actionId: '17',
+    name: '192.168.1.5',
+    status: 'failed',
+    role: 'taskTracker',
+    sf: '100',
+    message: 'completed 23%'
+  },
+  {
+    actionId: '18',
+    name: '192.168.1.6',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 14%'
+  },
+  {
+    actionId: '19',
+    name: '192.168.1.7',
+    status: 'completed',
+    sf: '100',
+    role: 'DataNode',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '20',
+    name: '192.168.1.8',
+    status: 'failed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '21',
+    name: '192.168.1.9',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 27%'
+  },
+  {
+    actionId: '22',
+    name: '192.168.1.10',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '23',
+    name: '192.168.1.11',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '24',
+    name: '192.168.1.12',
+    status: 'completed',
+    sf: '100',
+    role: 'taskTracker',
+    message: 'completed 19%'
+  },
+  {
+    actionId: '25',
+    name: '192.168.1.1',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '26',
+    name: '192.168.1.2',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 20%'
+  },
+  {
+    actionId: '27',
+    name: '192.168.1.3',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '28',
+    name: '192.168.1.4',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 40%'
+  },
+  {
+    actionId: '29',
+    name: '192.168.1.5',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 23%'
+  },
+  {
+    actionId: '30',
+    name: '192.168.1.6',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 14%'
+  },
+  {
+    actionId: '31',
+    name: '192.168.1.7',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '32',
+    name: '192.168.1.8',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '33',
+    name: '192.168.1.9',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 27%'
+  },
+  {
+    actionId: '34',
+    name: '192.168.1.10',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '35',
+    name: '192.168.1.11',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 30%'
+  },
+  {
+    actionId: '36',
+    name: '192.168.1.12',
+    status: 'completed',
+    sf: '100',
+    role: 'Pig Client',
+    message: 'completed 19%'
+  }
+]);

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

@@ -102,6 +102,12 @@ Em.I18n.translations = {
   'installer.step8.header': 'Review',
 
   'installer.step9.header': 'Install, Start and Test',
+  'installer.step9.body': 'Wait to complete the cluster installation. Installing, Starting and Testing selected services',
+  'installer.step9.status.success': 'Successfully installed the cluster',
+  'installer.step9.status.failed': 'Failure in installation',
+  'installer.step9.host.status.success': 'success',
+  'installer.step9.host.status.warning': 'tolerable failures encountered',
+  'installer.step9.host.status.failed': 'failures encountered',
 
   'installer.step10.header': 'Summary'
 };

+ 5 - 1
ambari-web/app/models/hosts.js

@@ -19,11 +19,15 @@
 var App = require('app');
 
 App.HostInfo = Ember.Object.extend({
-  hostName: '',
+  elementId: 'host',
+  name: '',
   status: 'info',
   cpu: '',
   memory: '',
   message: 'Information',
+  progress: '0',
+  barColor: 'progress-info',
+  //barColor: true,
   isChecked: true
 
 });

+ 11 - 0
ambari-web/app/models/service.js

@@ -19,6 +19,17 @@
 
 var App = require('app');
 
+App.ServiceInfo = Ember.Object.extend({
+  elementId: 'service',
+  serviceName: '',
+  displayName: '',
+  isDisabled: '',
+  isHidden: '',
+  isSelected: 'true',
+  description: ''
+});
+
+
 /*App.User = Em.Object.extend({
  username: null
  });*/

+ 6 - 11
ambari-web/app/templates/installer/step3.hbs

@@ -18,7 +18,7 @@
 
 <h2>{{t installer.step3.header}}</h2>
 <p class="alert alert-info">{{t installer.step3.body}}</p>
-<div id="hosts" class="box">
+<div class="box">
   <div class="box-header">
     <div class="button-section">
       <a class="btn btn-primary decommission" {{bindAttr disabled="isSubmitDisabled"}}
@@ -27,7 +27,7 @@
         Retry
       </a>
       <a class="btn btn-primary" {{bindAttr disabled="isSubmitDisabled"}}
-         href="#" {{action removeHosts target="controller" }}><i
+         href="#" {{action removeBtn target="controller" }}><i
         class="icon-trash icon-white"></i>
         Remove
       </a>
@@ -57,7 +57,7 @@
       </th>
       <th>Status</th>
       <!--  given by the parsing function that parses data from bootstrap call -->
-      <th>Name</th>
+      <th>Host</th>
       <!-- retrieved from local storage initially -->
       <th>Message</th>
       <!-- given by the parsing function that parses data from bootstrap call, dynamically assign the color -->
@@ -66,7 +66,6 @@
     </tr>
     </thead>
 
-
     <tbody>
 
     {{#each host in controller}}
@@ -81,13 +80,10 @@
       </td>
 
       <td>
-        {{host.hostName}}
+        {{host.name}}
       </td>
       <td>
-       <a href=""></a>
-
         <a  href="javascript:void(null)" data-toggle="modal" {{action "hostLogPopup" target="controller"}}>{{host.message}}</a>
-
       </td>
       <td>
         <a href="javascript:void(null)" {{action removeItem target="view"}}><i
@@ -96,11 +92,10 @@
     </tr>
     {{/if}}
     {{/view}}
-
-
-
     {{/each}}
+
     </tbody>
+
   </table>
   <div class="box-footer">
     <hr/>

+ 93 - 3
ambari-web/app/templates/installer/step9.hbs

@@ -17,7 +17,97 @@
 -->
 
 <h2>{{t installer.step9.header}}</h2>
-<div class="btn-area">
-    <a class="btn" {{action back}}>Back</a>
-    <a class="btn btn-success" {{action next}}>Next</a>
+<p class="alert alert-info">{{t installer.step9.body}}</p>
+
+<div>
+  {{view.isStepCompleted}}
+  <div {{bindAttr class = "isStepCompleted::progress-striped isStepCompleted::active view.barColor :progress "}}>
+    <div class="bar" {{bindAttr style = "view.barWidth"}}><strong
+      style="color: #00008b;">Overall Progress</strong>
+    </div>
+  </div>
+  <span class="pull-right">{{progress}}%</span> <br/><br/>
+</div>
+
+<div class="box">
+  <div class="box-header">
+    <div class="pull-left">
+      <a
+        class="btn btn-primary " {{bindAttr disabled="isSubmitDisabled"}}
+        href="#" {{action retry target="controller"}}><i
+        class="icon-repeat icon-white"></i>
+        Retry
+      </a>
+      <a
+        class="btn btn-info"
+        href="#" {{action mockBtn target="controller"}}>
+        mockData
+      </a>
+      <a
+        class="btn btn-primary "
+        href="#" {{action pollBtn target="controller"}}><i
+        class="icon-repeat icon-white"></i>
+        pollData
+      </a>
+    </div>
+
+  </div>
+  <table class="table table-bordered table-striped">
+    <thead>
+    <tr>
+      <th class="span3">
+        Host
+      </th>
+      <th class="span4">Status</th>
+      <!--  given by the parsing function that parses data from bootstrap call -->
+      <th class="span4">Message</th>
+      <!-- retrieved from local storage initially -->
+    </tr>
+    </thead>
+
+    <tbody>
+
+    {{#each host in controller}}
+    {{#view App.HostStatusView objBinding="host"}}
+    <td>
+      {{host.name}}
+    </td>
+    <td>
+      <div>
+        <div {{bindAttr class="isStepCompleted::progress-striped isStepCompleted::active view.barColor :progress"}}>
+          <div class="bar" {{bindAttr style="view.barWidth"}}>
+          </div>
+        </div>
+        <span class="pull-right">{{host.progress}}%</span>
+      </div>
+
+    </td>
+    <td>
+      <a {{bindAttr class="view.isFailed:text-error view.isSuccess:text-success view.isWarning:text-warning"}}
+        href="javascript:void(null)"
+        data-toggle="modal" {{action "hostLogPopup" target="controller"}}>{{host.message}}</a>
+    </td>
+
+    {{/view}}
+    {{/each}}
+
+    </tbody>
+  </table>
+  <div class="box-footer">
+    <hr/>
+    <div class="footer-pagination">
+    </div>
+  </div>
+</div>
+
+<div>
+  <h4 {{bindAttr class = "view.resultMsgColor"}}>{{view.resultMsg}} </h4>
+
+  <div class="btn-area">
+    <a
+      class="btn pull-left" {{bindAttr disabled="isSubmitDisabled"}} {{action back target="controller"}}>&larr;
+      Back</a>
+    <a
+      class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action submit target="controller"}}>Next &rarr;</a>
+  </div>
 </div>

+ 1 - 1
ambari-web/app/views.js

@@ -41,5 +41,5 @@ require('views/installer/step5_view');
 require('views/installer/step6_view');
 require('views/installer/step7_view');
 require('views/installer/step8');
-require('views/installer/step9');
+require('views/installer/step9_view');
 require('views/installer/step10');

+ 0 - 3
ambari-web/app/views/installer/step3_view.js

@@ -26,12 +26,10 @@ App.InstallerStep3View = Em.View.extend({
 
   didInsertElement: function () {
     $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
-    console.log("TRACE: step3->didInsetElement");
     var controller = this.get('controller');
     var hosts = controller.loadHosts();
     controller.renderHosts(hosts);
     controller.startBootstrap();
-    console.log("TRACE: Back to step3 view");
   }
 });
 
@@ -57,7 +55,6 @@ App.HostView = Em.View.extend({
     } else if (category === "Failed" && hostInfo.get('status') == "error") {
       this.set('isVisible', true);
     } else {
-      console.log("TRACE: In View->hideItem->false condition item....");
       this.set('isVisible', false);
     }
   }.observes('category')

+ 0 - 26
ambari-web/app/views/installer/step9.js

@@ -1,26 +0,0 @@
-/**
- * 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');
-
-App.InstallerStep9View = Em.View.extend({
-
-  templateName: require('templates/installer/step9')
-
-});

+ 117 - 0
ambari-web/app/views/installer/step9_view.js

@@ -0,0 +1,117 @@
+/**
+ * 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');
+
+App.InstallerStep9View = Em.View.extend({
+
+  templateName: require('templates/installer/step9'),
+  barColor: 'progress-info',
+  resultMsg: '',
+  resultMsgColor: '',
+
+  didInsertElement: function () {
+    var controller = this.get('controller');
+    controller.clear();
+    var hosts = controller.loadHosts();
+    controller.renderHosts(hosts);
+    //TODO: uncomment following line after the hook up with the API call
+    //controller.startPolling();
+  },
+
+  barWidth: function () {
+    var controller = this.get('controller');
+    var barWidth = 'width: ' + controller.get('progress') + '%;';
+    return barWidth;
+  }.property('controller.progress'),
+
+  onStatus: function () {
+    if (this.get('controller.status') === 'info') {
+      console.log('TRACE: Inside info view step9');
+      this.set('barColor', 'progress-info');
+    } else if (this.get('controller.status') === 'warning') {
+      console.log('TRACE: Inside warning view step9');
+      this.set('barColor', 'progress-warning');
+    } else if (this.get('controller.status') === 'failed') {
+      this.set('barColor', 'progress-danger');
+      console.log('TRACE: Inside error view step9');
+      this.set('resultMsg', Em.I18n.t('installer.step9.status.failed'));
+      this.set('resultMsgColor', 'alert-error');
+    } else if (this.get('controller.status') === 'success') {
+      console.log('TRACE: Inside success view step9');
+      this.set('barColor', 'progress-success');
+      this.set('resultMsg', Em.I18n.t('installer.step9.status.success'));
+      this.set('resultMsgColor', 'alert-success');
+    }
+  }.observes('controller.status')
+
+});
+
+App.HostStatusView = Em.View.extend({
+  tagName: 'tr',
+  obj: 'null',
+  barColor: 'progress-info',
+
+
+  barWidth: function () {
+    var barWidth = 'width: ' + this.get('obj.progress') + '%;';
+    return barWidth;
+  }.property('obj.progress'),
+
+  onStatus: function () {
+    if (this.get('obj.status') === 'info') {
+      this.set('barColor', 'progress-info');
+    } else if (this.get('obj.status') === 'warning') {
+      this.set('barColor', 'progress-warning');
+      this.set('obj.message', Em.I18n.t('installer.step9.host.status.warning'));
+    } else if (this.get('obj.status') === 'failed') {
+      this.set('barColor', 'progress-danger');
+      this.set('obj.message', Em.I18n.t('installer.step9.host.status.failed'));
+    } else if (this.get('obj.status') === 'success') {
+      this.set('barColor', 'progress-success');
+      this.set('obj.message', Em.I18n.t('installer.step9.host.status.success'));
+    }
+  }.observes('obj.status'),
+
+  isFailed: function() {
+    if(this.get('controller.isStepCompleted') === true && this.get('obj.status') === 'failed') {
+      return true;
+    } else {
+      return false;
+    }
+  }.property('controller.isStepCompleted','controller.status'),
+
+  isSuccess: function() {
+    if(this.get('controller.isStepCompleted') === true && this.get('obj.status') === 'success') {
+      return true;
+    } else {
+      return false;
+    }
+  }.property('controller.isStepCompleted','controller.status'),
+
+  isWarning: function() {
+    if(this.get('controller.isStepCompleted') === true && this.get('obj.status') === 'warning') {
+      return true;
+    } else {
+      return false;
+    }
+  }.property('controller.isStepCompleted','controller.status')
+
+});
+

+ 9 - 9
ambari-web/test/installer/step3_test.js

@@ -47,24 +47,24 @@ describe('App.InstallerStep3Controller', function () {
         }
       ];
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.1',
+        name: '192.168.1.1',
         status: 'error'
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.2',
+        name: '192.168.1.2',
         status: 'success'
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.3',
+        name: '192.168.1.3',
         status: 'pending'        //status should be overriden to 'error' after the parseHostInfo call
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.4',
+        name: '192.168.1.4',
         status: 'success'
       }));
 
       var result = controller.parseHostInfo(hostFrmServer, controller.content);
-      var host = controller.content.findProperty('hostName', '192.168.1.3');
+      var host = controller.content.findProperty('name', '192.168.1.3');
       expect(result).to.equal(true);
       expect(host.status).to.equal('error');
     })
@@ -75,22 +75,22 @@ describe('App.InstallerStep3Controller', function () {
     var controller = App.InstallerStep3Controller.create();
     it('should set all hosts to true on checking a global checkbox', function () {
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.1',
+        name: '192.168.1.1',
         status: 'error',
         isChecked: false
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.2',
+        name: '192.168.1.2',
         status: 'success',
         isChecked: false
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.3',
+        name: '192.168.1.3',
         status: 'pending', //status should be overriden to 'error' after the parseHostInfo call
         isChecked: true
       }));
       controller.content.pushObject(App.HostInfo.create({
-        hostName: '192.168.1.4',
+        name: '192.168.1.4',
         status: 'success',
         isChecked: false
       }));

+ 165 - 0
ambari-web/test/installer/step9_test.js

@@ -0,0 +1,165 @@
+/**
+ * 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 Ember = require('ember');
+var App = require('app');
+require('models/hosts');
+require('controllers/installer/step9_controller');
+
+describe('App.InstallerStep9Controller', function () {
+  //var controller = App.InstallerStep3Controller.create();
+
+  describe('#isStepFailed', function () {
+    var controller = App.InstallerStep9Controller.create();
+    it('should return true if even a single action of a role with 100% success factor fails', function () {
+      var polledData = new Ember.Set([
+        {
+          actionId: '1',
+          name: '192.168.1.1',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '2',
+          name: '192.168.1.2',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 20%'
+        },
+        {
+          actionId: '3',
+          name: '192.168.1.3',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '4',
+          name: '192.168.1.4',
+          status: 'failed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 40%'
+        }
+      ]);
+
+
+      expect(controller.isStepFailed(polledData)).to.equal(true);
+
+    })
+
+    it('should return false if action of a role fails but with less percentage than success factor of the role', function () {
+      var polledData = new Ember.Set([
+        {
+          actionId: '1',
+          name: '192.168.1.1',
+          status: 'failed',
+          sf: '30',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '2',
+          name: '192.168.1.2',
+          status: 'failed',
+          sf: '30',
+          role: 'DataNode',
+          message: 'completed 20%'
+        },
+        {
+          actionId: '3',
+          name: '192.168.1.3',
+          status: 'completed',
+          sf: '30',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '4',
+          name: '192.168.1.4',
+          status: 'completed',
+          sf: '30',
+          role: 'DataNode',
+          message: 'completed 40%'
+        }
+      ]);
+
+      expect(controller.isStepFailed(polledData)).to.equal(false);
+
+    })
+
+  })
+
+  describe('#setHostsStatus', function () {
+    var controller = App.InstallerStep9Controller.create();
+    it('sets the status of all hosts in the content to the passed status value', function () {
+      var mockData = new Ember.Set(
+        {
+          actionId: '1',
+          name: '192.168.1.1',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '2',
+          name: '192.168.1.2',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 20%'
+        },
+        {
+          actionId: '3',
+          name: '192.168.1.3',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 30%'
+        },
+        {
+          actionId: '4',
+          name: '192.168.1.4',
+          status: 'completed',
+          sf: '100',
+          role: 'DataNode',
+          message: 'completed 40%'
+        }
+      );
+      mockData.forEach(function(_polledData){
+        controller.content.pushObject(_polledData);
+      });
+
+      controller.setHostsStatus(mockData,'finish');
+      var result = controller.content.everyProperty('status','finish');
+      //console.log('value of pop is: '+ result.pop.actionId);
+      expect(result).to.equal(true);
+
+    })
+  })
+
+
+})
+
+