configs_saver.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  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'],
  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. /**
  263. * generates list of properties that was changed
  264. * @type {Array}
  265. */
  266. var modifiedConfigs = this.getModifiedConfigs(configs);
  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')).uniq();
  273. var configsToSave = this.generateDesiredConfigsJSON(modifiedConfigs, fileNamesToSave, this.get('serviceConfigVersionNote'));
  274. if (configsToSave.length > 0) {
  275. return JSON.stringify({
  276. Clusters: {
  277. desired_config: configsToSave
  278. }
  279. });
  280. } else {
  281. return null;
  282. }
  283. },
  284. /**
  285. * save site configs
  286. * @param configs
  287. * @private
  288. * @method saveSiteConfigs
  289. */
  290. saveSiteConfigs: function (configs) {
  291. this.formatConfigValues(configs);
  292. return configs;
  293. },
  294. /**
  295. * Represent boolean value as string (true => 'true', false => 'false') and trim other values
  296. * @param serviceConfigProperties
  297. * @private
  298. * @method formatConfigValues
  299. */
  300. formatConfigValues: function (serviceConfigProperties) {
  301. serviceConfigProperties.forEach(function (_config) {
  302. if (typeof _config.get('value') === "boolean") _config.set('value', _config.value.toString());
  303. _config.set('value', App.config.trimProperty(_config, true));
  304. });
  305. },
  306. /*********************************** 3. GENERATING JSON TO SAVE *****************************/
  307. /**
  308. * generating common JSON object for desired configs
  309. * @param configsToSave
  310. * @param fileNamesToSave
  311. * @param serviceConfigNote
  312. * @param {boolean} [isNotDefaultGroup=false]
  313. * @returns {Array}
  314. */
  315. generateDesiredConfigsJSON: function(configsToSave, fileNamesToSave, serviceConfigNote, isNotDefaultGroup) {
  316. var desired_config = [];
  317. if (Em.isArray(configsToSave) && Em.isArray(fileNamesToSave) && fileNamesToSave.length && configsToSave.length) {
  318. serviceConfigNote = serviceConfigNote || "";
  319. var tagVersion = "version" + (new Date).getTime();
  320. fileNamesToSave.forEach(function(fName) {
  321. if (this.allowSaveSite(fName)) {
  322. var properties = configsToSave.filterProperty('filename', fName);
  323. var type = App.config.getConfigTagFromFileName(fName);
  324. desired_config.push(this.createDesiredConfig(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup));
  325. }
  326. }, this);
  327. }
  328. return desired_config;
  329. },
  330. /**
  331. * for some file names we have a restriction
  332. * and can't save them, in this this method will return false
  333. * @param fName
  334. * @returns {boolean}
  335. */
  336. allowSaveSite: function(fName) {
  337. switch (fName) {
  338. case 'mapred-queue-acls.xml':
  339. return false;
  340. case 'core-site.xml':
  341. var serviceName = this.get('content.serviceName');
  342. var serviceType = App.StackService.find().findProperty('serviceName',serviceName).get('serviceType');
  343. return ['HDFS', 'GLUSTERFS', 'RANGER_KMS'].contains(this.get('content.serviceName')) || serviceType === 'HCFS';
  344. default :
  345. return true;
  346. }
  347. },
  348. /**
  349. * generating common JSON object for desired config
  350. * @param {string} type - file name without '.xml'
  351. * @param {string} tagVersion - version + timestamp
  352. * @param {App.ConfigProperty[]} properties - array of properties from model
  353. * @param {string} serviceConfigNote
  354. * @param {boolean} [isNotDefaultGroup=false]
  355. * @returns {{type: string, tag: string, properties: {}, properties_attributes: {}|undefined, service_config_version_note: string|undefined}}
  356. */
  357. createDesiredConfig: function(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup) {
  358. Em.assert('type and tagVersion should be defined', type && tagVersion);
  359. var desired_config = {
  360. "type": type,
  361. "tag": tagVersion,
  362. "properties": {}
  363. };
  364. if (!isNotDefaultGroup) {
  365. desired_config.service_config_version_note = serviceConfigNote || "";
  366. }
  367. var attributes = { final: {} };
  368. if (Em.isArray(properties)) {
  369. properties.forEach(function(property) {
  370. if (property.get('isRequiredByAgent')) {
  371. desired_config.properties[property.get('name')] = this.formatValueBeforeSave(property);
  372. /**
  373. * add is final value
  374. */
  375. if (property.get('isFinal')) {
  376. attributes.final[property.get('name')] = "true";
  377. }
  378. }
  379. }, this);
  380. }
  381. if (Object.keys(attributes.final).length) {
  382. desired_config.properties_attributes = attributes;
  383. }
  384. return desired_config;
  385. },
  386. /**
  387. * format value before save performs some changing of values
  388. * according to the rules that includes heapsizeException trimming and some custom rules
  389. * @param {App.ConfigProperty} property
  390. * @returns {string}
  391. */
  392. formatValueBeforeSave: function(property) {
  393. var name = property.get('name');
  394. var value = property.get('value');
  395. var kdcTypesMap = App.router.get('mainAdminKerberosController.kdcTypesValues');
  396. //TODO check for core-site
  397. if (this.get('heapsizeRegExp').test(name) && !this.get('heapsizeException').contains(name) && !(value).endsWith("m")) {
  398. return value += "m";
  399. }
  400. if (typeof property.get('value') === "boolean") {
  401. return property.get('value').toString();
  402. }
  403. switch (name) {
  404. case 'kdc_type':
  405. return Em.keys(kdcTypesMap).filter(function(key) {
  406. return kdcTypesMap[key] === property.get('value');
  407. })[0];
  408. case 'storm.zookeeper.servers':
  409. case 'nimbus.seeds':
  410. if (Em.isArray(value)) {
  411. return JSON.stringify(value).replace(/"/g, "'");
  412. } else {
  413. return value;
  414. }
  415. break;
  416. default:
  417. return App.config.trimProperty(property, true);
  418. }
  419. },
  420. /*********************************** 4. AJAX REQUESTS **************************************/
  421. /**
  422. * save config group
  423. * @param overridenConfigs
  424. * @param selectedConfigGroup
  425. * @param showPopup
  426. */
  427. saveGroup: function(overridenConfigs, selectedConfigGroup, showPopup) {
  428. var groupHosts = [];
  429. var fileNamesToSave = overridenConfigs.mapProperty('filename').uniq();
  430. selectedConfigGroup.get('hosts').forEach(function (hostName) {
  431. groupHosts.push({"host_name": hostName});
  432. });
  433. var id = selectedConfigGroup.get('configGroupId');
  434. id = Em.isNone(id) ? selectedConfigGroup.get('id') : id;
  435. this.putConfigGroupChanges({
  436. ConfigGroup: {
  437. "id": id,
  438. "cluster_name": App.get('clusterName'),
  439. "group_name": selectedConfigGroup.get('name'),
  440. "tag": selectedConfigGroup.get('service.id'),
  441. "description": selectedConfigGroup.get('description'),
  442. "hosts": groupHosts,
  443. "service_config_version_note": this.get('serviceConfigVersionNote'),
  444. "desired_configs": this.generateDesiredConfigsJSON(overridenConfigs, fileNamesToSave, null, true)
  445. }
  446. }, showPopup);
  447. },
  448. /**
  449. * persist properties of config groups to server
  450. * show result popup if <code>showPopup</code> is true
  451. * @param data {Object}
  452. * @param showPopup {Boolean}
  453. * @method putConfigGroupChanges
  454. */
  455. putConfigGroupChanges: function (data, showPopup) {
  456. var ajaxOptions = {
  457. name: 'config_groups.update_config_group',
  458. sender: this,
  459. data: {
  460. id: data.ConfigGroup.id,
  461. configGroup: data
  462. }
  463. };
  464. if (showPopup) {
  465. ajaxOptions.success = "putConfigGroupChangesSuccess";
  466. }
  467. return App.ajax.send(ajaxOptions);
  468. },
  469. /**
  470. * Saves configuration of set of sites. The provided data
  471. * contains the site name and tag to be used.
  472. * @param {Object[]} services
  473. * @param {boolean} showPopup
  474. * @return {$.ajax}
  475. * @method putChangedConfigurations
  476. */
  477. putChangedConfigurations: function (services, showPopup) {
  478. var ajaxData = {
  479. name: 'common.across.services.configurations',
  480. sender: this,
  481. data: {
  482. data: '[' + services.toString() + ']'
  483. },
  484. error: 'doPUTClusterConfigurationSiteErrorCallback'
  485. };
  486. if (showPopup) {
  487. ajaxData.success = 'doPUTClusterConfigurationSiteSuccessCallback'
  488. }
  489. return App.ajax.send(ajaxData);
  490. },
  491. /*********************************** 5. AFTER SAVE INFO ************************************/
  492. /**
  493. * @private
  494. * @method putConfigGroupChangesSuccess
  495. */
  496. putConfigGroupChangesSuccess: function () {
  497. this.set('saveConfigsFlag', true);
  498. this.onDoPUTClusterConfigurations();
  499. },
  500. /**
  501. * @private
  502. * @method doPUTClusterConfigurationSiteSuccessCallback
  503. */
  504. doPUTClusterConfigurationSiteSuccessCallback: function () {
  505. this.onDoPUTClusterConfigurations();
  506. },
  507. /**
  508. * @private
  509. * @method doPUTClusterConfigurationSiteErrorCallback
  510. */
  511. doPUTClusterConfigurationSiteErrorCallback: function () {
  512. this.set('saveConfigsFlag', false);
  513. this.doPUTClusterConfigurationSiteSuccessCallback();
  514. },
  515. /**
  516. * On save configs handler. Open save configs popup with appropriate message
  517. * and clear config dependencies list.
  518. * @private
  519. * @method onDoPUTClusterConfigurations
  520. */
  521. onDoPUTClusterConfigurations: function () {
  522. var header, message, messageClass, value, status = 'unknown', urlParams = '',
  523. result = {
  524. flag: this.get('saveConfigsFlag'),
  525. message: null,
  526. value: null
  527. },
  528. extendedModel = App.Service.extendedModel[this.get('content.serviceName')],
  529. currentService = extendedModel ? App[extendedModel].find(this.get('content.serviceName')) : App.Service.find(this.get('content.serviceName'));
  530. if (!result.flag) {
  531. result.message = Em.I18n.t('services.service.config.failSaveConfig');
  532. }
  533. App.router.get('clusterController').updateClusterData();
  534. App.router.get('updateController').updateComponentConfig(function () {
  535. });
  536. var flag = result.flag;
  537. if (result.flag === true) {
  538. header = Em.I18n.t('services.service.config.saved');
  539. message = Em.I18n.t('services.service.config.saved.message');
  540. messageClass = 'alert alert-success';
  541. // warn the user if any of the components are in UNKNOWN state
  542. urlParams += ',ServiceComponentInfo/installed_count,ServiceComponentInfo/total_count';
  543. if (this.get('content.serviceName') === 'HDFS') {
  544. urlParams += '&ServiceComponentInfo/service_name.in(HDFS)'
  545. }
  546. } else {
  547. header = Em.I18n.t('common.failure');
  548. message = result.message;
  549. messageClass = 'alert alert-error';
  550. value = result.value;
  551. }
  552. if(currentService){
  553. App.QuickViewLinks.proto().set('content', currentService);
  554. App.QuickViewLinks.proto().loadTags();
  555. }
  556. this.showSaveConfigsPopup(header, flag, message, messageClass, value, status, urlParams);
  557. this.clearDependentConfigs();
  558. },
  559. /**
  560. * Show save configs popup
  561. * @return {App.ModalPopup}
  562. * @private
  563. * @method showSaveConfigsPopup
  564. */
  565. showSaveConfigsPopup: function (header, flag, message, messageClass, value, status, urlParams) {
  566. var self = this;
  567. if (flag) {
  568. this.set('forceTransition', flag);
  569. self.loadStep();
  570. }
  571. return App.ModalPopup.show({
  572. header: header,
  573. primary: Em.I18n.t('ok'),
  574. secondary: null,
  575. onPrimary: function () {
  576. this.hide();
  577. if (!flag) {
  578. self.completeSave();
  579. }
  580. },
  581. onClose: function () {
  582. this.hide();
  583. self.completeSave();
  584. },
  585. disablePrimary: true,
  586. bodyClass: Ember.View.extend({
  587. flag: flag,
  588. message: function () {
  589. return this.get('isLoaded') ? message : Em.I18n.t('services.service.config.saving.message');
  590. }.property('isLoaded'),
  591. messageClass: function () {
  592. return this.get('isLoaded') ? messageClass : 'alert alert-info';
  593. }.property('isLoaded'),
  594. setDisablePrimary: function () {
  595. this.get('parentView').set('disablePrimary', !this.get('isLoaded'));
  596. }.observes('isLoaded'),
  597. runningHosts: [],
  598. runningComponentCount: 0,
  599. unknownHosts: [],
  600. unknownComponentCount: 0,
  601. siteProperties: value,
  602. isLoaded: false,
  603. componentsFilterSuccessCallback: function (response) {
  604. var count = 0,
  605. view = this,
  606. lazyLoadHosts = function (dest) {
  607. lazyLoading.run({
  608. initSize: 20,
  609. chunkSize: 50,
  610. delay: 50,
  611. destination: dest,
  612. source: hosts,
  613. context: view
  614. });
  615. },
  616. /**
  617. * Map components for their hosts
  618. * Return format:
  619. * <code>
  620. * {
  621. * host1: [component1, component2, ...],
  622. * host2: [component3, component4, ...]
  623. * }
  624. * </code>
  625. * @return {object}
  626. */
  627. setComponents = function (item, components) {
  628. item.host_components.forEach(function (c) {
  629. var name = c.HostRoles.host_name;
  630. if (!components[name]) {
  631. components[name] = [];
  632. }
  633. components[name].push(App.format.role(item.ServiceComponentInfo.component_name));
  634. });
  635. return components;
  636. },
  637. /**
  638. * Map result of <code>setComponents</code> to array
  639. * @return {{name: string, components: string}[]}
  640. */
  641. setHosts = function (components) {
  642. var hosts = [];
  643. Em.keys(components).forEach(function (key) {
  644. hosts.push({
  645. name: key,
  646. components: components[key].join(', ')
  647. });
  648. });
  649. return hosts;
  650. },
  651. components = {},
  652. hosts = [];
  653. switch (status) {
  654. case 'unknown':
  655. response.items.filter(function (item) {
  656. return (item.ServiceComponentInfo.total_count > item.ServiceComponentInfo.started_count + item.ServiceComponentInfo.installed_count);
  657. }).forEach(function (item) {
  658. var total = item.ServiceComponentInfo.total_count,
  659. started = item.ServiceComponentInfo.started_count,
  660. installed = item.ServiceComponentInfo.installed_count,
  661. unknown = total - started + installed;
  662. components = setComponents(item, components);
  663. count += unknown;
  664. });
  665. hosts = setHosts(components);
  666. this.set('unknownComponentCount', count);
  667. lazyLoadHosts(this.get('unknownHosts'));
  668. break;
  669. case 'started':
  670. response.items.filterProperty('ServiceComponentInfo.started_count').forEach(function (item) {
  671. var started = item.ServiceComponentInfo.started_count;
  672. components = setComponents(item, components);
  673. count += started;
  674. hosts = setHosts(components);
  675. });
  676. this.set('runningComponentCount', count);
  677. lazyLoadHosts(this.get('runningHosts'));
  678. break;
  679. }
  680. },
  681. componentsFilterErrorCallback: function () {
  682. this.set('isLoaded', true);
  683. },
  684. didInsertElement: function () {
  685. return App.ajax.send({
  686. name: 'components.filter_by_status',
  687. sender: this,
  688. data: {
  689. clusterName: App.get('clusterName'),
  690. urlParams: urlParams
  691. },
  692. success: 'componentsFilterSuccessCallback',
  693. error: 'componentsFilterErrorCallback'
  694. });
  695. },
  696. getDisplayMessage: function () {
  697. var displayMsg = [];
  698. var siteProperties = this.get('siteProperties');
  699. if (siteProperties) {
  700. siteProperties.forEach(function (_siteProperty) {
  701. var displayProperty = _siteProperty.siteProperty;
  702. var displayNames = _siteProperty.displayNames;
  703. if (displayNames && displayNames.length) {
  704. if (displayNames.length === 1) {
  705. displayMsg.push(displayProperty + Em.I18n.t('as') + displayNames[0]);
  706. } else {
  707. var name;
  708. displayNames.forEach(function (_name, index) {
  709. if (index === 0) {
  710. name = _name;
  711. } else if (index === siteProperties.length - 1) {
  712. name = name + Em.I18n.t('and') + _name;
  713. } else {
  714. name = name + ', ' + _name;
  715. }
  716. }, this);
  717. displayMsg.push(displayProperty + Em.I18n.t('as') + name);
  718. }
  719. } else {
  720. displayMsg.push(displayProperty);
  721. }
  722. }, this);
  723. }
  724. return displayMsg;
  725. }.property('siteProperties'),
  726. runningHostsMessage: function () {
  727. return Em.I18n.t('services.service.config.stopService.runningHostComponents').format(this.get('runningComponentCount'), this.get('runningHosts.length'));
  728. }.property('runningComponentCount', 'runningHosts.length'),
  729. unknownHostsMessage: function () {
  730. return Em.I18n.t('services.service.config.stopService.unknownHostComponents').format(this.get('unknownComponentCount'), this.get('unknownHosts.length'));
  731. }.property('unknownComponentCount', 'unknownHosts.length'),
  732. templateName: require('templates/main/service/info/configs_save_popup')
  733. })
  734. })
  735. },
  736. /*********************************** 6. ADDITIONAL *******************************************/
  737. /**
  738. * If some configs are changed and user navigates away or select another config-group, show this popup with propose to save changes
  739. * @param {String} path
  740. * @param {object} callback - callback with action to change configs view(change group or version)
  741. * @return {App.ModalPopup}
  742. * @method showSavePopup
  743. */
  744. showSavePopup: function (path, callback) {
  745. var self = this;
  746. return App.ModalPopup.show({
  747. header: Em.I18n.t('common.warning'),
  748. bodyClass: Em.View.extend({
  749. templateName: require('templates/common/configs/save_configuration'),
  750. showSaveWarning: true,
  751. notesArea: Em.TextArea.extend({
  752. classNames: ['full-width'],
  753. placeholder: Em.I18n.t('dashboard.configHistory.info-bar.save.popup.placeholder'),
  754. onChangeValue: function() {
  755. this.get('parentView.parentView').set('serviceConfigNote', this.get('value'));
  756. }.observes('value')
  757. })
  758. }),
  759. footerClass: Ember.View.extend({
  760. templateName: require('templates/main/service/info/save_popup_footer'),
  761. isSaveDisabled: function() {
  762. return self.get('isSubmitDisabled');
  763. }.property()
  764. }),
  765. primary: Em.I18n.t('common.save'),
  766. secondary: Em.I18n.t('common.cancel'),
  767. onSave: function () {
  768. self.set('serviceConfigVersionNote', this.get('serviceConfigNote'));
  769. self.saveStepConfigs();
  770. this.hide();
  771. },
  772. onDiscard: function () {
  773. self.set('preSelectedConfigVersion', null);
  774. if (path) {
  775. self.set('forceTransition', true);
  776. App.router.route(path);
  777. } else if (callback) {
  778. self.doCancel();
  779. // Prevent multiple popups
  780. self.set('hash', self.getHash());
  781. callback();
  782. }
  783. this.hide();
  784. },
  785. onCancel: function () {
  786. this.hide();
  787. }
  788. });
  789. },
  790. /**
  791. * Save "final" attribute for properties
  792. * @param {Array} properties - array of properties
  793. * @returns {Object|null}
  794. * @method getConfigAttributes
  795. */
  796. getConfigAttributes: function(properties) {
  797. var attributes = {
  798. final: {}
  799. };
  800. var finalAttributes = attributes.final;
  801. var hasAttributes = false;
  802. properties.forEach(function (property) {
  803. if (property.isRequiredByAgent !== false && property.isFinal) {
  804. hasAttributes = true;
  805. finalAttributes[property.name] = "true";
  806. }
  807. });
  808. if (hasAttributes) {
  809. return attributes;
  810. }
  811. return null;
  812. },
  813. /**
  814. * create site object
  815. * @param {string} siteName
  816. * @param {string} tagName
  817. * @param {object[]} siteObj
  818. * @return {Object}
  819. * @method createSiteObj
  820. */
  821. createSiteObj: function (siteName, tagName, siteObj) {
  822. var heapsizeException = this.get('heapsizeException');
  823. var heapsizeRegExp = this.get('heapsizeRegExp');
  824. var siteProperties = {};
  825. siteObj.forEach(function (_siteObj) {
  826. var value = _siteObj.value;
  827. if (_siteObj.isRequiredByAgent == false) return;
  828. // site object name follow the format *permsize/*heapsize and the value NOT ends with "m"
  829. if (heapsizeRegExp.test(_siteObj.name) && !heapsizeException.contains(_siteObj.name) && !(_siteObj.value).endsWith("m")) {
  830. value += "m";
  831. }
  832. siteProperties[_siteObj.name] = value;
  833. switch (siteName) {
  834. case 'falcon-startup.properties':
  835. case 'falcon-runtime.properties':
  836. case 'pig-properties':
  837. siteProperties[_siteObj.name] = value;
  838. break;
  839. default:
  840. siteProperties[_siteObj.name] = this.setServerConfigValue(_siteObj.name, value);
  841. }
  842. }, this);
  843. var result = {"type": siteName, "tag": tagName, "properties": siteProperties};
  844. var attributes = this.getConfigAttributes(siteObj);
  845. if (attributes) {
  846. result['properties_attributes'] = attributes;
  847. }
  848. return result;
  849. },
  850. /**
  851. * This method will be moved to config's decorators class.
  852. *
  853. * For now, provide handling for special properties that need
  854. * be specified in special format required for server.
  855. *
  856. * @param configName {String} - name of config property
  857. * @param value {*} - value of config property
  858. *
  859. * @return {String} - formatted value
  860. * @method setServerConfigValue
  861. */
  862. setServerConfigValue: function (configName, value) {
  863. switch (configName) {
  864. case 'storm.zookeeper.servers':
  865. case 'nimbus.seeds':
  866. if(Em.isArray(value)) {
  867. return JSON.stringify(value).replace(/"/g, "'");
  868. } else {
  869. return value;
  870. }
  871. break;
  872. default:
  873. return value;
  874. }
  875. }
  876. });