Repository: ambari Updated Branches: refs/heads/branch-feature-AMBARI-21674 a11a8712a -> d43882d1a
UI changes for prompting user during registration failure for new OS family (Sonia Garudi via ncole) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d43882d1 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d43882d1 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d43882d1 Branch: refs/heads/branch-feature-AMBARI-21674 Commit: d43882d1aa55dc2f7e358fec176641b57a6c71f6 Parents: a11a871 Author: Nate Cole <[email protected]> Authored: Thu Nov 30 09:07:47 2017 -0500 Committer: Nate Cole <[email protected]> Committed: Thu Nov 30 09:07:47 2017 -0500 ---------------------------------------------------------------------- .../ambari/server/bootstrap/BSHostStatus.java | 9 ++ .../server/bootstrap/BSHostStatusCollector.java | 7 + .../app/controllers/wizard/step3_controller.js | 114 +++++++++++++++ ambari-web/app/messages.js | 3 + ambari-web/app/styles/wizard.less | 22 +++ ambari-web/app/templates/wizard/step3.hbs | 47 +++++++ .../test/controllers/wizard/step3_test.js | 140 +++++++++++++++++++ 7 files changed, 342 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatus.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatus.java b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatus.java index 3d1b31c..a91a1f2 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatus.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatus.java @@ -41,7 +41,16 @@ public class BSHostStatus { private String statusAction; @XmlElement private String log; + @XmlElement + private String osType; + + public String getOsType() { + return osType; + } + public void setOsType(String osType) { + this.osType = osType; + } public void setStatus(String status) { this.status = status; http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatusCollector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatusCollector.java b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatusCollector.java index b72ca20..45e0870 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatusCollector.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSHostStatusCollector.java @@ -79,6 +79,13 @@ class BSHostStatusCollector { if (statusCode.equals("0")) { status.setStatus("DONE"); } + //the status code 44 is returned if ambari-repo property isn't set for the host's os_type in ambari.properties file. + //'44:<os_type>' is written to the .done file for the respective host. + if (statusCode.startsWith("44")) { + String[] sc = statusCode.split(":"); + status.setOsType(sc[1]); + statusCode = sc[0]; + } updateStatus(status, statusCode); } catch (IOException e) { http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-web/app/controllers/wizard/step3_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/wizard/step3_controller.js b/ambari-web/app/controllers/wizard/step3_controller.js index 29393e3..5ff2cdc 100644 --- a/ambari-web/app/controllers/wizard/step3_controller.js +++ b/ambari-web/app/controllers/wizard/step3_controller.js @@ -184,6 +184,9 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check this.set('isLoaded', false); this.set('isSubmitDisabled', true); this.set('stopChecking', false); + this.set('newAmbariOsTypes', []); + this.set('promptAmbariRepoUrl', false); + this.set('bootstrapInProgress', false); }, /** @@ -301,6 +304,24 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check return App.showConfirmationPopup(function () { App.router.send('removeHosts', hosts); self.hosts.removeObjects(hosts); + hosts.forEach(function(_host) { + var ambariOsTypeIndex = -1; + self.newAmbariOsTypes.some(function(os, index) { + if (os.hosts.contains(_host.name)){ + ambariOsTypeIndex = index; + return true; + } + }); + if (ambariOsTypeIndex != -1) { + self.newAmbariOsTypes[ambariOsTypeIndex].hosts.removeObject(_host.name); + if (self.newAmbariOsTypes[ambariOsTypeIndex].hosts.length == 0) { + self.newAmbariOsTypes.removeAt(ambariOsTypeIndex); + } + if (!self.newAmbariOsTypes.length) { + self.set('promptAmbariRepoUrl', false); + } + } + }); self.stopRegistration(); if (!self.hosts.length) { self.set('isSubmitDisabled', true); @@ -371,6 +392,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check * @method retryHosts */ retryHosts: function (hosts) { + this.set('promptAmbariRepoUrl', false); var self = this; var bootStrapData = JSON.stringify({ 'verbose': true, @@ -385,6 +407,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check this.set('isHostsWarningsLoaded', false); this.set('stopChecking', false); this.set('isSubmitDisabled', true); + this.set('bootstrapInProgress', true); if (this.get('content.installOptions.manualInstall')) { this.startRegistration(); } else { @@ -523,6 +546,37 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check var installedHosts = App.Host.find().mapProperty('hostName'); var isErrorStatus = data.status == 'ERROR'; this.set('isBootstrapFailed', isErrorStatus); + + this.set('bootstrapInProgress', keepPolling); + // check for prompting ambari repo url + if (!keepPolling && data.hostsStatus.someProperty('statusCode', "44")) { + data.hostsStatus.forEach(function(host) { + if (host.statusCode == 44) { + var ambariOsTypeIndex = -1; + this.newAmbariOsTypes.some(function(diffOs, index) { + if (diffOs.os_type == host.osType) { + ambariOsTypeIndex = index; + return true; + } + }); + if (ambariOsTypeIndex == -1) { + var tmpNewAmbariOsType = { + 'os_type' : host.osType, + 'ambari_repo' : "", + 'hosts' : [] + }; + tmpNewAmbariOsType.hosts.push(host.hostName); + this.newAmbariOsTypes.pushObject(tmpNewAmbariOsType); + } else { + if (!this.newAmbariOsTypes[ambariOsTypeIndex].hosts.contains(host.hostName)){ + this.newAmbariOsTypes[ambariOsTypeIndex].hosts.push(host.hostName); + } + } + } + },this); + this.set('promptAmbariRepoUrl',true); + } + if (isErrorStatus || data.hostsStatus.mapProperty('hostName').removeObjects(installedHosts).length != this.get('bootHosts').length) { var hosts = this.get('bootHosts'); @@ -552,6 +606,66 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check }, /** + * check ambari repository URL entered on the UI + * @method checkAmbariRepoUI + */ + checkAmbariRepoUI : (function() { + var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/; + this.newAmbariOsTypes.forEach(function(obj) { + if (obj.ambari_repo == '' || obj.ambari_repo == null) { + Em.set(obj, 'ambariRepoUIError', ""); + Em.set(obj, 'hasError', false); + } else if (/\s/.test(obj.ambari_repo) || !regex.test(obj.ambari_repo)) { + Em.set(obj, 'ambariRepoUIError', Em.I18n.t('installer.step3.ambariRepoUIError.stringError')); + Em.set(obj, 'hasError', true); + } else { + Em.set(obj, 'ambariRepoUIError', ""); + Em.set(obj, 'hasError', false); + } + }, this); + }).observes('[email protected]_repo'), + + /** + * return true if 1 or more ostypes have invalid URL + * @type {bool} + */ + invalidAmbariRepoUrlExists : (function() { + return this.newAmbariOsTypes.someProperty('hasError', true); + }).property('[email protected]'), + + /** + * return true if all textboxes for ambari repo url are empty + * @type {bool} + */ + allAmbariRepoUrlsEmpty : (function() { + return this.newAmbariOsTypes.getEach('ambari_repo').every(function(value) { + return value == null || value == ""; + }); + }).property('[email protected]_repo'), + + /** + * check conditions to disable ambari repo submit button + * @type {bool} + */ + checkAmbariRepoSubmitDisabled : function() { + return (this.get('isRegistrationInProgress') || !this.get('isWarningsLoaded')) || App.get('router.btnClickInProgress') || this.get('bootstrapInProgress'); + }.property('isRegistrationInProgress', 'isWarningsLoaded', 'bootstrapInProgress'), + + /** + * Is ambari repo submit button disabled + * @type {bool} + */ + isAmbariRepoURLSubmitDisabled : Em.computed.or('invalidAmbariRepoUrlExists', 'allAmbariRepoUrlsEmpty', 'checkAmbariRepoSubmitDisabled'), + + /** + * Do bootstrap calls with entered ambari repo url + * @method bootstrapWithAmbariRepoUrl + */ + bootstrapWithAmbariRepoUrl : function() { + + }, + + /** * Start hosts registration * @method startRegistration */ http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 390f803..a7a238c 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -871,6 +871,9 @@ Em.I18n.translations = { 'installer.step3.hostLogPopup.copy':'press CTRL+C', 'installer.step3.hostsTable.selectAll':'Select All Hosts', 'installer.step3.selectedHosts.popup.header':'Selected Hosts', + 'ambari.repository.url.os.textbox':'Please enter valid Ambari Repository URLs for the below OS types', + 'ambari.repository.baseUrl':'Ambari Repository Base URL', + 'installer.step3.ambariRepoUIError.stringError':'Please enter a valid URL', 'installer.step4.header':'Choose Services', 'installer.step4.body':'Choose which services you want to install on your cluster.', http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-web/app/styles/wizard.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/wizard.less b/ambari-web/app/styles/wizard.less index cb9eb92..fb51701 100644 --- a/ambari-web/app/styles/wizard.less +++ b/ambari-web/app/styles/wizard.less @@ -189,6 +189,28 @@ #warningsSection { margin: 0px 10px; } + .ambari-repo-url-panel { + p.help-block.validation-block { + &::before { + content: '\e083'; + color: #EF6162; + } + } + .btn-area { + padding: 10px; + } + input { + width:85%; + } + td { + .onerror { + .ember-text-field { + border-color: #EF6162; + } + } + } + } + } #step4, #step5, #step6 { a.selected { http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-web/app/templates/wizard/step3.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/wizard/step3.hbs b/ambari-web/app/templates/wizard/step3.hbs index e00012c..571c86e 100644 --- a/ambari-web/app/templates/wizard/step3.hbs +++ b/ambari-web/app/templates/wizard/step3.hbs @@ -140,6 +140,53 @@ {{/unless}} </div> {{/unless}} + + {{#if promptAmbariRepoUrl}} + <div class="ambari-repo-url-panel"> + <div class="step-title"> <p>Ambari Repositories</p></div> + <div class="step-description"> + <div class="alert alert-info"> + <label>{{t ambari.repository.url.os.textbox}}</label> + </div> + </div> + <table class="table table-hover"> + <thead> + <tr> + <th class="col-sm-2">{{t common.os}} </th> + <th class="col-sm-3">{{t ambari.repository.baseUrl}}</th> + </tr> + </thead> + <tbody> + {{#each os in newAmbariOsTypes}} + <tr> + <td class="col-sm-2">{{os.os_type }}</td> + <td class="col-sm-9" colspan="2"> + <table class="table table-condensed no-borders inner-table"> + <tbody> + <tr> + <td class="col-sm-3" {{bindAttr class="os.hasError:onerror"}}>{{view Ember.TextField valueBinding="os.ambari_repo" }}</td> + </tr> + <tr> + <td class="col-sm-3"> + {{#if os.hasError}} + <p class="help-block validation-block col-sm-9" {{QAAttr "ambari-repo-validation"}}>{{os.ambariRepoUIError}}</p> + {{/if}} + </td> + </tr> + </tbody> + </table> + </td> + </tr> + {{/each}} + </tbody> + </table> + + <div class="btn-area"> + <button type="button" class="btn" {{bindAttr disabled="isAmbariRepoURLSubmitDisabled" }} {{action bootstrapWithAmbariRepoUrl target="controller"}}>Validate + </div> + </div> + {{/if}} + </div> </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/d43882d1/ambari-web/test/controllers/wizard/step3_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/wizard/step3_test.js b/ambari-web/test/controllers/wizard/step3_test.js index b8bc794..be1affe 100644 --- a/ambari-web/test/controllers/wizard/step3_test.js +++ b/ambari-web/test/controllers/wizard/step3_test.js @@ -378,6 +378,41 @@ describe('App.WizardStep3Controller', function () { expect(c.get('isSubmitDisabled')).to.equal(true); }); + it('should remove ambari repo prompt if no host of os_type exist', function () { + var hosts = [ + Em.Object.create({name: 'host1'}), + Em.Object.create({name: 'host2'}) + ]; + c.reopen({ + hosts: hosts, + promptAmbariRepoUrl: true, + newAmbariOsTypes : [ + Em.Object.create({os_type : 'os1', hosts : ['host2']}) + ] + }); + var removeHosts = [{name: 'host2'}]; + c.removeHosts(removeHosts).onPrimary();; + expect(c.get('promptAmbariRepoUrl')).to.equal(false); + }); + + it('should not remove ambari repo prompt if a host of os_type exist', function () { + var hosts = [ + Em.Object.create({name: 'host1'}), + Em.Object.create({name: 'host2'}), + Em.Object.create({name: 'host3'}) + ]; + c.reopen({ + hosts: hosts, + promptAmbariRepoUrl: true, + newAmbariOsTypes : [ + Em.Object.create({os_type : 'os1', hosts : ['host2','host3']}) + ] + }); + var removeHosts = [{name: 'host2'}]; + c.removeHosts(removeHosts).onPrimary();; + expect(c.get('promptAmbariRepoUrl')).to.equal(true); + }); + }); describe('#removeSelectedHosts', function () { @@ -2560,6 +2595,111 @@ describe('App.WizardStep3Controller', function () { }); + describe('#doBootstrapSuccessCallback', function () { + + it('should prompt for ambari repo url if os_type of host added is different', function () { + c.reopen({ + bootHosts: [ + Em.Object.create({name: 'host1', bootStatus: 'FAILED'}) + ], + newAmbariOsTypes: [] + }); + var data1 = { + status: 'ERROR', + hostsStatus: [ + Em.Object.create({hostName: 'host1', status: 'FAILED', statusCode: '44', osType: 'os1', log: 'log'}) + ] + }; + c.doBootstrapSuccessCallback(data1); + expect(c.get('promptAmbariRepoUrl')).to.equal(true); + }); + }); + + describe('#checkAmbariRepoUI', function () { + + it('check space', function () { + var newAmbariOsTypes = [ + Em.Object.create({os_type : 'os1', ambari_repo : "ambari repo", hosts : ['host1']}) + ]; + c.reopen({ + newAmbariOsTypes: newAmbariOsTypes + }); + c.checkAmbariRepoUI(); + expect(c.get('newAmbariOsTypes')[0].hasError).to.equal(true); + expect(c.get('newAmbariOsTypes')[0].ambariRepoUIError).to.equal("Please enter a valid URL"); + }); + + it('check invalid url', function () { + var newAmbariOsTypes = [ + Em.Object.create({os_type : 'os1', ambari_repo : "http//ambari-repo", hosts : ['host1']}) + ]; + c.reopen({ + newAmbariOsTypes: newAmbariOsTypes + }); + c.checkAmbariRepoUI(); + expect(c.get('newAmbariOsTypes')[0].hasError).to.equal(true); + expect(c.get('newAmbariOsTypes')[0].ambariRepoUIError).to.equal("Please enter a valid URL"); + }); + }); + + describe('#isAmbariRepoURLSubmitDisabled', function () { + + var cases = [ + { + allAmbariRepoUrlsEmpty: true, + invalidAmbariRepoUrlExists: false, + isAmbariRepoURLSubmitDisabled: true, + description: 'all textboxes are empty', + title: 'all empty ' + }, + { + allAmbariRepoUrlsEmpty: false, + invalidAmbariRepoUrlExists: true, + isAmbariRepoURLSubmitDisabled: true, + description: 'multiple textboxes with some empty and some with invalid urls', + title: 'has invalid, has empty' + }, + { + allAmbariRepoUrlsEmpty: false, + invalidAmbariRepoUrlExists: false, + isAmbariRepoURLSubmitDisabled: false, + description: 'multiple textboxes with some empty and some with valid urls', + title: 'has valid, has empty' + }, + { + allAmbariRepoUrlsEmpty: false, + invalidAmbariRepoUrlExists: false, + isAmbariRepoURLSubmitDisabled: false, + description: 'all textboxes valid urls', + title: 'all valid' + } + ]; + + cases.forEach(function (item) { + + describe(item.description, function () { + + beforeEach(function () { + c.reopen({ + checkAmbariRepoSubmitDisabled: false, + allAmbariRepoUrlsEmpty: item.allAmbariRepoUrlsEmpty, + invalidAmbariRepoUrlExists: item.invalidAmbariRepoUrlExists + }); + sinon.stub(App, 'get').withArgs('router.btnClickInProgress').returns(false); + }); + + afterEach(function () { + App.get.restore(); + }); + + it(item.title, function () { + expect(c.get('isAmbariRepoURLSubmitDisabled')).to.equal(item.isAmbariRepoURLSubmitDisabled); + }); + }); + }); + + }); + describe('#getJDKNameSuccessCallback', function () { it('should set proper data to controller properties', function () {
