Преглед изворни кода

AMBARI-735. Clean up Installer Welcome page and Install Options page. (Jaimin Jetly)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1384991 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako пре 12 година
родитељ
комит
ed71f92e79

+ 3 - 0
AMBARI-666-CHANGES.txt

@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
   NEW FEATURES
 
+  AMBARI-735. Clean up Installer Welcome page and Install Options page.
+  (Jaimin Jetly via yusaku)
+
   AMBARI-738. s/Node/Host/g. (hitesh)
 
   AMBARI-737. ServiceComponentNode FSM implementation. (hitesh)

+ 7 - 7
ambari-web/app/controllers/installer/step1.js

@@ -22,10 +22,9 @@ App.InstallerStep1Controller = Em.Controller.extend({
   name: 'installerStep1Controller',
   content: [],
   clusterName: '',
-  validClusterName: true,
+  invalidClusterName: false,
   clusterNameError: '',
-
-  evaluateStep1: function () {
+  validateStep1: function () {
     //TODO: Done
     //task1 =  checks on valid cluster name
     //task2 (prereq(task1 says it's a valid cluster name)) =  storing cluster name in localstorage
@@ -33,22 +32,22 @@ App.InstallerStep1Controller = Em.Controller.extend({
     console.log('TRACE: Entering controller:InstallerStep1:evaluateStep1 function');
     if (this.get('clusterName') == '') {
       this.set('clusterNameError', App.messages.step1_clusterName_error_required);
-      this.set('validClusterName', false);
+      this.set('invalidClusterName', true);
       result = false;
     } else if (/\s/.test(this.get('clusterName'))) {
       console.log('White spaces not allowed for cluster name');
       this.set('clusterNameError', App.messages.step1_clusterName_error_whitespaces);
-      this.set('validClusterName', false);
+      this.set('invalidClusterName', true);
       result = false;
     } else if (/[^\w\s]/gi.test(this.get('clusterName'))) {
       console.log('Special characters are not allowed for the cluster name');
       this.set('clusterNameError', App.messages.step1_clusterName_error_specialChar);
-      this.set('validClusterName', false);
+      this.set('invalidClusterName', true);
       result = false;
     } else {
       console.log('value of clusterNmae is: ' + this.get('clusterName'));
       this.set('clusterNameError', '');
-      this.set('validClusterName', true);
+      this.set('invalidClusterName', false);
       result = true;
     }
     if (result === true) {
@@ -57,4 +56,5 @@ App.InstallerStep1Controller = Em.Controller.extend({
     console.log('Exiting the evaluatestep1 function');
     return result;
   }.observes('clusterName')
+
 })

+ 123 - 89
ambari-web/app/controllers/installer/step2.js

@@ -27,16 +27,16 @@ App.InstallerStep2Controller = Em.Controller.extend({
   hostNameErr: false,
   manualInstall: false,
   hostNameNotRequireErr: false,
+  hostNameErrMsg: '',
   sshKey: '',
   passphrase: '',
   confirmPassphrase: '',
   sshKeyNullErr: false,
-  passphraseNullErr: false,
   passphraseMatchErr: false,
   localRepo: false,
   localRepoPath: '',
   softRepoLocalPathNullErr: false,
-  showPopup: false,
+  isSubmitDisabled: true,
 
   installType: function () {
     if (this.get('manualInstall') === true) {
@@ -52,49 +52,116 @@ App.InstallerStep2Controller = Em.Controller.extend({
     }
   }.observes('localRepo'),
 
-  hostNamesErr: function () {
-    if (this.get('hostNameEmptyError') || this.get('hostNameNotRequireErr') || this.get('hostNameErr')) {
-      this.set('sshKeyNullErr', false);
-      this.set('passphraseMatchErr', false);
-      this.set('softRepoLocalPathNullErr', false);
-      return true;
-    } else {
-      return false;
+  checkHostNames: function () {
+    this.hostNameArr = this.get('hostNames').split(new RegExp("\\s"));
+    for (var i = 0; i < this.hostNameArr.length; i++) {
+      //TODO: other validation for hostnames will be covered over here
+      // For now hostname that are starting or ending with '-' are not allowed
+      if (/^\-/.test(this.hostNameArr[i]) || /\-$/.test(this.hostNameArr[i])) {
+        console.log('Invalid host name: ' + this.hostNameArr[i]);
+        var hostNameErrMsg = App.messages.step2_hostNameErr;
+        this.set('hostNameErrMsg', hostNameErrMsg);
+        this.set('hostNameErr', true);
+        this.set('hostNameEmptyError', false);
+        this.set('hostNameNotRequireErr', false);
+        return false;
+      }
     }
-  }.property('hostNameEmptyError', 'hostNameNotRequireErr', 'hostNameErr'),
+    this.set('hostNameErrMsg', '');
+    this.set('hostNameEmptyError', false);
+    this.set('hostNameNotRequireErr', false);
+    this.set('hostNameErr', false);
+    return true;
+  },
 
-  hostManageErr: function () {
-    if (this.get('hostNameEmptyError') || this.get('hostNameNotRequireErr') || this.get('hostNameErr') || this.get('sshKeyNullErr') || this.get('passphraseMatchErr')) {
-      return true;
+  hostErr: function () {
+    if (this.get('hostNames') === '' && this.get('manualInstall') === false) {
+      this.set('hostNameEmptyError', true);
+      this.set('hostNameNotRequireErr', false);
+      this.set('hostNameErr', false);
+      this.set('hostNameErrMsg', App.messages.step2_hostNameEmptyError);
+    } else if (this.get('hostNames') !== '' && this.get('manualInstall') === true) {
+      this.set('hostNameNotRequireErr', true);
+      this.set('hostNameEmptyError', false);
+      this.set('hostNameErr', false);
+      this.set('hostNameErrMsg', App.messages.step2_hostNameNotRequireErr);
     } else {
-      return false
+      this.set('hostNameErr', false);
+      this.set('hostNameEmptyError', false);
+      this.set('hostNameNotRequireErr', false);
+      this.set('hostNameErrMsg', '');
     }
-  }.property('hostNameEmptyError', 'hostNameNotRequireErr', 'hostNameErr', 'sshKeyNullErr', 'passphraseMatchErr'),
+
+  }.observes('hostNames', 'manualInstall'),
 
   sshKeyErr: function () {
-    if (this.get('sshKeyNullErr') === true) {
-      this.set('passphraseMatchErr', false);
+    if (this.get('manualInstall') === false) {
+      if (this.get('sshKey') === '') {
+        this.set('sshKeyNullErr', true);
+      }
+      else {
+        this.set('sshKeyNullErr', false);
+      }
+    }
+
+  }.observes('manualInstall', 'sshKey'),
+
+  passphraseErr: function () {
+    if (this.get('manualInstall') === false) {
+      if (this.get('passphrase') !== this.get('confirmPassphrase')) {
+        this.set('passphraseMatchErr', true);
+      } else {
+        this.set('passphraseMatchErr', false);
+      }
+    }
+  }.observes('manualInstall', 'passphrase', 'confirmPassphrase'),
+
+  localRepoErr: function () {
+    if (this.get('localRepo') === true) {
+      if (this.get('localRepoPath') === '') {
+        this.set('softRepoLocalPathNullErr', true);
+      } else {
+        this.set('softRepoLocalPathNullErr', false);
+      }
+    } else {
+      this.set('softRepoLocalPathNullErr', false);
     }
+  }.observes('localRepoPath'),
 
-  }.observes('sshKeyNullErr'),
+  validateStep2: function () {
+    this.hostErr();
+    this.sshKeyErr();
+    this.passphraseErr();
+    this.localRepoErr();
+    return (this.checkHostNames());
+  },
+
+
+  hostManageErr: function () {
+    return (this.get('hostNameEmptyError') || this.get('hostNameNotRequireErr') ||
+      this.get('hostNameErr') || this.get('sshKeyNullErr') || this.get('passphraseMatchErr'));
+  }.property('hostNameErrMsg', 'sshKeyNullErr', 'passphraseMatchErr'),
 
   sshLessInstall: function () {
     if (this.get('manualInstall') === true) {
       this.set('hostManageErr', false);
       this.set('hostNameEmptyError', false);
-      this.set('hostNameNotRequireErr', false);
       this.set('sshKeyNullErr', false);
       this.set('passphraseMatchErr', false);
     }
   }.observes('manualInstall'),
 
   advOptErr: function () {
-    if (this.get('softRepoLocalPathNullErr')) {
-      return true;
+    return this.get('softRepoLocalPathNullErr');
+  }.property('softRepoLocalPathNullErr'),
+
+  step2Err: function () {
+    if (this.get('hostManageErr') === true || this.get('advOptErr') === true) {
+      this.set('isSubmitDisabled', true);
     } else {
-      return false
+      this.set('isSubmitDisabled', false);
     }
-  }.property('softRepoLocalPathNullErr'),
+  }.observes('hostManageErr', 'advOptErr'),
 
   softRepo: function () {
     if (this.get('localRepo') === false) {
@@ -104,7 +171,7 @@ App.InstallerStep2Controller = Em.Controller.extend({
 
 
   evaluateStep2: function () {
-    // TODO:DONE
+
     //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
@@ -118,61 +185,20 @@ App.InstallerStep2Controller = Em.Controller.extend({
     console.log('TRACE: Entering controller:InstallerStep2:evaluateStep2 function');
     /**                 task1                **/
     console.log('value of manual install is: ' + this.get('manualInstall'));
-    if (this.get('hostNames') === '' && this.get('manualInstall') === false) {
-      this.set('hostNameEmptyError', true);
-      this.set('hostNameNotRequireErr', false);
-      return false;
-    } else if (this.get('hostNames') !== '' && this.get('manualInstall') === true) {
-      this.set('hostNameNotRequireErr', true);
-      this.set('hostNameEmptyError', false);
-      return false;
-    } else {
-      this.set('hostNameEmptyError', false);
-      this.set('hostNameNotRequireErr', false);
-    }
 
-    if (this.get('manualInstall') === false) {
-      if (this.get('sshKey') === '') {
-        this.set('sshKeyNullErr', true);
-        return false;
-      }
-      else {
-        this.set('sshKeyNullErr', false);
-      }
-      if (this.get('passphrase') !== this.get('confirmPassphrase')) {
-        this.set('passphraseMatchErr', true);
-        return false;
-      } else {
-        this.set('passphraseMatchErr', false);
-      }
-    }
+    var validateResult = this.validateStep2();
 
-    if (this.get('localRepo') === true) {
-      if (this.get('localRepoPath') === '') {
-        this.set('softRepoLocalPathNullErr', true);
-        return false;
-      } else {
-        this.set('softRepoLocalPathNullErr', false);
-      }
+    if (this.get('isSubmitDisabled') === true || validateResult === false) {
+      console.log("ERROR: error in validation");
+      return false;
     } else {
-      this.set('softRepoLocalPathNullErr', false);
+      if (this.get('manualInstall') === true) {
+        this.manualInstallPopup();
+        return true;
+      }
     }
 
 
-    /**                 task2  task3 task4                      **/
-    this.hostNameArr = this.get('hostNames').split('\s');
-    for (var i = 0; i < this.hostNameArr.length; i++) {
-      //TODO: other validation for hostnames will be covered over here
-      // For now hostname that are starting or ending with '-' are not allowed
-      if (/^\-/.test(this.hostNameArr[i]) || /\-$/.test(this.hostNameArr[i])) {
-        console.log('Invalide host name' + this.hostNameArr[i]);
-        alert('Invalide host name: ' + this.hostNameArr[i]);
-        this.set('hostNameErr', true);
-        return false;
-      } else {
-        this.set('hostNameErr', false);
-      }
-    }
     var hostInfo = {};
     for (var i = 0; i < this.hostNameArr.length; i++) {
       hostInfo[this.hostNameArr[i]] = {'name': this.hostNameArr[i]};
@@ -198,7 +224,7 @@ App.InstallerStep2Controller = Em.Controller.extend({
 
     /**                      task6                  **/
 
-
+    //just an additional check. If manualInstall is true, program should have not reached over here
     if (this.get('manualInstall') === false) {
       // For now using mock jquery call
       //TODO: hook up with bootstrap call
@@ -211,36 +237,44 @@ App.InstallerStep2Controller = Em.Controller.extend({
         timeout: 2000,
         success: function () {
           console.log("TRACE: In success function for the post bootstrap function");
-          return true;
+          App.transitionTo('step3');
         },
         error: function () {
-          console.log("ERRORRORR");
+          console.log("ERROR: bootstrap post call failed");
           return false;
         },
         statusCode: {
           404: function () {
             console.log("URI not found.");
-            alert("URI not found");
+            alert("URI not found,. This needs to be hooked up with a @POST bootstrap call");
             //After the bootstrap call hook up change the below return statement to "return false"
+            console.log("TRACE: In faliure function for the post bootstrap function");
+            //Remove below line, once bootstrap has been implemented
+            App.router.transitionTo('step3');
+
             return true;
           }
         },
         dataType: 'application/json'
       });
     } else {
-      //popup window for "manual install" caution. if "OK" button is hit return true else return false
-      console.log('In showpopup function');
-      this.set('showPopup', true);
-
-      return true;
+      console.log("ERROR: ASSERTION FAILED -> program should have never reached over here");
     }
 
-    console.log("TRACE: program should never reach over here!!!:( ");
+  },
 
-    if ('success' == 'success') {
-      return true;
-    } else {
-      return false;
-    }
+
+  manualInstallPopup: function (event) {
+    App.ModalPopup.show({
+      header: 'Manual Install',
+      onPrimary: function () {
+        this.hide();
+        App.router.transitionTo('step3');
+      },
+      bodyClass: Ember.View.extend({
+        templateName: require('templates/installer/step2ManualInstallPopup')
+      })
+    });
   }
+
 });

+ 57 - 3
ambari-web/app/controllers/installer/step3.js

@@ -15,18 +15,71 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 var App = require('app');
+
 App.InstallerStep3Controller = Em.Controller.extend({
   name: 'installerStep3Controller',
   content: [],
+  /*
+   This flag is "true" While bootstrapping is in process.
+   Parsing function or timeout on bootstrap rest call can make it false.
+   */
+  bootstrap: '',
+
+  /*
+   * Below function will be called on successfully leaving step2 and entering
+   * step3. "Retry" button shall also make use of it.
+   */
+
+  startBootstrap: function () {
+    console.log("TRACE: Entering controller->installer->step3->startBootstrap() function");
+    App.bootstrap = self.setInterval(function () {
+      this.doBootstrap()
+    }, 200);
+  },
+
+  stopBootstrap: function () {
+    window.clearInterval(App.bootstrap);
+  },
+
+  doBootstrap: function () {
+    $.ajax({
+      type: 'GET',
+      url: '/ambari_server/api/bootstrap',
+      async: false,
+      timeout: 2000,
+      success: function () {
+        console.log("TRACE: In success function for the GET bootstrap call");
+      },
+      error: function () {
+        console.log("ERRORRORR");
+        self.set('bootstrap', false);  //Never toggle this for now, flow goes in infinite loop
+        this.stopBootstrap();
+      },
+      statusCode: {
+        404: function () {
+          console.log("URI not found.");
+          alert("URI not found");
+          result = false;
+        }
+      },
+      dataType: 'application/json'
+    });
+
+  },
+
+  retry: function () {
+    this.doBootstrap();
+  },
+
+
   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.
      *
-     * task1 will be a function called on entering step3 from step3 connectoutlet or init function in InstallerStep3 View.
+     *
      * 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
@@ -41,6 +94,7 @@ App.InstallerStep3Controller = Em.Controller.extend({
     //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;
   }
 });
+

+ 3 - 3
ambari-web/app/initialize.js

@@ -30,6 +30,6 @@ require('router');
 App.initialize();
 
 console.log('after initialize');
-console.log('localStorage:Ambari.authenticated=' + localStorage.getItem('Ambari' + 'authenticated'));
-console.log('localStorage:currentStep=' + localStorage.getItem(App.get('router').getLoginName() + 'Installer' + 'currentStep'));
-console.log('router.authenticated=' + App.get('router.loggedIn'));
+console.log('TRACE: app.js-> localStorage:Ambari.authenticated=' + localStorage.getItem('Ambari' + 'authenticated'));
+console.log('TRACE: app.js-> localStorage:currentStep=' + localStorage.getItem(App.get('router').getLoginName() + 'Installer' + 'currentStep'));
+console.log('TRACE: app.js-> router.authenticated=' + App.get('router.loggedIn'));

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

@@ -44,6 +44,7 @@ App.messages = {
   'step8_header': 'Review',
   'step9_header': 'Install, Start and Test',
   'step10_header': 'Summary',
+  'step1_clusterPop': 'Enter a unique cluster name. Cluster name cannot be changed later.',
   'step1_clusterName_error_required': 'Cluster Name is required',
   'step1_clusterName_error_whitespaces': 'Cluster Name cannot contain white spaces',
   'step1_clusterName_error_specialChar': 'Cluster Name cannot contain special character',
@@ -53,21 +54,28 @@ App.messages = {
   'step2_targetHosts_label': 'Specify Hosts to Manage',
   'step2_hostNameEmptyError': 'host names cannot be left empty',
   'step2_sshKeyNullErr': 'ssh key cannot be empty',
-  'step2_passphraseMatchErr': '\"Confirm passphrase\" doesn\'t matches \"passphrase\" value',
+  'step2_passphraseMatchErr': 'Passphrases do not match',
   'step2_hostNameNotRequireErr': 'Host names not required for ssh-less install of ambari agents',
+  'step2_hostNameErr': 'Invalid host name: host name cannot start or end with hyphen',
   'step2_manualInstallOpt': 'Do not use SSH to automatically configure the hosts ',
   'step2_localRepoOpt': '',
   'step2_softRepo_default_localPath': '/etc/yum/repos.d/hdp',
   'step2_softRepo_remotePath': '',
   'step2_advancedOption_label': 'Advanced Options',
   'step2_repoConf_label': 'Software Repository Configuration File Path',
-  'step2_localRepoOpt': 'use a local software repository',
+  'step2_localRepoOpt': 'Use a local software repository',
+  'step2_localRepoErr': 'Local repository file path is required',
   'step2_localRepoExplan': '<p class=\"text-info\">The repository configuration file should be installed on each host in your cluster. ' +
     'This file instructs package manager to use your local software repository to retrieve software packages,instead of ' +
     'using internet.</p>',
+  'step2_hostPatternPop': 'Space, tab or new line can be used as deliminiter',
+  'step2_localRepoPop': 'The repository configuration file should be installed on each host' +
+    'in your cluster. This file instructs package manager to use your local' +
+    'software repository to retrieve software packages, instead of using the internet.',
   'step2_manualInstallExplain': 'Ambari agents will not be installed ' +
     'automatically. You will require to install it manually on each host of ' +
-    'your cluster. Agents should be started before you step to next stage.',
+    'your cluster. Agents should be started before you move to next stage.',
+  'step2_popupBody': 'Ambari agents should be manually installed before you proceed ahead.',
   'welcome_body': '<p>Ambari makes it easy for you to install, configure, and manage your Hadoop cluster.<br>First, ' +
     'we will walk you through setting up your cluster with a step-by-step wizard.</p>',
   'welcome_note': 'Before you proceed, make sure you have performed all the pre-installation steps.',

+ 11 - 6
ambari-web/app/routes/installer.js

@@ -48,9 +48,10 @@ module.exports = Em.Route.extend({
       router.setInstallerCurrentStep('1', false);
       router.get('installerController').connectOutlet('installerStep1');
     },
+
     next: function (router, context) {
       console.log('In step1 transiting to step2');
-      var result = router.get('installerStep1Controller').evaluateStep1();
+      var result = router.get('installerStep1Controller').validateStep1();
       if (result === true) {
         App.InstallerStep1View.remove;
         router.transitionTo('step2');
@@ -68,11 +69,9 @@ module.exports = Em.Route.extend({
     },
     back: Em.Router.transitionTo('step1'),
     next: function (router, context) {
-      console.log('In step2 transiting to step3');
-      $('#myModal').modal('hide');
       var result = router.get('installerStep2Controller').evaluateStep2();
-      if (result) {
-        router.transitionTo('step3');
+      if (result === false) {
+        console.log('ERROR: error in evaluateStep2 function');
       }
     }
   }),
@@ -84,7 +83,13 @@ module.exports = Em.Route.extend({
       router.get('installerController').connectOutlet('installerStep3');
     },
     back: Em.Router.transitionTo('step2'),
-    next: Em.Router.transitionTo('step4')
+    next: function (router, context) {
+      var result = router.get('installerStep3Controller').evaluateStep3();
+      if (result) {
+        console.log('In step3 transiting to step4');
+        router.transitionTo('step4');
+      }
+    }
   }),
 
   step4: Em.Route.extend({

+ 4 - 3
ambari-web/app/templates/installer/step1.hbs

@@ -19,12 +19,12 @@
 
 <h2>{{App.messages.step1_header}}</h2>
 <p class="alert alert-info">{{{App.messages.step1_body}}}</p>
-<div {{bindAttr class="validClusterName::error :control-group"}}>
+<div {{bindAttr class="invalidClusterName:error :control-group"}}>
   <label class="control-label" for="cluster-name">Name of Your Cluster
     <a href="javascript:void(null)"
        rel="popover"
        title="Cluster name"
-       data-content="Enter a unique cluster name. Cluster name cannot be changed later.">
+      {{bindAttr data-content= "App.messages.step1_clusterPop"}}>
       Learn more</a>
   </label>
 
@@ -36,7 +36,8 @@
 </div>
 
 <div class="btn-area">
-  <a class="btn btn-success" {{action next}}>Next</a>
+  <a
+    class="btn btn-success" {{bindAttr disabled= "invalidClusterName"}} {{action next}}>Next</a>
 </div>
 
 

+ 20 - 50
ambari-web/app/templates/installer/step2.hbs

@@ -17,26 +17,21 @@
 -->
 
 <h2>{{App.messages.step2_header}}</h2>
-{{#view App.Step2_parentView}}
 
 <h5 {{bindAttr class="hostManageErr:text-error"}}>{{App.messages.step2_targetHosts_label}}</h5>
-{{#view App.Step2_child_HostManageView}}
 
-<div {{bindAttr class="hostNamesErr:error :control-group"}}>
+<div {{bindAttr class="hostNameErrMsg:error :control-group"}}>
   <p>{{App.messages.step2_targetHosts}}
     <a href="javascript:void(null)"
        rel="popover"
        title="Delimiters"
-       data-content="Space, tabs or new line can be used as delimineter">
+      {{bindAttr data-content= "App.messages.step2_hostPatternPop"}}>
       Pattern expressions</a></p>
 
   <div class="controls">
     {{view Ember.TextArea class="span6" valueBinding="hostNames" rows="5" placeholder="host names"}}
-    {{#if hostNameEmptyError}}
-    <p class="help-inline">{{App.messages.step2_hostNameEmptyError}}</p>
-    {{/if}}
-    {{#if hostNameNotRequireErr}}
-    <p class="help-inline">{{App.messages.step2_hostNameNotRequireErr}}</p>
+    {{#if hostNameErrMsg}}
+    <p class="help-inline">{{hostNameErrMsg}}</p>
     {{/if}}
   </div>
 </div>
@@ -70,19 +65,14 @@
   </div>
 
 </div>
-{{/view}}
-
 
-{{#view App.Step2_child_AdvOpt}}
 <h5 {{bindAttr class="advOptErr:text-error"}}>{{App.messages.step2_advancedOption_label}}</h5>
 <label class="checkbox">
   {{App.messages.step2_manualInstallOpt}}
   <a href="javascript:void(null)"
      rel="popover"
      title="Manual Install"
-     data-content="Ambari agents will not be installed automatically. You will require
-     to install it manually on each host of your cluster. Agents should be
-     started before you move to next stage.">
+    {{bindAttr data-content= "App.messages.step2_manualInstallExplain"}}>
     Learn more</a>
   {{view Ember.Checkbox checkedBinding="manualInstall"}}
 </label>
@@ -91,56 +81,36 @@
   <a href="javascript:void(null)"
      rel="popover"
      title="Local repository"
-     data-content="The repository configuration file should be installed on each host
-     in your cluster. This file instructs package manager to use your local
-     software repository to retrieve software packages,instead of using internet.">
+    {{bindAttr data-content= "App.messages.step2_localRepoPop"}}>
     Learn more</a>
   {{view Ember.Checkbox checkedBinding="localRepo"}}
 </label>
+
 {{#if localRepo}}
-<div>
-  <label>{{App.messages.step2_repoConf_label}}</label>
-  {{view Ember.TextField type="text" placeholder="/etc/yum/repos.d/hdp" valueBinding="localRepoPath"}}
-  {{#if softRepoLocalPathNullErr}}
-  <p class="text-error">Local repository file path cannot be null</p>
-  {{/if}}
+<div {{bindAttr class="softRepoLocalPathNullErr:error :control-group"}}>
+  <div class="controls">
+    {{view Ember.TextField type="text" placeholder="/etc/yum/repos.d/hdp" valueBinding="localRepoPath"}}
+    {{#if softRepoLocalPathNullErr}}
+    <p class="help-inline">{{App.messages.step2_localRepoErr}}</p>
+    {{/if}}
+  </div>
   <div class="alert alert-info">
     {{{App.messages.step2_localRepoExplan}}}
   </div>
 </div>
 {{/if}}
-{{/view}}
+
 
 <div class="btn-area">
   <a class="btn" {{action back}}>Previous</a>
   {{#if manualInstall}}
-  <a href="#manualInstallModal" role="button" class="btn btn-success"
-     data-toggle="modal" {{action "clickHandler" target="view"}}>Validate</a>
+  <a role="button" class="btn btn-success"
+     style="float:right"
+     data-toggle="modal" {{action evaluateStep2 target="controller"}}>Validate</a>
   {{else}}
-  <a class="btn btn-success" {{action next}}> Discover and Validate </a>
+  <a class="btn btn-success" style="float:right" {{action next}}> Discover and
+    Validate </a>
   {{/if}}
 </div>
 
-<div class="modal" id="manualInstallModal" tabindex="-1" role="dialog"
-     aria-labelledby="myModalLabel" style="display: none;" aria-hidden="true">
-  <div class="modal-header">
-    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">
-      x
-    </button>
-    <h3 id="manualInstallModalLabel">Manual Install</h3>
-  </div>
-  <div class="modal-body">
-    <p>Ambari agents shoud be manually installed before you proceed ahead.</p>
-  </div>
-  <div class="modal-footer">
-    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
-    <button class="btn btn-primary" data-dismiss="modal"
-            aria-hidden="true" {{action next}} >Proceed
-    </button>
-  </div>
-</div>
-
-{{/view}}
-
-
 

+ 112 - 7
ambari-web/app/templates/installer/step3.hbs

@@ -16,13 +16,118 @@
 * limitations under the License.
 -->
 
-
 <h2>{{App.messages.step3_header}} </h2>
 <p class="alert alert-info">{{{App.messages.step3_body}}}</p>
-
-<a class="btn" {{action retry}}>Retry</a>
-<a class="btn" {{action remove}}>Remove</a> <br/>
+<div id="hosts" class="box">
+  <div class="box-header">
+    <div class="button-section">
+      <a class="btn btn-primary decommission" href="#" {{action retry}}><i
+        class="icon-repeat icon-white"></i>
+        Retry
+      </a>
+      <a class="btn btn-primary" href="#" {{action remove}}><i
+        class="icon-trash icon-white"></i>
+        Remove
+      </a>
+    </div>
+  </div>
+  <table class="table table-bordered table-striped">
+    <!--<colgroup>-->
+    <!--<col class="span1">-->
+    <!--<col class="span7">-->
+    <!--</colgroup>-->
+    <thead>
+    <tr>
+      <th>
+        <label class="checkbox">
+          <input type="checkbox">
+        </label>
+      </th>
+      <th>Status</th>
+      <!--  given by the parsing function that parses data from bootstrap call -->
+      <th>Name</th>
+      <!-- retrieved from local storage initially -->
+      <th>Message</th>
+      <!-- given by the parsing function that parses data from bootstrap call, dynamically assign the color -->
+      <th>Delete</th>
+      <!-- trash icon -->
+    </tr>
+    </thead>
+    <tbody>
+    <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 class="icon-trash"></i></a>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+  <div class="box-footer">
+    <hr/>
+    <div class="footer-pagination">
+    </div>
+  </div>
+</div>
 <div class="btn-area">
-    <a class="btn" {{action back}}>Back</a>
-    <a class="btn btn-success" {{action next}}>Next</a>
-</div>
+  <a class="btn" {{action back}}>Previous</a>
+  <a class="btn btn-success" style="float:right" {{action next}}>Next</a>
+</div>

+ 0 - 27
ambari-web/app/views/installer/step2.js

@@ -23,36 +23,9 @@ App.InstallerStep2View = Em.View.extend({
 
   templateName: require('templates/installer/step2'),
 
-  didInsertElement: function () {
-
-  }
-
-});
-
-App.Step2_parentView = Em.View.extend({
-  isVisible: true,
-  click: function () {
-    console.log('parent of step2');
-  },
   didInsertElement: function () {
     $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
-
   }
 });
 
-App.Step2_child_HostManageView = Em.View.extend({
-  isVisible: true,
-  click: function () {
-    console.log('host management: child of step2');
-  }
-
-});
-
 
-App.Step2_child_AdvOpt = Em.View.extend({
-  isVisible: true,
-
-  click: function () {
-    console.log('Advance option : child of step2');
-  }
-});

+ 6 - 4
ambari-web/app/views/installer/step3.js

@@ -21,10 +21,12 @@ var App = require('app');
 
 App.InstallerStep3View = Em.View.extend({
 
-    templateName: require('templates/installer/step3'),
+  templateName: require('templates/installer/step3'),
 
-    submit: function(router, event) {
-        alert('form3 submitted');
-    }
+  didInsertElement: function () {
+    $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
+    var result = this.get('controller');
+    result.checkBootstrap();
+  }
 
 });