AMBARI-20698. Ability to export blueprint via Ambari installer UI (alexantonenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/4427a336 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/4427a336 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/4427a336 Branch: refs/heads/branch-feature-AMBARI-20859 Commit: 4427a3365732dd971dc294f4feda75e247be4605 Parents: 99d4ca1 Author: Alex Antonenko <[email protected]> Authored: Wed May 17 15:32:05 2017 +0300 Committer: Alex Antonenko <[email protected]> Committed: Wed May 17 15:51:50 2017 +0300 ---------------------------------------------------------------------- .../app/controllers/wizard/step8_controller.js | 167 ++++++++++++++++++- ambari-web/app/messages.js | 1 + ambari-web/app/templates/wizard/step8.hbs | 5 + .../test/controllers/wizard/step8_test.js | 132 +++++++++------ 4 files changed, 254 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/4427a336/ambari-web/app/controllers/wizard/step8_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js index 4678d03..7e318e0 100644 --- a/ambari-web/app/controllers/wizard/step8_controller.js +++ b/ambari-web/app/controllers/wizard/step8_controller.js @@ -18,6 +18,7 @@ var App = require('app'); var stringUtils = require('utils/string_utils'); +var fileUtils = require('utils/file_utils'); App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wizardDeployProgressControllerMixin, App.ConfigOverridable, App.ConfigsSaverMixin, { @@ -1764,7 +1765,171 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz }); }, + getComponentsForHost: function(host) { + if(!host.hostComponents) { + App.router.get('installerController').get('allHosts'); + } + var componentNameDetail = []; + host.hostComponents.mapProperty('componentName').forEach(function (componentName) { + if(componentName === 'CLIENT') { + this.get('content.clients').mapProperty('component_name').forEach(function (clientComponent) { + componentNameDetail.push({ name : clientComponent }); + }, this); + return; + } + componentNameDetail.push({ name : componentName }); + }, this); + return componentNameDetail; + }, + + getPropertyAttributesForConfigType : function(configs) { + //Currently only looks for final properties, if any + var finalProperties = configs.filterProperty('isFinal', 'true'); + var propertyAttributes = {}; + finalProperties.forEach(function (finalProperty) { + propertyAttributes[finalProperty['name']] = "true"; + }); + var finalPropertyMap = {}; + if (!App.isEmptyObject(finalProperties)) { + finalPropertyMap = { + "isFinal": propertyAttributes + }; + } + return finalPropertyMap; + }, + + getConfigurationDetailsForConfigType: function(configs) { + var configDetails = {}; + var self = this; + configs.forEach(function (propertyObj) { + configDetails[propertyObj['name']] = propertyObj['value']; + }, this); + var configurationsDetails = { + "properties_attributes": self.getPropertyAttributesForConfigType(configs), + "properties": configDetails + }; + return configurationsDetails; + }, + + hostInExistingHostGroup: function (newHost, cluster_template_host_groups) { + var hostGroupMatched = false; + cluster_template_host_groups.some(function (existingHostGroup) { + if(!hostGroupMatched) { + var fqdnInHostGroup = existingHostGroup.hosts[0].fqdn; + var componentsInExistingHostGroup = this.getRegisteredHosts().filterProperty('hostName', fqdnInHostGroup)[0].hostComponents; + if(componentsInExistingHostGroup.length !== newHost.hostComponents.length) { + return; + } else { + var componentMismatch = false; + componentsInExistingHostGroup.forEach(function (componentInExistingHostGroup, index) { + if(!componentMismatch) { + if(!newHost.hostComponents.mapProperty('componentName').includes(componentInExistingHostGroup.componentName)) { + componentMismatch = true; + } + } + }); + if(!componentMismatch) { + hostGroupMatched = true; + existingHostGroup["cardinality"]["cardinality"] = parseInt(existingHostGroup["cardinality"]["cardinality"]) + 1; + existingHostGroup.hosts.push({"fqdn" : newHost.hostName}) + return true; + } + } + } + }, this); + return hostGroupMatched; + }, + + generateBlueprint: function () { + console.log("Prepare blueprint for download..."); + var blueprint = {}; + var self = this; + //service configurations + var totalConf = []; + //Add cluster-env + var clusterEnv = this.get('configs').filterProperty('filename', 'cluster-env.xml'); + var configurations = {}; + configurations["cluster-env"] = self.getConfigurationDetailsForConfigType(clusterEnv); + totalConf.push(configurations); + //Add configurations for selected services + this.get('selectedServices').forEach(function (service) { + Object.keys(service.get('configTypes')).forEach(function (type) { + if (!this.get('serviceConfigTags').someProperty('type', type)) { + var configs = this.get('configs').filterProperty('filename', App.config.getOriginalFileName(type)); + var configurations = {}; + configurations[type] = self.getConfigurationDetailsForConfigType(configs); + totalConf.push(configurations); + } + }, this); + }, this); + + //TODO address configGroups + var host_groups = []; + var cluster_template_host_groups = []; + var counter = 0; + + this.getRegisteredHosts().filterProperty('isInstalled', false).map(function (host) { + var clusterTemplateHostGroupDetail = {}; + if(self.hostInExistingHostGroup(host, cluster_template_host_groups)) { + return; + } + + var hostGroupId = "host_group_" + counter; + var cardinality = {"cardinality": 1}; + var hostGroupDetail = { + "name": hostGroupId, + "components": self.getComponentsForHost(host), + cardinality + }; + hostGroupDetail.toJSON = function () { + var hostGroupDetailResult = {}; + for (var x in this) { + if (x === "cardinality") { + hostGroupDetailResult[x] = (this[x]["cardinality"]).toString(); + } else { + hostGroupDetailResult[x] = this[x]; + } + } + return hostGroupDetailResult; + } + host_groups.push(hostGroupDetail); + var hostListForGroup = []; + var hostDetail = { + "fqdn": host.hostName + } + hostListForGroup.push(hostDetail); + clusterTemplateHostGroupDetail = { + "name": hostGroupId, + "hosts": hostListForGroup, + cardinality + }; + clusterTemplateHostGroupDetail.toJSON = function () { + return _.omit(this, [ "cardinality" ]); + }; + + cluster_template_host_groups.push(clusterTemplateHostGroupDetail); + counter++; + }, this); + + var selectedStack = App.Stack.find().findProperty('isSelected', true); + blueprint = { //TODO: bp name + 'configurations':totalConf, + 'host_groups':host_groups, + 'Blueprints':{'stack_name':selectedStack.get('stackName'), 'stack_version':selectedStack.get('stackVersion')} + }; + fileUtils.downloadTextFile(JSON.stringify(blueprint), 'json', 'blueprint.json') + + var cluster_template = { + "blueprint": App.clusterStatus.clusterName, + "config_recommendation_strategy" : "NEVER_APPLY", + "provision_action" : "INSTALL_AND_START", + "configurations":[], + "host_groups":cluster_template_host_groups + }; + fileUtils.downloadTextFile(JSON.stringify(cluster_template), 'json', 'clustertemplate.json') + }, + downloadCSV: function() { App.router.get('kerberosWizardStep5Controller').getCSVData(false); } -}); +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/4427a336/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index b8e7728..f34cbdc 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -125,6 +125,7 @@ Em.I18n.translations = { 'common.more': 'More...', 'common.print':'Print', 'common.deploy':'Deploy', + 'common.generate.blueprint':'Generate Blueprint', 'common.message':'Message', 'common.tasks':'Tasks', 'common.open':'Open', http://git-wip-us.apache.org/repos/asf/ambari/blob/4427a336/ambari-web/app/templates/wizard/step8.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/wizard/step8.hbs b/ambari-web/app/templates/wizard/step8.hbs index ac32710..907b3d4 100644 --- a/ambari-web/app/templates/wizard/step8.hbs +++ b/ambari-web/app/templates/wizard/step8.hbs @@ -99,5 +99,10 @@ </button> <button type="button" class="btn btn-info pull-right" {{action printReview target="view"}}>{{t common.print}}</button> <button type="button" {{bindAttr class=":btn :btn-primary :pull-right :mrm controller.showDownloadCsv::hide"}} {{action downloadCSV target="controller"}}>{{t admin.kerberos.wizard.step5.downloadCSV}}</button> + {{#unless App.router.clusterInstallCompleted}} + <button type="button" class="btn btn-success pull-right" {{action generateBlueprint target="controller"}}><i class="glyphicon glyphicon-download-alt"></i> + {{t common.generate.blueprint}} + </button> + {{/unless}} </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/4427a336/ambari-web/test/controllers/wizard/step8_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/wizard/step8_test.js b/ambari-web/test/controllers/wizard/step8_test.js index 7cdb69a..1a3214e 100644 --- a/ambari-web/test/controllers/wizard/step8_test.js +++ b/ambari-web/test/controllers/wizard/step8_test.js @@ -50,7 +50,73 @@ var configs = Em.A([ Em.Object.create({filename: 'falcon-startup.properties.xml', name: 'p1', value: 'v1'}), Em.Object.create({filename: 'falcon-startup.properties.xml', name: 'p2', value: 'v2'}), Em.Object.create({filename: 'falcon-runtime.properties.xml', name: 'p1', value: 'v1'}), - Em.Object.create({filename: 'falcon-runtime.properties.xml', name: 'p2', value: 'v2'}) + Em.Object.create({filename: 'falcon-runtime.properties.xml', name: 'p2', value: 'v2'}), + Em.Object.create({filename: 'cluster-env.xml', name: 'p1', value: 'v1'}), +]); + +var services = Em.A([ + Em.Object.create({ + serviceName: 's1', + isSelected: true, + isInstalled: false, + displayNameOnSelectServicePage: 's01', + isClientOnlyService: false, + serviceComponents: Em.A([ + Em.Object.create({ + isClient: true + }) + ]), + configTypes: Em.A([ + Em.Object.create({ + type: 'cluster-env' + }) + ]), + isHiddenOnSelectServicePage: false + }), + Em.Object.create({ + serviceName: 's2', + isSelected: true, + isInstalled: false, + displayNameOnSelectServicePage: 's02', + serviceComponents: Em.A([ + Em.Object.create({ + isMaster: true + }) + ]), + configTypes: Em.A([ + Em.Object.create({ + type: 'hdfs-site' + }) + ]), + isHiddenOnSelectServicePage: false + }), + Em.Object.create({ + serviceName: 's3', + isSelected: true, + isInstalled: false, + displayNameOnSelectServicePage: 's03', + serviceComponents: Em.A([ + Em.Object.create({ + isHAComponentOnly: true + }) + ]), + configTypes: [], + isHiddenOnSelectServicePage: false + }), + Em.Object.create({ + serviceName: 's4', + isSelected: true, + isInstalled: false, + displayNameOnSelectServicePage: 's03', + isClientOnlyService: true, + serviceComponents: Em.A([ + Em.Object.create({ + isClient: true + }) + ]), + configTypes: [], + isHiddenOnSelectServicePage: false + }) ]); function getController() { @@ -211,54 +277,6 @@ describe('App.WizardStep8Controller', function () { describe('#loadServices', function () { beforeEach(function () { - var services = Em.A([ - Em.Object.create({ - serviceName: 's1', - isSelected: true, - displayNameOnSelectServicePage: 's01', - isClientOnlyService: false, - serviceComponents: Em.A([ - Em.Object.create({ - isClient: true - }) - ]), - isHiddenOnSelectServicePage: false - }), - Em.Object.create({ - serviceName: 's2', - isSelected: true, - displayNameOnSelectServicePage: 's02', - serviceComponents: Em.A([ - Em.Object.create({ - isMaster: true - }) - ]), - isHiddenOnSelectServicePage: false - }), - Em.Object.create({ - serviceName: 's3', - isSelected: true, - displayNameOnSelectServicePage: 's03', - serviceComponents: Em.A([ - Em.Object.create({ - isHAComponentOnly: true - }) - ]), - isHiddenOnSelectServicePage: false - }), - Em.Object.create({ - serviceName: 's4', - isSelected: true, - displayNameOnSelectServicePage: 's03', - isClientOnlyService: true, - serviceComponents: Em.A([ - Em.Object.create({ - isClient: true - }) - ]), - isHiddenOnSelectServicePage: false - }) - ]); var selectedServices = services.filterProperty('isSelected'); var slaveComponentHosts = Em.A([ Em.Object.create({ @@ -2323,5 +2341,19 @@ describe('App.WizardStep8Controller', function () { }); }); - + //TODO + describe('#generateBlueprint', function () { + console.log("testing.......") + beforeEach(function () { + installerStep8Controller = getController(); + installerStep8Controller.set('configs', configs); + installerStep8Controller.set('content.services', services.filterProperty('isSelected')) + installerStep8Controller.set('selectedServices', []); + sinon.spy(installerStep8Controller, 'getConfigurationDetailsForConfigType'); + }); + it('should call generateBlueprint', function() { + installerStep8Controller.generateBlueprint(); + expect(installerStep8Controller.getConfigurationDetailsForConfigType.calledThrice).to.be.true; + }); + }); });
