manage_config_groups_controller.js 33 KB

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