enhanced_configs.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var blueprintUtils = require('utils/blueprint');
  20. App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationParser, {
  21. /**
  22. * this value is used for observing
  23. * whether recommendations for dependent properties was received from server
  24. * @type {number}
  25. */
  26. recommendationTimeStamp: null,
  27. /**
  28. * this property is used to force update min/max values
  29. * for not default config groups
  30. * @type {boolean}
  31. */
  32. forceUpdateBoundaries: false,
  33. /**
  34. * object with loaded recommendations
  35. *
  36. * @type {Object}
  37. */
  38. recommendationsConfigs: null,
  39. /**
  40. * flag is true when Ambari changes some of the dependent properties
  41. * @type {boolean}
  42. */
  43. hasChangedDependencies: Em.computed.and('isControllerSupportsEnhancedConfigs', 'changedProperties.length'),
  44. /**
  45. * defines is block with changed dependent configs should be shown
  46. * rely on controller
  47. * @type {boolean}
  48. */
  49. isControllerSupportsEnhancedConfigs: Em.computed.existsIn('name', ['wizardStep7Controller','mainServiceInfoConfigsController']),
  50. dependenciesGroupMessage: Em.I18n.t('popup.dependent.configs.dependencies.for.groups'),
  51. /**
  52. * message fro alert box for dependent configs
  53. * @type {string}
  54. */
  55. dependenciesMessage: function() {
  56. var changedProperties = this.get('changedProperties').filterProperty('saveRecommended');
  57. var changedServices = changedProperties.mapProperty('serviceName').uniq();
  58. var cfgLenSuffix = changedProperties.length === 1 ? 'singular' : 'plural';
  59. var sLenSuffix = changedServices.length === 1 ? 'singular' : 'plural';
  60. return Em.I18n.t('popup.dependent.configs.dependencies.config.' + cfgLenSuffix).format(changedProperties.length)
  61. + Em.I18n.t('popup.dependent.configs.dependencies.service.' + sLenSuffix).format(changedServices.length);
  62. }.property('changedProperties.length'),
  63. /**
  64. * dependent properties that was changed by Ambari
  65. * @type {Object[]}
  66. */
  67. changedProperties: function() {
  68. return this.get('recommendations').filter(function(dp) {
  69. return (this.get('selectedConfigGroup.isDefault') && Em.get(dp, 'configGroup').contains('Default'))
  70. || [this.get('selectedConfigGroup.name'), this.get('selectedConfigGroup.dependentConfigGroups') && this.get('selectedConfigGroup.dependentConfigGroups')[Em.get(dp, 'serviceName')]].contains(Em.get(dp, 'configGroup'));
  71. }, this);
  72. }.property('recommendations.@each.saveRecommended', 'recommendations.@each.recommendedValue', 'selectedConfigGroup'),
  73. /**
  74. * defines if change dependent group message should be shown
  75. * @type {boolean}
  76. */
  77. showSelectGroupsPopup: Em.computed.and('!selectedConfigGroup.isDefault', 'selectedService.dependentServiceNames.length'),
  78. /**
  79. * set default values for dependentGroups
  80. * @method setDependentGroups
  81. */
  82. setDependentGroups: function () {
  83. if (this.get('selectedConfigGroup') && this.get('isControllerSupportsEnhancedConfigs') && !this.get('selectedConfigGroup.isDefault') && this.get('selectedService.dependentServiceNames.length')) {
  84. this.get('selectedService.dependentServiceNames').forEach(function (serviceName) {
  85. if (!this.get('selectedConfigGroup.dependentConfigGroups')[serviceName]) {
  86. var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
  87. if (stepConfig) {
  88. stepConfig.get('configGroups').filterProperty('isDefault', false).forEach(function (configGroup) {
  89. this.get('selectedService.configGroups').filterProperty('isDefault', false).forEach(function (currentServiceGroup) {
  90. if (currentServiceGroup.get('dependentConfigGroups')[serviceName] != configGroup.get('name')) {
  91. var dependentGroups = $.extend({},this.get('selectedConfigGroup.dependentConfigGroups'));
  92. dependentGroups[serviceName] = configGroup.get('name');
  93. this.set('selectedConfigGroup.dependentConfigGroups', dependentGroups);
  94. }
  95. }, this);
  96. }, this);
  97. }
  98. }
  99. }, this);
  100. }
  101. }.observes('selectedConfigGroup'),
  102. /******************************METHODS THAT WORKS WITH DEPENDENT CONFIGS *************************************/
  103. /**
  104. * get config group object for current service
  105. * @param serviceName
  106. * @returns {App.ConfigGroup|null}
  107. */
  108. getGroupForService: function(serviceName) {
  109. if (!this.get('stepConfigs') || this.get('stepConfigs.length') === 0) {
  110. return null;
  111. }
  112. if (this.get('selectedService.serviceName') === serviceName) {
  113. return this.get('selectedConfigGroup');
  114. } else {
  115. var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
  116. if (stepConfig) {
  117. var groups = stepConfig.get('configGroups');
  118. if (this.get('selectedConfigGroup.isDefault')) {
  119. return groups.length ? groups.findProperty('isDefault', true) : null;
  120. } else {
  121. return groups.length ? groups.findProperty('name', this.get('selectedConfigGroup.dependentConfigGroups')[serviceName]) : null;
  122. }
  123. } else {
  124. return null;
  125. }
  126. }
  127. },
  128. clearRecommendationsInfo: function() {
  129. this.set('recommendationsConfigs', null);
  130. },
  131. /**
  132. * sends request to get values for dependent configs
  133. * @param {{type: string, name: string}[]} changedConfigs - list of changed configs to track recommendations
  134. * @param {Function} [onComplete]
  135. * @returns {$.ajax|null}
  136. */
  137. loadConfigRecommendations: function(changedConfigs, onComplete) {
  138. var updateDependencies = Em.isArray(changedConfigs) && changedConfigs.length > 0;
  139. if (updateDependencies || Em.isNone(this.get('recommendationsConfigs'))) {
  140. var configGroup = this.get('selectedConfigGroup');
  141. var recommendations = this.get('hostGroups');
  142. var dataToSend = {
  143. recommend: updateDependencies ? 'configuration-dependencies' : 'configurations',
  144. hosts: this.get('hostNames'),
  145. services: this.get('serviceNames')
  146. };
  147. if (updateDependencies) {
  148. dataToSend.changed_configurations = changedConfigs;
  149. }
  150. recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(this.get('stepConfigs'));
  151. if (configGroup && !configGroup.get('isDefault') && configGroup.get('hosts.length') > 0) {
  152. recommendations.config_groups = [this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup)];
  153. } else {
  154. delete recommendations.config_groups;
  155. }
  156. dataToSend.recommendations = recommendations;
  157. var self = this;
  158. return App.ajax.send({
  159. name: 'config.recommendations',
  160. sender: this,
  161. data: {
  162. stackVersionUrl: App.get('stackVersionURL'),
  163. dataToSend: dataToSend
  164. },
  165. success: 'loadRecommendationsSuccess',
  166. error: 'loadRecommendationsError',
  167. callback: function() {
  168. self.set('recommendationTimeStamp', (new Date).getTime());
  169. if (onComplete) {
  170. onComplete()
  171. }
  172. }
  173. });
  174. } else {
  175. if (onComplete) {
  176. onComplete()
  177. }
  178. return null;
  179. }
  180. },
  181. /**
  182. * Defines if there is any changes made by user.
  183. * Check all properties except recommended properties from popup
  184. *
  185. * @returns {boolean}
  186. */
  187. isConfigHasInitialState: function() {
  188. return this.get('selectedConfigGroup.isDefault') && !Em.isNone(this.get('recommendationsConfigs'))
  189. && !this.get('stepConfigs').filter(function(stepConfig) {
  190. return stepConfig.get('changedConfigProperties').filter(function(c) {
  191. return !this.get('changedProperties').map(function(changed) {
  192. return App.config.configId(changed.propertyName, changed.propertyFileName);
  193. }).contains(App.config.configId(c.get('name'), c.get('filename')));
  194. }, this).length;
  195. }, this).length;
  196. },
  197. /**
  198. * generates JSON with config group info to send it for recommendations
  199. * @param configs
  200. * @param configGroup
  201. * @returns {{configurations: Object[], hosts: string[]}}
  202. */
  203. buildConfigGroupJSON: function(configs, configGroup) {
  204. Em.assert('configGroup can\'t be null', configGroup);
  205. var hosts = configGroup.get('hosts');
  206. var configurations = {};
  207. var overrides = configs.forEach(function(cp) {
  208. var override = cp.get('overrides') && cp.get('overrides').findProperty('group.name', configGroup.get('name'));
  209. if (override) {
  210. var tag = App.config.getConfigTagFromFileName(cp.get('filename'));
  211. if (!configurations[tag]) {
  212. configurations[tag] = { properties: {} };
  213. }
  214. configurations[tag].properties[cp.get('name')] = override.get('value');
  215. }
  216. });
  217. return {
  218. configurations: [configurations],
  219. hosts: hosts
  220. }
  221. },
  222. /**
  223. * shows popup with results for recommended value
  224. * if case properties that was changes belongs to not default group
  225. * user should pick to what config group from dependent service dependent properties will be saved
  226. * @param data
  227. * @param opt
  228. * @param params
  229. * @method dependenciesSuccess
  230. */
  231. loadRecommendationsSuccess: function (data, opt, params) {
  232. this._saveRecommendedValues(data, params.dataToSend.changed_configurations);
  233. if (this.isConfigHasInitialState()) {
  234. this.undoRedoRecommended(this.get('recommendations'), false);
  235. this.clearAllRecommendations();
  236. }
  237. this.set("recommendationsConfigs", Em.get(data, "resources.0.recommendations.blueprint.configurations"));
  238. },
  239. loadRecommendationsError: Em.K,
  240. changedDependentGroup: function() {
  241. var dependentServices = this.get('stepConfigs').filter(function(stepConfig) {
  242. return this.get('selectedService.dependentServiceNames').contains(stepConfig.get('serviceName'));
  243. }, this);
  244. App.showSelectGroupsPopup(this.get('selectedService.serviceName'),
  245. this.get('selectedService.configGroups').findProperty('name', this.get('selectedConfigGroup.name')),
  246. dependentServices, this.get('recommendations'))
  247. },
  248. /**
  249. * saves values from response for dependent config properties to <code>recommendations<code>
  250. * @param data
  251. * @param [changedConfigs=null]
  252. * @method saveRecommendedValues
  253. * @private
  254. */
  255. _saveRecommendedValues: function(data, changedConfigs) {
  256. Em.assert('invalid data - `data.resources[0].recommendations.blueprint.configurations` not defined ', data && data.resources[0] && Em.get(data.resources[0], 'recommendations.blueprint.configurations'));
  257. var recommendations = data.resources[0].recommendations;
  258. if (recommendations['config-groups'] && this.get('selectedConfigGroup') && !this.get('selectedConfigGroup.isDefault')) {
  259. var configFroGroup = recommendations['config-groups'][0];
  260. this.get('stepConfigs').forEach(function(stepConfig) {
  261. var configGroup = this.getGroupForService(stepConfig.get('serviceName'));
  262. if (configGroup) {
  263. this.updateOverridesByRecommendations(configFroGroup.configurations, stepConfig.get('configs'), changedConfigs, configGroup);
  264. this.updateOverridesByRecommendations(configFroGroup.dependent_configurations, stepConfig.get('configs'), changedConfigs, configGroup);
  265. this.toggleProperty('forceUpdateBoundaries');
  266. }
  267. }, this);
  268. } else {
  269. var configObject = recommendations.blueprint.configurations;
  270. this.get('stepConfigs').forEach(function(stepConfig) {
  271. this.updateConfigsByRecommendations(configObject, stepConfig.get('configs'), changedConfigs);
  272. }, this);
  273. this.addByRecommendations(configObject, changedConfigs);
  274. }
  275. this.cleanUpRecommendations();
  276. },
  277. /**
  278. * method to show popup with dependent configs
  279. * @method showChangedDependentConfigs
  280. */
  281. showChangedDependentConfigs: function(event, callback, secondary) {
  282. var self = this;
  283. var recommendations = event ? this.get('changedProperties') : this.get('recommendations');
  284. if (recommendations.length > 0) {
  285. App.showDependentConfigsPopup(recommendations, function() {
  286. self.onSaveRecommendedPopup(recommendations);
  287. if (callback) callback();
  288. }, secondary);
  289. } else {
  290. if (callback) callback();
  291. }
  292. },
  293. /**
  294. * update configs when toggle checkbox on dependent configs popup
  295. */
  296. onSaveRecommendedPopup: function(recommendations) {
  297. var propertiesToUpdate = recommendations.filter(function(c) {
  298. return Em.get(c, 'saveRecommendedDefault') != Em.get(c, 'saveRecommended');
  299. }),
  300. propertiesToUndo = propertiesToUpdate.filterProperty('saveRecommended', false),
  301. propertiesToRedo = propertiesToUpdate.filterProperty('saveRecommended', true);
  302. this.undoRedoRecommended(propertiesToUndo, false);
  303. this.undoRedoRecommended(propertiesToRedo, true);
  304. this.set('recommendationTimeStamp', (new Date).getTime());
  305. },
  306. /**
  307. * run through config properties list (form dependent popup)
  308. * and set value to default (undo) or recommended (redo)
  309. * this happens when toggle checkbox in popup
  310. * @param {Object[]} propertiesToUpdate
  311. * @param {boolean} redo
  312. */
  313. undoRedoRecommended: function(propertiesToUpdate, redo) {
  314. propertiesToUpdate.forEach(function(p) {
  315. var initial = redo ? Em.get(p, 'initialValue') : Em.get(p, 'recommendedValue');
  316. var recommended = redo ? Em.get(p, 'recommendedValue') : Em.get(p, 'initialValue');
  317. var stepConfig = this.get('stepConfigs').findProperty('serviceName', Em.get(p, 'serviceName'));
  318. var config = stepConfig.get('configs').find(function(scp) {
  319. return scp.get('name') == Em.get(p, 'propertyName') && scp.get('filename') == App.config.getOriginalFileName(Em.get(p, 'propertyFileName'));
  320. });
  321. var selectedGroup = App.ServiceConfigGroup.find().filterProperty('serviceName', Em.get(p, 'serviceName')).findProperty('name', Em.get(p, 'configGroup'));
  322. if (selectedGroup.get('isDefault')) {
  323. if (Em.isNone(recommended)) {
  324. stepConfig.get('configs').removeObject(config);
  325. } else if (Em.isNone(initial)) {
  326. this._addConfigByRecommendation(stepConfig.get('configs'), Em.get(p, 'propertyName'), Em.get(p, 'propertyFileName'), recommended);
  327. } else {
  328. Em.set(config, 'value', recommended);
  329. }
  330. } else {
  331. if (Em.isNone(recommended)) {
  332. config.get('overrides').removeObject(config.getOverride(selectedGroup.get('name')));
  333. } else if (Em.isNone(initial)) {
  334. this._addConfigOverrideRecommendation(config, recommended, null, selectedGroup);
  335. } else {
  336. var override = config.getOverride(selectedGroup.get('name'));
  337. if (override) {
  338. override.set('value', recommended);
  339. }
  340. }
  341. }
  342. }, this);
  343. },
  344. /**
  345. * disable saving recommended value for current config
  346. * @param config
  347. * @param {boolean} saveRecommended
  348. * @method removeCurrentFromDependentList
  349. */
  350. removeCurrentFromDependentList: function (config, saveRecommended) {
  351. var recommendation = this.getRecommendation(config.get('name'), config.get('filename'), config.get('group.name'));
  352. if (recommendation) this.saveRecommendation(recommendation, saveRecommended);
  353. }
  354. });