/** * 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_collection'); var stringUtils = require('utils/string_utils'); var validator = require('utils/validator'); var configTagFromFileNameMap = {}; App.config = Em.Object.create({ CONFIG_GROUP_NAME_MAX_LENGTH: 18, /** * filename exceptions used to support substandard sitenames which don't have "xml" extension * @type {string[]} */ filenameExceptions: ['alert_notification'], preDefinedServiceConfigs: [], /** * * Returns file name version that stored on server. * * Example: * App.config.getOriginalFileName('core-site') // returns core-site.xml * App.config.getOriginalFileName('zoo.cfg') // returns zoo.cfg * * @param {String} fileName * @method getOriginalFileName **/ getOriginalFileName: function (fileName) { if (/\.xml$/.test(fileName)) return fileName; return this.get('filenameExceptions').contains(fileName) ? fileName : fileName + '.xml'; }, /** * truncate Config Group name to length and paste "..." in the middle */ truncateGroupName: function (name) { if (name && name.length > App.config.CONFIG_GROUP_NAME_MAX_LENGTH) { var middle = Math.floor(App.config.CONFIG_GROUP_NAME_MAX_LENGTH / 2); name = name.substring(0, middle) + "..." + name.substring(name.length - middle); } return name; }, /** * Check if Hive installation with new MySQL database created via Ambari is allowed * @param osFamily * @returns {boolean} */ isManagedMySQLForHiveAllowed: function (osFamily) { var osList = ['redhat5', 'suse11']; return !osList.contains(osFamily); }, /** * * Returns the configuration tagName from supplied filename * * Example: * App.config.getConfigTagFromFileName('core-site.xml') // returns core-site * App.config.getConfigTagFromFileName('zoo.cfg') // returns zoo.cfg * * @param {String} fileName * @method getConfigTagFromFileName **/ getConfigTagFromFileName: function (fileName) { if (configTagFromFileNameMap[fileName]) { return configTagFromFileNameMap[fileName]; } var ret = fileName.endsWith('.xml') ? fileName.slice(0, -4) : fileName; configTagFromFileNameMap[fileName] = ret; return ret; }, /** * * @param name * @param fileName * @returns {string} */ configId: function(name, fileName) { return name + "__" + App.config.getConfigTagFromFileName(fileName); }, setPreDefinedServiceConfigs: function (isMiscTabToBeAdded) { var stackServices = App.StackService.find().filterProperty('id'); // Only include services that has configTypes related to them for service configuration page var servicesWithConfigTypes = stackServices.filter(function (service) { var configtypes = service.get('configTypes'); return configtypes && !!Object.keys(configtypes).length; }, this); var allTabs; if (isMiscTabToBeAdded) { var nonServiceTab = require('data/service_configs'); var miscService = nonServiceTab.findProperty('serviceName', 'MISC'); var tagTypes = {}; servicesWithConfigTypes.mapProperty('configTypes').forEach(function (configTypes) { for (var fileName in configTypes) { if (fileName.endsWith('-env') && !miscService.get('configTypes')[fileName]) { tagTypes[fileName] = configTypes[fileName]; } } }); miscService.set('configTypes', $.extend(miscService.get('configTypes'), tagTypes)); allTabs = servicesWithConfigTypes.concat(nonServiceTab); } else { allTabs = servicesWithConfigTypes; } this.set('preDefinedServiceConfigs', allTabs); }, secureConfigs: require('data/HDP2/secure_mapping'), secureConfigsMap: function () { var ret = {}; this.get('secureConfigs').forEach(function (sc) { ret[sc.name] = true; }); return ret; }.property('secureConfigs.[]'), kerberosIdentities: require('data/HDP2/kerberos_identities').configProperties, kerberosIdentitiesMap: function() { var map = {}; this.get('kerberosIdentities').forEach(function (c) { map[this.configId(c.name, c.filename)] = c; }, this); return map; }.property('kerberosIdentities'), customStackMapping: require('data/custom_stack_map'), mapCustomStack: function () { var baseStackFolder = App.get('currentStackName'), singMap = { "1": ">", "-1": "<", "0": "=" }; this.get('customStackMapping').every(function (stack) { if(stack.stackName == App.get('currentStackName')){ var versionCompare = Em.compare(App.get('currentStackVersionNumber'), stack.stackVersionNumber); if(singMap[versionCompare+""] === stack.sign){ baseStackFolder = stack.baseStackFolder; return false; } } return true; }); return baseStackFolder; }, allPreDefinedSiteProperties: function() { var sitePropertiesForCurrentStack = this.preDefinedConfigFile(this.mapCustomStack(), 'site_properties'); if (sitePropertiesForCurrentStack) { return sitePropertiesForCurrentStack.configProperties; } else if (App.get('isHadoop23Stack')) { return require('data/HDP2.3/site_properties').configProperties; } else { return require('data/HDP2.2/site_properties').configProperties; } }.property('App.isHadoop23Stack'), preDefinedSiteProperties: function () { var serviceNames = App.StackService.find().mapProperty('serviceName').concat('MISC'); return this.get('allPreDefinedSiteProperties').filter(function(p) { return serviceNames.contains(p.serviceName); }); }.property('allPreDefinedSiteProperties'), /** * map of preDefinedSiteProperties provide search by index * @type {object} */ preDefinedSitePropertiesMap: function () { var map = {}; this.get('preDefinedSiteProperties').forEach(function (c) { map[this.configId(c.name, c.filename)] = c; }, this); return map; }.property('preDefinedSiteProperties'), preDefinedConfigFile: function(folder, file) { try { return require('data/{0}/{1}'.format(folder, file)); } catch (err) { // the file doesn't exist, which might be expected. } }, serviceByConfigTypeMap: function () { var ret = {}; App.StackService.find().forEach(function(s) { s.get('configTypeList').forEach(function (ct) { ret[ct] = s; }); }); return ret; }.property(), /** * Generate configs collection with Ember or plain config objects * from config JSON * * @param configJSON * @param useEmberObject * @returns {Array} */ getConfigsFromJSON: function(configJSON, useEmberObject) { var configs = [], filename = App.config.getOriginalFileName(configJSON.type), properties = configJSON.properties, finalAttributes = Em.get(configJSON, 'properties_attributes.final') || {}; for (var index in properties) { var serviceConfigObj = this.getDefaultConfig(index, filename); if (serviceConfigObj.isRequiredByAgent !== false) { serviceConfigObj.value = serviceConfigObj.savedValue = this.formatPropertyValue(serviceConfigObj, properties[index]); serviceConfigObj.isFinal = serviceConfigObj.savedIsFinal = finalAttributes[index] === "true"; serviceConfigObj.isEditable = serviceConfigObj.isReconfigurable; } if (useEmberObject) { configs.push(App.ServiceConfigProperty.create(serviceConfigObj)); } else { configs.push(serviceConfigObj); } } return configs; }, /** * Get config from configsCollections or * generate new default config in collection does not contain * such config * * @param name * @param fileName * @param coreObject * @returns {*|Object} */ getDefaultConfig: function(name, fileName, coreObject) { var cfg = App.configsCollection.getConfigByName(name, fileName) || App.config.createDefaultConfig(name, fileName, false); if (Em.typeOf(coreObject) === 'object') { Em.setProperties(cfg, coreObject); } return cfg; }, /** * This method sets default values for config property * These property values has the lowest priority and can be overridden be stack/UI * config property but is used when such properties are absent in stack/UI configs * @param {string} name * @param {string} fileName * @param {boolean} definedInStack * @param {Object} [coreObject] * @returns {Object} */ createDefaultConfig: function(name, fileName, definedInStack, coreObject) { var service = this.get('serviceByConfigTypeMap')[App.config.getConfigTagFromFileName(fileName)]; var serviceName = service ? service.get('serviceName') : 'MISC'; var tpl = { /** core properties **/ id: this.configId(name, fileName), name: name, filename: this.getOriginalFileName(fileName), value: '', savedValue: null, isFinal: false, savedIsFinal: null, /** UI and Stack properties **/ recommendedValue: null, recommendedIsFinal: null, supportsFinal: this.shouldSupportFinal(serviceName, fileName), supportsAddingForbidden: this.shouldSupportAddingForbidden(serviceName, fileName), serviceName: serviceName, displayName: name, displayType: this.getDefaultDisplayType(coreObject ? coreObject.value : ''), description: '', category: this.getDefaultCategory(definedInStack, fileName), isSecureConfig: this.getIsSecure(name), showLabel: true, isVisible: true, isUserProperty: !definedInStack, isRequired: definedInStack, group: null, isRequiredByAgent: true, isReconfigurable: true, unit: null, hasInitialValue: false, isOverridable: true, index: Infinity, dependentConfigPattern: null, options: null, radioName: null, widgetType: null, errorMessage: '', warnMessage: '' }; return Object.keys(coreObject|| {}).length ? $.extend(tpl, coreObject) : tpl; }, /** * This method creates host name properties * @param serviceName * @param componentName * @param value * @param stackComponent * @returns Object */ createHostNameProperty: function(serviceName, componentName, value, stackComponent) { var hostOrHosts = stackComponent.get('isMultipleAllowed') ? 'hosts' : 'host'; var name = componentName.toLowerCase() + '_' + hostOrHosts; var filename = serviceName.toLowerCase() + "-site.xml"; return { "id": App.config.configId(name, filename), "name": name, "displayName": stackComponent.get('displayName') + ' ' + (value.length > 1 ? 'hosts' : 'host'), "value": value, "recommendedValue": value, "description": "The " + hostOrHosts + " that has been assigned to run " + stackComponent.get('displayName'), "displayType": "component" + hostOrHosts.capitalize(), "isOverridable": false, "isRequiredByAgent": false, "serviceName": serviceName, "filename": filename, "category": componentName, "index": 0 } }, /** * Get component name from config name string * * @param configName * @returns {string} */ getComponentName: function(configName) { var match = configName.match(/^(.*)_host[s]?$/) || [], component = match[1]; return component ? component.toUpperCase() : ""; }, /** * This method merge properties form stackConfigProperty which are taken from stack * with UIConfigProperty which are hardcoded on UI * @param coreObject * @param stackProperty * @param preDefined * @param [propertiesToSkip] */ mergeStaticProperties: function(coreObject, stackProperty, preDefined, propertiesToSkip) { propertiesToSkip = propertiesToSkip || ['name', 'filename', 'value', 'savedValue', 'isFinal', 'savedIsFinal']; for (var k in coreObject) { if (coreObject.hasOwnProperty(k)) { if (!propertiesToSkip.contains(k)) { coreObject[k] = this.getPropertyIfExists(k, coreObject[k], stackProperty, preDefined); } } } return coreObject; }, /** * This method using for merging some properties from two objects * if property exists in firstPriority result will be it's property * else if property exists in secondPriority result will be it's property * otherwise defaultValue will be returned * @param {String} propertyName * @param {*} defaultValue=null * @param {Em.Object|Object} firstPriority * @param {Em.Object|Object} [secondPriority=null] * @returns {*} */ getPropertyIfExists: function(propertyName, defaultValue, firstPriority, secondPriority) { firstPriority = firstPriority || {}; secondPriority = secondPriority || {}; var fp = Em.get(firstPriority, propertyName); if (firstPriority && !Em.isNone(fp)) { return fp; } else { var sp = Em.get(secondPriority, propertyName); if (secondPriority && !Em.isNone(sp)) { return sp; } else { return defaultValue; } } }, /** * Get displayType for properties that has not defined value * @param value * @returns {string} */ getDefaultDisplayType: function(value) { return value && !stringUtils.isSingleLine(value) ? 'multiLine' : 'string'; }, /** * Get category for properties that has not defined value * @param stackConfigProperty * @param fileName * @returns {string} */ getDefaultCategory: function(stackConfigProperty, fileName) { return (stackConfigProperty ? 'Advanced ' : 'Custom ') + this.getConfigTagFromFileName(fileName); }, /** * Get isSecureConfig for properties that has not defined value * @param propertyName * @returns {boolean} */ getIsSecure: function(propertyName) { return !!this.get('secureConfigsMap')[propertyName]; }, /** * Returns description, formatted if needed * * @param {String} description * @param {String} displayType * @returns {String} */ getDescription: function(description, displayType) { var additionalDescription = Em.I18n.t('services.service.config.password.additionalDescription'); if ('password' === displayType) { if (description && !description.contains(additionalDescription)) { return description + '
' + additionalDescription; } else { return additionalDescription; } } return description }, /** * parse Kerberos descriptor * * @param kerberosDescriptor * @returns {{}} */ parseDescriptor: function(kerberosDescriptor) { var identitiesMap = {}; Em.get(kerberosDescriptor, 'KerberosDescriptor.kerberos_descriptor.services').forEach(function (service) { this.parseIdentities(service, identitiesMap); if (Array.isArray(service.components)) { service.components.forEach(function (component) { this.parseIdentities(component, identitiesMap); }, this); } }, this); return identitiesMap; }, /** * Looking for configs identities and add them to identitiesMap * * @param item * @param identitiesMap */ parseIdentities: function (item, identitiesMap) { if (item.identities) { item.identities.forEach(function (identity) { Em.keys(identity).without('name').forEach(function (item) { if (identity[item].configuration) { var cfg = identity[item].configuration.split('/'), name = cfg[1], fileName = cfg[0]; identitiesMap[App.config.configId(name, fileName)] = true; } }); }); } return identitiesMap; }, /** * Update description for disabled kerberos configs which are identities * * @param description * @returns {*} */ kerberosIdentitiesDescription: function(description) { if (!description) return Em.I18n.t('services.service.config.secure.additionalDescription'); description = description.trim(); return (description.endsWith('.') ? description + ' ' : description + '. ') + Em.I18n.t('services.service.config.secure.additionalDescription'); }, /** * Get view class based on display type of config * * @param displayType * @param dependentConfigPattern * @param unit * @returns {*} */ getViewClass: function (displayType, dependentConfigPattern, unit) { switch (displayType) { case 'checkbox': case 'boolean': return dependentConfigPattern ? App.ServiceConfigCheckboxWithDependencies : App.ServiceConfigCheckbox; case 'password': return App.ServiceConfigPasswordField; case 'combobox': return App.ServiceConfigComboBox; case 'radio button': return App.ServiceConfigRadioButtons; case 'directories': return App.ServiceConfigTextArea; case 'directory': return App.ServiceConfigTextField; case 'content': return App.ServiceConfigTextAreaContent; case 'multiLine': return App.ServiceConfigTextArea; case 'custom': return App.ServiceConfigBigTextArea; case 'componentHost': return App.ServiceConfigMasterHostView; case 'label': return App.ServiceConfigLabelView; case 'componentHosts': return App.ServiceConfigComponentHostsView; case 'supportTextConnection': return App.checkConnectionView; case 'capacityScheduler': return App.CapacitySceduler; default: return unit ? App.ServiceConfigTextFieldWithUnit : App.ServiceConfigTextField; } }, /** * Returns validator function based on config type * * @param displayType * @returns {Function} */ getValidator: function (displayType) { switch (displayType) { case 'checkbox': case 'custom': return function () { return '' }; case 'int': return function (value) { return !validator.isValidInt(value) && !validator.isConfigValueLink(value) ? Em.I18n.t('errorMessage.config.number.integer') : ''; }; case 'float': return function (value) { return !validator.isValidFloat(value) && !validator.isConfigValueLink(value) ? Em.I18n.t('errorMessage.config.number.float') : ''; }; case 'directories': case 'directory': return function (value, name) { if (App.config.isDirHeterogeneous(name)) { if (!validator.isValidDataNodeDir(value)) return Em.I18n.t('errorMessage.config.directory.heterogeneous'); } else { if (!validator.isValidDir(value)) return Em.I18n.t('errorMessage.config.directory.default'); } if (!validator.isAllowedDir(value)) { return Em.I18n.t('errorMessage.config.directory.allowed'); } return validator.isNotTrimmedRight(value) ? Em.I18n.t('errorMessage.config.spaces.trailing') : ''; }; case 'email': return function (value) { return !validator.isValidEmail(value) ? Em.I18n.t('errorMessage.config.mail') : ''; }; case 'supportTextConnection': case 'host': return function (value) { return validator.isNotTrimmed(value) ? Em.I18n.t('errorMessage.config.spaces.trim') : ''; }; case 'password': return function (value, name, retypedPassword) { return value !== retypedPassword ? Em.I18n.t('errorMessage.config.password') : ''; }; case 'user': case 'database': case 'db_user': return function (value) { return !validator.isValidDbName(value) ? Em.I18n.t('errorMessage.config.user') : ''; }; default: return function (value, name) { if (['javax.jdo.option.ConnectionURL', 'oozie.service.JPAService.jdbc.url'].contains(name) && !validator.isConfigValueLink(value) && validator.isConfigValueLink(value)) { return Em.I18n.t('errorMessage.config.spaces.trim'); } else { return validator.isNotTrimmedRight(value) ? Em.I18n.t('errorMessage.config.spaces.trailing') : ''; } }; } }, /** * Defines if config support heterogeneous devices * * @param {string} name * @returns {boolean} */ isDirHeterogeneous: function(name) { return ['dfs.datanode.data.dir'].contains(name); }, /** * format property value depending on displayType * and one exception for 'kdc_type' * @param serviceConfigProperty * @param [originalValue] * @returns {*} */ formatPropertyValue: function(serviceConfigProperty, originalValue) { var value = Em.isNone(originalValue) ? Em.get(serviceConfigProperty, 'value') : originalValue, displayType = Em.get(serviceConfigProperty, 'displayType') || Em.get(serviceConfigProperty, 'valueAttributes.type'); if (Em.get(serviceConfigProperty, 'name') === 'kdc_type') { return App.router.get('mainAdminKerberosController.kdcTypesValues')[value]; } if ( /^\s+$/.test("" + value)) { return " "; } switch (displayType) { case 'int': if (/\d+m$/.test(value) ) { return value.slice(0, value.length - 1); } else { var int = parseInt(value); return isNaN(int) ? "" : int.toString(); } case 'float': var float = parseFloat(value); return isNaN(float) ? "" : float.toString(); case 'componentHosts': if (typeof(value) == 'string') { return value.replace(/\[|]|'|'/g, "").split(','); } return value; case 'content': case 'string': case 'multiLine': case 'directories': case 'directory': return this.trimProperty({ displayType: displayType, value: value }); default: return value; } }, /** * Format float value * * @param {*} value * @returns {string|*} */ formatValue: function(value) { return validator.isValidFloat(value) ? parseFloat(value).toString() : value; }, /** * Get step config by file name * * @param stepConfigs * @param fileName * @returns {Object|null} */ getStepConfigForProperty: function (stepConfigs, fileName) { return stepConfigs.find(function (s) { return s.get('configTypes').contains(App.config.getConfigTagFromFileName(fileName)); }); }, /** * * @param configs * @returns {Object[]} */ sortConfigs: function(configs) { return configs.sort(function(a, b) { if (Em.get(a, 'index') > Em.get(b, 'index')) return 1; if (Em.get(a, 'index') < Em.get(b, 'index')) return -1; if (Em.get(a, 'name') > Em.get(b, 'index')) return 1; if (Em.get(a, 'name') < Em.get(b, 'index')) return -1; return 0; }); }, /** * create new ServiceConfig object by service name * @param {string} serviceName * @param {App.ServiceConfigGroup[]} [configGroups] * @param {App.ServiceConfigProperty[]} [configs] * @param {Number} [initConfigsLength] * @return {App.ServiceConfig} * @method createServiceConfig */ createServiceConfig: function (serviceName, configGroups, configs, initConfigsLength) { var preDefinedServiceConfig = App.config.get('preDefinedServiceConfigs').findProperty('serviceName', serviceName); return App.ServiceConfig.create({ serviceName: preDefinedServiceConfig.get('serviceName'), displayName: preDefinedServiceConfig.get('displayName'), configCategories: preDefinedServiceConfig.get('configCategories'), configs: configs || [], configGroups: configGroups || [], initConfigsLength: initConfigsLength || 0 }); }, /** * GETs all cluster level sites in one call. * * @return {$.ajax} */ loadConfigsByTags: function (tags) { var urlParams = []; tags.forEach(function (_tag) { urlParams.push('(type=' + _tag.siteName + '&tag=' + _tag.tagName + ')'); }); var params = urlParams.join('|'); return App.ajax.send({ name: 'config.on_site', sender: this, data: { params: params } }); }, configTypesInfoMap: {}, /** * Get config types and config type attributes from stack service * * @param service * @return {object} */ getConfigTypesInfoFromService: function (service) { var configTypesInfoMap = this.get('configTypesInfoMap'); if (configTypesInfoMap[service]) { // don't recalculate return configTypesInfoMap[service]; } var configTypes = service.get('configTypes'); var configTypesInfo = { items: [], supportsFinal: [], supportsAddingForbidden: [] }; if (configTypes) { for (var key in configTypes) { if (configTypes.hasOwnProperty(key)) { configTypesInfo.items.push(key); if (configTypes[key].supports && configTypes[key].supports.final === "true") { configTypesInfo.supportsFinal.push(key); } if (configTypes[key].supports && configTypes[key].supports.adding_forbidden === "true"){ configTypesInfo.supportsAddingForbidden.push(key); } } } } configTypesInfoMap[service] = configTypesInfo; this.set('configTypesInfoMap', configTypesInfoMap); return configTypesInfo; }, /** * * @param configs */ addYarnCapacityScheduler: function(configs) { var value = '', savedValue = '', recommendedValue = '', excludedConfigs = App.config.getPropertiesFromTheme('YARN'); var connectedConfigs = configs.filter(function(config) { return !excludedConfigs.contains(App.config.configId(config.get('name'), config.get('filename'))) && (config.get('filename') === 'capacity-scheduler.xml'); }); var names = connectedConfigs.mapProperty('name'); connectedConfigs.forEach(function (config) { value += config.get('name') + '=' + config.get('value') + '\n'; if (!Em.isNone(config.get('savedValue'))) { savedValue += config.get('name') + '=' + config.get('savedValue') + '\n'; } if (!Em.isNone(config.get('recommendedValue'))) { recommendedValue += config.get('name') + '=' + config.get('recommendedValue') + '\n'; } }, this); var isFinal = connectedConfigs.someProperty('isFinal', true); var savedIsFinal = connectedConfigs.someProperty('savedIsFinal', true); var recommendedIsFinal = connectedConfigs.someProperty('recommendedIsFinal', true); var cs = App.config.createDefaultConfig('capacity-scheduler', 'capacity-scheduler.xml', true, { 'value': value, 'serviceName': 'YARN', 'savedValue': savedValue || null, 'recommendedValue': recommendedValue || null, 'isFinal': isFinal, 'savedIsFinal': savedIsFinal, 'recommendedIsFinal': recommendedIsFinal, 'category': 'CapacityScheduler', 'displayName': 'Capacity Scheduler', 'description': 'Capacity Scheduler properties', 'displayType': 'capacityScheduler' }); configs = configs.filter(function(c) { return !(names.contains(c.get('name')) && (c.get('filename') === 'capacity-scheduler.xml')); }); configs.push(App.ServiceConfigProperty.create(cs)); return configs; }, /** * * @param serviceName * @returns {Array} */ getPropertiesFromTheme: function (serviceName) { var properties = []; App.Tab.find().forEach(function (t) { if (!t.get('isAdvanced') && t.get('serviceName') === serviceName) { t.get('sections').forEach(function (s) { s.get('subSections').forEach(function (ss) { properties = properties.concat(ss.get('configProperties')); }); }); } }, this); return properties; }, /** * transform one config with textarea content * into set of configs of file * @param configs * @param filename * @return {*} */ textareaIntoFileConfigs: function (configs, filename) { var configsTextarea = configs.findProperty('name', 'capacity-scheduler'); if (configsTextarea && !App.get('testMode')) { var properties = configsTextarea.get('value').split('\n'); properties.forEach(function (_property) { var name, value; if (_property) { _property = _property.split('='); name = _property[0]; value = (_property[1]) ? _property[1] : ""; configs.push(Em.Object.create({ name: name, value: value, savedValue: value, serviceName: configsTextarea.get('serviceName'), filename: filename, isFinal: configsTextarea.get('isFinal'), isNotDefaultValue: configsTextarea.get('isNotDefaultValue'), isRequiredByAgent: configsTextarea.get('isRequiredByAgent'), group: null })); } }); return configs.without(configsTextarea); } return configs; }, /** * trim trailing spaces for all properties. * trim both trailing and leading spaces for host displayType and hive/oozie datebases url. * for directory or directories displayType format string for further using. * for password and values with spaces only do nothing. * @param {Object} property * @returns {*} */ trimProperty: function (property) { var displayType = Em.get(property, 'displayType'); var value = Em.get(property, 'value'); var name = Em.get(property, 'name'); var rez; switch (displayType) { case 'directories': case 'directory': rez = value.replace(/,/g, ' ').trim().split(/\s+/g).join(','); break; case 'host': rez = value.trim(); break; case 'password': break; default: if (name == 'javax.jdo.option.ConnectionURL' || name == 'oozie.service.JPAService.jdbc.url') { rez = value.trim(); } rez = (typeof value == 'string') ? value.replace(/(\s+$)/g, '') : value; } return ((rez == '') || (rez == undefined)) ? value : rez; }, /** * Generate minimal config property object used in *_properties.js files. * Example: * * var someProperties = App.config.generateConfigPropertiesByName([ * 'property_1', 'property_2', 'property_3'], { category: 'General', filename: 'myFileName'}); * // someProperties contains Object[] * [ * { * name: 'property_1', * displayName: 'property_1', * isVisible: true, * isReconfigurable: true, * category: 'General', * filename: 'myFileName' * }, * ....... * ] * * @param {string[]} names * @param {Object} properties - additional properties which will merge with base object definition * @returns {object[]} * @method generateConfigPropertiesByName */ generateConfigPropertiesByName: function (names, properties) { return names.map(function (item) { var baseObj = { name: item }; if (properties) return $.extend(baseObj, properties); else return baseObj; }); }, /** * load cluster stack configs from server and run mapper * @returns {$.ajax} * @method loadConfigsFromStack */ loadClusterConfigsFromStack: function () { return App.ajax.send({ name: 'configs.stack_configs.load.cluster_configs', sender: this, data: { stackVersionUrl: App.get('stackVersionURL') }, success: 'saveConfigsToModel' }); }, /** * load stack configs from server and run mapper * @param {String[]} [serviceNames=null] * @returns {$.ajax} * @method loadConfigsFromStack */ loadConfigsFromStack: function (serviceNames) { serviceNames = serviceNames || []; var name = serviceNames.length > 0 ? 'configs.stack_configs.load.services' : 'configs.stack_configs.load.all'; return App.ajax.send({ name: name, sender: this, data: { stackVersionUrl: App.get('stackVersionURL'), serviceList: serviceNames.join(',') }, success: 'saveConfigsToModel' }); }, /** * Runs stackConfigPropertiesMapper * @param {object} data * @method saveConfigsToModel */ saveConfigsToModel: function (data) { App.stackConfigPropertiesMapper.map(data); }, /** * Check if config filename supports final attribute * @param serviceName * @param filename * @returns {boolean} */ shouldSupportFinal: function (serviceName, filename) { var unsupportedServiceNames = ['MISC', 'Cluster']; if (!serviceName || unsupportedServiceNames.contains(serviceName) || !filename) { return false; } else { var stackService = App.StackService.find(serviceName); if (!stackService) { return false; } return !!this.getConfigTypesInfoFromService(stackService).supportsFinal.find(function (configType) { return filename.startsWith(configType); }); } }, shouldSupportAddingForbidden: function(serviceName, filename) { var unsupportedServiceNames = ['MISC', 'Cluster']; if (!serviceName || unsupportedServiceNames.contains(serviceName) || !filename) { return false; } else { var stackServiceName = App.StackService.find().findProperty('serviceName', serviceName); if (!stackServiceName) { return false; } var stackService = App.StackService.find(serviceName); return !!this.getConfigTypesInfoFromService(stackService).supportsAddingForbidden.find(function (configType) { return filename.startsWith(configType); }); } }, /** * Remove all ranger-related configs, that should be available only if Ranger is installed * @param configs - stepConfigs object */ removeRangerConfigs: function (configs) { configs.forEach(function (service) { var filteredConfigs = []; service.get('configs').forEach(function (config) { if (!/^ranger-/.test(config.get('filename'))) { filteredConfigs.push(config); } }); service.set('configs', filteredConfigs); var filteredCategories = []; service.get('configCategories').forEach(function (category) { if (!/ranger-/.test(category.get('name'))) { filteredCategories.push(category); } }); service.set('configCategories', filteredCategories); }); }, /** * Create config with non default config group. Some custom config properties * can be created and assigned to non-default config group. * * @param {Em.Object} override - config value * @param {Em.Object} configGroup - config group to set * @return {Object} **/ createCustomGroupConfig: function (override, configGroup) { App.assertObject(override); App.assertEmberObject(configGroup); var newOverride = App.ServiceConfigProperty.create(this.createDefaultConfig(override.propertyName, override.filename, false, override)); newOverride.setProperties({ 'isOriginalSCP': false, 'overrides': null, 'group': configGroup, 'parentSCP': null }); if (!configGroup.get('properties.length')) { configGroup.set('properties', Em.A([])); } configGroup.set('properties', configGroup.get('properties').concat(newOverride)); return newOverride; }, /** * @param {App.ServiceConfigProperty} serviceConfigProperty * @param {Object} override - plain object with properties that is different from parent SCP * @param {App.ServiceConfigGroup} configGroup * @returns {App.ServiceConfigProperty} */ createOverride: function(serviceConfigProperty, override, configGroup) { App.assertObject(serviceConfigProperty); App.assertEmberObject(configGroup); if (Em.isNone(serviceConfigProperty.get('overrides'))) serviceConfigProperty.set('overrides', []); var newOverride = App.ServiceConfigProperty.create(serviceConfigProperty); newOverride.setProperties({ 'savedValue': null, 'savedIsFinal': null }); if (!Em.isNone(override)) { for (var key in override) { newOverride.set(key, override[key]); } } newOverride.setProperties({ 'isOriginalSCP': false, 'overrides': null, 'group': configGroup, 'parentSCP': serviceConfigProperty }); if (!configGroup.get('properties.length')) { configGroup.set('properties', Em.A([])); } configGroup.set('properties', configGroup.get('properties').concat(newOverride)); serviceConfigProperty.get('overrides').pushObject(newOverride); var savedOverrides = serviceConfigProperty.get('overrides').filterProperty('savedValue'); serviceConfigProperty.set('overrideValues', savedOverrides.mapProperty('savedValue')); serviceConfigProperty.set('overrideIsFinalValues', savedOverrides.mapProperty('savedIsFinal')); return newOverride; }, /** * Merge values in "stored" to "base" if name matches, it's a value only merge. * @param base {Array} Em.Object * @param stored {Array} Object */ mergeStoredValue: function(base, stored) { if (stored) { base.forEach(function (p) { var sp = stored.filterProperty("filename", p.filename).findProperty("name", p.name); if (sp) { p.set("value", sp.value); } }); } }, /** * Helper method to get property from the stepConfigs * * @param {String} name - config property name * @param {String} fileName - config property filename * @param {Object[]} stepConfigs * @return {App.ServiceConfigProperty|Boolean} - App.ServiceConfigProperty instance or false when property not found */ findConfigProperty: function(stepConfigs, name, fileName) { if (!name && !fileName) return false; if (stepConfigs && stepConfigs.length) { return stepConfigs.mapProperty('configs').filter(function(item) { return item.length; }).reduce(function(p, c) { if (p) { return p.concat(c); } }).filterProperty('filename', fileName).findProperty('name', name); } return false; }, /** * Update config property value based on its current value and list of zookeeper server hosts. * Used to prevent sort order issues. * siteConfigs object formatted according server's persist format e.g. * * * { * 'yarn-site': { * 'property_name1': 'property_value1' * 'property_name2': 'property_value2' * ..... * } * } * * * @method updateHostsListValue * @param {Object} siteConfigs - prepared site config object to store * @param {String} propertyName - name of the property to update * @param {String} hostsList - list of ZooKeeper Server names to set as config property value * @return {String} - result value */ updateHostsListValue: function(siteConfigs, propertyName, hostsList) { var value = hostsList; var propertyHosts = (siteConfigs[propertyName] || '').split(','); var hostsToSet = hostsList.split(','); if (!Em.isEmpty(siteConfigs[propertyName])) { var diffLength = propertyHosts.filter(function(hostName) { return !hostsToSet.contains(hostName); }).length; if (diffLength == 0 && propertyHosts.length == hostsToSet.length) { value = siteConfigs[propertyName]; } } siteConfigs[propertyName] = value; return value; }, /** * Load cluster-env configs mapped to array * @return {*|{then}} */ getClusterEnvConfigs: function () { var dfd = $.Deferred(); App.ajax.send({ name: 'config.cluster_env_site', sender: this }).done(function (data) { App.router.get('configurationController').getConfigsByTags([{ siteName: data.items[data.items.length - 1].type, tagName: data.items[data.items.length - 1].tag }]).done(function (clusterEnvConfigs) { var configsObject = clusterEnvConfigs[0].properties; var configsArray = []; for (var property in configsObject) { if (configsObject.hasOwnProperty(property)) { configsArray.push(Em.Object.create({ name: property, value: configsObject[property], filename: 'cluster-env.xml' })); } } dfd.resolve(configsArray); }); }); return dfd.promise(); } });