diff --git a/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/datanode.py b/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/datanode.py index 8b9468a1c07..0c835f6338f 100644 --- a/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/datanode.py +++ b/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/datanode.py @@ -107,6 +107,23 @@ def status(self, env): env.set_params(status_params) datanode(action="status") + def refresh_namenode(self, env): + import params + env.set_params(params) + + if params.security_enabled: + Execute(params.dn_kinit_cmd, user = params.hdfs_user) + + hdfs_binary = self.get_hdfs_binary() + dfsadmin_base_command = get_dfsadmin_base_command(hdfs_binary) + command = dfsadmin_base_command + " -refreshNamenodes " + params.dfs_dn_ipc_address + Execute(command, + user=params.hdfs_user, + logoutput=True) + + Logger.info("DataNode has successfully registered to new namenode.") + return True + @retry(times=24, sleep_time=5, err_class=Fail) def check_datanode_shutdown(self, hdfs_binary): """ diff --git a/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/namenode.py b/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/namenode.py index ace1a30e8ff..5663d7e53e3 100644 --- a/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/namenode.py +++ b/ambari-server/src/main/resources/stacks/BIGTOP/3.2.0/services/HDFS/package/scripts/namenode.py @@ -207,6 +207,47 @@ def status(self, env): env.set_params(status_params) namenode(action="status", env=env) + def enter_safemode(self, env): + import params + env.set_params(params) + + if params.security_enabled: + Execute(params.nn_kinit_cmd, + user=params.hdfs_user + ) + + Execute("hdfs dfsadmin -safemode enter", + user=params.hdfs_user, + logoutput=True + ) + def save_namespace(self, env): + import params + env.set_params(params) + + if params.security_enabled: + Execute(params.nn_kinit_cmd, + user=params.hdfs_user + ) + + Execute("hdfs dfsadmin -saveNamespace", + user=params.hdfs_user, + logoutput=True + ) + + def leave_safemode(self, env): + import params + env.set_params(params) + + if params.security_enabled: + Execute(params.nn_kinit_cmd, + user=params.hdfs_user + ) + + Execute("hdfs dfsadmin -safemode leave", + user=params.hdfs_user, + logoutput=True + ) + def decommission(self, env): import params diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js index fbfa68d05c1..083dbc98d83 100644 --- a/ambari-web/app/controllers.js +++ b/ambari-web/app/controllers.js @@ -51,6 +51,11 @@ require('controllers/main/admin/highAvailability/nameNode/rollbackHA/step1_contr require('controllers/main/admin/highAvailability/nameNode/rollbackHA/step2_controller'); require('controllers/main/admin/highAvailability/nameNode/rollbackHA/step3_controller'); require('controllers/main/admin/highAvailability/nameNode/rollbackHA/rollback_wizard_controller'); +require('controllers/main/admin/highAvailability/nameNode/multipleNN/wizard_controller'); +require('controllers/main/admin/highAvailability/nameNode/multipleNN/step1_controller'); +require('controllers/main/admin/highAvailability/nameNode/multipleNN/step2_controller'); +require('controllers/main/admin/highAvailability/nameNode/multipleNN/step3_controller'); +require('controllers/main/admin/highAvailability/nameNode/multipleNN/step4_controller'); require('controllers/main/admin/highAvailability/resourceManager/wizard_controller'); require('controllers/main/admin/highAvailability/resourceManager/step1_controller'); require('controllers/main/admin/highAvailability/resourceManager/step2_controller'); diff --git a/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step1_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step1_controller.js new file mode 100644 index 00000000000..e6c55a21b52 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step1_controller.js @@ -0,0 +1,23 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep1Controller = Em.Controller.extend({ + name: "multipleNameNodeWizardStep1Controller", +}); diff --git a/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step2_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step2_controller.js new file mode 100644 index 00000000000..659225983fc --- /dev/null +++ b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step2_controller.js @@ -0,0 +1,37 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep2Controller = Em.Controller.extend(App.AssignMasterComponents, { + + name: "multipleNameNodeWizardStep2Controller", + + useServerValidation: false, + + mastersToShow: ['NAMENODE'], + + mastersToAdd: ['NAMENODE'], + + showCurrentPrefix: ['NAMENODE'], + + showAdditionalPrefix: ['NAMENODE'], + + showInstalledMastersFirst: true + +}); diff --git a/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step3_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step3_controller.js new file mode 100644 index 00000000000..4fcbb3b46b5 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step3_controller.js @@ -0,0 +1,333 @@ +/** + * 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. + */ + +/** + * @typedef {object} nnHaConfigDependencies + * @property {string} namespaceId + * @property {object} serverConfigs + * @property {string|number} nnHttpPort + * @property {string|number} nnHttpsPort + * @property {string|number} nnRpcPort + * @property {string|number} zkClientPort + */ + +var App = require('app'); +var blueprintUtils = require('utils/blueprint'); +//should handle +require('utils/configs/multiple_nn_config_initializer'); + +App.MultipleNameNodeWizardStep3Controller = Em.Controller.extend(App.BlueprintMixin, { + name: "multipleNameNodeWizardStep3Controller", + + selectedService: null, + stepConfigs: [], + serverConfigData: {}, + haConfig: $.extend(true, {}, require('data/configs/wizards/multiple_nn_haproperties').haConfig), + once: false, + isLoaded: false, + isNextDisabled: Em.computed.not('isLoaded'), + versionLoaded: true, + + hideDependenciesInfoBar: true, + + /** + * Map of sites and properties to delete + * @type Object + */ + configsToRemove: { + }, + + clearStep: function () { + this.get('stepConfigs').clear(); + this.set('serverConfigData', {}); + }, + + loadStep: function () { + this.clearStep(); + this.loadConfigsTags(); + }, + + loadConfigsTags: function () { + return App.ajax.send({ + name: 'config.tags', + sender: this, + success: 'onLoadConfigsTags', + error: 'onTaskError' + }); + }, + + + onLoadConfigsTags: function (data) { + var urlParams = []; + var hdfsSiteTag = data.Clusters.desired_configs['hdfs-site'].tag; + var coreSiteTag = data.Clusters.desired_configs['core-site'].tag; + var zkSiteTag = data.Clusters.desired_configs['zoo.cfg'].tag; + + urlParams.push('(type=hdfs-site&tag=' + hdfsSiteTag + ')'); + urlParams.push('(type=core-site&tag=' + coreSiteTag + ')'); + urlParams.push('(type=zoo.cfg&tag=' + zkSiteTag + ')'); + this.set("hdfsSiteTag", {name : "hdfsSiteTag", value : hdfsSiteTag}); + this.set("coreSiteTag", {name : "coreSiteTag", value : coreSiteTag}); + this.set("zkSiteTag", {name : "zkSiteTag", value : zkSiteTag}); + + if (App.Service.find().someProperty('serviceName', 'HBASE')) { + var hbaseSiteTag = data.Clusters.desired_configs['hbase-site'].tag; + urlParams.push('(type=hbase-site&tag=' + hbaseSiteTag + ')'); + this.set("hbaseSiteTag", {name : "hbaseSiteTag", value : hbaseSiteTag}); + } + if (App.Service.find().someProperty('serviceName', 'AMBARI_METRICS')) { + var amsHbaseSiteTag = data.Clusters.desired_configs['ams-hbase-site'].tag; + urlParams.push('(type=ams-hbase-site&tag=' + amsHbaseSiteTag + ')'); + this.set("amsHbaseSiteTag", {name : "amsHbaseSiteTag", value : amsHbaseSiteTag}); + } + if(App.Service.find().someProperty('serviceName', 'RANGER')) { + var rangerEnvTag = data.Clusters.desired_configs['ranger-env'].tag; + urlParams.push('(type=ranger-env&tag=' + rangerEnvTag + ')'); + this.set("rangerEnvTag", {name : "rangerEnvTag", value : rangerEnvTag}); + if('ranger-hdfs-plugin-properties' in data.Clusters.desired_configs) { + var rangerHdfsPluginPropertiesTag = data.Clusters.desired_configs['ranger-hdfs-plugin-properties'].tag; + urlParams.push('(type=ranger-hdfs-plugin-properties&tag=' + rangerHdfsPluginPropertiesTag + ')'); + this.set("rangerHdfsPluginPropertiesTag", { + name: "rangerHdfsPluginPropertiesTag", + value: rangerHdfsPluginPropertiesTag + }); + } + if('ranger-hdfs-audit' in data.Clusters.desired_configs) { + var rangerHdfsAuditTag = data.Clusters.desired_configs['ranger-hdfs-audit'].tag; + urlParams.push('(type=ranger-hdfs-audit&tag=' + rangerHdfsAuditTag + ')'); + this.set("rangerHdfsAuditTag", {name: "rangerHdfsAuditTag", value: rangerHdfsAuditTag}); + } + if('ranger-yarn-audit' in data.Clusters.desired_configs) { + var yarnAuditTag = data.Clusters.desired_configs['ranger-yarn-audit'].tag; + urlParams.push('(type=ranger-yarn-audit&tag=' + yarnAuditTag + ')'); + this.set("yarnAuditTag", {name: "yarnAuditTag", value: yarnAuditTag}); + } + if (App.Service.find().someProperty('serviceName', 'HBASE')) { + if('ranger-hbase-audit' in data.Clusters.desired_configs) { + var rangerHbaseAuditTag = data.Clusters.desired_configs['ranger-hbase-audit'].tag; + urlParams.push('(type=ranger-hbase-audit&tag=' + rangerHbaseAuditTag + ')'); + this.set("rangerHbaseAuditTag", {name: "rangerHbaseAuditTag", value: rangerHbaseAuditTag}); + } + if('ranger-hbase-plugin-properties' in data.Clusters.desired_configs) { + var rangerHbasePluginPropertiesTag = data.Clusters.desired_configs['ranger-hbase-plugin-properties'].tag; + urlParams.push('(type=ranger-hbase-plugin-properties&tag=' + rangerHbasePluginPropertiesTag + ')'); + this.set("rangerHbasePluginPropertiesTag", { + name: "rangerHbasePluginPropertiesTag", + value: rangerHbasePluginPropertiesTag + }); + } + } + if (App.Service.find().someProperty('serviceName', 'KAFKA')) { + if('ranger-kafka-audit' in data.Clusters.desired_configs) { + var rangerKafkaAuditTag = data.Clusters.desired_configs['ranger-kafka-audit'].tag; + urlParams.push('(type=ranger-kafka-audit&tag=' + rangerKafkaAuditTag + ')'); + this.set("rangerKafkaAuditTag", {name: "rangerKafkaAuditTag", value: rangerKafkaAuditTag}); + } + } + if (App.Service.find().someProperty('serviceName', 'HIVE')) { + if('ranger-hive-audit' in data.Clusters.desired_configs) { + var rangerHiveAuditTag = data.Clusters.desired_configs['ranger-hive-audit'].tag; + urlParams.push('(type=ranger-hive-audit&tag=' + rangerHiveAuditTag + ')'); + this.set("rangerHiveAuditTag", {name: "rangerHiveAuditTag", value: rangerHiveAuditTag}); + } + if('ranger-hive-plugin-properties' in data.Clusters.desired_configs) { + var rangerHivePluginPropertiesTag = data.Clusters.desired_configs['ranger-hive-plugin-properties'].tag; + urlParams.push('(type=ranger-hive-plugin-properties&tag=' + rangerHivePluginPropertiesTag + ')'); + this.set("rangerHivePluginPropertiesTag", { + name: "rangerHivePluginPropertiesTag", + value: rangerHivePluginPropertiesTag + }); + } + } + if (App.Service.find().someProperty('serviceName', 'RANGER_KMS')) { + if('ranger-kms-audit' in data.Clusters.desired_configs) { + var rangerKMSAuditTag = data.Clusters.desired_configs['ranger-kms-audit'].tag; + urlParams.push('(type=ranger-kms-audit&tag=' + rangerKMSAuditTag + ')'); + this.set("rangerKMSAuditTag", {name: "rangerKMSAuditTag", value: rangerKMSAuditTag}); + } + } + } + App.ajax.send({ + name: 'admin.get.all_configurations', + sender: this, + data: { + urlParams: urlParams.join('|') + }, + success: 'onLoadConfigs', + error: 'onTaskError' + }); + }, + + onLoadConfigs: function onLoadConfigs(data) { + var self = this; + this.set('serverConfigData', data); + this.removeConfigs(this.get('configsToRemove'), data); + this.tweakServiceConfigs(this.get('haConfig.configs')); + this.renderServiceConfigs(this.get('haConfig')); + //Construct Configs Object + var siteNames = ['hdfs-site']; + var configsFromServer = this.get('serverConfigData.items'); + var hdfsConfigs = configsFromServer.findProperty('type', 'hdfs-site'); + var configToSave = { + type: 'hdfs-site', + properties: hdfsConfigs && hdfsConfigs.properties + }; + if (hdfsConfigs && hdfsConfigs.properties_attributes) { + configToSave.properties_attributes = hdfsConfigs.properties_attributes; + } + for(const property of this.get('haConfig.configs')){ + configToSave.properties[property.name]=property.value + } + App.ajax.send({ + name: 'common.service.configurations', + sender: self, + data: { + desired_config: configToSave + }, + error: 'onTaskError' + }); + + this.set('isLoaded', true); + }, + /** + * Generate set of data used to correctly initialize config values and names + * + * @returns {nnHaConfigDependencies} + * @private + * @method _prepareDependencies + */ + _prepareDependencies: function () { + var ret = {}; + var configsFromServer = this.get('serverConfigData.items'); + ret.serverConfigs = configsFromServer; + var hdfsConfigs = configsFromServer.findProperty('type', 'hdfs-site').properties; + var zkConfigs = configsFromServer.findProperty('type', 'zoo.cfg').properties; + var nameNodes = this.get('content.masterComponentHosts').filterProperty('component', 'NAMENODE'); + ret.newNamenodeIndex = 'nn' + nameNodes.length; + ret.newNameNode = nameNodes.filterProperty('isInstalled', false).mapProperty('hostName')[0]; + var listNameNodes = []; + for(let i=0 ;iserverConfigData + * @param configsToRemove - map of config sites and properties to remove + * @param configs - configuration object + * @returns {Object} + */ + removeConfigs: function (configsToRemove, configs) { + Em.keys(configsToRemove).forEach(function (site) { + var siteConfigs = configs.items.findProperty('type', site); + if (siteConfigs) { + configsToRemove[site].forEach(function (property) { + delete siteConfigs.properties[property]; + }); + } + }); + return configs; + }, + + renderServiceConfigs: function (_serviceConfig) { + var serviceConfig = App.ServiceConfig.create({ + serviceName: _serviceConfig.serviceName, + displayName: _serviceConfig.displayName, + configCategories: [], + showConfig: true, + configs: [] + }); + + _serviceConfig.configCategories.forEach(function (_configCategory) { + if (App.Service.find().someProperty('serviceName', _configCategory.name)) { + serviceConfig.configCategories.pushObject(_configCategory); + } + }, this); + + this.loadComponentConfigs(_serviceConfig, serviceConfig); + + this.get('stepConfigs').pushObject(serviceConfig); + this.set('selectedService', this.get('stepConfigs').objectAt(0)); + this.set('once', true); + }, + + /** + * Load child components to service config object + * @param _componentConfig + * @param componentConfig + */ + loadComponentConfigs: function (_componentConfig, componentConfig) { + _componentConfig.configs.forEach(function (_serviceConfigProperty) { + var serviceConfigProperty = App.ServiceConfigProperty.create(_serviceConfigProperty); + componentConfig.configs.pushObject(serviceConfigProperty); + serviceConfigProperty.set('isEditable', serviceConfigProperty.get('isReconfigurable')); + }, this); + } +}); diff --git a/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step4_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step4_controller.js new file mode 100644 index 00000000000..9236b031671 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/step4_controller.js @@ -0,0 +1,208 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep4Controller = App.HighAvailabilityProgressPageController.extend(App.WizardEnableDone, { + + name: "multipleNameNodeWizardStep4Controller", + + commands: ['installNameNode', 'installZKFC', 'enterSafeMode' , 'saveNamespace', 'leaveSafeMode', 'formatZKFC', 'bootstrapNameNode', 'startZKFC', 'startNameNode', 'refreshConfigs', 'refreshNamenodes'], + + tasksMessagesPrefix: 'admin.multipleNameNode.wizard.step', + + initializeTasks: function initializeTasks() { + this._super(); + }, + + newNameNodeHosts: function () { + return this.get('content.masterComponentHosts').filterProperty('component', 'NAMENODE').filterProperty('isInstalled', false).mapProperty('hostName'); + }.property('content.masterComponentHosts.@each.hostName'), + + allDatanodeHosts: function () { + return this.get('content.masterComponentHosts').filterProperty('component', 'DATANODE').filterProperty('isInstalled', true).mapProperty('hostName'); + }.property('content.masterComponentHosts.@each.hostName'), + + oldNameNodeHosts: function () { + return this.get('content.masterComponentHosts').filterProperty('component', 'NAMENODE').filterProperty('isInstalled', true).mapProperty('hostName'); + }.property('content.masterComponentHosts.@each.hostName'), + + reconfigureServices: function reconfigureServices() { + var servicesModel = App.Service.find(); + var configs = []; + var data = this.get('content.serviceConfigProperties'); + var note = Em.I18n.t('admin.multipleNameNode.wizard.step4.save.configuration.note'); + configs.push({ + Clusters: { + desired_config: this.reconfigureSites(['hdfs-site'], data, note) + } + }); + return App.ajax.send({ + name: 'common.service.multiConfigurations', + sender: this, + data: { + configs: configs + }, + success: 'onSaveConfigs', + error: 'onTaskError', + }); + }, + + onSaveConfigs: function () { + this.onTaskCompleted(); + }, + + installHDFSClients: function installHDFSClients() { + var nnHostNames = this.get('content.masterComponentHosts').filterProperty('component', 'NAMENODE').mapProperty('hostName'); + this.createInstallComponentTask('HDFS_CLIENT', nnHostNames, 'HDFS'); + }, + + installNameNode: function installNameNode() { + this.createInstallComponentTask('NAMENODE', this.get('newNameNodeHosts'), "HDFS"); + }, + + installZKFC: function installZKFC() { + this.createInstallComponentTask('ZKFC', this.get('newNameNodeHosts'), "HDFS"); + }, + + startNameNode: function () { + this.updateComponent('NAMENODE', this.get('newNameNodeHosts')[0], "HDFS", "Start"); + }, + + formatZKFC: function() { + var self = this; + var nameNodeHosts = this.get('newNameNodeHosts'); + + if (!Array.isArray(nameNodeHosts) || nameNodeHosts.length === 0) { + console.error('No NameNode hosts found to format ZKFC.'); + return; + } + + nameNodeHosts.forEach(function(host) { + App.ajax.send({ + name: 'nameNode.federation.formatZKFC', + sender: self, + data: { + host: host + }, + success: 'startPolling', + error: 'onTaskError' + }); + }); + }, + + startZKFC: function () { + this.updateComponent('ZKFC', this.get('newNameNodeHosts')[0], "HDFS", "Start"); + }, + + startRangerAdmin: function () { + var hostNames = App.HostComponent.find().filterProperty('componentName', 'RANGER_ADMIN').mapProperty('hostName'); + this.updateComponent('RANGER_ADMIN', hostNames, "RANGER", "Start"); + }, + + startRangerUsersync: function () { + var hostNames = App.HostComponent.find().filterProperty('componentName', 'RANGER_USERSYNC').mapProperty('hostName'); + this.updateComponent('RANGER_USERSYNC', hostNames, "RANGER", "Start"); + }, + + startNameNode: function () { + this.updateComponent('NAMENODE', this.get('newNameNodeHosts')[0], "HDFS", "Start"); + }, + + enterSafeMode: function enterSafeMode() { + App.ajax.send({ + name: 'multipleNameNode.entersafeMode', + sender: this, + data: { + host: this.get('oldNameNodeHosts')[1] + }, + success: 'startPolling', + error: 'onTaskError' + }); + }, + + leaveSafeMode: function leaveSafeMode() { + App.ajax.send({ + name: 'multipleNameNode.leavesafeMode', + sender: this, + data: { + host: this.get('oldNameNodeHosts')[1] + }, + success: 'startPolling', + error: 'onTaskError' + }); + }, + + saveNamespace: function saveNamespace() { + App.ajax.send({ + name: 'multipleNameNode.saveNamespace', + sender: this, + data: { + host: this.get('oldNameNodeHosts')[1] + }, + success: 'startPolling', + error: 'onTaskError' + }); + }, + + bootstrapNameNode: function bootstrapNameNode() { + App.ajax.send({ + name: 'nameNode.federation.bootstrapNameNode', + sender: this, + data: { + host: this.get('newNameNodeHosts')[0] + }, + success: 'startPolling', + error: 'onTaskError' + }); + }, + + refreshConfigs: function () { + var allDatanodeHosts = App.SlaveComponent.find('DATANODE').get('hostNames'); + var resource_filters = [ + { + "service_name" : "HDFS", + "component_name" : "DATANODE", + "hosts": allDatanodeHosts.join(",") + } + ]; + App.ajax.send({ + name: 'host.host_component.refresh_configs', + sender: this, + data: { + resource_filters: resource_filters, + context: "refresh configs" + }, + success: 'startPolling', + error: 'onTaskError' + }); + }, + + refreshNamenodes: function () { + var allDatanodeHosts = App.SlaveComponent.find('DATANODE').get('hostNames'); + App.ajax.send({ + name: 'multipleNameNode.refreshNamenodes', + sender: this, + data: { + host: allDatanodeHosts + }, + success: 'startPolling', + error: 'onTaskError' + }); + } +}); diff --git a/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/wizard_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/wizard_controller.js new file mode 100644 index 00000000000..c619ce8a572 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/highAvailability/nameNode/multipleNN/wizard_controller.js @@ -0,0 +1,170 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +var App = require('app'); + +App.MultipleNameNodeWizardController = App.WizardController.extend({ + + name: 'multipleNameNodeWizardController', + + totalSteps: 4, + + /** + * @type {string} + */ + displayName: Em.I18n.t('admin.multipleNameNode.wizard.header'), + + isFinished: false, + + content: Em.Object.create({ + controllerName: 'multipleNameNodeWizardController' + }), + + /** + * Load data for all steps until current step + */ + loadMap: { + '1': [ + { + type: 'sync', + callback: function () { + this.load('cluster'); + } + } + ], + '2': [ + { + type: 'async', + callback: function () { + var self = this, + dfd = $.Deferred(); + this.loadNNHosts(); + this.loadServicesFromServer(); + this.loadMasterComponentHosts().done(function () { + self.loadConfirmedHosts(); + dfd.resolve(); + }); + return dfd.promise(); + } + } + ], + '4': [ + { + type: 'sync', + callback: function () { + this.loadServiceConfigProperties(); + this.loadTasksStatuses(); + this.loadTasksRequestIds(); + this.loadRequestIds(); + } + } + ] + }, + + init: function () { + this._super(); + this.clearStep(); + }, + + clearStep: function () { + this.set('isFinished', false); + }, + + setCurrentStep: function (currentStep, completed) { + this._super(currentStep, completed); + App.clusterStatus.setClusterStatus({ + clusterName: this.get('content.cluster.name'), + wizardControllerName: 'multipleNameNodeWizardController', + localdb: App.db.data + }); + }, + + /** + * Save hosts for additional and current ResourceManagers to local db and controller.content + * @param nnHosts + */ + saveNNHosts: function (nnHosts) { + this.set('content.nnHosts', nnHosts); + this.setDBProperty('nnHosts', nnHosts); + }, + + /** + * Load hosts for additional and current ResourceManagers from local db to controller.content + */ + loadNNHosts: function() { + var nnHosts = this.getDBProperty('nnHosts'); + this.set('content.nnHosts', nnHosts); + }, + + /** + * Load serviceConfigProperties to model + */ + loadServiceConfigProperties: function () { + var serviceConfigProperties = this.getDBProperty('serviceConfigProperties'); + this.set('content.serviceConfigProperties', serviceConfigProperties); + }, + + /** + * Save configs to load and apply them on Configure Components step + * @param configs + */ + saveConfigs: function (configs) { + this.set('content.configs', configs); + this.setDBProperty('configs', configs); + }, + + /** + * Save config properties + * @param stepController HighAvailabilityWizardStep3Controller + */ + saveServiceConfigProperties: function(stepController) { + var serviceConfigProperties = []; + var data = stepController.get('serverConfigData'); + + var _content = stepController.get('stepConfigs')[0]; + _content.get('configs').forEach(function (_configProperties) { + var siteObj = data.items.findProperty('type', _configProperties.get('filename')); + if (siteObj) { + siteObj.properties[_configProperties.get('name')] = _configProperties.get('value'); + } + }, this); + this.setDBProperty('serviceConfigProperties', data); + this.set('content.serviceConfigProperties', data); + }, + + + /** + * Remove all loaded data. + * Created as copy for App.router.clearAllSteps + */ + clearAllSteps: function () { + this.clearInstallOptions(); + // clear temporary information stored during the install + this.set('content.cluster', this.getCluster()); + }, + + /** + * Clear all temporary data + */ + finish: function () { + this.resetDbNamespace(); + App.router.get('updateController').updateAll(); + this.set('isFinished', true); + } +}); diff --git a/ambari-web/app/controllers/main/admin/highAvailability_controller.js b/ambari-web/app/controllers/main/admin/highAvailability_controller.js index f73f895ec7b..eafc3a23509 100644 --- a/ambari-web/app/controllers/main/admin/highAvailability_controller.js +++ b/ambari-web/app/controllers/main/admin/highAvailability_controller.js @@ -164,6 +164,29 @@ App.MainAdminHighAvailabilityController = App.WizardController.extend({ return true; }, + /** + * enable Multiple NameNode + * @return {Boolean} + */ + enableMultipleNamenode: function () { + //Prerequisite Checks + var message = []; + if (!App.HostComponent.find().filterProperty('componentName', 'ZOOKEEPER_SERVER').everyProperty('workStatus', 'STARTED')) { + message.push(Em.I18n.t('admin.nameNodeFederation.wizard.required.zookeepers')); + } + + if (!App.HostComponent.find().filterProperty('componentName', 'JOURNALNODE').everyProperty('workStatus', 'STARTED')) { + message.push(Em.I18n.t('admin.nameNodeFederation.wizard.required.journalnodes')); + } + if (message.length > 0) { + this.showErrorPopup(message); + return false; + } + App.router.transitionTo('main.services.enableMultipleNamenode'); + return true; + }, + + /** * open Manage JournalNode Wizard if there are two started NameNodes with defined active/standby state * @returns {boolean} diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js index 0d3753fd554..b1e9788c88c 100644 --- a/ambari-web/app/controllers/main/service/item.js +++ b/ambari-web/app/controllers/main/service/item.js @@ -1513,6 +1513,12 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow }); }, + enableMultipleNamenode: function () { + var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController'); + highAvailabilityController.enableMultipleNamenode(); + }, + + openNameNodeFederationWizard: function () { var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController'); highAvailabilityController.enableNameNodeFederation(); diff --git a/ambari-web/app/data/configs/wizards/multiple_nn_haproperties.js b/ambari-web/app/data/configs/wizards/multiple_nn_haproperties.js new file mode 100644 index 00000000000..f0a4c124007 --- /dev/null +++ b/ambari-web/app/data/configs/wizards/multiple_nn_haproperties.js @@ -0,0 +1,128 @@ +/** + * 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 = + { + "haConfig": { + serviceName: 'MISC', + displayName: 'MISC', + configCategories: [ + App.ServiceConfigCategory.create({ name: 'HDFS', displayName: 'HDFS'}), + App.ServiceConfigCategory.create({ name: 'HBASE', displayName: 'HBase'}), + App.ServiceConfigCategory.create({ name: 'AMBARI_METRICS', displayName: 'Ambari Metrics'}), + App.ServiceConfigCategory.create({ name: 'RANGER', displayName: 'Ranger'}) + ], + sites: ['core-site', 'hdfs-site', 'hbase-site', 'accumulo-site', 'ams-hbase-site', 'hawq-site', 'hdfs-client', 'ranger-env', 'ranger-knox-plugin-properties', 'ranger-kms-audit', 'ranger-storm-plugin-properties', 'ranger-hbase-plugin-properties', 'ranger-hdfs-plugin-properties', 'ranger-hive-plugin-properties', 'ranger-kafka-audit', 'ranger-knox-audit', 'ranger-hdfs-audit', 'ranger-hive-audit', 'ranger-atlas-audit', 'ranger-storm-audit', 'ranger-hbase-audit', 'ranger-yarn-audit'], + configs: [ + /**********************************************HDFS***************************************/ + + { + "name": "dfs.ha.namenodes.{{namespaceId}}", + "displayName": "dfs.ha.namenodes.{{namespaceId}}", + "description": "The prefix for a given nameservice, contains a comma-separated list of namenodes for a given nameservice.", + "isReconfigurable": false, + "recommendedValue": "nn1,nn2,nn3", + "value": "{{listNameNodes}}", + "category": "HDFS", + "filename": "hdfs-site", + "serviceName": 'MISC' + }, + { + "name": "dfs.namenode.rpc-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.rpc-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "RPC address that handles all clients requests for new NameNode.", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:8020", + "value": "{{newNameNode}}:8020", + "category": "HDFS", + "filename": "hdfs-site", + "serviceName": 'MISC' + }, + { + "name": "dfs.namenode.http-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.http-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "The fully-qualified HTTP address for new NameNode.", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:50070", + "value": "{{newNameNode}}:50070", + "category": "HDFS", + "filename": "hdfs-site", + serviceName: 'MISC' + }, + + { + "name": "dfs.namenode.https-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.https-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "The fully-qualified HTTP address for new NameNode.", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:50470", + "value": "{{newNameNode}}:50470", + "category": "HDFS", + "filename": "hdfs-site", + "serviceName": 'MISC' + }, + + /**********************************************HAWQ***************************************/ + { + "name": "dfs.ha.namenodes.{{namespaceId}}", + "displayName": "dfs.ha.namenodes.{{namespaceId}}", + "description": "The prefix for a given nameservice, contains a comma-separated list of namenodes for a given nameservice.", + "isReconfigurable": false, + "recommendedValue": "nn1,nn2,nn3", + "value": "{{listNameNodes}}", + "category": "HAWQ", + "filename": "hdfs-client", + "serviceName": 'MISC' + }, + { + "name": "dfs.namenode.rpc-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.rpc-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "RPC address that handles all clients requests for new NameNode..", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:8020", + "value": "{{newNameNode}}:8020", + "category": "HAWQ", + "filename": "hdfs-client", + "serviceName": 'MISC' + }, + { + "name": "dfs.namenode.http-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.http-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "The fully-qualified HTTP address for new NameNode.", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:50070", + "value": "{{newNameNode}}:50070", + "category": "HAWQ", + "filename": "hdfs-client", + "serviceName": 'MISC' + }, + { + "name": "dfs.namenode.https-address.{{namespaceId}}.{{newNamenodeIndex}}", + "displayName": "dfs.namenode.https-address.{{namespaceId}}.{{newNamenodeIndex}}", + "description": "The fully-qualified HTTPS address for new NameNode.", + "isReconfigurable": false, + "recommendedValue": "0.0.0.0:50470", + "value": "{{newNameNode}}:50470", + "category": "HAWQ", + "filename": "hdfs-client", + "serviceName": 'MISC' + } + + ] + } + }; diff --git a/ambari-web/app/data/controller_route.js b/ambari-web/app/data/controller_route.js index 914ee88f152..7c6f814353a 100644 --- a/ambari-web/app/data/controller_route.js +++ b/ambari-web/app/data/controller_route.js @@ -85,6 +85,10 @@ module.exports = [ wizardControllerName: App.router.get('nameNodeFederationWizardController.name'), route: 'main.services.enableNameNodeFederation' }, + { + wizardControllerName: App.router.get('multipleNameNodeWizardController.name'), + route: 'main.services.enableMultipleNamenode' + }, { wizardControllerName: App.router.get('routerFederationWizardController.name'), route: 'main.services.enableRouterFederation' diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 6ccaf175afd..fc231b897ce 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -1727,6 +1727,40 @@ Em.I18n.translations = { 'admin.nameNodeFederation.wizard.step4.task16.title': 'Start NameNode', 'admin.nameNodeFederation.wizard.step4.task17.title': 'Restart Required Services', + 'admin.multipleNameNode.wizard.header': 'Add Multiple Namenode', + 'admin.multipleNameNode.closePopup2': 'Add New Namenode Wizard is in progress. You must allow the wizard to complete for Ambari to be in usable state. If you choose to quit, you must follow manual instructions to complete or revert Add New Namenode as documented in the Ambari User Guide. Are you sure you want to exit the wizard?', + 'admin.multipleNameNode.closePopup': 'Are you sure you want to quit?', + 'admin.multipleNameNode.wizard.step1.header': 'Get Started', + 'admin.multipleNameNode.wizard.step1.body':'This wizard will walk you through adding multiple standby namenodes on your cluster.
' + + 'Once enabled, you will be running a Standby Namenode in addition to your Active and Standby Namenode.
' + + 'This allows for an Active-Standby Namenode configuration that automatically performs failover.

', + 'admin.multipleNameNode.wizard.step2.header': 'Select Hosts', + 'admin.multipleNameNode.wizard.step2.body': 'Select hosts running the NameNodes for {0}', + 'admin.multipleNameNode.wizard.step3.header': 'Review', + 'admin.multipleNameNode.wizard.step3.confirm.host.body':'Confirm your host selections.', + 'admin.multipleNameNode.wizard.step3.currentNN': 'Current Namenodes', + 'admin.multipleNameNode.wizard.step3.additionalNN': 'Additional Namenodes', + 'admin.multipleNameNode.wizard.step3.confirm.config.body':'
' + + '

Review Configuration Changes.

' + + 'The following lists the configuration changes that will be made by the Wizard to enable Multiple Namenodes. This information is for review only and is not editable.' + + '
', + 'admin.multipleNameNode.wizard.step4.header': 'Configure Components', + 'admin.multipleNameNode.wizard.step4.task0.title': 'Install Additional Namenode', + 'admin.multipleNameNode.wizard.step4.task1.title': 'Install ZKFC', + 'admin.multipleNameNode.wizard.step4.task2.title': 'Enter Safe Mode', + 'admin.multipleNameNode.wizard.step4.task3.title': 'Save Namespace', + 'admin.multipleNameNode.wizard.step4.task4.title': 'Leave Safe Mode', + 'admin.multipleNameNode.wizard.step4.task5.title': 'Format ZKFC', + 'admin.multipleNameNode.wizard.step4.task6.title': 'Bootstrap Additional Namenode', + 'admin.multipleNameNode.wizard.step4.task7.title': 'Start ZKFC', + 'admin.multipleNameNode.wizard.step4.task8.title': 'Start New Namenode', + 'admin.multipleNameNode.wizard.step4.task9.title': 'Refresh configs', + 'admin.multipleNameNode.wizard.step4.task10.title': 'Refresh Namenodes', + 'admin.multipleNameNode.wizard.step4.notice.completed':'Multiple Namenode has been enabled successfully.', + 'admin.multipleNameNode.wizard.step4.header': 'Configure Components', + 'admin.multipleNameNode.wizard.step4.notice.inProgress':'Please wait while your new Namenode is being deployed.', + 'admin.multipleNameNode.button.enable':'Add Multiple Standby', + 'admin.routerFederation.button.enable':'Add DFSRouter', 'admin.routerFederation.wizard.header': 'Add HDFS Router', 'admin.routerFederation.closePopup': 'Are you sure you want to quit?', diff --git a/ambari-web/app/models/host_component.js b/ambari-web/app/models/host_component.js index c404e5691ba..8b5cd0b542b 100644 --- a/ambari-web/app/models/host_component.js +++ b/ambari-web/app/models/host_component.js @@ -575,6 +575,12 @@ App.HostComponentActionMap = { cssClass: 'icon icon-sitemap', disabled: !App.get('isHaEnabled') || App.get('allHostNames.length') < 4 }, + MULTIPLE_NN: { + action: 'enableMultipleNamenode', + label: Em.I18n.t('admin.multipleNameNode.button.enable'), + cssClass: 'icon icon-sitemap', + disabled: !App.get('isHaEnabled'), + }, TOGGLE_RBF_FEDERATION: { action: 'openRouterFederationWizard', label: Em.I18n.t('admin.routerFederation.button.enable'), diff --git a/ambari-web/app/models/service.js b/ambari-web/app/models/service.js index a1bb73ba37f..ca07821aaca 100644 --- a/ambari-web/app/models/service.js +++ b/ambari-web/app/models/service.js @@ -117,7 +117,7 @@ App.Service = DS.Model.extend({ serviceTypes: function() { var typeServiceMap = { GANGLIA: ['MONITORING'], - HDFS: ['HA_MODE', 'FEDERATION', 'DFSRouter'], + HDFS: ['HA_MODE', 'FEDERATION', 'MULTIPLENN', 'DFSRouter'], YARN: ['HA_MODE'], RANGER: ['HA_MODE'], HAWQ: ['HA_MODE'] diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js index f7217c3eb32..ce1d00b6e0b 100644 --- a/ambari-web/app/routes/main.js +++ b/ambari-web/app/routes/main.js @@ -893,6 +893,8 @@ module.exports = Em.Route.extend(App.RouterRedirections, { enableNameNodeFederation: require('routes/namenode_federation_routes'), + enableMultipleNamenode: require('routes/multiple_namenode_routes'), + enableRouterFederation : require('routes/dfsrouter_federation_routes'), addHawqStandby: require('routes/add_hawq_standby_routes'), diff --git a/ambari-web/app/routes/multiple_namenode_routes.js b/ambari-web/app/routes/multiple_namenode_routes.js new file mode 100644 index 00000000000..53d1ed69c8e --- /dev/null +++ b/ambari-web/app/routes/multiple_namenode_routes.js @@ -0,0 +1,181 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +module.exports = App.WizardRoute.extend({ + route: '/highAvailability/NameNode/MultipleNN/enable', + + breadcrumbs: { + label: Em.I18n.t('admin.multipleNameNode.wizard.header') + }, + + enter: function (router, transition) { + var multipleNameNodeWizardController = router.get('multipleNameNodeWizardController'); + multipleNameNodeWizardController.dataLoading().done(function () { + //Set HDFS as current service + App.router.set('mainServiceItemController.content', App.Service.find().findProperty('serviceName', 'HDFS')); + App.router.get('updateController').set('isWorking', false); + var popup = App.ModalPopup.show({ + classNames: ['wizard-modal-wrapper'], + modalDialogClasses: ['modal-xlg'], + header: Em.I18n.t('admin.multipleNameNode.wizard.header'), + bodyClass: App.MultipleNameNodeWizardView.extend({ + controller: multipleNameNodeWizardController + }), + primary: Em.I18n.t('form.cancel'), + showFooter: false, + secondary: null, + + onClose: function () { + var multipleNameNodeWizardController = router.get('multipleNameNodeWizardController'), + currStep = multipleNameNodeWizardController.get('currentStep'); + if (parseInt(currStep) === 4) { + App.showConfirmationPopup(function () { + multipleNameNodeWizardController.resetOnClose(multipleNameNodeWizardController, 'main.services.index'); + }, Em.I18n.t('admin.multipleNameNode.closePopup2')); + } else { + multipleNameNodeWizardController.resetOnClose(multipleNameNodeWizardController, 'main.services.index'); + } + }, + didInsertElement: function () { + this._super(); + this.fitHeight(); + } + }); + multipleNameNodeWizardController.set('popup', popup); + var currentClusterStatus = App.clusterStatus.get('value'); + if (currentClusterStatus) { + switch (currentClusterStatus.clusterState) { + case 'MULTIPLE_NAMENODE_DEPLOY' : + multipleNameNodeWizardController.setCurrentStep(currentClusterStatus.localdb.MultipleNameNodeWizard.currentStep); + break; + default: + var currStep = App.router.get('multipleNameNodeWizardController.currentStep'); + multipleNameNodeWizardController.setCurrentStep(currStep); + break; + } + } + Em.run.next(function () { + App.router.get('wizardWatcherController').setUser(multipleNameNodeWizardController.get('name')); + router.transitionTo('step' + multipleNameNodeWizardController.get('currentStep')); + }); + }); + }, + + step1: Em.Route.extend({ + route: '/step1', + connectOutlets: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('1'); + controller.connectOutlet('multipleNameNodeWizardStep1', controller.get('content')); + }) + }, + unroutePath: function () { + return false; + }, + next: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.setDBProperty('nnHosts', undefined); + controller.clearMasterComponentHosts(); + router.transitionTo('step2'); + } + }), + + step2: Em.Route.extend({ + route: '/step2', + connectOutlets: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('2'); + controller.loadAllPriorSteps(); + controller.connectOutlet('multipleNameNodeWizardStep2', controller.get('content')); + }) + }, + unroutePath: function () { + return false; + }, + next: function (router) { + var wizardController = router.get('multipleNameNodeWizardController'); + var stepController = router.get('multipleNameNodeWizardStep2Controller'); + var currentNN = stepController.get('servicesMasters').filterProperty('component_name', 'NAMENODE').findProperty('isInstalled', true); + var additionalNN = stepController.get('servicesMasters').filterProperty('component_name', 'NAMENODE').findProperty('isInstalled', false); + var nnHost = { + currentNN: currentNN.get('selectedHost'), + additionalNN: additionalNN.get('selectedHost') + }; + wizardController.saveNNHosts(nnHost); + wizardController.saveMasterComponentHosts(stepController); + router.transitionTo('step3'); + }, + back: function (router) { + router.transitionTo('step1'); + } + }), + + step3: Em.Route.extend({ + route: '/step3', + connectOutlets: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('3'); + controller.loadAllPriorSteps(); + controller.connectOutlet('multipleNameNodeWizardStep3', controller.get('content')); + }) + }, + unroutePath: function () { + return false; + }, + next: function (router) { + var wizardController = router.get('multipleNameNodeWizardController'); + var stepController = router.get('multipleNameNodeWizardStep3Controller'); + wizardController.saveServiceConfigProperties(stepController); + var configs = stepController.get('selectedService.configs'); + wizardController.saveConfigs(configs); + router.transitionTo('step4'); + }, + back: Em.Router.transitionTo('step2') + }), + + step4: Em.Route.extend({ + route: '/step4', + connectOutlets: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('4'); + controller.setLowerStepsDisable(4); + controller.loadAllPriorSteps(); + controller.connectOutlet('multipleNameNodeWizardStep4', controller.get('content')); + }) + }, + unroutePath: function (router, path) { + // allow user to leave route if wizard has finished + if (router.get('multipleNameNodeWizardController').get('isFinished')) { + this._super(router, path); + } else { + return false; + } + }, + next: function (router) { + var controller = router.get('multipleNameNodeWizardController'); + controller.resetOnClose(controller, 'main.services.index'); + } + }) + +}); diff --git a/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step1.hbs b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step1.hbs new file mode 100644 index 00000000000..b6af29d4d2e --- /dev/null +++ b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step1.hbs @@ -0,0 +1,28 @@ +{{! +* 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. +}} +
+

{{t admin.multipleNameNode.wizard.step1.header}}

+

+ {{t admin.multipleNameNode.wizard.step1.body}} +

+
+ \ No newline at end of file diff --git a/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step3.hbs b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step3.hbs new file mode 100644 index 00000000000..8172070439f --- /dev/null +++ b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step3.hbs @@ -0,0 +1,70 @@ +{{! +* 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. +}} +
+

{{t admin.multipleNameNode.wizard.step3.header}}

+

+ {{t admin.multipleNameNode.wizard.step3.confirm.host.body}} +

+ +
+
+
+
+ + {{#each host in view.curNameNodes}} + + + + + + {{/each}} + {{#each host in view.addNameNodes}} + + + + + + {{/each}} +
+ {{t admin.multipleNameNode.wizard.step3.currentNN}}: + {{host}}
+ {{t admin.multipleNameNode.wizard.step3.additionalNN}}: + {{host}} {{t admin.highAvailability.wizard.step3.toBeInstalled}}
+
+
+
+ {{#if controller.isLoaded}} + {{{t admin.multipleNameNode.wizard.step3.confirm.config.body}}} + {{view App.ServiceConfigView isNotEditableBinding="controller.isNotEditable"}} + {{else}} + {{view App.SpinnerView}} + {{/if}} +
+
+
+
+ + \ No newline at end of file diff --git a/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step4.hbs b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step4.hbs new file mode 100644 index 00000000000..08dc3b9d867 --- /dev/null +++ b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/step4.hbs @@ -0,0 +1,18 @@ +{{! +* 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. +}} +{{template "templates/common/progress"}} diff --git a/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/wizard.hbs b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/wizard.hbs new file mode 100644 index 00000000000..ab387d99ea5 --- /dev/null +++ b/ambari-web/app/templates/main/admin/highAvailability/nameNode/multipleNN/wizard.hbs @@ -0,0 +1,35 @@ +{{! +* 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. +}} + +
+ +
\ No newline at end of file diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js index 1c195b9e3d4..c2f4977fc75 100644 --- a/ambari-web/app/utils/ajax/ajax.js +++ b/ambari-web/app/utils/ajax/ajax.js @@ -3275,6 +3275,83 @@ var urls = { } } }, + 'multipleNameNode.entersafeMode': { + 'real': '/clusters/{clusterName}/requests', + 'mock': '', + 'format': function (data) { + return { + type: 'POST', + data: JSON.stringify({ + "RequestInfo": { + "command" : "ENTER_SAFEMODE", "context" : "Enter Safemode" + }, + "Requests/resource_filters": [{ + "service_name" : "HDFS", + "component_name" : "NAMENODE", + "hosts": data.host + }] + }) + } + } + }, + 'multipleNameNode.saveNamespace': { + 'real': '/clusters/{clusterName}/requests', + 'mock': '', + 'format': function (data) { + return { + type: 'POST', + data: JSON.stringify({ + "RequestInfo": { + "command" : "SAVE_NAMESPACE", "context" : "Save Namespace" + }, + "Requests/resource_filters": [{ + "service_name" : "HDFS", + "component_name" : "NAMENODE", + "hosts": data.host + }] + }) + } + } + }, + 'multipleNameNode.leavesafeMode': { + 'real': '/clusters/{clusterName}/requests', + 'mock': '', + 'format': function (data) { + return { + type: 'POST', + data: JSON.stringify({ + "RequestInfo": { + "command" : "LEAVE_SAFEMODE", "context" : "Leave Safemode" + }, + "Requests/resource_filters": [{ + "service_name" : "HDFS", + "component_name" : "NAMENODE", + "hosts": data.host + }] + }) + } + } + }, + 'multipleNameNode.refreshNamenodes': { + 'real': '/clusters/{clusterName}/requests', + 'mock': '', + 'format': function (data) { + return { + type: 'POST', + data: JSON.stringify({ + "RequestInfo": { + "command" : "REFRESH_NAMENODE", "context" : "Refresh Namenode" + }, + "Requests/resource_filters": [{ + "service_name" : "HDFS", + "component_name" : "DATANODE", + "hosts": data.host.join(",") + }] + }) + } + } + }, + 'hiveServerInteractive.getStatus': { real: '', mock: '', diff --git a/ambari-web/app/utils/configs/multiple_nn_config_initializer.js b/ambari-web/app/utils/configs/multiple_nn_config_initializer.js new file mode 100644 index 00000000000..d353feb7e47 --- /dev/null +++ b/ambari-web/app/utils/configs/multiple_nn_config_initializer.js @@ -0,0 +1,125 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('utils/configs/ha_config_initializer_class'); +require('utils/configs/hosts_based_initializer_mixin'); + +/** + * @typedef {topologyLocalDB} extendedTopologyLocalDB + * @property {string[]} installedServices list of installed service names + */ + +/** + * Setting for rename-initializer + * Used for configs which should be renamed + * Replace some part if their names with namespaceId (provided by user on the wizard's 1st step) + * + * @param {string} toReplace + * @returns {{type: string, toReplace: string}} + */ +function getRenameWithNamespaceConfig(toReplace) { + return { + type: 'rename', + toReplace: toReplace + }; +} + +/** + * Initializer for configs that are updated when NameNode HA-mode is activated + * + * @class {MultipleNNConfigInitializer} + */ +App.MultipleNNConfigInitializer = App.HaConfigInitializerClass.create(App.HostsBasedInitializerMixin, { + + initializers: function () { + + return { + 'dfs.ha.namenodes.${dfs.nameservices}': getRenameWithNamespaceConfig('${dfs.nameservices}'), + 'dfs.namenode.rpc-address.${dfs.nameservices}.nn3': [this.getHostWithPortConfig('NAMENODE', false, '', '', '8020', false), getRenameWithNamespaceConfig('${dfs.nameservices}')], + 'dfs.namenode.http-address.${dfs.nameservices}.nn3': [this.getHostWithPortConfig('NAMENODE', false, '', '', '50070', false), getRenameWithNamespaceConfig('${dfs.nameservices}')], + 'dfs.namenode.https-address.${dfs.nameservices}.nn3': [this.getHostWithPortConfig('NAMENODE', false, '', '', '50470', false), getRenameWithNamespaceConfig('${dfs.nameservices}')] + }; + }.property(), + + uniqueInitializers: { + 'xasecure.audit.destination.hdfs.dir': '_initXasecureAuditDestinationHdfsDir' + }, + + initializerTypes: [{ name: 'rename', method: '_initWithRename' }], + + /** + * Initializer for configs that should be renamed + * Some part of their names should be replaced with namespaceId (user input this value on the wizard's 1st step) + * Affects both - name and displayName + * Important! It's not the same as _updateInitializers! + * Main diff - this initializer used for configs + * with names that come with some "predicates" in their names. _updateInitializers is used to determine needed + * config name that depends on other config values or someting else + * + * @param {configProperty} configProperty + * @param {extendedTopologyLocalDB} localDB + * @param {nnHaConfigDependencies} dependencies + * @param {object} initializer + * @returns {object} + * @private + * @method _initWithRename + */ + _initWithRename: function (configProperty, localDB, dependencies, initializer) { + var replaceWith = dependencies.namespaceId; + var toReplace = initializer.toReplace; + Em.assert('`dependencies.namespaceId` should be not empty string', !!replaceWith); + var name = Em.getWithDefault(configProperty, 'name', ''); + var displayName = Em.getWithDefault(configProperty, 'displayName', ''); + name = name.replace(toReplace, replaceWith); + displayName = displayName.replace(toReplace, replaceWith); + Em.setProperties(configProperty, { + name: name, + displayName: displayName + }); + return configProperty; + }, + + /** + * Unique initializer for xasecure.audit.destination.hdfs.dir + * + * @param {configProperty} configProperty + * @param {extendedTopologyLocalDB} localDB + * @param {nnHaConfigDependencies} dependencies + * @param {object} initializer + * @method _initXasecureAuditDestinationHdfsDir + * @return {object} + * @private + */ + _initXasecureAuditDestinationHdfsDir: function(configProperty, localDB, dependencies, initializer) { + if (localDB.installedServices.contains('RANGER')) { + var oldValue = dependencies.serverConfigs.findProperty('type', 'ranger-env').properties['xasecure.audit.destination.hdfs.dir']; + // Example of value - hdfs://c6401.ambari.apache.org:8020/ranger/audit + // Replace hostname and port with Namespace + var valueArray = oldValue.split("/"); + valueArray[2] = dependencies.namespaceId; + var newValue = valueArray.join("/"); + Em.setProperties(configProperty, { + value: newValue, + recommendedValue: newValue + }); + } + return configProperty; + } + +}); \ No newline at end of file diff --git a/ambari-web/app/utils/db.js b/ambari-web/app/utils/db.js index 701a70cca27..f7d8cb81410 100644 --- a/ambari-web/app/utils/db.js +++ b/ambari-web/app/utils/db.js @@ -51,6 +51,7 @@ var InitialData = { 'NameNodeFederationWizard': {}, 'RouterFederationWizard': {}, 'RollbackHighAvailabilityWizard': {}, + 'MultipleNameNodeWizard': {}, 'MainAdminStackAndUpgrade': {}, 'KerberosDisable': {}, 'tmp': {} diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js index 30aa6bc7ee3..32a61bcfad4 100644 --- a/ambari-web/app/views.js +++ b/ambari-web/app/views.js @@ -187,6 +187,11 @@ require('views/main/admin/highAvailability/nameNode/rollbackHA/step1_view'); require('views/main/admin/highAvailability/nameNode/rollbackHA/step2_view'); require('views/main/admin/highAvailability/nameNode/rollbackHA/step3_view'); require('views/main/admin/highAvailability/nameNode/rollbackHA/rollback_wizard_view'); +require('views/main/admin/highAvailability/nameNode/multipleNN/wizard_view'); +require('views/main/admin/highAvailability/nameNode/multipleNN/step1_view'); +require('views/main/admin/highAvailability/nameNode/multipleNN/step2_view'); +require('views/main/admin/highAvailability/nameNode/multipleNN/step3_view'); +require('views/main/admin/highAvailability/nameNode/multipleNN/step4_view'); require('views/main/admin/highAvailability/journalNode/wizard_view'); require('views/main/admin/highAvailability/journalNode/progress_view'); require('views/main/admin/highAvailability/journalNode/step1_view'); diff --git a/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step1_view.js b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step1_view.js new file mode 100644 index 00000000000..9dde2bd3529 --- /dev/null +++ b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step1_view.js @@ -0,0 +1,23 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep1View = Em.View.extend({ + templateName: require('templates/main/admin/highAvailability/nameNode/multipleNN/step1'), +}); \ No newline at end of file diff --git a/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step2_view.js b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step2_view.js new file mode 100644 index 00000000000..c4b17d82f44 --- /dev/null +++ b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step2_view.js @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +var App = require('app'); + +App.MultipleNameNodeWizardStep2View = App.AssignMasterComponentsView.extend({ + title: Em.I18n.t('admin.multipleNameNode.wizard.step2.header'), + alertMessage: Em.computed.i18nFormat('admin.multipleNameNode.wizard.step2.body', 'controller.content.nameServiceId') +}); \ No newline at end of file diff --git a/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step3_view.js b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step3_view.js new file mode 100644 index 00000000000..fa1b82b29dc --- /dev/null +++ b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step3_view.js @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep3View = Em.View.extend({ + + templateName: require('templates/main/admin/highAvailability/nameNode/multipleNN/step3'), + + didInsertElement: function () { + this.get('controller').loadStep(); + }, + curNameNodes: function () { + return this.get('controller.content.masterComponentHosts').filterProperty('component', 'NAMENODE').filterProperty('isInstalled', true).mapProperty('hostName'); + }.property('controller.content.masterComponentHosts'), + addNameNodes: function () { + return this.get('controller.content.masterComponentHosts').filterProperty('component', 'NAMENODE').filterProperty('isInstalled', false).mapProperty('hostName'); + }.property('controller.content.masterComponentHosts'), +}); \ No newline at end of file diff --git a/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step4_view.js b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step4_view.js new file mode 100644 index 00000000000..aa16f05ceb3 --- /dev/null +++ b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/step4_view.js @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MultipleNameNodeWizardStep4View = App.HighAvailabilityProgressPageView.extend({ + + templateName: require('templates/main/admin/highAvailability/nameNode/multipleNN/step4'), + + headerTitle: Em.I18n.t('admin.multipleNameNode.wizard.step4.header'), + + noticeInProgress: Em.I18n.t('admin.multipleNameNode.wizard.step4.notice.inProgress'), + + noticeCompleted: Em.I18n.t('admin.multipleNameNode.wizard.step4.notice.completed'), + + submitButtonText: Em.I18n.t('common.complete'), + + labelWidth: 'col-md-5' + +}); \ No newline at end of file diff --git a/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/wizard_view.js b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/wizard_view.js new file mode 100644 index 00000000000..4e574e6a4a0 --- /dev/null +++ b/ambari-web/app/views/main/admin/highAvailability/nameNode/multipleNN/wizard_view.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +var App = require('app'); + +App.MultipleNameNodeWizardView = Em.View.extend(App.WizardMenuMixin, App.WizardHostsLoading, { + + templateName: require('templates/main/admin/highAvailability/nameNode/multipleNN/wizard'), + + didInsertElement: function() { + var currentStep = this.get('controller.currentStep'); + if (currentStep > 3) { + this.get('controller').setLowerStepsDisable(currentStep); + } + } +}); \ No newline at end of file diff --git a/ambari-web/app/views/main/service/item.js b/ambari-web/app/views/main/service/item.js index b887e1eae14..5d9cb5750ea 100644 --- a/ambari-web/app/views/main/service/item.js +++ b/ambari-web/app/views/main/service/item.js @@ -248,6 +248,13 @@ App.MainServiceItemView = Em.View.extend(App.HiveInteractiveCheck, { break; } } + if (service.get('serviceTypes').contains('MULTIPLENN') && App.isAuthorized('SERVICE.ENABLE_HA')) { + switch (service.get('serviceName')) { + case 'HDFS': + options.push(actionMap.MULTIPLE_NN); + break; + } + } if (serviceCheckSupported) { options.push(actionMap.RUN_SMOKE_TEST); }