manage_config_groups_controller.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  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 validator = require('utils/validator');
  20. var hostsManagement = require('utils/hosts');
  21. var numberUtils = require('utils/number_utils');
  22. App.ManageConfigGroupsController = Em.Controller.extend(App.ConfigOverridable, {
  23. name: 'manageConfigGroupsController',
  24. /**
  25. * Determines if needed data is already loaded
  26. * Loading chain starts at <code>loadHosts</code> and is complete on the <code>loadConfigGroups</code> (if user on
  27. * the Installer) or on the <code>_onLoadConfigGroupsSuccess</code> (otherwise)
  28. * @type {boolean}
  29. */
  30. isLoaded: false,
  31. /**
  32. * Determines if user currently is on the Cluster Installer
  33. * @type {boolean}
  34. */
  35. isInstaller: false,
  36. /**
  37. * Determines if user currently is on the Add Service Wizard
  38. * @type {boolean}
  39. */
  40. isAddService: false,
  41. /**
  42. * Current service name
  43. * @type {string}
  44. */
  45. serviceName: null,
  46. /**
  47. * @type {App.ConfigGroup[]}
  48. */
  49. configGroups: [],
  50. /**
  51. * @type {App.ConfigGroup[]}
  52. */
  53. originalConfigGroups: [],
  54. /**
  55. * @type {App.ConfigGroup}
  56. */
  57. selectedConfigGroup: null,
  58. /**
  59. * @type {string[]}
  60. */
  61. selectedHosts: [],
  62. /**
  63. * List of all hosts in the cluster
  64. * @type {{
  65. * id: string,
  66. * ip: string,
  67. * osType: string,
  68. * osArch: string,
  69. * hostName: string,
  70. * publicHostName: string,
  71. * cpu: number,
  72. * memory: number,
  73. * diskTotal: string,
  74. * diskFree: string,
  75. * disksMounted: number,
  76. * hostComponents: {
  77. * componentName: string,
  78. * displayName: string
  79. * }[]
  80. * }[]}
  81. */
  82. clusterHosts: [],
  83. /**
  84. * trigger <code>selectDefaultGroup</code> after group delete
  85. * @type {null}
  86. */
  87. groupDeleteTrigger: null,
  88. /**
  89. * List of available service components for <code>serviceName</code>
  90. * @type {{componentName: string, displayName: string, selected: boolean}[]}
  91. */
  92. componentsForFilter: function () {
  93. return App.StackServiceComponent.find().filterProperty('serviceName', this.get('serviceName')).map(function (component) {
  94. return Em.Object.create({
  95. componentName: component.get('componentName'),
  96. displayName: App.format.role(component.get('componentName'), false),
  97. selected: false
  98. });
  99. });
  100. }.property('serviceName'),
  101. /**
  102. * Determines when host may be deleted from config group
  103. * @type {boolean}
  104. */
  105. isDeleteHostsDisabled: function () {
  106. var selectedConfigGroup = this.get('selectedConfigGroup');
  107. if (selectedConfigGroup) {
  108. return selectedConfigGroup.get('isDefault') || this.get('selectedHosts').length === 0;
  109. }
  110. return true;
  111. }.property('selectedConfigGroup', 'selectedConfigGroup.hosts.length', 'selectedHosts.length'),
  112. /**
  113. * Map with modified/deleted/created config groups
  114. * @type {{
  115. * toClearHosts: App.ConfigGroup[],
  116. * toDelete: App.ConfigGroup[],
  117. * toSetHosts: App.ConfigGroup[],
  118. * toCreate: App.ConfigGroup[]
  119. * }}
  120. */
  121. hostsModifiedConfigGroups: {},
  122. /**
  123. * Trim the tooltip text to show first 500 characters of properties list
  124. * @type {string}
  125. */
  126. tooltipText: function() {
  127. var selectedConfigGroup = this.get('selectedConfigGroup'),
  128. propertiesList = selectedConfigGroup.get('propertiesList'),
  129. trimLength = 500,
  130. trimmedText = "",
  131. noOfRemainingProperties = 0,
  132. index = 0,
  133. propertyText = "",
  134. addDots = false;
  135. if(propertiesList.length > trimLength) {
  136. // Adjust trim length based on occurrence of <br/> around trim length
  137. index = propertiesList.substring(trimLength-10, trimLength+10).indexOf("<br/>");
  138. if(index > -1) {
  139. trimLength = trimLength - 10 + index;
  140. } else {
  141. addDots = true;
  142. }
  143. trimmedText = propertiesList.substring(0, trimLength);
  144. if(addDots) {
  145. trimmedText += " ...";
  146. }
  147. noOfRemainingProperties = (propertiesList.substring(trimLength).match(new RegExp("<br/>", "g")) || []).length - 1;
  148. if(noOfRemainingProperties > 0) {
  149. propertyText = (noOfRemainingProperties > 1) ? "properties" : "property";
  150. trimmedText += "<br/> and " + noOfRemainingProperties + " more " + propertyText;
  151. }
  152. } else {
  153. trimmedText = propertiesList;
  154. }
  155. return trimmedText;
  156. }.property('selectedConfigGroup.propertiesList'),
  157. /**
  158. * Check when some config group was changed and updates <code>hostsModifiedConfigGroups</code> once
  159. * @method hostsModifiedConfigGroupsObs
  160. */
  161. hostsModifiedConfigGroupsObs: function() {
  162. Em.run.once(this, this.hostsModifiedConfigGroupsObsOnce);
  163. }.observes('selectedConfigGroup.hosts.@each', 'selectedConfigGroup.hosts.length', 'selectedConfigGroup.description', 'configGroups', 'isLoaded'),
  164. /**
  165. * Update <code>hostsModifiedConfigGroups</code>-value
  166. * Called once in the <code>hostsModifiedConfigGroupsObs</code>
  167. * @method hostsModifiedConfigGroupsObsOnce
  168. * @returns {boolean}
  169. */
  170. hostsModifiedConfigGroupsObsOnce: function() {
  171. if (!this.get('isLoaded')) {
  172. return false;
  173. }
  174. var groupsToClearHosts = [];
  175. var groupsToDelete = [];
  176. var groupsToSetHosts = [];
  177. var groupsToCreate = [];
  178. var groups = this.get('configGroups');
  179. var originalGroups = [];
  180. var originalGroupsMap = {};
  181. this.get('originalConfigGroups').forEach(function(item){
  182. if (!item.is_default) {
  183. originalGroupsMap[item.id] = item;
  184. originalGroups.push(item);
  185. }
  186. }, this);
  187. groups.forEach(function (groupRecord) {
  188. if (!groupRecord.get('isDefault')) {
  189. var originalGroup = originalGroupsMap[groupRecord.get('id')];
  190. if (originalGroup) {
  191. if (!(JSON.stringify(groupRecord.get('hosts').slice().sort()) === JSON.stringify(originalGroup.hosts.sort()))) {
  192. groupsToClearHosts.push(groupRecord);
  193. if (groupRecord.get('hosts').length) {
  194. groupsToSetHosts.push(groupRecord);
  195. }
  196. // should update name or description
  197. } else if (groupRecord.get('description') !== originalGroup.description || groupRecord.get('name') !== originalGroup.name) {
  198. groupsToSetHosts.push(groupRecord);
  199. }
  200. delete originalGroupsMap[groupRecord.get('id')];
  201. } else {
  202. groupsToCreate.push({
  203. id: groupRecord.get('id'),
  204. name: groupRecord.get('name'),
  205. description: groupRecord.get('description'),
  206. hosts: groupRecord.get('hosts').slice(0),
  207. service_id: groupRecord.get('serviceName'),
  208. desired_configs: groupRecord.get('desiredConfigs'),
  209. properties: groupRecord.get('properties')
  210. });
  211. }
  212. }
  213. });
  214. //groups to delete
  215. for (var id in originalGroupsMap) {
  216. groupsToDelete.push(App.ServiceConfigGroup.find(id));
  217. }
  218. this.set('hostsModifiedConfigGroups', {
  219. toClearHosts: groupsToClearHosts,
  220. toDelete: groupsToDelete,
  221. toSetHosts: groupsToSetHosts,
  222. toCreate: groupsToCreate,
  223. initialGroups: originalGroups
  224. });
  225. },
  226. /**
  227. * Determines if some changes were done with config groups
  228. * @use hostsModifiedConfigGroups
  229. * @type {boolean}
  230. */
  231. isHostsModified: function () {
  232. if (!this.get('isLoaded')) {
  233. return false;
  234. }
  235. var ignoreKeys = ['initialGroups'];
  236. var modifiedGroups = this.get('hostsModifiedConfigGroups');
  237. return Em.keys(modifiedGroups).map(function (key) {
  238. return ignoreKeys.contains(key) ? 0 : Em.get(modifiedGroups[key], 'length');
  239. }).reduce(Em.sum, 0) > 0;
  240. }.property('hostsModifiedConfigGroups'),
  241. /**
  242. * Resort config groups according to order:
  243. * default group first, other - last
  244. * @method resortConfigGroup
  245. */
  246. resortConfigGroup: function() {
  247. var configGroups = Em.copy(this.get('configGroups'));
  248. if(configGroups.length < 2) return;
  249. var defaultConfigGroup = configGroups.findProperty('isDefault');
  250. configGroups.removeObject(defaultConfigGroup);
  251. var sorted = [defaultConfigGroup].concat(configGroups.sortProperty('name'));
  252. this.removeObserver('configGroups.@each.name', this, 'resortConfigGroup');
  253. this.set('configGroups', sorted);
  254. this.addObserver('configGroups.@each.name', this, 'resortConfigGroup');
  255. }.observes('configGroups.@each.name'),
  256. /**
  257. * Load hosts from server or
  258. * get them from installerController if user on the install wizard
  259. * get them from isAddServiceController if user on the add service wizard
  260. * @method loadHosts
  261. */
  262. loadHosts: function() {
  263. this.set('isLoaded', false);
  264. if (this.get('isInstaller')) {
  265. var allHosts = this.get('isAddService') ? App.router.get('addServiceController').get('allHosts') : App.router.get('installerController').get('allHosts');
  266. this.set('clusterHosts', allHosts);
  267. this.loadConfigGroups(this.get('serviceName'));
  268. }
  269. else {
  270. this.loadHostsFromServer();
  271. this.loadConfigGroups(this.get('serviceName'));
  272. }
  273. },
  274. /**
  275. * Request all hosts directly from server
  276. * @method loadHostsFromServer
  277. * @return {$.ajax}
  278. */
  279. loadHostsFromServer: function() {
  280. return App.ajax.send({
  281. name: 'hosts.config_groups',
  282. sender: this,
  283. data: {},
  284. success: '_loadHostsFromServerSuccessCallback',
  285. error: '_loadHostsFromServerErrorCallback'
  286. });
  287. },
  288. /**
  289. * Success-callback for <code>loadHostsFromServer</code>
  290. * Parse hosts response and wrap them into Ember.Object
  291. * @param {object} data
  292. * @method _loadHostsFromServerSuccessCallback
  293. * @private
  294. */
  295. _loadHostsFromServerSuccessCallback: function (data) {
  296. var wrappedHosts = [];
  297. data.items.forEach(function (host) {
  298. var hostComponents = [];
  299. var diskInfo = host.Hosts.disk_info.filter(function(item) {
  300. return /^ext|^ntfs|^fat|^xfs/i.test(item.type);
  301. });
  302. if (diskInfo.length) {
  303. diskInfo = diskInfo.reduce(function(a, b) {
  304. return {
  305. available: parseInt(a.available) + parseInt(b.available),
  306. size: parseInt(a.size) + parseInt(b.size)
  307. };
  308. });
  309. }
  310. host.host_components.forEach(function (hostComponent) {
  311. hostComponents.push(Em.Object.create({
  312. componentName: hostComponent.HostRoles.component_name,
  313. displayName: App.format.role(hostComponent.HostRoles.component_name, false)
  314. }));
  315. }, this);
  316. wrappedHosts.pushObject(Em.Object.create({
  317. id: host.Hosts.host_name,
  318. ip: host.Hosts.ip,
  319. osType: host.Hosts.os_type,
  320. osArch: host.Hosts.os_arch,
  321. hostName: host.Hosts.host_name,
  322. publicHostName: host.Hosts.public_host_name,
  323. cpu: host.Hosts.cpu_count,
  324. memory: host.Hosts.total_mem,
  325. diskTotal: numberUtils.bytesToSize(diskInfo.size, 0, undefined, 1024),
  326. diskFree: numberUtils.bytesToSize(diskInfo.available, 0, undefined, 1024),
  327. disksMounted: host.Hosts.disk_info.length,
  328. hostComponents: hostComponents
  329. }
  330. ));
  331. }, this);
  332. this.set('clusterHosts', wrappedHosts);
  333. },
  334. /**
  335. * Error-callback for <code>loadHostsFromServer</code>
  336. * @method _loadHostsFromServerErrorCallback
  337. * @private
  338. */
  339. _loadHostsFromServerErrorCallback: function () {
  340. this.set('clusterHosts', []);
  341. },
  342. /**
  343. * Load config groups from server if user is on the already installed cluster
  344. * If not - use loaded data form wizardStep7Controller
  345. * @param {string} serviceName
  346. * @method loadConfigGroups
  347. */
  348. loadConfigGroups: function (serviceName) {
  349. if (this.get('isInstaller')) {
  350. var configGroups = App.router.get('wizardStep7Controller.selectedService.configGroups').slice(0);
  351. var originalConfigGroups = this.generateOriginalConfigGroups(configGroups);
  352. this.setProperties({
  353. configGroups: configGroups,
  354. originalConfigGroups: originalConfigGroups,
  355. isLoaded: true
  356. });
  357. }
  358. else {
  359. this.set('serviceName', serviceName);
  360. App.ajax.send({
  361. name: 'service.load_config_groups',
  362. data: {
  363. serviceName: serviceName
  364. },
  365. sender: this,
  366. success: '_onLoadConfigGroupsSuccess'
  367. });
  368. }
  369. },
  370. /**
  371. * Success-callback for <code>loadConfigGroups</code>
  372. * @param {object} data
  373. * @private
  374. * @method _onLoadConfigGroupsSuccess
  375. */
  376. _onLoadConfigGroupsSuccess: function (data) {
  377. var serviceName = this.get('serviceName');
  378. App.configGroupsMapper.map(data, false, [serviceName]);
  379. var configGroups = App.ServiceConfigGroup.find().filterProperty('serviceName', serviceName);
  380. var rawConfigGroups = this.generateOriginalConfigGroups(configGroups);
  381. var groupToTypeToTagMap = {};
  382. rawConfigGroups.forEach(function (item) {
  383. if (Array.isArray(item.desired_configs)) {
  384. item.desired_configs.forEach(function (config) {
  385. if (!groupToTypeToTagMap[item.name]) {
  386. groupToTypeToTagMap[item.name] = {};
  387. }
  388. groupToTypeToTagMap[item.name][config.type] = config.tag;
  389. });
  390. }
  391. });
  392. this.set('configGroups', configGroups);
  393. this.set('originalConfigGroups', rawConfigGroups);
  394. this.loadProperties(groupToTypeToTagMap);
  395. this.set('isLoaded', true);
  396. },
  397. /**
  398. *
  399. * @param {Array} configGroups
  400. * @returns {Array}
  401. */
  402. generateOriginalConfigGroups: function(configGroups) {
  403. var self = this;
  404. return configGroups.map(function (item) {
  405. return self.createOriginalRecord(item);
  406. });
  407. },
  408. /**
  409. * Return object to use for loading to model with correct names for object keys
  410. * @param configGroup - config group object from model
  411. * @returns {Object}
  412. */
  413. createOriginalRecord: function (configGroup) {
  414. return {
  415. id: configGroup.get('id'),
  416. name: configGroup.get('name'),
  417. service_name: configGroup.get('serviceName'),
  418. description: configGroup.get('description'),
  419. hosts: configGroup.get('hosts').slice(0),
  420. service_id: configGroup.get('serviceName'),
  421. desired_configs: configGroup.get('desiredConfigs'),
  422. is_default: configGroup.get('isDefault'),
  423. child_config_groups: configGroup.get('childConfigGroups') ? configGroup.get('childConfigGroups').mapProperty('id') : [],
  424. parent_config_group_id: configGroup.get('parentConfigGroup.id'),
  425. properties: configGroup.get('properties')
  426. };
  427. },
  428. /**
  429. *
  430. * @param {object} groupToTypeToTagMap
  431. * @method loadProperties
  432. */
  433. loadProperties: function (groupToTypeToTagMap) {
  434. var typeTagToGroupMap = {};
  435. var urlParams = [];
  436. for (var group in groupToTypeToTagMap) {
  437. var overrideTypeTags = groupToTypeToTagMap[group];
  438. for (var type in overrideTypeTags) {
  439. var tag = overrideTypeTags[type];
  440. typeTagToGroupMap[type + "///" + tag] = group;
  441. urlParams.push('(type=' + type + '&tag=' + tag + ')');
  442. }
  443. }
  444. var params = urlParams.join('|');
  445. if (urlParams.length) {
  446. App.ajax.send({
  447. name: 'config.host_overrides',
  448. sender: this,
  449. data: {
  450. params: params,
  451. typeTagToGroupMap: typeTagToGroupMap
  452. },
  453. success: '_onLoadPropertiesSuccess'
  454. });
  455. }
  456. },
  457. /**
  458. * Success-callback for <code>loadProperties</code>
  459. * @param {object} data
  460. * @param {object} opt
  461. * @param {object} params
  462. * @private
  463. * @method _onLoadPropertiesSuccess
  464. */
  465. _onLoadPropertiesSuccess: function (data, opt, params) {
  466. var groupToPropertiesMap = {};
  467. data.items.forEach(function (configs) {
  468. var group = params.typeTagToGroupMap[configs.type + "///" + configs.tag];
  469. if (!groupToPropertiesMap[group]) {
  470. groupToPropertiesMap[group] = [];
  471. }
  472. for (var config in configs.properties) {
  473. groupToPropertiesMap[group].push({
  474. name: config,
  475. value: configs.properties[config],
  476. type: configs.type
  477. });
  478. }
  479. }, this);
  480. for (var g in groupToPropertiesMap) {
  481. this.get('configGroups').findProperty('name', g).set('properties', groupToPropertiesMap[g]);
  482. }
  483. },
  484. /**
  485. * Show popup with properties overridden in the selected config group
  486. * @method showProperties
  487. */
  488. showProperties: function () {
  489. var properties = this.get('selectedConfigGroup.propertiesList').htmlSafe();
  490. if (properties) {
  491. App.showAlertPopup(Em.I18n.t('services.service.config_groups_popup.properties'), properties);
  492. }
  493. },
  494. /**
  495. * Show popup with hosts to add to the selected config group
  496. * @returns {boolean}
  497. * @method addHosts
  498. */
  499. addHosts: function () {
  500. if (this.get('selectedConfigGroup.isAddHostsDisabled')) {
  501. return false;
  502. }
  503. var availableHosts = this.get('selectedConfigGroup.availableHosts');
  504. var popupDescription = {
  505. header: Em.I18n.t('hosts.selectHostsDialog.title'),
  506. dialogMessage: Em.I18n.t('hosts.selectHostsDialog.message').format(this.get('selectedConfigGroup.displayName'))
  507. };
  508. hostsManagement.launchHostsSelectionDialog(availableHosts, [], false, this.get('componentsForFilter'), this.addHostsCallback.bind(this), popupDescription);
  509. },
  510. /**
  511. * Remove selected hosts from default group (<code>selectedConfigGroup.parentConfigGroup</code>) and add them to the <code>selectedConfigGroup</code>
  512. * @param {string[]} selectedHosts
  513. * @method addHostsCallback
  514. */
  515. addHostsCallback: function (selectedHosts) {
  516. if (selectedHosts) {
  517. var group = this.get('selectedConfigGroup');
  518. var parentGroupHosts = group.get('parentConfigGroup.hosts');
  519. var newHostsForParentGroup = parentGroupHosts.filter(function(hostName) {
  520. return !selectedHosts.contains(hostName);
  521. });
  522. group.get('hosts').pushObjects(selectedHosts);
  523. group.set('parentConfigGroup.hosts', newHostsForParentGroup);
  524. }
  525. },
  526. /**
  527. * Delete hosts from <code>selectedConfigGroup</code> and move them to the Default group (<code>selectedConfigGroup.parentConfigGroup</code>)
  528. * @method deleteHosts
  529. */
  530. deleteHosts: function () {
  531. if (this.get('isDeleteHostsDisabled')) {
  532. return;
  533. }
  534. var hosts = this.get('selectedHosts').slice();
  535. var newHosts = [];
  536. this.get('selectedConfigGroup.parentConfigGroup.hosts').pushObjects(hosts);
  537. this.get('selectedConfigGroup.hosts').forEach(function(host) {
  538. if (!hosts.contains(host)) {
  539. newHosts.pushObject(host);
  540. }
  541. });
  542. this.set('selectedConfigGroup.hosts', newHosts);
  543. this.set('selectedHosts', []);
  544. },
  545. /**
  546. * show popup for confirmation delete config group
  547. * @method confirmDelete
  548. */
  549. confirmDelete: function () {
  550. var self = this;
  551. App.showConfirmationPopup(function() {
  552. self.deleteConfigGroup();
  553. });
  554. },
  555. /**
  556. * delete selected config group (stored in the <code>selectedConfigGroup</code>)
  557. * then select default config group
  558. * @method deleteConfigGroup
  559. */
  560. deleteConfigGroup: function () {
  561. var selectedConfigGroup = this.get('selectedConfigGroup');
  562. if (this.get('isDeleteGroupDisabled')) {
  563. return;
  564. }
  565. //move hosts of group to default group (available hosts)
  566. this.set('selectedHosts', selectedConfigGroup.get('hosts'));
  567. this.deleteHosts();
  568. this.get('configGroups').removeObject(selectedConfigGroup);
  569. App.configGroupsMapper.deleteRecord(selectedConfigGroup);
  570. this.set('selectedConfigGroup', this.get('configGroups').findProperty('isDefault'));
  571. this.propertyDidChange('groupDeleteTrigger');
  572. },
  573. /**
  574. * rename new config group (not allowed for default group)
  575. * @method renameConfigGroup
  576. */
  577. renameConfigGroup: function () {
  578. if(this.get('selectedConfigGroup.isDefault')) {
  579. return;
  580. }
  581. var self = this;
  582. var renameGroupPopup = App.ModalPopup.show({
  583. header: Em.I18n.t('services.service.config_groups.rename_config_group_popup.header'),
  584. bodyClass: Em.View.extend({
  585. templateName: require('templates/main/service/new_config_group')
  586. }),
  587. configGroupName: self.get('selectedConfigGroup.name'),
  588. configGroupDesc: self.get('selectedConfigGroup.description'),
  589. warningMessage: null,
  590. isDescriptionDirty: false,
  591. validate: function () {
  592. var warningMessage = '';
  593. var originalGroup = self.get('selectedConfigGroup');
  594. var groupName = this.get('configGroupName').trim();
  595. if (originalGroup.get('description') !== this.get('configGroupDesc') && !this.get('isDescriptionDirty')) {
  596. this.set('isDescriptionDirty', true);
  597. }
  598. if (originalGroup.get('name').trim() === groupName) {
  599. if (this.get('isDescriptionDirty')) {
  600. warningMessage = '';
  601. } else {
  602. warningMessage = Em.I18n.t("config.group.selection.dialog.err.name.exists");
  603. }
  604. } else {
  605. if (self.get('configGroups').mapProperty('name').contains(groupName)) {
  606. warningMessage = Em.I18n.t("config.group.selection.dialog.err.name.exists");
  607. }
  608. else if (groupName && !validator.isValidConfigGroupName(groupName)) {
  609. warningMessage = Em.I18n.t("form.validator.configGroupName");
  610. }
  611. }
  612. this.set('warningMessage', warningMessage);
  613. }.observes('configGroupName', 'configGroupDesc'),
  614. disablePrimary: function () {
  615. return !(this.get('configGroupName').trim().length > 0 && (this.get('warningMessage') !== null && !this.get('warningMessage')));
  616. }.property('warningMessage', 'configGroupName', 'configGroupDesc'),
  617. onPrimary: function () {
  618. self.get('selectedConfigGroup').setProperties({
  619. name: this.get('configGroupName'),
  620. description: this.get('configGroupDesc')
  621. });
  622. App.store.commit();
  623. this.hide();
  624. }
  625. });
  626. this.set('renameGroupPopup', renameGroupPopup);
  627. },
  628. /**
  629. * add new config group (or copy existing)
  630. * @param {boolean} duplicated true - copy <code>selectedConfigGroup</code>, false - create a new one
  631. * @method addConfigGroup
  632. */
  633. addConfigGroup: function (duplicated) {
  634. duplicated = (duplicated === true);
  635. var self = this;
  636. var addGroupPopup = App.ModalPopup.show({
  637. header: Em.I18n.t('services.service.config_groups.add_config_group_popup.header'),
  638. bodyClass: Em.View.extend({
  639. templateName: require('templates/main/service/new_config_group')
  640. }),
  641. configGroupName: duplicated ? self.get('selectedConfigGroup.name') + ' Copy' : "",
  642. configGroupDesc: duplicated ? self.get('selectedConfigGroup.description') + ' (Copy)' : "",
  643. warningMessage: '',
  644. didInsertElement: function(){
  645. this._super();
  646. this.validate();
  647. this.$('input').focus();
  648. },
  649. validate: function () {
  650. var warningMessage = '';
  651. var groupName = this.get('configGroupName').trim();
  652. if (self.get('configGroups').mapProperty('name').contains(groupName)) {
  653. warningMessage = Em.I18n.t("config.group.selection.dialog.err.name.exists");
  654. }
  655. else if (groupName && !validator.isValidConfigGroupName(groupName)) {
  656. warningMessage = Em.I18n.t("form.validator.configGroupName");
  657. }
  658. this.set('warningMessage', warningMessage);
  659. }.observes('configGroupName'),
  660. disablePrimary: function () {
  661. return !(this.get('configGroupName').trim().length > 0 && !this.get('warningMessage'));
  662. }.property('warningMessage', 'configGroupName'),
  663. onPrimary: function () {
  664. var defaultConfigGroup = self.get('configGroups').findProperty('isDefault'),
  665. properties = [], serviceName = self.get('serviceName'),
  666. groupName = this.get('configGroupName').trim(),
  667. newGroupId = (new Date()).getTime();
  668. if (duplicated) {
  669. self.get('selectedConfigGroup.properties').forEach(function (item) {
  670. var property = App.ServiceConfigProperty.create($.extend(false, {}, item));
  671. property.set('group', App.ServiceConfigGroup.find(newGroupId));
  672. properties.push(property);
  673. });
  674. }
  675. App.store.load(App.ServiceConfigGroup, {
  676. id: newGroupId,
  677. name: groupName,
  678. description: this.get('configGroupDesc'),
  679. isDefault: false,
  680. parent_config_group_id: App.ServiceConfigGroup.getParentConfigGroupId(serviceName),
  681. service_id: serviceName,
  682. service_name: serviceName,
  683. hosts: [],
  684. desired_configs: duplicated ? self.get('selectedConfigGroup.desiredConfigs') : [],
  685. properties: duplicated ? properties : [],
  686. is_temporary: true
  687. });
  688. App.store.commit();
  689. var childConfigGroups = defaultConfigGroup.get('childConfigGroups').mapProperty('id');
  690. childConfigGroups.push(newGroupId);
  691. App.store.load(App.ServiceConfigGroup, App.configGroupsMapper.generateDefaultGroup(self.get('serviceName'), defaultConfigGroup.get('hosts'), childConfigGroups));
  692. App.store.commit();
  693. self.get('configGroups').pushObject(App.ServiceConfigGroup.find(newGroupId));
  694. this.hide();
  695. }
  696. });
  697. this.set('addGroupPopup', addGroupPopup);
  698. },
  699. /**
  700. * Duplicate existing config group
  701. * @method duplicateConfigGroup
  702. */
  703. duplicateConfigGroup: function() {
  704. this.addConfigGroup(true);
  705. },
  706. /**
  707. * Show popup with config groups
  708. * User may edit/create/delete them
  709. * @param {Em.Controller} controller
  710. * @param {App.Service} service
  711. * @returns {App.ModalPopup}
  712. * @method manageConfigurationGroups
  713. */
  714. manageConfigurationGroups: function (controller, service) {
  715. var configsController = this;
  716. var serviceData = (controller && controller.get('selectedService')) || service;
  717. var serviceName = serviceData.get('serviceName');
  718. var displayName = serviceData.get('displayName');
  719. this.setProperties({
  720. isInstaller: !!controller,
  721. serviceName: serviceName
  722. });
  723. if (controller) {
  724. configsController.set('isAddService', controller.get('content.controllerName') == 'addServiceController');
  725. }
  726. return App.ModalPopup.show({
  727. header: Em.I18n.t('services.service.config_groups_popup.header').format(displayName),
  728. bodyClass: App.MainServiceManageConfigGroupView.extend({
  729. serviceName: serviceName,
  730. displayName: displayName,
  731. controller: configsController
  732. }),
  733. classNames: ['sixty-percent-width-modal', 'manage-configuration-group-popup'],
  734. primary: Em.I18n.t('common.save'),
  735. autoHeight: false,
  736. subViewController: configsController,
  737. /**
  738. * handle onPrimary action particularly in wizard
  739. * @param {Em.Controller} controller
  740. * @param {object} modifiedConfigGroups
  741. */
  742. onPrimaryWizard: function (controller, modifiedConfigGroups) {
  743. controller.set('selectedService.configGroups', configsController.get('configGroups'));
  744. controller.selectedServiceObserver();
  745. if (controller.get('name') == "wizardStep7Controller") {
  746. if (controller.get('selectedService.selected') === false && modifiedConfigGroups.toDelete.length > 0) {
  747. controller.setGroupsToDelete(modifiedConfigGroups.toDelete);
  748. }
  749. configsController.persistConfigGroups();
  750. this.updateConfigGroupOnServicePage();
  751. }
  752. this.hide();
  753. },
  754. onClose: function () {
  755. //<code>_super</code> has to be called before <code>resetGroupChanges</code>
  756. var originalGroups = this.get('subViewController.originalConfigGroups').slice(0);
  757. this._super();
  758. this.resetGroupChanges(originalGroups);
  759. },
  760. onSecondary: function () {
  761. this.onClose();
  762. },
  763. /**
  764. * reset group changes made by user
  765. * @param {Array} originalGroups
  766. */
  767. resetGroupChanges: function (originalGroups) {
  768. if (this.get('subViewController.isHostsModified')) {
  769. App.ServiceConfigGroup.find().clear();
  770. App.store.commit();
  771. App.store.loadMany(App.ServiceConfigGroup, originalGroups);
  772. App.store.commit();
  773. }
  774. },
  775. /**
  776. * run requests which delete config group and clear its hosts
  777. * @param {Function} finishFunction
  778. * @param {object} modifiedConfigGroups
  779. */
  780. runClearCGQueue: function (finishFunction, modifiedConfigGroups) {
  781. var counter = 0;
  782. var dfd = $.Deferred();
  783. var doneFunction = function (xhr, text, errorThrown) {
  784. counter--;
  785. if (counter === 0) dfd.resolve();
  786. finishFunction(xhr, text, errorThrown);
  787. };
  788. modifiedConfigGroups.toClearHosts.forEach(function (cg) {
  789. counter++;
  790. configsController.updateConfigurationGroup(cg, doneFunction, doneFunction)
  791. }, this);
  792. modifiedConfigGroups.toDelete.forEach(function (cg) {
  793. counter++;
  794. configsController.deleteConfigurationGroup(cg, doneFunction, doneFunction);
  795. }, this);
  796. if (counter === 0) dfd.resolve();
  797. return dfd.promise();
  798. },
  799. /**
  800. * run requests which change properties of config group
  801. * @param {Function} finishFunction
  802. * @param {object} modifiedConfigGroups
  803. */
  804. runModifyCGQueue: function (finishFunction, modifiedConfigGroups) {
  805. var counter = 0;
  806. var dfd = $.Deferred();
  807. var doneFunction = function (xhr, text, errorThrown) {
  808. counter--;
  809. if (counter === 0) dfd.resolve();
  810. finishFunction(xhr, text, errorThrown);
  811. };
  812. modifiedConfigGroups.toSetHosts.forEach(function (cg) {
  813. counter++;
  814. configsController.updateConfigurationGroup(cg, doneFunction, doneFunction);
  815. }, this);
  816. if (counter === 0) dfd.resolve();
  817. return dfd.promise();
  818. },
  819. /**
  820. * run requests which create new config group
  821. * @param {Function} finishFunction
  822. * @param {object} modifiedConfigGroups
  823. */
  824. runCreateCGQueue: function (finishFunction, modifiedConfigGroups) {
  825. var counter = 0;
  826. var dfd = $.Deferred();
  827. var doneFunction = function (xhr, text, errorThrown) {
  828. counter--;
  829. if (counter === 0) dfd.resolve();
  830. finishFunction(xhr, text, errorThrown);
  831. };
  832. modifiedConfigGroups.toCreate.forEach(function (cg) {
  833. counter++;
  834. configsController.postNewConfigurationGroup(cg, doneFunction);
  835. }, this);
  836. if (counter === 0) dfd.resolve();
  837. return dfd.promise();
  838. },
  839. onPrimary: function () {
  840. var modifiedConfigGroups = configsController.get('hostsModifiedConfigGroups');
  841. var errors = [];
  842. var self = this;
  843. var finishFunction = function (xhr, text, errorThrown) {
  844. if (xhr && errorThrown) {
  845. var error = xhr.status + "(" + errorThrown + ") ";
  846. try {
  847. var json = $.parseJSON(xhr.responseText);
  848. error += json.message;
  849. } catch (err) {
  850. }
  851. errors.push(error);
  852. }
  853. };
  854. // Save modified config-groups
  855. if (controller) {
  856. //called only in Wizard
  857. return this.onPrimaryWizard(controller, modifiedConfigGroups);
  858. }
  859. this.runClearCGQueue(finishFunction, modifiedConfigGroups).done(function () {
  860. self.runModifyCGQueue(finishFunction, modifiedConfigGroups).done(function () {
  861. self.runCreateCGQueue(finishFunction, modifiedConfigGroups).done(function () {
  862. if (errors.length > 0) {
  863. self.get('subViewController').set('errorMessage', errors.join(". "));
  864. } else {
  865. if (!self.get('isAddService') && !self.get('isInstaller') && !modifiedConfigGroups.toCreate.everyProperty('properties.length', 0)) {
  866. //update service config versions only if it is service configs page and at least one newly created group had properties
  867. App.router.get('mainServiceInfoConfigsController').loadServiceConfigVersions().done(function () {
  868. self.updateConfigGroupOnServicePage();
  869. self.hide();
  870. });
  871. } else {
  872. self.updateConfigGroupOnServicePage();
  873. self.hide();
  874. }
  875. }
  876. });
  877. });
  878. });
  879. },
  880. updateConfigGroupOnServicePage: function () {
  881. var selectedConfigGroup = configsController.get('selectedConfigGroup');
  882. var managedConfigGroups = configsController.get('configGroups').slice(0);
  883. if (!controller) {
  884. controller = App.router.get('mainServiceInfoConfigsController');
  885. //controller.set('configGroups', managedConfigGroups);
  886. controller.loadConfigGroups([controller.get('content.serviceName')]);
  887. } else {
  888. controller.set('selectedService.configGroups', managedConfigGroups);
  889. }
  890. var selectEventObject = {};
  891. //check whether selectedConfigGroup exists
  892. if (selectedConfigGroup && controller.get('configGroups').someProperty('name', selectedConfigGroup.get('name'))) {
  893. selectEventObject.context = selectedConfigGroup;
  894. } else {
  895. selectEventObject.context = managedConfigGroups.findProperty('isDefault', true);
  896. }
  897. controller.selectConfigGroup(selectEventObject);
  898. },
  899. updateButtons: function () {
  900. var modified = this.get('subViewController.isHostsModified');
  901. this.set('disablePrimary', !modified);
  902. }.observes('subViewController.isHostsModified')
  903. });
  904. }
  905. });