assign_master_controller.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  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 stringUtils = require('utils/string_utils');
  19. var numberUtils = require('utils/number_utils');
  20. var blueprintUtils = require('utils/blueprint');
  21. App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App.AssignMasterComponents, {
  22. name: "assignMasterOnStep7Controller",
  23. useServerValidation: false,
  24. showInstalledMastersFirst: false,
  25. configWidgetContext: {},
  26. configActionComponent: {},
  27. content: function () {
  28. return this.get('configWidgetContext.controller.content') || {};
  29. }.property('configWidgetContext.controller.content'),
  30. popup: null,
  31. mastersToCreate: [],
  32. markSavedComponentsAsInstalled: true,
  33. /**
  34. * Array of master component names, that should be addable
  35. * Are used in HA wizards to add components, that are not addable for other wizards
  36. * @type {Array}
  37. * @override
  38. */
  39. mastersAddableInHA: Em.computed.alias('App.components.isMasterAddableOnlyOnHA'),
  40. /**
  41. * Marks component add/delete action to be performed ahead.
  42. * @param context {Object} Context of the calling function
  43. * @param action {String} ADD|DELETE
  44. * @param hostComponent {Object}
  45. * @public
  46. * @method {execute}
  47. */
  48. execute: function (context, action, hostComponent) {
  49. this.set('configWidgetContext', context);
  50. this.set('content', context.get('controller.content'));
  51. this.set('configActionComponent', hostComponent);
  52. var missingDependentServices = this.getAllMissingDependentServices();
  53. var isNonWizardPage = !this.get('content.controllerName');
  54. switch (action) {
  55. case 'ADD':
  56. if (missingDependentServices.length && isNonWizardPage) {
  57. this.showInstallServicesPopup(missingDependentServices);
  58. } else {
  59. this.set('mastersToCreate', [hostComponent.componentName]);
  60. this.showAssignComponentPopup();
  61. }
  62. break;
  63. case 'DELETE':
  64. this.set('mastersToCreate', [hostComponent.componentName]);
  65. this.removeMasterComponent();
  66. break;
  67. }
  68. },
  69. /**
  70. * Used to set showAddControl/showRemoveControl flag
  71. * @param componentName
  72. * @override
  73. */
  74. updateComponent: function(componentName) {
  75. this._super(componentName);
  76. if (!this.get('mastersToCreate').contains(componentName)) {
  77. this.get("selectedServicesMasters").filterProperty("component_name", componentName).forEach(function(c) {
  78. c.set('showAddControl', false);
  79. c.set('showRemoveControl', false);
  80. });
  81. }
  82. },
  83. /**
  84. * Assign Master page will be displayed in the popup
  85. * @private
  86. * @method
  87. */
  88. showAssignComponentPopup: function () {
  89. var self = this;
  90. // Master component hosts should be loaded only when content.controller name is not defined i.e non-wizard pages
  91. if (!this.get('content.controllerName')) {
  92. this.loadMasterComponentHosts();
  93. }
  94. var mastersToCreate = this.get('mastersToCreate');
  95. var masterToCreateDisplayName = App.format.role(mastersToCreate[0]);
  96. var configWidgetContext = this.get('configWidgetContext');
  97. var config = this.get('configWidgetContext.config');
  98. var popup = App.ModalPopup.show({
  99. classNames: ['full-width-modal', 'add-service-wizard-modal'],
  100. header: Em.I18n.t('assign.master.popup.header').format(masterToCreateDisplayName),
  101. bodyClass: App.AssignMasterOnStep7View.extend({
  102. controller: self
  103. }),
  104. primary: Em.I18n.t('form.cancel'),
  105. showFooter: false,
  106. onClose: function () {
  107. this.showWarningPopup();
  108. },
  109. showWarningPopup: function() {
  110. var mainPopupContext = this;
  111. App.ModalPopup.show({
  112. encodeBody: false,
  113. header: Em.I18n.t('common.warning'),
  114. primaryClass: 'btn-warning',
  115. body: Em.I18n.t('assign.master.popup.cancel.body').format(masterToCreateDisplayName),
  116. onPrimary: function () {
  117. configWidgetContext.toggleProperty('controller.forceUpdateBoundaries');
  118. var value = config.get('initialValue');
  119. config.set('value', value);
  120. configWidgetContext.setValue(value);
  121. configWidgetContext.sendRequestRorDependentConfigs(config);
  122. this.hide();
  123. mainPopupContext.hide();
  124. }
  125. });
  126. },
  127. didInsertElement: function () {
  128. this._super();
  129. this.fitHeight();
  130. self.set('configWidgetContext.controller.saveInProgress', false);
  131. }
  132. });
  133. this.set('popup', popup);
  134. },
  135. /**
  136. * Displays the popup to install required service dependencies for being added component with this config change
  137. * @param missingDependentServices {String[]} Array of service display names
  138. */
  139. showInstallServicesPopup: function (missingDependentServices) {
  140. var displayServices = stringUtils.getFormattedStringFromArray(missingDependentServices);
  141. var configWidgetContext = this.get('configWidgetContext');
  142. var config = this.get('configWidgetContext.config');
  143. var configDisplayName = config.get('displayName').toLowerCase();
  144. return App.ModalPopup.show({
  145. header: Em.I18n.t('installer.step7.missing.service.header'),
  146. body: Em.I18n.t('installer.step7.missing.service.body').format(displayServices, configDisplayName),
  147. primaryClass: 'btn-danger',
  148. onPrimary: function () {
  149. configWidgetContext.toggleProperty('controller.forceUpdateBoundaries');
  150. var value = config.get('initialValue');
  151. config.set('value', value);
  152. configWidgetContext.setValue(value);
  153. configWidgetContext.sendRequestRorDependentConfigs(config);
  154. this._super();
  155. },
  156. secondary: null,
  157. showCloseButton: false,
  158. didInsertElement: function () {
  159. this._super();
  160. configWidgetContext.set('controller.saveInProgress', false);
  161. }
  162. });
  163. },
  164. /**
  165. * This method is used while installing or adding a service
  166. * Removes the masterComponent that was previously being tracked to be added to the cluster
  167. * @private
  168. * @method {removeMasterComponent}
  169. */
  170. removeMasterComponent: function () {
  171. var componentsToDelete = this.get('mastersToCreate');
  172. var componentsFromConfigs = this.get('content.componentsFromConfigs');
  173. if (this.get('content.controllerName')) {
  174. var masterComponentHosts = this.get('content.masterComponentHosts');
  175. var recommendationsHostGroups = this.get('content.recommendationsHostGroups');
  176. componentsToDelete.forEach(function (_componentName) {
  177. masterComponentHosts = masterComponentHosts.rejectProperty('component', _componentName);
  178. recommendationsHostGroups.blueprint.host_groups.forEach(function(hostGroup){
  179. hostGroup.components = hostGroup.components.rejectProperty('name', _componentName);
  180. }, this);
  181. componentsFromConfigs = componentsFromConfigs.without(_componentName);
  182. }, this);
  183. this.get('content').set('masterComponentHosts', masterComponentHosts);
  184. this.set('content.componentsFromConfigs', componentsFromConfigs);
  185. this.set('content.recommendationsHostGroups', recommendationsHostGroups);
  186. } else {
  187. this.clearComponentsToBeAdded(componentsToDelete[0]);
  188. var hostComponent = App.HostComponent.find().findProperty('componentName', componentsToDelete[0]);
  189. if (hostComponent) {
  190. App.set('componentToBeDeleted', Em.Object.create({
  191. componentName: componentsToDelete[0],
  192. hostName: hostComponent.get('hostName')
  193. }));
  194. }
  195. }
  196. var configActionComponent = this.get('configActionComponent');
  197. this.get('configWidgetContext.config').set('configActionComponent', configActionComponent);
  198. },
  199. /**
  200. * Load active host list to <code>hosts</code> variable
  201. * @override
  202. * @method renderHostInfo
  203. */
  204. renderHostInfo: function () {
  205. var parentController = this.get('content.controllerName');
  206. if (parentController) {
  207. this._super();
  208. } else {
  209. var hosts = App.Host.find().toArray();
  210. var result = [];
  211. for (var p = 0; p < hosts.length; p++) {
  212. result.push(Em.Object.create({
  213. host_name: hosts[p].get('hostName'),
  214. cpu: hosts[p].get('cpu'),
  215. memory: hosts[p].get('memory'),
  216. maintenance_state: hosts[p].get('maintenance_state'),
  217. disk_info: hosts[p].get('diskInfo'),
  218. host_info: Em.I18n.t('installer.step5.hostInfo').fmt(hosts[p].get('hostName'), numberUtils.bytesToSize(hosts[p].get('memory'), 1, 'parseFloat', 1024), hosts[p].get('cpu'))
  219. }));
  220. }
  221. this.set("hosts", result);
  222. this.sortHosts(result);
  223. }
  224. },
  225. /**
  226. * This method is called on Service->config page and is responsible to load the "Assign Master popup"
  227. * with the installed master component hosts.
  228. * @private
  229. * @method {loadMasterComponentHosts}
  230. */
  231. loadMasterComponentHosts: function () {
  232. var stackMasterComponents = App.get('components.masters').uniq();
  233. var masterComponentHosts = [];
  234. App.HostComponent.find().filter(function (component) {
  235. return stackMasterComponents.contains(component.get('componentName'));
  236. }).forEach(function (item) {
  237. masterComponentHosts.push({
  238. component: item.get('componentName'),
  239. hostName: item.get('hostName'),
  240. isInstalled: true,
  241. serviceId: item.get('service.id'),
  242. display_name: item.get('displayName')
  243. })
  244. });
  245. this.set("masterComponentHosts", masterComponentHosts);
  246. },
  247. /**
  248. * Returns array of dependent services that are yet not installed in the cluster
  249. * @private
  250. * @method getAllMissingDependentServices
  251. * @return missingDependentServices {Array}
  252. */
  253. getAllMissingDependentServices: function () {
  254. var configActionComponentName = this.get('configActionComponent').componentName;
  255. var componentStackService = App.StackServiceComponent.find(configActionComponentName).get('stackService');
  256. var dependentServices = componentStackService.get('requiredServices');
  257. return dependentServices.filter(function (item) {
  258. return !App.Service.find().findProperty('serviceName', item);
  259. }).map(function (item) {
  260. return App.StackService.find(item).get('displayName');
  261. });
  262. },
  263. /**
  264. * This method saves masterComponent layout that is used on subsequent "Review" and "Install start and Test services" pages.
  265. * @private
  266. * @method {saveMasterComponentHosts}
  267. */
  268. saveMasterComponentHosts: function() {
  269. var controller = App.router.get(this.get('content.controllerName'));
  270. var componentsFromConfigs = this.get('content.componentsFromConfigs');
  271. controller.saveMasterComponentHosts(this, true);
  272. controller.loadMasterComponentHosts(true);
  273. componentsFromConfigs = componentsFromConfigs.concat(this.get('mastersToCreate'));
  274. this.set('content.componentsFromConfigs', componentsFromConfigs);
  275. },
  276. /**
  277. * This method saves host group layout that is used for blueprint validation call made while transitioning to "Review" page.
  278. * @private
  279. * @method {saveRecommendationsHostGroups}
  280. */
  281. saveRecommendationsHostGroups: function() {
  282. var recommendationsHostGroups = this.get('content.recommendationsHostGroups');
  283. var mastersToCreate = this.get('mastersToCreate');
  284. mastersToCreate.forEach(function(componentName) {
  285. var hostName = this.getSelectedHostName(componentName);
  286. if (hostName && recommendationsHostGroups) {
  287. var hostGroups = recommendationsHostGroups.blueprint_cluster_binding.host_groups;
  288. var isHostPresent = false;
  289. var i = 0;
  290. while (i < hostGroups.length) {
  291. var hosts = hostGroups[i].hosts;
  292. isHostPresent = hosts.someProperty('fqdn', hostName);
  293. if (isHostPresent) break;
  294. i++;
  295. }
  296. if (isHostPresent) {
  297. var hostGroupName = hostGroups[i].name;
  298. var hostGroup = recommendationsHostGroups.blueprint.host_groups.findProperty('name', hostGroupName);
  299. var addHostComponentInGroup = !hostGroup.components.someProperty('name', componentName);
  300. if (addHostComponentInGroup) {
  301. hostGroup.components.pushObject({name: componentName});
  302. }
  303. }
  304. }
  305. }, this);
  306. this.set('content.recommendationsHostGroups', recommendationsHostGroups);
  307. },
  308. /**
  309. * Get the fqdn hostname as selected by the user for the component.
  310. * @param componentName
  311. * @return {String}
  312. */
  313. getSelectedHostName: function(componentName) {
  314. var selectedServicesMasters = this.get('selectedServicesMasters');
  315. return selectedServicesMasters.findProperty('component_name', componentName).selectedHost;
  316. },
  317. /**
  318. * set App.componentToBeAdded to use it on subsequent validation call while saving configuration
  319. * @param componentName {String}
  320. * @param hostName {String}
  321. * @method {setGlobalComponentToBeAdded}
  322. */
  323. setGlobalComponentToBeAdded: function(componentName, hostName) {
  324. var componentToBeAdded = Em.Object.create({
  325. componentName: componentName,
  326. hostNames: [hostName]
  327. });
  328. App.set('componentToBeAdded', componentToBeAdded);
  329. },
  330. /**
  331. * clear 'componentToBeDeleted' object
  332. * @param componentName {String}
  333. * @public
  334. * @method {clearComponentsToBeDeleted}
  335. */
  336. clearComponentsToBeDeleted: function(componentName) {
  337. var componentsToBeDeleted = App.get('componentToBeDeleted');
  338. if (!App.isEmptyObject(componentsToBeDeleted) && componentsToBeDeleted.get('componentName') === componentName) {
  339. App.set('componentToBeDeleted', {});
  340. }
  341. },
  342. /**
  343. * clear 'componentToBeAdded' object
  344. * @param componentName {String}
  345. */
  346. clearComponentsToBeAdded: function(componentName) {
  347. var componentsToBeAdded = App.get('componentToBeAdded');
  348. if (!App.isEmptyObject(componentsToBeAdded) && componentsToBeAdded.get('componentName') === componentName) {
  349. App.set('componentToBeAdded', {});
  350. }
  351. },
  352. /**
  353. * Submit button click handler
  354. * @method submit
  355. */
  356. submit: function () {
  357. var self = this;
  358. App.get('router.mainAdminKerberosController').getKDCSessionState(function() {
  359. self.get('popup').hide();
  360. var context = self.get('configWidgetContext');
  361. context.toggleProperty('controller.forceUpdateBoundaries');
  362. var configActionComponent = self.get('configActionComponent');
  363. var componentHostName = self.getSelectedHostName(configActionComponent.componentName);
  364. var config = self.get('configWidgetContext.config');
  365. var oldValueKey = context.get('controller.wizardController.name') === 'installerController' ? 'initialValue' : 'savedValue';
  366. // TODO remove after stack advisor is able to handle this case
  367. // workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive from Install Wizard
  368. var serviceConfigs = context.get('controller.stepConfigs').findProperty('serviceName', context.get('controller.selectedService.serviceName')).get('configs');
  369. var dependencies = context.get('config.configAction.dependencies');
  370. if (self.get('content.controllerName')) {
  371. self.saveMasterComponentHosts();
  372. self.saveRecommendationsHostGroups();
  373. // TODO remove after stack advisor is able to handle this case
  374. // workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive from Install Wizard
  375. var miscConfigs = context.get('controller.stepConfigs').findProperty('serviceName', 'MISC').get('configs');
  376. serviceConfigs = serviceConfigs.concat(miscConfigs);
  377. } else {
  378. self.setGlobalComponentToBeAdded(configActionComponent.componentName, componentHostName);
  379. self.clearComponentsToBeDeleted(configActionComponent.componentName);
  380. }
  381. configActionComponent.hostName = componentHostName;
  382. config.set('configActionComponent', configActionComponent);
  383. /* TODO uncomment after stack advisor is able to handle this case
  384. context.get('controller').loadConfigRecommendations([{
  385. type: App.config.getConfigTagFromFileName(config.get('fileName')),
  386. name: config.get('name'),
  387. old_value: config.get(oldValueKey)
  388. }]);
  389. */
  390. // TODO remove after stack advisor is able to handle this case
  391. // workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive
  392. if (dependencies) {
  393. var foreignKeys = {};
  394. if (dependencies.foreignKeys) {
  395. dependencies.foreignKeys.forEach(function (dependency) {
  396. var matchingProperty = serviceConfigs.find(function (property) {
  397. return property.get('filename') === App.config.getOriginalFileName(dependency.fileName) && property.get('name') === dependency.propertyName;
  398. });
  399. if (matchingProperty) {
  400. foreignKeys[dependency.key] = matchingProperty.get('value');
  401. }
  402. });
  403. }
  404. if (dependencies.properties && dependencies.initializer) {
  405. var initializer = App.get(dependencies.initializer.name);
  406. var setup = Em.getProperties(foreignKeys, dependencies.initializer.setupKeys);
  407. initializer.setup(setup);
  408. var blueprintObject = {};
  409. dependencies.properties.forEach(function (property) {
  410. var propertyObject = Em.getProperties(property, ['name', 'fileName']);
  411. if (property.nameTemplate) {
  412. var name = property.nameTemplate;
  413. Em.keys(foreignKeys).forEach(function (key) {
  414. name = name.replace('{{' + key + '}}', foreignKeys[key]);
  415. });
  416. propertyObject.name = name;
  417. }
  418. if (!blueprintObject[property.fileName]) {
  419. blueprintObject[property.fileName] = {
  420. properties: {}
  421. };
  422. }
  423. var masterComponents = [];
  424. if (self.get('content.controllerName')) {
  425. var savedMasterComponents = context.get('controller.content.masterComponentHosts').filter(function (componentObject) {
  426. return dependencies.initializer.componentNames.contains(componentObject.component);
  427. });
  428. masterComponents = savedMasterComponents.map(function (componentObject) {
  429. var masterComponent = Em.getProperties(componentObject, ['component', 'hostName']);
  430. masterComponent.isInstalled = true;
  431. return masterComponent;
  432. });
  433. } else {
  434. var hostsMap = blueprintUtils.getComponentForHosts();
  435. Em.keys(hostsMap).forEach(function (hostName) {
  436. hostsMap[hostName].forEach(function (componentName) {
  437. if (dependencies.initializer.componentNames.contains(componentName)) {
  438. masterComponents.push({
  439. component: componentName,
  440. hostName: hostName,
  441. isInstalled: true
  442. });
  443. }
  444. });
  445. });
  446. }
  447. var result = initializer.initialValue(propertyObject, {
  448. masterComponentHosts: masterComponents
  449. });
  450. var propertiesMap = blueprintObject[propertyObject.fileName].properties;
  451. propertiesMap[propertyObject.name] = result.value;
  452. if (property.isHostsList) {
  453. var service = App.config.get('serviceByConfigTypeMap')[propertyObject.fileName];
  454. if (service) {
  455. var serviceName = service.get('serviceName');
  456. var configs = serviceName === context.get('controller.selectedService.serviceName') ? serviceConfigs :
  457. context.get('controller.stepConfigs').findProperty('serviceName', serviceName).get('configs');
  458. var originalFileName = App.config.getOriginalFileName(propertyObject.fileName);
  459. var currentProperty = configs.find(function (configProperty) {
  460. return configProperty.get('filename') === originalFileName && configProperty.get('name') === propertyObject.name;
  461. });
  462. if (currentProperty) {
  463. propertiesMap[propertyObject.name] = currentProperty.get('value');
  464. App.config.updateHostsListValue(propertiesMap, propertyObject.fileName, propertyObject.name, propertyObject.value, property.isHostsArray);
  465. }
  466. }
  467. }
  468. context.get('controller').loadRecommendationsSuccess({
  469. resources: [
  470. {
  471. recommendations: {
  472. blueprint: {
  473. configurations: blueprintObject
  474. }
  475. }
  476. }
  477. ]
  478. }, null, {
  479. dataToSend: {
  480. changed_configurations: [{
  481. type: App.config.getConfigTagFromFileName(config.get('fileName')),
  482. name: config.get('name'),
  483. old_value: config.get(oldValueKey)
  484. }]
  485. }
  486. });
  487. initializer.cleanup();
  488. });
  489. }
  490. }
  491. });
  492. },
  493. /**
  494. * function called for onclcik event on cancel button for the popup
  495. */
  496. onCancel: function() {
  497. this.get('popup').showWarningPopup();
  498. }
  499. });