Browse Source

AMBARI-762. Implement Confirm Hosts page for Ambari installer. (Jaimin Jetly)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1388306 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 năm trước cách đây
mục cha
commit
6c995c099f

+ 3 - 0
AMBARI-666-CHANGES.txt

@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
   NEW FEATURES
 
+  AMBARI-762. Implement Confirm Hosts page for Ambari installer
+  (Jaimin Jetly via yusaku)
+
   AMBARI-763. Implement Installer Step 6 (Assign Slaves). (yusaku)
 
   AMBARI-760. Fix injection in data access objects to use guice provider.

+ 8 - 10
ambari-web/app/controllers/installer/step2_controller.js

@@ -53,14 +53,16 @@ App.InstallerStep2Controller = Em.Controller.extend({
   }.observes('localRepo'),
 
   validateHostNames: function () {
-    this.hostNameArr = this.get('hostNames').split(new RegExp("\\s"));
-    for (var i = 0; i < this.hostNameArr.length; i++) {
+    this.hostNameArr = this.get('hostNames').trim().split(new RegExp("\\s+","g"));
+   // this.hostNameArr = this.get('hostNames').trim().match(/\w+|"[^"]+"/g);
+    for (var index in this.hostNameArr) {
+      console.log("host name is: " + this.hostNameArr[index]);
       //TODO: other validation for hostnames will be covered over here
       // For now hostnames that start or end with '-' are not allowed
-      if (/^\-/.test(this.hostNameArr[i]) || /\-$/.test(this.hostNameArr[i])) {
-        console.log('Invalid host name: ' + this.hostNameArr[i]);
-        this.set('hostNameErrMsg', Em.I18n.t('installer.step2.hostName.error.invalid'));
+      if (/^\-/.test(this.hostNameArr[index]) || /\-$/.test(this.hostNameArr[index])) {
+        console.log('Invalid host name: ' + this.hostNameArr[index]);
         this.set('hostNameErr', true);
+        this.set('hostNameErrMsg', Em.I18n.t('installer.step2.hostName.error.invalid'));
         this.set('hostNameEmptyError', false);
         this.set('hostNameNotRequiredErr', false);
         return false;
@@ -163,10 +165,6 @@ App.InstallerStep2Controller = Em.Controller.extend({
 
 
   evaluateStep2: function () {
-
-    //task1 = do primary validations on whole step before executing any further steps
-    //task2 = parsing hostnames string to hostnames json array
-    //task3 = check validation for every hostname and store it in localstorage
     //task4 = Storing ambari agent Install type in localStorage (installType maps at host level and so every host will have this as an property)
     //task5 = Storing path of software repository(remote/local repo) to localStorage
     //task6 = call to rest API: @Post http://ambari_server/api/bootstrap
@@ -177,7 +175,7 @@ App.InstallerStep2Controller = Em.Controller.extend({
     console.log('TRACE: Entering controller:InstallerStep2:evaluateStep2 function');
     console.log('value of manual install is: ' + this.get('manualInstall'));
 
-    var validateResult = this.validateStep2();
+    var validateResult = !this.validateStep2();
 
     if (this.get('isSubmitDisabled') === true ) {
       console.log("ERROR: error in validation");

+ 150 - 128
ambari-web/app/controllers/installer/step3_controller.js

@@ -19,116 +19,141 @@ var App = require('app');
 
 App.InstallerStep3Controller = Em.ArrayController.extend({
   name: 'installerStep3Controller',
-  hostInfo: [],
-  hostNames: [],
-  category: 'All',
   content: [],
-  /*
-   This flag is "true" While bootstrapping is in process.
-   Parsing function or timeout on bootstrap rest call can make it false.
-   */
-  bootstrap: '',
-  mockData:{},
+  bootHosts: [],
+  isSubmitDisabled: false,
+  categories: ['Hosts', 'Succeeded', 'Failed'],
+  category: 'Hosts',
+  allChecked: true,
+
+  onAllChecked: function () {
+    var hosts = this.visibleHosts();
+    if (this.get('allChecked') === true) {
+      hosts.setEach('isChecked', true);
+    } else {
+      hosts.setEach('isChecked', false);
+    }
+  }.observes('allChecked'),
 
+  mockData: require('data/mock/step3_hosts'),
+  mockRetryData: require('data/mock/step3_retry_hosts'),
 
-  renderHosts: function() {
+  /* Loads the hostinfo from localStorage on the insertion of view. It's being called from view */
+  loadHosts: function () {
+    this.clear();
     var hostInfo = [];
     hostInfo = App.db.getHosts();
-    this.hostNames.clear();
-    this.clear();
-
-    this.hostNames = new Ember.Set();
-    for(var index in hostInfo) {
-      this.hostNames.add(hostInfo[index].name);
-     // alert(hostInfo[index].name);
-    }
-    /*hostInfo.forEach(function(_hostNames) {
-      hostNames.add = _hostNames.name;
-    });*/
-    console.log("TRACE: step3->controller->renderHosts");
-/*
-   this.hostInfo = [
-    {
-      hostName: 'jaimin',
-      status:'success'
-    },
-    {
-      hostName: 'jetly',
-      status:'success'
-    },
-    {
-      hostName: 'villa',
-      status:'Verifying SSH connection'
-    },
-    {
-      hostName: 'jack',
-      status:'SSH connection failed'
-    },
-    {
-      hostName: 'george',
-      status:'success'
-    },
-    {
-      hostName: 'maria',
-      status:'success'
-    },
-    {
-      hostName: 'adam',
-      status:'Verifying SSH connection'
-    },
-    {
-      hostName: 'jennifer',
-      status:'SSH connection failed'
-    },
-    {
-      hostName: 'john',
-      status:'success'
-    },
-    {
-      hostName: 'tom',
-      status:'success'
-    },
-    {
-      hostName: 'harry',
-      status:'success'
-    },
-    {
-      hostName: 'susan',
-      status:'success'
+    var hosts = new Ember.Set();
+    for (var index in hostInfo) {
+      hostInfo[index].status = "pending";
+      hosts.add(hostInfo[index]);
     }
-  ];
-  */
-  var self = this;
-    this.hostNames.forEach(function(_hostInfo) {
-        var hostInfo = App.HostInfo.create({
-          hostName: _hostInfo
-        });
+    hosts.forEach(function (_host) {
+      console.log("TRACE: host name is: " + _host.name);
+    });
+    return hosts;
+  },
+
+  /* renders the set of passed hosts */
+  renderHosts: function (hostsInfo) {
+    var self = this;
+    hostsInfo.forEach(function (_hostInfo) {
+      var hostInfo = App.HostInfo.create({
+        hostName: _hostInfo.name,
+        status: _hostInfo.status
+      });
 
       console.log('pushing ' + hostInfo.hostName);
-      //self.set('content',hostInfo);
-      //self.replaceContent(0, hostInfo.get('length'), hostInfo);
       self.content.pushObject(hostInfo);
     });
+  },
+
+  /* Below function parses and updates the content, and governs the possibility
+   of the next doBootstrap (polling) call
+   */
+
+  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);
+      if (host !== null && host !== undefined) { // check if hostname extracted from REST API data matches any hostname in content
+        host.set('status', _hostFrmServer.status);
+      }
+    });
+    result = !this.content.someProperty('status', 'pending');
+    return result;
+  },
+
+  /* Below function returns the current set of visible hosts on view (All,succeded,failed) */
+  visibleHosts: function () {
+    var result;
+    if (this.get('category') === 'Succeeded') {
+      return (this.filterProperty('status', 'success'));
+    } else if (this.get('category') === 'Failed') {
+      return (this.filterProperty('status', 'error'));
+    } else if (this.get('category') === 'Hosts') {
+      return this.content;
+    }
+  },
 
-  //this.startBootstrap();
+  /* Below function removes a single element on the trsah icon click. Being called from view */
+  removeElement: function (hostInfo) {
+    console.log('TRACE: In removeElement');
+    var hosts = [hostInfo];
+    App.db.removeHosts(hosts);    // remove from localStorage
+    this.removeObject(hostInfo);     // remove from the content to rerender the view
   },
 
-  /*
-   * Below function will be called on successfully leaving step2 and entering
-   * step3. "Retry" button shall also make use of it.
+
+  retry: function () {
+    if (this.get('isSubmitDisabled')) {
+      return;
+    }
+    var hosts = this.visibleHosts();
+    var selectedHosts = hosts.filterProperty('isChecked', true);
+    selectedHosts.forEach(function (_host) {
+      console.log('Retrying:  ' + _host.hostName);
+    });
+
+    //TODO: uncomment below code to hookup with @GET bootstrap API
+    /*
+     this.set('bootHosts',selectedHosts);
+     this.doBootstrap();
+     */
+
+    //TODO: comment below lines while hooking up with actual @GET bootstrap API
+    var mockHosts = this.mockRetryData;
+    mockHosts.forEach(function (_host) {
+      console.log('Retrying:  ' + _host.name);
+    });
+    this.parseHostInfo(mockHosts, selectedHosts);
+  },
+
+
+  /* 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
    */
 
-  startBootstrap: function () {
-    console.log("TRACE: Entering controller->installer->step3->startBootstrap() function");
-    var self = this;
-    this.set('bootstrap',window.setInterval(function () {
-      self.doBootstrap()
-    }, 5000));
+  /* Removes set of selected visisble hosts on the remove button click */
+  removeHosts: 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);
+    });
+    App.db.removeHosts(selectedHosts);
+    this.removeObjects(selectedHosts);
   },
 
 
-  stopBootstrap: function () {
-    window.clearInterval(this.bootstrap);
+  startBootstrap: function () {
+    this.set('isSubmitDisabled', true);
+    this.set('bootHosts', this.get('content'));
+    this.doBootstrap();
   },
 
   doBootstrap: function () {
@@ -137,62 +162,59 @@ App.InstallerStep3Controller = Em.ArrayController.extend({
       type: 'GET',
       url: '/ambari_server/api/bootstrap',
       async: false,
-      timeout: 2000,
+      timeout: 5000,
       success: function (data) {
         console.log("TRACE: In success function for the GET bootstrap call");
-
-
+        var result = self.parseHostInfo(data, this.get('bootHosts'));
+        if (result !== true) {
+          window.setTimeout(self.doBootstrap, 3000);
+        } else {
+          self.stopBootstrap();
+        }
       },
+
       error: function () {
         console.log("ERROR");
-
-        self.stopBootstrap(); //Never toggle this for now, flow goes in infinite loop
+        self.stopBootstrap();
       },
+
       statusCode: {
         404: function () {
           console.log("URI not found.");
-          alert("URI not found");
-          result = false;
+          //alert("URI not found");
         }
       },
+
       dataType: 'application/json'
     });
 
   },
 
-
-
-
-  retry: function () {
-    this.doBootstrap();
+  stopBootstrap: function () {
+    //TODO: uncomment following line after the hook up with the API call
+    // this.set('isSubmitDisabled',false);
   },
 
-  remove: function () {
-
+  submit: function () {
+    if (!this.get('isSubmitDisabled')) {
+      App.get('router').transitionTo('step4');
+    }
+  },
+  hostLogPopup: function (event) {
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step3.hostLog.popup.header'),
+      bodyClass: Ember.View.extend({
+        templateName: require('templates/installer/step3HostLogPopup')
+      })
+    });
   },
 
-  evaluateStep3: function () {
-    // TODO: evaluation at the end of step3
-    /* Not sure if below tasks are to be covered over here
-     * as these functions are meant to be called at the end of a step
-     * and the following tasks are interactive to the page and not on clicking next button.
-     *
-     *
-     * task2 will be a parsing function that on reaching a particular condition(all hosts are in success or faliue status)  will stop task1
-     * task3 will be a function binded to remove button
-     * task4 will be a function binded to retry button
-     *
-     *
-     * keeping it over here for now
-     */
-
-
-    //task1 = start polling with rest API @Get http://ambari_server/api/bootstrap.
-    //task2 = stop polling when all the hosts have either success or failure status.
-    //task3(prerequisite = remove) = Remove set of selected hosts from the localStorage
-    //task4(prerequisite = retry) = temporarily store list of checked host and call to rest API: @Post http://ambari_server/api/bootstrap
-
-    return true;
+  // TODO: dummy button. Remove this after the hook up with actual REST API.
+  mockBtn: function () {
+    this.set('isSubmitDisabled', false);
+    var hostInfo = this.mockData;
+    this.renderHosts(hostInfo);
   }
+
 });
 

+ 50 - 0
ambari-web/app/data/mock/step3_hosts.js

@@ -0,0 +1,50 @@
+module.exports = new Ember.Set([
+  {
+    name: '192.168.1.1',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.2',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.3',
+    status: 'pending'
+  },
+  {
+    name: '192.168.1.4',
+    status: 'error'
+  },
+  {
+    name: '192.168.1.5',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.6',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.7',
+    status: 'pending'
+  },
+  {
+    name: '192.168.1.8',
+    status: 'error'
+  },
+  {
+    name: '192.168.1.9',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.10',
+    status: 'pending'
+  },
+  {
+    name: '192.168.1.11',
+    status: 'pending'
+  },
+  {
+    name: '192.168.1.12',
+    status: 'pending'
+  }
+]);

+ 55 - 0
ambari-web/app/data/mock/step3_retry_hosts.js

@@ -0,0 +1,55 @@
+module.exports = new Ember.Set([
+  {
+    name: '192.168.1.1',
+    status: 'error'
+  },
+  {
+    name: '192.168.1.2',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.3',
+    status: 'error'
+  },
+  {
+    name: '192.168.1.4',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.5',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.6',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.7',
+    status: 'success'
+
+  },
+  {
+    name: '192.168.1.8',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.9',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.10',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.11',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.12',
+    status: 'success'
+  },
+  {
+    name: '192.168.1.13',
+    status: 'success'
+  }
+]);

+ 19 - 1
ambari-web/app/ember.js

@@ -1 +1,19 @@
-module.exports = Ember;
+/**
+ * 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=Ember;

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

@@ -82,6 +82,8 @@ Em.I18n.translations = {
   'installer.step3.header': 'Confirm Hosts',
   'installer.step3.body': 'Here are the results of the host discovery process.<br>' +
     'Please verify and remove the ones that you do not want to be the part of the cluster.',
+  'installer.step3.hostLog.popup.header': 'Log file for the host',
+  'installer.step3.hostLog.popup.body': 'Placeholder for the log file',
 
   'installer.step4.header': 'Choose Services',
   'installer.step4.body': 'Choose which services you want to install on your cluster.<br>Note that some services have dependencies (e.g., HBase requires ZooKeeper.)',

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

@@ -23,6 +23,8 @@ App.HostInfo = Ember.Object.extend({
   status: 'info',
   cpu: '',
   memory: '',
-  message: 'starting'
+  message: 'Information',
+  isChecked: true
+
 });
 

+ 0 - 1
ambari-web/app/routes/installer.js

@@ -84,7 +84,6 @@ module.exports = Em.Route.extend({
     },
     back: Em.Router.transitionTo('step2'),
     next: function (router, context) {
-      var result = router.get('installerStep3Controller').evaluateStep3();
       if (result) {
         console.log('In step3 transiting to step4');
         router.transitionTo('step4');

+ 1 - 1
ambari-web/app/templates/installer/step1.hbs

@@ -34,7 +34,7 @@
 </div>
 
 <div class="btn-area">
-  <a class="btn btn-success" {{bindAttr disabled= "invalidClusterName"}} {{action next}}>Next</a>
+  <a class="btn btn-success pull-right" {{bindAttr disabled= "invalidClusterName"}} {{action next}}>Next &rarr;</a>
 </div>
 
 

+ 6 - 7
ambari-web/app/templates/installer/step2.hbs

@@ -83,7 +83,7 @@
   {{view Ember.Checkbox checkedBinding="localRepo"}}
 </label>
 
-{{#if localRepo}}
+{{#if "localRepo"}}
 <div {{bindAttr class="softRepoLocalPathNullErr:error :control-group"}}>
   <div class="alert alert-info">
     {{t installer.step2.localRepo.info}}
@@ -98,14 +98,13 @@
 {{/if}}
 
 <div class="btn-area">
-  <a class="btn" {{action back}}>Previous</a>
+  <a class="btn pull-left" {{action back}}>&larr; Back</a>
   {{#if manualInstall}}
-  <a role="button" class="btn btn-success"
-     style="float:right"
-     data-toggle="modal" {{action evaluateStep2 target="controller"}}>Validate</a>
+  <a role="button" class="btn btn-success pull-right"
+     data-toggle="modal" {{action evaluateStep2 target="controller"}}>Validate &rarr;</a>
   {{else}}
-  <a class="btn btn-success" style="float:right" {{action next}}> Discover and
-    Validate </a>
+  <a class="btn btn-success pull-right" {{action next}}> Discover and
+    Validate &rarr; </a>
   {{/if}}
 </div>
 

+ 36 - 89
ambari-web/app/templates/installer/step3.hbs

@@ -21,16 +21,29 @@
 <div id="hosts" class="box">
   <div class="box-header">
     <div class="button-section">
-      <a class="btn btn-primary decommission"
+      <a class="btn btn-primary decommission" {{bindAttr disabled="isSubmitDisabled"}}
          href="#" {{action retry target="controller"}}><i
         class="icon-repeat icon-white"></i>
         Retry
       </a>
-      <a class="btn btn-primary"
-         href="#" {{action remove target="controller" }}><i
+      <a class="btn btn-primary" {{bindAttr disabled="isSubmitDisabled"}}
+         href="#" {{action removeHosts target="controller" }}><i
         class="icon-trash icon-white"></i>
         Remove
       </a>
+      <a class="btn btn-info"
+         href="#" {{action mockBtn target="controller" }}>
+        Mock Data
+      </a>
+
+      <div class="dropdown pull-right">
+        {{view Ember.Select class="pull-right"
+       contentBinding="controller.categories"
+       selectionBinding="controller.category"
+       }}
+        <h5 class="pull-right text-info">Filter By: &nbsp</h5>
+      </div>
+
     </div>
   </div>
 
@@ -39,9 +52,8 @@
     <thead>
     <tr>
       <th>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
+        {{view Ember.Checkbox checkedBinding="allChecked"}}
+        {{controller.category}}
       </th>
       <th>Status</th>
       <!--  given by the parsing function that parses data from bootstrap call -->
@@ -56,105 +68,40 @@
 
 
     <tbody>
+
     {{#each host in controller}}
+    {{#view App.HostView categoryBinding="controller.category" hostInfoBinding="host"}}
+    {{#if view.isVisible}}
     <tr {{bindAttr class = "host.status"}}>
       <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
+        {{view Ember.Checkbox checkedBinding="host.isChecked"}}
       </td>
       <td>
+        {{host.status}}
       </td>
-      </td>
+
       <td>
         {{host.hostName}}
       </td>
       <td>
-        {{host.message}}
-      </td>
-      <td>
-        <a href="javascript:void(null)" {{action remove}}><i
-          class="icon-trash"></i></a>
-      </td>
-    </tr>
-    {{/each}}
-    </tbody>
-  </table>
+       <a href=""></a>
+
+        <a  href="javascript:void(null)" data-toggle="modal" {{action "hostLogPopup" target="controller"}}>{{host.message}}</a>
 
-  <!--
-    <tr class="warning">
-      <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
-      </td>
-      <td></td>
-      <td>ip-12-12-12-1231.co2.ama.internal</td>
-      <td>Verifying SSH connection</td>
-      <td>
-        <a href="javascript:void(null)" {{action remove}}><i
-          class="icon-trash"></i></a>
-      </td>
-    </tr>
-    <tr class="success">
-      <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
-      </td>
-      <td></td>
-      <td>ip-12-12-12-1232.co2.ama.internal</td>
-      <td>Succeed</td>
-      <td>
-        <a href="javascript:void(null)" {{action remove}}><i
-          class="icon-trash"></i></a>
-      </td>
-    </tr>
-    <tr class="warning">
-      <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
-      </td>
-      <td></td>
-      <td>ip-12-12-12-1233.co2.ama.internal</td>
-      <td>Verifying SSH connection</td>
-      <td>
-        <a href="javascript:void(null)" {{action remove}}><i
-          class="icon-trash"></i></a>
-      </td>
-    </tr>
-    <tr class="warning">
-      <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
-      </td>
-      <td></td>
-      <td>ip-12-12-12-1234.co2.ama.internal</td>
-      <td>Verifying SSH connection</td>
-      <td>
-        <a href="javascript:void(null)" {{action remove}}><i
-          class="icon-trash"></i></a>
-      </td>
-    </tr>
-    <tr class="error">
-      <td>
-        <label class="checkbox">
-          <input type="checkbox">
-        </label>
       </td>
-      <td></td>
-      <td>ip-12-12-12-1235.co2.ama.internal</td>
-      <td>ssh connection failed</td>
       <td>
-        <a href="javascript:void(null)" {{action remove}}><i
+        <a href="javascript:void(null)" {{action removeItem target="view"}}><i
           class="icon-trash"></i></a>
       </td>
     </tr>
+    {{/if}}
+    {{/view}}
+
+
+
+    {{/each}}
     </tbody>
   </table>
-  -->
   <div class="box-footer">
     <hr/>
     <div class="footer-pagination">
@@ -162,6 +109,6 @@
   </div>
 </div>
 <div class="btn-area">
-  <a class="btn" {{action back}}>Previous</a>
-  <a class="btn btn-success" style="float:right" {{action next}}>Next</a>
+  <a class="btn pull-left" {{bindAttr disabled="isSubmitDisabled"}} {{action back}}>&larr; Previous</a>
+  <a class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action submit target="controller"}}>Next &rarr;</a>
 </div>

+ 1 - 0
ambari-web/app/templates/installer/step3HostLogPopup.hbs

@@ -0,0 +1 @@
+<p>{{t installer.step3.hostLog.popup.body}}</p>

+ 2 - 2
ambari-web/app/templates/installer/step4.hbs

@@ -50,7 +50,7 @@
   </table>
 
   <div class="btn-area">
-    <a class="btn" {{action back}}>Back</a>
-    <a class="btn btn-success" style="float:right" {{action submit target="controller"}}>Next</a>
+    <a class="btn pull-left" {{action back}}>&larr; Back</a>
+    <a class="btn btn-success pull-right" {{action submit target="controller"}}>Next &rarr;</a>
   </div>
 </div>

+ 13 - 3
ambari-web/app/utils/db.js

@@ -154,9 +154,19 @@ App.db.setSoftRepo = function(softRepo) {
 	var user = App.db.data.app.loginName;
 	if(App.db.data[user] == undefined) {
 		App.db.data[user] = {'name':user};
-		App.db.data[user].Installer.softRepo = softRepo;
-		localStorage.setObject('ambari',App.db.data);
 	}
+  App.db.data[user].Installer.softRepo = softRepo;
+  localStorage.setObject('ambari',App.db.data);
+}
+
+App.db.removeHosts = function(hostInfo) {
+  console.log('TRACE: Entering db:setSoftRepo function');
+  var hostList = App.db.getHosts();
+  hostInfo.forEach(function(_hostInfo) {
+    var host = _hostInfo.hostName;
+    delete hostList[host];
+  });
+  App.db.setHosts(hostList);
 }
 
 App.db.setSelectedServiceNames = function(serviceNames) {
@@ -238,7 +248,7 @@ App.db.isCompleted = function() {
   return App.db.data[user].Installer.completed;
 }
 
-App.db.getHosts = function(name,hostInfo) {
+App.db.getHosts = function() {
 	console.log('TRACE: Entering db:getHosts function');
   App.db.data = localStorage.getObject('ambari');
   var user = App.db.data.app.loginName;

+ 33 - 4
ambari-web/app/views/installer/step3_view.js

@@ -22,14 +22,43 @@ var App = require('app');
 App.InstallerStep3View = Em.View.extend({
 
   templateName: require('templates/installer/step3'),
+  category: '',
 
   didInsertElement: function () {
     $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
-    var result = this.get('controller');
     console.log("TRACE: step3->didInsetElement");
-    var result = this.get('controller');
-    result.renderHosts();
+    var controller = this.get('controller');
+    var hosts = controller.loadHosts();
+    controller.renderHosts(hosts);
+    controller.startBootstrap();
     console.log("TRACE: Back to step3 view");
   }
+});
 
-});
+App.HostView = Em.View.extend({
+
+  isVisible: true,
+  category: 'Hosts',
+  removeItem: function () {
+    var hostInfo = this.get('hostInfo');
+    this.get('controller').removeElement(hostInfo);
+
+  },
+
+  hideItem: function () {
+    var controller = this.get('controller');
+    var hostInfo = this.get('hostInfo');
+    var category = this.get('category');
+    if (category === "Hosts") {
+      this.set('isVisible', true);
+    } else if (category === "Succeeded" && hostInfo.get('status') == "success") {
+      this.set('isVisible', true);
+      return true;
+    } 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')
+});

+ 88 - 1
ambari-web/test/installer/step3_test.js

@@ -16,4 +16,91 @@
  * limitations under the License.
  */
 
-// TODO
+
+var Ember = require('ember');
+var App = require('app');
+require('models/hosts');
+require('controllers/installer/step3_controller');
+
+describe('App.InstallerStep3Controller', function () {
+  //var controller = App.InstallerStep3Controller.create();
+
+  describe('#parseHostInfo', function () {
+    var controller = App.InstallerStep3Controller.create();
+    it('should return true if there is no host with pending status in the data provided by REST bootstrap call.It should also update the status on the client side', function () {
+      var hostFrmServer = [
+        {
+          name: '192.168.1.1',
+          status: 'error'
+        },
+        {
+          name: '192.168.1.2',
+          status: 'success'
+        },
+        {
+          name: '192.168.1.3',
+          status: 'error'
+        },
+        {
+          name: '192.168.1.4',
+          status: 'success'
+        }
+      ];
+      controller.content.pushObject(App.HostInfo.create({
+        hostName: '192.168.1.1',
+        status: 'error'
+      }));
+      controller.content.pushObject(App.HostInfo.create({
+        hostName: '192.168.1.2',
+        status: 'success'
+      }));
+      controller.content.pushObject(App.HostInfo.create({
+        hostName: '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',
+        status: 'success'
+      }));
+
+      var result = controller.parseHostInfo(hostFrmServer, controller.content);
+      var host = controller.content.findProperty('hostName', '192.168.1.3');
+      expect(result).to.equal(true);
+      expect(host.status).to.equal('error');
+    })
+  })
+
+
+  describe('#onAllChecked', 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',
+        status: 'error',
+        isChecked: false
+      }));
+      controller.content.pushObject(App.HostInfo.create({
+        hostName: '192.168.1.2',
+        status: 'success',
+        isChecked: false
+      }));
+      controller.content.pushObject(App.HostInfo.create({
+        hostName: '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',
+        status: 'success',
+        isChecked: false
+      }));
+      controller.onAllChecked();
+      controller.content.forEach(function (hostName) {
+        var result = hostName.isChecked;
+        expect(result).to.equal(true);
+      });
+
+    })
+  })
+})
+