configs_saver.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  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 dataManipulationUtils = require('utils/data_manipulation');
  20. var lazyLoading = require('utils/lazy_loading');
  21. /**
  22. * this mixin has method to save configs (used in MainServiceInfoConfigsController)
  23. * all methods are divided into couple groups :
  24. * 0. HELPERS - some helper methods
  25. * 1. PRE SAVE CHECKS - warning popups and validations checks
  26. * 2. PREPARE CONFIGS TO SAVE - filtering and formatting changed configs
  27. * 2.1 PREPARE DATABASE CONFIGS - modify database properties
  28. * 2.2 ADD DYNAMIC CONFIGS - !!!NEED INVESTIGATION
  29. * 3. GENERATING JSON TO SAVE - generating json data
  30. * 4. AJAX REQUESTS - ajax request
  31. * 5. AFTER SAVE INFO - after save methods like to show popup with result
  32. * 6. ADDITIONAL
  33. */
  34. App.ConfigsSaverMixin = Em.Mixin.create({
  35. /**
  36. * @type {boolean}
  37. */
  38. saveConfigsFlag: true,
  39. /**
  40. * file names of changed configs
  41. * @type {string[]}
  42. */
  43. modifiedFileNames: [],
  44. /**
  45. * List of heapsize properties not to be parsed
  46. * @type {string[]}
  47. */
  48. heapsizeException: ['hadoop_heapsize', 'yarn_heapsize', 'nodemanager_heapsize', 'resourcemanager_heapsize', 'apptimelineserver_heapsize', 'jobhistory_heapsize', 'nfsgateway_heapsize', 'accumulo_master_heapsize', 'accumulo_tserver_heapsize', 'accumulo_monitor_heapsize', 'accumulo_gc_heapsize', 'accumulo_other_heapsize', 'hbase_master_heapsize', 'hbase_regionserver_heapsize', 'metrics_collector_heapsize'],
  49. /**
  50. * Regular expression for heapsize properties detection
  51. * @type {regexp}
  52. */
  53. heapsizeRegExp: /_heapsize|_newsize|_maxnewsize|_permsize|_maxpermsize$/,
  54. /**
  55. * clear info to default
  56. * @method clearSaveInfo
  57. */
  58. clearSaveInfo: function() {
  59. this.set('modifiedFileNames', []);
  60. },
  61. /**
  62. * method to run saving configs
  63. * @method saveStepConfigs
  64. */
  65. saveStepConfigs: function() {
  66. if (!this.get("isSubmitDisabled")) {
  67. this.startSave();
  68. this.showWarningPopupsBeforeSave();
  69. }
  70. },
  71. /**
  72. * get config group object for current service
  73. * @param serviceName
  74. * @returns {App.ConfigGroup}
  75. */
  76. getGroupFromModel: function(serviceName) {
  77. if (this.get('selectedService.serviceName') === serviceName) {
  78. return this.get('selectedConfigGroup');
  79. } else {
  80. var groups = App.ServiceConfigGroup.find().filterProperty('serviceName', serviceName);
  81. if (this.get('selectedConfigGroup.isDefault')) {
  82. return groups.length ? groups.findProperty('isDefault', true) : null;
  83. } else {
  84. return groups.length ? groups.findProperty('name', this.get('selectedConfigGroup.dependentConfigGroups')[serviceName]) : null;
  85. }
  86. }
  87. },
  88. /**
  89. * Save changed configs and config groups
  90. * @method saveConfigs
  91. */
  92. saveConfigs: function () {
  93. var selectedConfigGroup = this.get('selectedConfigGroup');
  94. if (selectedConfigGroup.get('isDefault')) {
  95. var data = [];
  96. this.get('stepConfigs').forEach(function(stepConfig) {
  97. var serviceConfig = this.getServiceConfigToSave(stepConfig.get('serviceName'), stepConfig.get('configs'));
  98. if (serviceConfig) {
  99. data.push(serviceConfig);
  100. }
  101. }, this);
  102. if (Em.isArray(data) && data.length) {
  103. this.putChangedConfigurations(data, true);
  104. } else {
  105. this.onDoPUTClusterConfigurations();
  106. }
  107. } else {
  108. this.get('stepConfigs').forEach(function(stepConfig) {
  109. var serviceName = stepConfig.get('serviceName');
  110. var configs = stepConfig.get('configs');
  111. var configGroup = this.getGroupFromModel(serviceName);
  112. if (configGroup) {
  113. if (configGroup.get('isDefault')) {
  114. var configsToSave = this.getServiceConfigToSave(serviceName, configs);
  115. if (configsToSave) {
  116. this.putChangedConfigurations([configsToSave], false);
  117. }
  118. } else {
  119. var overridenConfigs = this.getConfigsForGroup(configs, configGroup.get('name'));
  120. if (Em.isArray(overridenConfigs)) {
  121. this.saveGroup(overridenConfigs, configGroup, this.get('content.serviceName') === serviceName);
  122. }
  123. }
  124. }
  125. }, this);
  126. }
  127. },
  128. /*********************************** 0. HELPERS ********************************************/
  129. /**
  130. * tells controller in saving configs was started
  131. * for now just changes flag <code>saveInProgress<code> to true
  132. * @private
  133. * @method startSave
  134. */
  135. startSave: function() {
  136. this.set("saveInProgress", true);
  137. },
  138. /**
  139. * tells controller that save has been finished
  140. * for now just changes flag <code>saveInProgress<code> to true
  141. * @private
  142. * @method completeSave
  143. */
  144. completeSave: function() {
  145. this.set("saveInProgress", false);
  146. },
  147. /**
  148. * Are some unsaved changes available
  149. * @returns {boolean}
  150. * @method hasUnsavedChanges
  151. */
  152. hasUnsavedChanges: function () {
  153. return !Em.isNone(this.get('hash')) && this.get('hash') != this.getHash();
  154. },
  155. /*********************************** 1. PRE SAVE CHECKS ************************************/
  156. /**
  157. * show some warning popups before user save configs
  158. * @private
  159. * @method showWarningPopupsBeforeSave
  160. */
  161. showWarningPopupsBeforeSave: function() {
  162. var self = this;
  163. if (this.isDirChanged()) {
  164. App.showConfirmationPopup(function() {
  165. self.showChangedDependentConfigs(null, function() {
  166. self.restartServicePopup();
  167. });
  168. },
  169. Em.I18n.t('services.service.config.confirmDirectoryChange').format(self.get('content.displayName')),
  170. this.completeSave.bind(this)
  171. );
  172. } else {
  173. self.showChangedDependentConfigs(null, function() {
  174. self.restartServicePopup();
  175. }, this.completeSave.bind(this));
  176. }
  177. },
  178. /**
  179. * Runs config validation before save
  180. * @private
  181. * @method restartServicePopup
  182. */
  183. restartServicePopup: function () {
  184. this.serverSideValidation()
  185. .done(this.saveConfigs.bind(this))
  186. .fail(this.completeSave.bind(this));
  187. },
  188. /**
  189. * Define if user has changed some dir properties
  190. * @return {Boolean}
  191. * @private
  192. * @method isDirChanged
  193. */
  194. isDirChanged: function () {
  195. var dirChanged = false;
  196. var serviceName = this.get('content.serviceName');
  197. if (serviceName === 'HDFS') {
  198. var hdfsConfigs = this.get('stepConfigs').findProperty('serviceName', 'HDFS').get('configs');
  199. if ((hdfsConfigs.findProperty('name', 'dfs.namenode.name.dir') && hdfsConfigs.findProperty('name', 'dfs.namenode.name.dir').get('isNotDefaultValue')) ||
  200. (hdfsConfigs.findProperty('name', 'dfs.namenode.checkpoint.dir') && hdfsConfigs.findProperty('name', 'dfs.namenode.checkpoint.dir').get('isNotDefaultValue')) ||
  201. (hdfsConfigs.findProperty('name', 'dfs.datanode.data.dir') && hdfsConfigs.findProperty('name', 'dfs.datanode.data.dir').get('isNotDefaultValue'))) {
  202. dirChanged = true;
  203. }
  204. }
  205. return dirChanged;
  206. },
  207. /*********************************** 2. GENERATING DATA TO SAVE ****************************/
  208. /**
  209. * get config properties for that fileNames that was changed
  210. * @param stepConfigs
  211. * @private
  212. * @returns {Array}
  213. */
  214. getModifiedConfigs: function(stepConfigs) {
  215. var modifiedConfigs = stepConfigs
  216. // get only modified and created configs
  217. .filter(function (config) {
  218. return config.get('isNotDefaultValue') || config.get('isNotSaved');
  219. })
  220. // get file names and add file names that was modified, for example after property removing
  221. .mapProperty('filename').concat(this.get('modifiedFileNames')).uniq()
  222. // get configs by filename
  223. .map(function (fileName) {
  224. return stepConfigs.filterProperty('filename', fileName);
  225. });
  226. if (!!modifiedConfigs.length) {
  227. // concatenate results
  228. modifiedConfigs = modifiedConfigs.reduce(function (current, prev) {
  229. return current.concat(prev);
  230. });
  231. }
  232. return modifiedConfigs;
  233. },
  234. /**
  235. * get configs that belongs to config group
  236. * @param stepConfigs
  237. * @private
  238. * @param configGroupName
  239. */
  240. getConfigsForGroup: function(stepConfigs, configGroupName) {
  241. var overridenConfigs = [];
  242. stepConfigs.filterProperty('overrides').forEach(function (config) {
  243. overridenConfigs = overridenConfigs.concat(config.get('overrides'));
  244. });
  245. // find custom original properties that assigned to selected config group
  246. return overridenConfigs.concat(stepConfigs.filterProperty('group')
  247. .filter(function (config) {
  248. return config.get('group.name') == configGroupName;
  249. }));
  250. },
  251. /**
  252. *
  253. * @param serviceName
  254. * @param configs
  255. * @private
  256. * @returns {*}
  257. */
  258. getServiceConfigToSave: function(serviceName, configs) {
  259. if (serviceName === 'YARN') {
  260. configs = App.config.textareaIntoFileConfigs(configs, 'capacity-scheduler.xml');
  261. }
  262. //generates list of properties that was changed
  263. var modifiedConfigs = this.getModifiedConfigs(configs);
  264. var serviceFilenames = Object.keys(App.StackService.find(serviceName).get('configTypes')).map(function (type) {
  265. return App.config.getOriginalFileName(type);
  266. });
  267. // save modified original configs that have no group
  268. modifiedConfigs = this.saveSiteConfigs(modifiedConfigs.filter(function (config) {
  269. return !config.get('group');
  270. }));
  271. if (!Em.isArray(modifiedConfigs) || modifiedConfigs.length == 0) return null;
  272. var fileNamesToSave = modifiedConfigs.mapProperty('filename').concat(this.get('modifiedFileNames')).filter(function(filename) {
  273. return serviceFilenames.contains(filename);
  274. }).uniq();
  275. var configsToSave = this.generateDesiredConfigsJSON(modifiedConfigs, fileNamesToSave, this.get('serviceConfigVersionNote'));
  276. if (configsToSave.length > 0) {
  277. return JSON.stringify({
  278. Clusters: {
  279. desired_config: configsToSave
  280. }
  281. });
  282. } else {
  283. return null;
  284. }
  285. },
  286. /**
  287. * save site configs
  288. * @param configs
  289. * @private
  290. * @method saveSiteConfigs
  291. */
  292. saveSiteConfigs: function (configs) {
  293. this.formatConfigValues(configs);
  294. return configs;
  295. },
  296. /**
  297. * Represent boolean value as string (true => 'true', false => 'false') and trim other values
  298. * @param serviceConfigProperties
  299. * @private
  300. * @method formatConfigValues
  301. */
  302. formatConfigValues: function (serviceConfigProperties) {
  303. serviceConfigProperties.forEach(function (_config) {
  304. if (typeof _config.get('value') === "boolean") _config.set('value', _config.value.toString());
  305. _config.set('value', App.config.trimProperty(_config, true));
  306. });
  307. },
  308. /*********************************** 3. GENERATING JSON TO SAVE *****************************/
  309. /**
  310. * generating common JSON object for desired configs
  311. * @param configsToSave
  312. * @param fileNamesToSave
  313. * @param serviceConfigNote
  314. * @param {boolean} [isNotDefaultGroup=false]
  315. * @returns {Array}
  316. */
  317. generateDesiredConfigsJSON: function(configsToSave, fileNamesToSave, serviceConfigNote, isNotDefaultGroup) {
  318. var desired_config = [];
  319. if (Em.isArray(configsToSave) && Em.isArray(fileNamesToSave) && fileNamesToSave.length && configsToSave.length) {
  320. serviceConfigNote = serviceConfigNote || "";
  321. var tagVersion = "version" + (new Date).getTime();
  322. fileNamesToSave.forEach(function(fName) {
  323. if (this.allowSaveSite(fName)) {
  324. var properties = configsToSave.filterProperty('filename', fName);
  325. var type = App.config.getConfigTagFromFileName(fName);
  326. desired_config.push(this.createDesiredConfig(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup));
  327. }
  328. }, this);
  329. }
  330. return desired_config;
  331. },
  332. /**
  333. * for some file names we have a restriction
  334. * and can't save them, in this this method will return false
  335. * @param fName
  336. * @returns {boolean}
  337. */
  338. allowSaveSite: function(fName) {
  339. switch (fName) {
  340. case 'mapred-queue-acls.xml':
  341. return false;
  342. case 'core-site.xml':
  343. var serviceName = this.get('content.serviceName');
  344. var serviceType = App.StackService.find().findProperty('serviceName',serviceName).get('serviceType');
  345. return ['HDFS', 'GLUSTERFS', 'RANGER_KMS'].contains(this.get('content.serviceName')) || serviceType === 'HCFS';
  346. default :
  347. return true;
  348. }
  349. },
  350. /**
  351. * generating common JSON object for desired config
  352. * @param {string} type - file name without '.xml'
  353. * @param {string} tagVersion - version + timestamp
  354. * @param {App.ConfigProperty[]} properties - array of properties from model
  355. * @param {string} serviceConfigNote
  356. * @param {boolean} [isNotDefaultGroup=false]
  357. * @returns {{type: string, tag: string, properties: {}, properties_attributes: {}|undefined, service_config_version_note: string|undefined}}
  358. */
  359. createDesiredConfig: function(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup) {
  360. Em.assert('type and tagVersion should be defined', type && tagVersion);
  361. var desired_config = {
  362. "type": type,
  363. "tag": tagVersion,
  364. "properties": {}
  365. };
  366. if (!isNotDefaultGroup) {
  367. desired_config.service_config_version_note = serviceConfigNote || "";
  368. }
  369. var attributes = { final: {} };
  370. if (Em.isArray(properties)) {
  371. properties.forEach(function(property) {
  372. if (property.get('isRequiredByAgent')) {
  373. desired_config.properties[property.get('name')] = this.formatValueBeforeSave(property);
  374. /**
  375. * add is final value
  376. */
  377. if (property.get('isFinal')) {
  378. attributes.final[property.get('name')] = "true";
  379. }
  380. }
  381. }, this);
  382. }
  383. if (Object.keys(attributes.final).length) {
  384. desired_config.properties_attributes = attributes;
  385. }
  386. return desired_config;
  387. },
  388. /**
  389. * format value before save performs some changing of values
  390. * according to the rules that includes heapsizeException trimming and some custom rules
  391. * @param {App.ConfigProperty} property
  392. * @returns {string}
  393. */
  394. formatValueBeforeSave: function(property) {
  395. var name = property.get('name');
  396. var value = property.get('value');
  397. var kdcTypesMap = App.router.get('mainAdminKerberosController.kdcTypesValues');
  398. //TODO check for core-site
  399. if (this.get('heapsizeRegExp').test(name) && !this.get('heapsizeException').contains(name) && !(value).endsWith("m")) {
  400. return value += "m";
  401. }
  402. if (typeof property.get('value') === "boolean") {
  403. return property.get('value').toString();
  404. }
  405. switch (name) {
  406. case 'kdc_type':
  407. return Em.keys(kdcTypesMap).filter(function(key) {
  408. return kdcTypesMap[key] === property.get('value');
  409. })[0];
  410. case 'storm.zookeeper.servers':
  411. case 'nimbus.seeds':
  412. if (Em.isArray(value)) {
  413. return JSON.stringify(value).replace(/"/g, "'");
  414. } else {
  415. return value;
  416. }
  417. break;
  418. default:
  419. return App.config.trimProperty(property, true);
  420. }
  421. },
  422. /*********************************** 4. AJAX REQUESTS **************************************/
  423. /**
  424. * save config group
  425. * @param overridenConfigs
  426. * @param selectedConfigGroup
  427. * @param showPopup
  428. */
  429. saveGroup: function(overridenConfigs, selectedConfigGroup, showPopup) {
  430. var groupHosts = [];
  431. var fileNamesToSave = overridenConfigs.mapProperty('filename').uniq();
  432. selectedConfigGroup.get('hosts').forEach(function (hostName) {
  433. groupHosts.push({"host_name": hostName});
  434. });
  435. var id = selectedConfigGroup.get('configGroupId');
  436. id = Em.isNone(id) ? selectedConfigGroup.get('id') : id;
  437. this.putConfigGroupChanges({
  438. ConfigGroup: {
  439. "id": id,
  440. "cluster_name": App.get('clusterName'),
  441. "group_name": selectedConfigGroup.get('name'),
  442. "tag": selectedConfigGroup.get('service.id'),
  443. "description": selectedConfigGroup.get('description'),
  444. "hosts": groupHosts,
  445. "service_config_version_note": this.get('serviceConfigVersionNote'),
  446. "desired_configs": this.generateDesiredConfigsJSON(overridenConfigs, fileNamesToSave, null, true)
  447. }
  448. }, showPopup);
  449. },
  450. /**
  451. * persist properties of config groups to server
  452. * show result popup if <code>showPopup</code> is true
  453. * @param data {Object}
  454. * @param showPopup {Boolean}
  455. * @method putConfigGroupChanges
  456. */
  457. putConfigGroupChanges: function (data, showPopup) {
  458. var ajaxOptions = {
  459. name: 'config_groups.update_config_group',
  460. sender: this,
  461. data: {
  462. id: data.ConfigGroup.id,
  463. configGroup: data
  464. }
  465. };
  466. if (showPopup) {
  467. ajaxOptions.success = "putConfigGroupChangesSuccess";
  468. }
  469. return App.ajax.send(ajaxOptions);
  470. },
  471. /**
  472. * Saves configuration of set of sites. The provided data
  473. * contains the site name and tag to be used.
  474. * @param {Object[]} services
  475. * @param {boolean} showPopup
  476. * @return {$.ajax}
  477. * @method putChangedConfigurations
  478. */
  479. putChangedConfigurations: function (services, showPopup) {
  480. var ajaxData = {
  481. name: 'common.across.services.configurations',
  482. sender: this,
  483. data: {
  484. data: '[' + services.toString() + ']'
  485. },
  486. error: 'doPUTClusterConfigurationSiteErrorCallback'
  487. };
  488. if (showPopup) {
  489. ajaxData.success = 'doPUTClusterConfigurationSiteSuccessCallback'
  490. }
  491. return App.ajax.send(ajaxData);
  492. },
  493. /*********************************** 5. AFTER SAVE INFO ************************************/
  494. /**
  495. * @private
  496. * @method putConfigGroupChangesSuccess
  497. */
  498. putConfigGroupChangesSuccess: function () {
  499. this.set('saveConfigsFlag', true);
  500. this.onDoPUTClusterConfigurations();
  501. },
  502. /**
  503. * @private
  504. * @method doPUTClusterConfigurationSiteSuccessCallback
  505. */
  506. doPUTClusterConfigurationSiteSuccessCallback: function () {
  507. this.onDoPUTClusterConfigurations();
  508. },
  509. /**
  510. * @private
  511. * @method doPUTClusterConfigurationSiteErrorCallback
  512. */
  513. doPUTClusterConfigurationSiteErrorCallback: function () {
  514. this.set('saveConfigsFlag', false);
  515. this.doPUTClusterConfigurationSiteSuccessCallback();
  516. },
  517. /**
  518. * On save configs handler. Open save configs popup with appropriate message
  519. * and clear config dependencies list.
  520. * @private
  521. * @method onDoPUTClusterConfigurations
  522. */
  523. onDoPUTClusterConfigurations: function () {
  524. var header, message, messageClass, value, status = 'unknown', urlParams = '',
  525. result = {
  526. flag: this.get('saveConfigsFlag'),
  527. message: null,
  528. value: null
  529. },
  530. extendedModel = App.Service.extendedModel[this.get('content.serviceName')],
  531. currentService = extendedModel ? App[extendedModel].find(this.get('content.serviceName')) : App.Service.find(this.get('content.serviceName'));
  532. if (!result.flag) {
  533. result.message = Em.I18n.t('services.service.config.failSaveConfig');
  534. }
  535. App.router.get('clusterController').updateClusterData();
  536. App.router.get('updateController').updateComponentConfig(function () {
  537. });
  538. var flag = result.flag;
  539. if (result.flag === true) {
  540. header = Em.I18n.t('services.service.config.saved');
  541. message = Em.I18n.t('services.service.config.saved.message');
  542. messageClass = 'alert alert-success';
  543. // warn the user if any of the components are in UNKNOWN state
  544. urlParams += ',ServiceComponentInfo/installed_count,ServiceComponentInfo/total_count';
  545. if (this.get('content.serviceName') === 'HDFS') {
  546. urlParams += '&ServiceComponentInfo/service_name.in(HDFS)'
  547. }
  548. } else {
  549. header = Em.I18n.t('common.failure');
  550. message = result.message;
  551. messageClass = 'alert alert-error';
  552. value = result.value;
  553. }
  554. if(currentService){
  555. App.QuickViewLinks.proto().set('content', currentService);
  556. App.QuickViewLinks.proto().loadTags();
  557. }
  558. this.showSaveConfigsPopup(header, flag, message, messageClass, value, status, urlParams);
  559. this.clearAllRecommendations();
  560. },
  561. /**
  562. * Show save configs popup
  563. * @return {App.ModalPopup}
  564. * @private
  565. * @method showSaveConfigsPopup
  566. */
  567. showSaveConfigsPopup: function (header, flag, message, messageClass, value, status, urlParams) {
  568. var self = this;
  569. if (flag) {
  570. self.loadStep();
  571. }
  572. return App.ModalPopup.show({
  573. header: header,
  574. primary: Em.I18n.t('ok'),
  575. secondary: null,
  576. onPrimary: function () {
  577. this.hide();
  578. if (!flag) {
  579. self.completeSave();
  580. }
  581. },
  582. onClose: function () {
  583. this.hide();
  584. self.completeSave();
  585. },
  586. disablePrimary: true,
  587. bodyClass: Ember.View.extend({
  588. flag: flag,
  589. message: function () {
  590. return this.get('isLoaded') ? message : Em.I18n.t('services.service.config.saving.message');
  591. }.property('isLoaded'),
  592. messageClass: function () {
  593. return this.get('isLoaded') ? messageClass : 'alert alert-info';
  594. }.property('isLoaded'),
  595. setDisablePrimary: function () {
  596. this.get('parentView').set('disablePrimary', !this.get('isLoaded'));
  597. }.observes('isLoaded'),
  598. runningHosts: [],
  599. runningComponentCount: 0,
  600. unknownHosts: [],
  601. unknownComponentCount: 0,
  602. siteProperties: value,
  603. isLoaded: false,
  604. componentsFilterSuccessCallback: function (response) {
  605. var count = 0,
  606. view = this,
  607. lazyLoadHosts = function (dest) {
  608. lazyLoading.run({
  609. initSize: 20,
  610. chunkSize: 50,
  611. delay: 50,
  612. destination: dest,
  613. source: hosts,
  614. context: view
  615. });
  616. },
  617. /**
  618. * Map components for their hosts
  619. * Return format:
  620. * <code>
  621. * {
  622. * host1: [component1, component2, ...],
  623. * host2: [component3, component4, ...]
  624. * }
  625. * </code>
  626. * @return {object}
  627. */
  628. setComponents = function (item, components) {
  629. item.host_components.forEach(function (c) {
  630. var name = c.HostRoles.host_name;
  631. if (!components[name]) {
  632. components[name] = [];
  633. }
  634. components[name].push(App.format.role(item.ServiceComponentInfo.component_name));
  635. });
  636. return components;
  637. },
  638. /**
  639. * Map result of <code>setComponents</code> to array
  640. * @return {{name: string, components: string}[]}
  641. */
  642. setHosts = function (components) {
  643. var hosts = [];
  644. Em.keys(components).forEach(function (key) {
  645. hosts.push({
  646. name: key,
  647. components: components[key].join(', ')
  648. });
  649. });
  650. return hosts;
  651. },
  652. components = {},
  653. hosts = [];
  654. switch (status) {
  655. case 'unknown':
  656. response.items.filter(function (item) {
  657. return (item.ServiceComponentInfo.total_count > item.ServiceComponentInfo.started_count + item.ServiceComponentInfo.installed_count);
  658. }).forEach(function (item) {
  659. var total = item.ServiceComponentInfo.total_count,
  660. started = item.ServiceComponentInfo.started_count,
  661. installed = item.ServiceComponentInfo.installed_count,
  662. unknown = total - started + installed;
  663. components = setComponents(item, components);
  664. count += unknown;
  665. });
  666. hosts = setHosts(components);
  667. this.set('unknownComponentCount', count);
  668. lazyLoadHosts(this.get('unknownHosts'));
  669. break;
  670. case 'started':
  671. response.items.filterProperty('ServiceComponentInfo.started_count').forEach(function (item) {
  672. var started = item.ServiceComponentInfo.started_count;
  673. components = setComponents(item, components);
  674. count += started;
  675. hosts = setHosts(components);
  676. });
  677. this.set('runningComponentCount', count);
  678. lazyLoadHosts(this.get('runningHosts'));
  679. break;
  680. }
  681. },
  682. componentsFilterErrorCallback: function () {
  683. this.set('isLoaded', true);
  684. },
  685. didInsertElement: function () {
  686. return App.ajax.send({
  687. name: 'components.filter_by_status',
  688. sender: this,
  689. data: {
  690. clusterName: App.get('clusterName'),
  691. urlParams: urlParams
  692. },
  693. success: 'componentsFilterSuccessCallback',
  694. error: 'componentsFilterErrorCallback'
  695. });
  696. },
  697. getDisplayMessage: function () {
  698. var displayMsg = [];
  699. var siteProperties = this.get('siteProperties');
  700. if (siteProperties) {
  701. siteProperties.forEach(function (_siteProperty) {
  702. var displayProperty = _siteProperty.siteProperty;
  703. var displayNames = _siteProperty.displayNames;
  704. if (displayNames && displayNames.length) {
  705. if (displayNames.length === 1) {
  706. displayMsg.push(displayProperty + Em.I18n.t('as') + displayNames[0]);
  707. } else {
  708. var name;
  709. displayNames.forEach(function (_name, index) {
  710. if (index === 0) {
  711. name = _name;
  712. } else if (index === siteProperties.length - 1) {
  713. name = name + Em.I18n.t('and') + _name;
  714. } else {
  715. name = name + ', ' + _name;
  716. }
  717. }, this);
  718. displayMsg.push(displayProperty + Em.I18n.t('as') + name);
  719. }
  720. } else {
  721. displayMsg.push(displayProperty);
  722. }
  723. }, this);
  724. }
  725. return displayMsg;
  726. }.property('siteProperties'),
  727. runningHostsMessage: Em.computed.i18nFormat('services.service.config.stopService.runningHostComponents', 'runningComponentCount', 'runningHosts.length'),
  728. unknownHostsMessage: Em.computed.i18nFormat('services.service.config.stopService.unknownHostComponents', 'unknownComponentCount', 'unknownHosts.length'),
  729. templateName: require('templates/main/service/info/configs_save_popup')
  730. })
  731. })
  732. },
  733. /*********************************** 6. ADDITIONAL *******************************************/
  734. /**
  735. * If some configs are changed and user navigates away or select another config-group, show this popup with propose to save changes
  736. * @param {String} path
  737. * @param {object} callback - callback with action to change configs view(change group or version)
  738. * @return {App.ModalPopup}
  739. * @method showSavePopup
  740. */
  741. showSavePopup: function (transitionCallback, callback) {
  742. var self = this;
  743. var passwordWasChanged = this.get('passwordConfigsAreChanged');
  744. return App.ModalPopup.show({
  745. header: Em.I18n.t('common.warning'),
  746. bodyClass: Em.View.extend({
  747. templateName: require('templates/common/configs/save_configuration'),
  748. showSaveWarning: true,
  749. showPasswordChangeWarning: passwordWasChanged,
  750. notesArea: Em.TextArea.extend({
  751. value: passwordWasChanged ? Em.I18n.t('dashboard.configHistory.info-bar.save.popup.notesForPasswordChange') : '',
  752. classNames: ['full-width'],
  753. placeholder: Em.I18n.t('dashboard.configHistory.info-bar.save.popup.placeholder'),
  754. didInsertElement: function () {
  755. if (this.get('value')) {
  756. this.onChangeValue();
  757. }
  758. },
  759. onChangeValue: function() {
  760. this.get('parentView.parentView').set('serviceConfigNote', this.get('value'));
  761. }.observes('value')
  762. })
  763. }),
  764. footerClass: Em.View.extend({
  765. templateName: require('templates/main/service/info/save_popup_footer'),
  766. isSaveDisabled: function() {
  767. return self.get('isSubmitDisabled');
  768. }.property()
  769. }),
  770. primary: Em.I18n.t('common.save'),
  771. secondary: Em.I18n.t('common.cancel'),
  772. onSave: function () {
  773. self.set('serviceConfigVersionNote', this.get('serviceConfigNote'));
  774. self.saveStepConfigs();
  775. this.hide();
  776. },
  777. onDiscard: function () {
  778. self.set('preSelectedConfigVersion', null);
  779. if (transitionCallback) {
  780. transitionCallback();
  781. } else if (callback) {
  782. self.doCancel();
  783. // Prevent multiple popups
  784. self.set('hash', self.getHash());
  785. callback();
  786. }
  787. this.hide();
  788. },
  789. onCancel: function () {
  790. this.hide();
  791. }
  792. });
  793. },
  794. /**
  795. * Save "final" attribute for properties
  796. * @param {Array} properties - array of properties
  797. * @returns {Object|null}
  798. * @method getConfigAttributes
  799. */
  800. getConfigAttributes: function(properties) {
  801. var attributes = {
  802. final: {}
  803. };
  804. var finalAttributes = attributes.final;
  805. var hasAttributes = false;
  806. properties.forEach(function (property) {
  807. if (property.isRequiredByAgent !== false && property.isFinal) {
  808. hasAttributes = true;
  809. finalAttributes[property.name] = "true";
  810. }
  811. });
  812. if (hasAttributes) {
  813. return attributes;
  814. }
  815. return null;
  816. },
  817. /**
  818. * create site object
  819. * @param {string} siteName
  820. * @param {string} tagName
  821. * @param {object[]} siteObj
  822. * @return {Object}
  823. * @method createSiteObj
  824. */
  825. createSiteObj: function (siteName, tagName, siteObj) {
  826. var heapsizeException = this.get('heapsizeException');
  827. var heapsizeRegExp = this.get('heapsizeRegExp');
  828. var siteProperties = {};
  829. siteObj.forEach(function (_siteObj) {
  830. var value = _siteObj.value;
  831. if (_siteObj.isRequiredByAgent == false) return;
  832. // site object name follow the format *permsize/*heapsize and the value NOT ends with "m"
  833. if (heapsizeRegExp.test(_siteObj.name) && !heapsizeException.contains(_siteObj.name) && !(_siteObj.value).endsWith("m")) {
  834. value += "m";
  835. }
  836. siteProperties[_siteObj.name] = value;
  837. switch (siteName) {
  838. case 'falcon-startup.properties':
  839. case 'falcon-runtime.properties':
  840. case 'pig-properties':
  841. siteProperties[_siteObj.name] = value;
  842. break;
  843. default:
  844. siteProperties[_siteObj.name] = this.setServerConfigValue(_siteObj.name, value);
  845. }
  846. }, this);
  847. var result = {"type": siteName, "tag": tagName, "properties": siteProperties};
  848. var attributes = this.getConfigAttributes(siteObj);
  849. if (attributes) {
  850. result['properties_attributes'] = attributes;
  851. }
  852. return result;
  853. },
  854. /**
  855. * This method will be moved to config's decorators class.
  856. *
  857. * For now, provide handling for special properties that need
  858. * be specified in special format required for server.
  859. *
  860. * @param configName {String} - name of config property
  861. * @param value {*} - value of config property
  862. *
  863. * @return {String} - formatted value
  864. * @method setServerConfigValue
  865. */
  866. setServerConfigValue: function (configName, value) {
  867. switch (configName) {
  868. case 'storm.zookeeper.servers':
  869. case 'nimbus.seeds':
  870. if(Em.isArray(value)) {
  871. return JSON.stringify(value).replace(/"/g, "'");
  872. } else {
  873. return value;
  874. }
  875. break;
  876. default:
  877. return value;
  878. }
  879. }
  880. });