step5_controller.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  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 numberUtils = require('utils/number_utils');
  20. var validationUtils = require('utils/validator');
  21. App.WizardStep5Controller = Em.Controller.extend({
  22. name: "wizardStep5Controller",
  23. /**
  24. * Step title
  25. * Custom if <code>App.ReassignMasterController</code> is used
  26. * @type {string}
  27. */
  28. title: function () {
  29. if (this.get('content.controllerName') == 'reassignMasterController') {
  30. return Em.I18n.t('installer.step5.reassign.header');
  31. }
  32. return Em.I18n.t('installer.step5.header');
  33. }.property('content.controllerName'),
  34. /**
  35. * Is ReassignWizard used
  36. * @type {bool}
  37. */
  38. isReassignWizard: function () {
  39. return this.get('content.controllerName') == 'reassignMasterController';
  40. }.property('content.controllerName'),
  41. /**
  42. * Is isHighAvailabilityWizard used
  43. * @type {bool}
  44. */
  45. isHighAvailabilityWizard: function () {
  46. return this.get('content.controllerName') == 'highAvailabilityWizardController';
  47. }.property('content.controllerName'),
  48. /**
  49. * Is AddServiceWizard used
  50. * @type {bool}
  51. */
  52. isAddServiceWizard: function () {
  53. return this.get('content.controllerName') == 'addServiceController';
  54. }.property('content.controllerName'),
  55. /**
  56. * Master components which could be assigned to multiple hosts
  57. * @type {string[]}
  58. */
  59. multipleComponents: function () {
  60. return App.get('components.multipleMasters');
  61. }.property('App.components.multipleMasters'),
  62. /**
  63. * Master components which could be assigned to multiple hosts
  64. * @type {string[]}
  65. */
  66. addableComponents: function () {
  67. return App.get('components.addableMasterInstallerWizard');
  68. }.property('App.components.addableMasterInstallerWizard'),
  69. /**
  70. * Define state for submit button
  71. * @type {bool}
  72. */
  73. submitDisabled: false,
  74. /**
  75. * Trigger for executing host names check for components
  76. * Should de "triggered" when host changed for some component and when new multiple component is added/removed
  77. * @type {bool}
  78. */
  79. hostNameCheckTrigger: false,
  80. /**
  81. * List of hosts
  82. * @type {Array}
  83. */
  84. hosts: [],
  85. /**
  86. * Name of multiple component which host name was changed last
  87. * @type {Object|null}
  88. */
  89. componentToRebalance: null,
  90. /**
  91. * Name of component which host was changed last
  92. * @type {string}
  93. */
  94. lastChangedComponent: null,
  95. /**
  96. * Flag for rebalance multiple components
  97. * @type {number}
  98. */
  99. rebalanceComponentHostsCounter: 0,
  100. /**
  101. * @type {Ember.Enumerable}
  102. */
  103. servicesMasters: [],
  104. /**
  105. * @type {Ember.Enumerable}
  106. */
  107. selectedServicesMasters: [],
  108. /**
  109. * Is data for current step loaded
  110. * @type {bool}
  111. */
  112. isLoaded: false,
  113. /**
  114. * Validation error messages which don't related with any master
  115. */
  116. generalErrorMessages: [],
  117. /**
  118. * Validation warning messages which don't related with any master
  119. */
  120. generalWarningMessages: [],
  121. /**
  122. * List of host with assigned masters
  123. * Format:
  124. * <code>
  125. * [
  126. * {
  127. * host_name: '',
  128. * hostInfo: {},
  129. * masterServices: [],
  130. * masterServicesToDisplay: [] // used only in template
  131. * },
  132. * ....
  133. * ]
  134. * </code>
  135. * @type {Ember.Enumerable}
  136. */
  137. masterHostMapping: function () {
  138. var mapping = [], mappingObject, mappedHosts, hostObj;
  139. //get the unique assigned hosts and find the master services assigned to them
  140. mappedHosts = this.get("selectedServicesMasters").mapProperty("selectedHost").uniq();
  141. mappedHosts.forEach(function (item) {
  142. hostObj = this.get("hosts").findProperty("host_name", item);
  143. // User may input invalid host name (this is handled in hostname checker). Here we just skip it
  144. if (!hostObj) return;
  145. var masterServices = this.get("selectedServicesMasters").filterProperty("selectedHost", item),
  146. masterServicesToDisplay = [];
  147. masterServices.mapProperty('display_name').uniq().forEach(function (n) {
  148. masterServicesToDisplay.pushObject(masterServices.findProperty('display_name', n));
  149. });
  150. mappingObject = Em.Object.create({
  151. host_name: item,
  152. hostInfo: hostObj.host_info,
  153. masterServices: masterServices,
  154. masterServicesToDisplay: masterServicesToDisplay
  155. });
  156. mapping.pushObject(mappingObject);
  157. }, this);
  158. return mapping.sortProperty('host_name');
  159. }.property("selectedServicesMasters.@each.selectedHost", 'selectedServicesMasters.@each.isHostNameValid'),
  160. /**
  161. * Count of hosts without masters
  162. * @type {number}
  163. */
  164. remainingHosts: function () {
  165. if (this.get('content.controllerName') === 'installerController') {
  166. return 0;
  167. } else {
  168. return (this.get("hosts.length") - this.get("masterHostMapping.length"));
  169. }
  170. }.property('masterHostMapping.length', 'selectedServicesMasters.@each.selectedHost'),
  171. /**
  172. * Update submit button status
  173. * @metohd updateIsSubmitDisabled
  174. */
  175. updateIsSubmitDisabled: function () {
  176. var self = this;
  177. if (self.thereIsNoMasters()) {
  178. return false;
  179. }
  180. if (App.supports.serverRecommendValidate) {
  181. self.set('submitDisabled', true);
  182. // reset previous recommendations
  183. App.router.set('installerController.recommendations', null);
  184. if (self.get('servicesMasters').length === 0) {
  185. return;
  186. }
  187. var isSubmitDisabled = this.get('servicesMasters').someProperty('isHostNameValid', false);
  188. if (!isSubmitDisabled) {
  189. self.recommendAndValidate();
  190. }
  191. } else {
  192. var isSubmitDisabled = this.get('servicesMasters').someProperty('isHostNameValid', false);
  193. self.set('submitDisabled', isSubmitDisabled);
  194. return isSubmitDisabled;
  195. }
  196. }.observes('servicesMasters.@each.selectedHost', 'servicesMasters.@each.isHostNameValid'),
  197. /**
  198. * Send AJAX request to validate current host layout
  199. * @param blueprint - blueprint for validation (can be with/withour slave/client components)
  200. */
  201. validate: function(blueprint, callback) {
  202. var self = this;
  203. var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName');
  204. var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName');
  205. var services = installedServices.concat(selectedServices).uniq();
  206. var hostNames = self.get('hosts').mapProperty('host_name');
  207. App.ajax.send({
  208. name: 'config.validations',
  209. sender: self,
  210. data: {
  211. stackVersionUrl: App.get('stackVersionURL'),
  212. hosts: hostNames,
  213. services: services,
  214. validate: 'host_groups',
  215. recommendations: blueprint
  216. },
  217. success: 'updateValidationsSuccessCallback'
  218. }).
  219. retry({
  220. times: App.maxRetries,
  221. timeout: App.timeout
  222. }).
  223. then(function() {
  224. if (callback) {
  225. callback();
  226. }
  227. }, function () {
  228. App.showReloadPopup();
  229. console.log('Load validations failed');
  230. }
  231. );
  232. },
  233. /**
  234. * Success-callback for validations request
  235. * @param {object} data
  236. * @method updateValidationsSuccessCallback
  237. */
  238. updateValidationsSuccessCallback: function (data) {
  239. var self = this;
  240. this.set('generalErrorMessages', []);
  241. this.set('generalWarningMessages', []);
  242. this.get('servicesMasters').setEach('warnMessage', null);
  243. this.get('servicesMasters').setEach('errorMessage', null);
  244. var anyErrors = false;
  245. var validationData = validationUtils.filterNotInstalledComponents(data);
  246. validationData.filterProperty('type', 'host-component').forEach(function(item) {
  247. var master = self.get('servicesMasters').find(function(m) {
  248. return m.component_name === item['component-name'] && m.selectedHost === item.host;
  249. });
  250. if (master) {
  251. if (item.level === 'ERROR') {
  252. anyErrors = true;
  253. master.set('errorMessage', item.message);
  254. } else if (item.level === 'WARN') {
  255. master.set('warnMessage', item.message);
  256. }
  257. } else {
  258. var details = " (" + item['component-name'] + " on " + item.host + ")";
  259. if (item.level === 'ERROR') {
  260. anyErrors = true;
  261. self.get('generalErrorMessages').push(item.message + details);
  262. } else if (item.level === 'WARN') {
  263. self.get('generalWarningMessages').push(item.message + details);
  264. }
  265. }
  266. });
  267. this.set('submitDisabled', anyErrors);
  268. },
  269. /**
  270. * Composes selected values of comboboxes into blueprint format
  271. */
  272. getCurrentBlueprint: function() {
  273. var self = this;
  274. var res = {
  275. blueprint: { host_groups: [] },
  276. blueprint_cluster_binding: { host_groups: [] }
  277. };
  278. var mapping = self.get('masterHostMapping');
  279. var i = 0;
  280. mapping.forEach(function(item) {
  281. i += 1;
  282. var group_name = 'host-group-' + i;
  283. var host_group = {
  284. name: group_name,
  285. components: item.masterServices.map(function(master) {
  286. return { name: master.component_name };
  287. })
  288. };
  289. var binding = {
  290. name: group_name,
  291. hosts: [ { fqdn: item.host_name } ]
  292. }
  293. res.blueprint.host_groups.push(host_group);
  294. res.blueprint_cluster_binding.host_groups.push(binding);
  295. });
  296. return res;
  297. },
  298. /**
  299. * Clear controller data (hosts, masters etc)
  300. * @method clearStep
  301. */
  302. clearStep: function () {
  303. this.set('hosts', []);
  304. this.set('selectedServicesMasters', []);
  305. this.set('servicesMasters', []);
  306. App.StackServiceComponent.find().forEach(function (stackComponent) {
  307. stackComponent.set('serviceComponentId', 1);
  308. }, this);
  309. },
  310. /**
  311. * Load controller data (hosts, host components etc)
  312. * @method loadStep
  313. */
  314. loadStep: function () {
  315. console.log("WizardStep5Controller: Loading step5: Assign Masters");
  316. this.clearStep();
  317. this.renderHostInfo();
  318. if (App.supports.serverRecommendValidate ) {
  319. this.loadComponentsRecommendationsFromServer(this.loadStepCallback);
  320. } else {
  321. this.loadComponentsRecommendationsLocally(this.loadStepCallback);
  322. }
  323. },
  324. /**
  325. * Callback after load controller data (hosts, host components etc)
  326. * @method loadStepCallback
  327. */
  328. loadStepCallback: function(components, self) {
  329. self.renderComponents(components);
  330. self.get('addableComponents').forEach(function (componentName) {
  331. self.updateComponent(componentName);
  332. }, self);
  333. if (self.thereIsNoMasters()) {
  334. console.log('no master components to add');
  335. App.router.send('next');
  336. }
  337. },
  338. /**
  339. * Returns true if there is no new master components which need assigment to host
  340. */
  341. thereIsNoMasters: function() {
  342. return !this.get("selectedServicesMasters").filterProperty('isInstalled', false).length;
  343. },
  344. /**
  345. * Used to set showAddControl flag for installer wizard
  346. * @method updateComponent
  347. */
  348. updateComponent: function (componentName) {
  349. var component = this.last(componentName);
  350. if (!component) {
  351. return;
  352. }
  353. var services = App.StackService.find().filterProperty('isInstalled', true).mapProperty('serviceName');
  354. var currentService = componentName.split('_')[0];
  355. var showControl = !services.contains(currentService);
  356. if (showControl) {
  357. var mastersLength = this.get("selectedServicesMasters").filterProperty("component_name", componentName).length;
  358. if (mastersLength < this.get("hosts.length") && !this.get('isReassignWizard') && !this.get('isHighAvailabilityWizard')) {
  359. component.set('showAddControl', true);
  360. } else if (mastersLength == 1 || this.get('isReassignWizard') || this.get('isHighAvailabilityWizard')) {
  361. component.set('showRemoveControl', false);
  362. }
  363. }
  364. },
  365. /**
  366. * Load active host list to <code>hosts</code> variable
  367. * @method renderHostInfo
  368. */
  369. renderHostInfo: function () {
  370. var hostInfo = this.get('content.hosts');
  371. var result = [];
  372. for (var index in hostInfo) {
  373. var _host = hostInfo[index];
  374. if (_host.bootStatus === 'REGISTERED') {
  375. result.push(Em.Object.create({
  376. host_name: _host.name,
  377. cpu: _host.cpu,
  378. memory: _host.memory,
  379. disk_info: _host.disk_info,
  380. host_info: Em.I18n.t('installer.step5.hostInfo').fmt(_host.name, numberUtils.bytesToSize(_host.memory, 1, 'parseFloat', 1024), _host.cpu)
  381. }));
  382. }
  383. }
  384. this.set("hosts", result);
  385. this.sortHosts(this.get('hosts'));
  386. this.set('isLoaded', true);
  387. },
  388. /**
  389. * Sort list of host-objects by properties (memory - desc, cpu - desc, hostname - asc)
  390. * @param {object[]} hosts
  391. */
  392. sortHosts: function (hosts) {
  393. hosts.sort(function (a, b) {
  394. if (a.get('memory') == b.get('memory')) {
  395. if (a.get('cpu') == b.get('cpu')) {
  396. return a.get('host_name').localeCompare(b.get('host_name')); // hostname asc
  397. }
  398. return b.get('cpu') - a.get('cpu'); // cores desc
  399. }
  400. return b.get('memory') - a.get('memory'); // ram desc
  401. });
  402. },
  403. /**
  404. * Get recommendations info from API
  405. * @return {undefined}
  406. * @param function(componentInstallationobjects, this) callback
  407. * @param bool includeMasters
  408. */
  409. loadComponentsRecommendationsFromServer: function(callback, includeMasters) {
  410. var self = this;
  411. if (App.router.get('installerController.recommendations')) {
  412. // Don't do AJAX call if recommendations has been already received
  413. // But if user returns to previous step (selecting services), stored recommendations will be cleared in routers' next handler and AJAX call will be made again
  414. callback(self.createComponentInstallationObjects(), self);
  415. } else {
  416. var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName');
  417. var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName');
  418. var services = installedServices.concat(selectedServices).uniq();
  419. var hostNames = self.get('hosts').mapProperty('host_name');
  420. var data = {
  421. stackVersionUrl: App.get('stackVersionURL'),
  422. hosts: hostNames,
  423. services: services,
  424. recommend: 'host_groups'
  425. };
  426. if (includeMasters) {
  427. data.recommendations = self.getCurrentBlueprint();
  428. }
  429. return App.ajax.send({
  430. name: 'wizard.loadrecommendations',
  431. sender: self,
  432. data: data,
  433. success: 'loadRecommendationsSuccessCallback'
  434. }).
  435. retry({
  436. times: App.maxRetries,
  437. timeout: App.timeout
  438. }).
  439. then(function () {
  440. callback(self.createComponentInstallationObjects(), self);
  441. },
  442. function () {
  443. App.showReloadPopup();
  444. console.log('Load recommendations failed');
  445. }
  446. );
  447. }
  448. },
  449. /**
  450. * Create components for displaying component-host comboboxes in UI assign dialog
  451. * expects installerController.recommendations will be filled with recommendations API call result
  452. * @return {Object[]}
  453. */
  454. createComponentInstallationObjects: function() {
  455. var self = this;
  456. var masterComponents = [];
  457. if (self.get('isAddServiceWizard')) {
  458. masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnAddServiceAssignMasterPage');
  459. } else {
  460. masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnInstallerAssignMasterPage');
  461. }
  462. var masterHosts = self.get('content.masterComponentHosts'); //saved to local storage info
  463. var selectedNotInstalledServices = self.get('content.services').filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName');
  464. var recommendations = App.router.get('installerController.recommendations');
  465. var resultComponents = [];
  466. var multipleComponentHasBeenAdded = {};
  467. recommendations.blueprint.host_groups.forEach(function(host_group) {
  468. var hosts = recommendations.blueprint_cluster_binding.host_groups.findProperty('name', host_group.name).hosts;
  469. hosts.forEach(function(host) {
  470. host_group.components.forEach(function(component) {
  471. var willBeAdded = true;
  472. var fullComponent = masterComponents.findProperty('componentName', component.name);
  473. // If it's master component which should be shown
  474. if (fullComponent) {
  475. // If service is already installed and not being added as a new service then render on UI only those master components
  476. // that have already installed hostComponents.
  477. // NOTE: On upgrade there might be a prior installed service with non-installed newly introduced serviceComponent
  478. var isNotSelectedService = !selectedNotInstalledServices.contains(fullComponent.get('serviceName'));
  479. if (isNotSelectedService) {
  480. willBeAdded = App.HostComponent.find().someProperty('componentName', component.name);
  481. }
  482. if (willBeAdded) {
  483. var savedComponents = masterHosts.filterProperty('component', component.name);
  484. if (self.get('multipleComponents').contains(component.name) && savedComponents.length > 0) {
  485. if (!multipleComponentHasBeenAdded[component.name]) {
  486. multipleComponentHasBeenAdded[component.name] = true;
  487. savedComponents.forEach(function(saved) {
  488. resultComponents.push(self.createComponentInstallationObject(fullComponent, host.fqdn, saved));
  489. });
  490. }
  491. } else {
  492. var savedComponent = masterHosts.findProperty('component', component.name);
  493. resultComponents.push(self.createComponentInstallationObject(fullComponent, host.fqdn, savedComponent));
  494. }
  495. }
  496. }
  497. });
  498. });
  499. });
  500. return resultComponents;
  501. },
  502. /**
  503. * Create component for displaying component-host comboboxes in UI assign dialog
  504. * @param fullComponent - full component description
  505. * @param hostName - host fqdn where component will be installed
  506. * @param savedComponent - the same object which function returns but created before
  507. * @return {Object}
  508. */
  509. createComponentInstallationObject: function(fullComponent, hostName, savedComponent) {
  510. var componentName = fullComponent.get('componentName');
  511. var componentObj = {};
  512. componentObj.component_name = componentName;
  513. componentObj.display_name = fullComponent.get('displayName');
  514. componentObj.serviceId = fullComponent.get('serviceName');
  515. componentObj.isServiceCoHost = App.StackServiceComponent.find().findProperty('componentName', componentName).get('isCoHostedComponent') && !this.get('isReassignWizard');
  516. if (savedComponent) {
  517. componentObj.selectedHost = savedComponent.hostName;
  518. componentObj.isInstalled = savedComponent.isInstalled;
  519. } else {
  520. componentObj.selectedHost = hostName;
  521. componentObj.isInstalled = false;
  522. }
  523. return componentObj;
  524. },
  525. /**
  526. * Success-callback for recommendations request
  527. * @param {object} data
  528. * @method loadRecommendationsSuccessCallback
  529. */
  530. loadRecommendationsSuccessCallback: function (data) {
  531. App.router.set('installerController.recommendations', data.resources[0].recommendations);
  532. },
  533. /**
  534. * Load services info to appropriate variable and return masterComponentHosts
  535. * @return {Object[]}
  536. */
  537. loadComponentsRecommendationsLocally: function (callback) {
  538. var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName');
  539. var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName');
  540. var services = installedServices.concat(selectedServices).uniq();
  541. var selectedNotInstalledServices = this.get('content.services').filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName');
  542. var masterComponents = [];
  543. //get full list from mock data
  544. if (this.get('isAddServiceWizard')) {
  545. masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnAddServiceAssignMasterPage');
  546. } else {
  547. masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnInstallerAssignMasterPage');
  548. }
  549. var masterHosts = this.get('content.masterComponentHosts'); //saved to local storage info
  550. var resultComponents = [];
  551. for (var index = 0; index < services.length; index++) {
  552. var componentInfo = masterComponents.filterProperty('serviceName', services[index]);
  553. // If service is already installed and not being added as a new service then render on UI only those master components
  554. // that have already installed hostComponents.
  555. // NOTE: On upgrade there might be a prior installed service with non-installed newly introduced serviceComponent
  556. var isNotSelectedService = !selectedNotInstalledServices.contains(services[index]);
  557. if (isNotSelectedService) {
  558. componentInfo = componentInfo.filter(function (_component) {
  559. return App.HostComponent.find().someProperty('componentName',_component.get('componentName'));
  560. });
  561. }
  562. componentInfo.forEach(function (_componentInfo) {
  563. if (this.get('multipleComponents').contains(_componentInfo.get('componentName'))) {
  564. var savedComponents = masterHosts.filterProperty('component', _componentInfo.get('componentName'));
  565. if (savedComponents.length) {
  566. savedComponents.forEach(function (item) {
  567. var multipleMasterHost = {};
  568. multipleMasterHost.component_name = _componentInfo.get('componentName');
  569. multipleMasterHost.display_name = _componentInfo.get('displayName');
  570. multipleMasterHost.selectedHost = item.hostName;
  571. multipleMasterHost.serviceId = services[index];
  572. multipleMasterHost.isInstalled = item.isInstalled;
  573. multipleMasterHost.isServiceCoHost = false;
  574. resultComponents.push(multipleMasterHost);
  575. })
  576. } else {
  577. var multipleMasterHosts = this.selectHostLocally(_componentInfo.get('componentName'));
  578. multipleMasterHosts.forEach(function (_host) {
  579. var multipleMasterHost = {};
  580. multipleMasterHost.component_name = _componentInfo.get('componentName');
  581. multipleMasterHost.display_name = _componentInfo.get('displayName');
  582. multipleMasterHost.selectedHost = _host;
  583. multipleMasterHost.serviceId = services[index];
  584. multipleMasterHost.isInstalled = false;
  585. multipleMasterHost.isServiceCoHost = false;
  586. resultComponents.push(multipleMasterHost);
  587. });
  588. }
  589. } else {
  590. var savedComponent = masterHosts.findProperty('component', _componentInfo.get('componentName'));
  591. var componentObj = {};
  592. componentObj.component_name = _componentInfo.get('componentName');
  593. componentObj.display_name = _componentInfo.get('displayName');
  594. componentObj.selectedHost = savedComponent ? savedComponent.hostName : this.selectHostLocally(_componentInfo.get('componentName')); // call the method that plays selectNode algorithm or fetches from server
  595. componentObj.isInstalled = savedComponent ? savedComponent.isInstalled : false;
  596. componentObj.serviceId = services[index];
  597. componentObj.isServiceCoHost = App.StackServiceComponent.find().findProperty('componentName', _componentInfo.get('componentName')).get('isCoHostedComponent') && !this.get('isReassignWizard');
  598. resultComponents.push(componentObj);
  599. }
  600. }, this);
  601. }
  602. callback(resultComponents, this);
  603. },
  604. /**
  605. * @param {string} componentName
  606. * @returns {bool}
  607. * @private
  608. * @method _isHiveCoHost
  609. */
  610. _isHiveCoHost: function (componentName) {
  611. return ['HIVE_METASTORE', 'WEBHCAT_SERVER'].contains(componentName) && !this.get('isReassignWizard');
  612. },
  613. /**
  614. * Put master components to <code>selectedServicesMasters</code>, which will be automatically rendered in template
  615. * @param {Ember.Enumerable} masterComponents
  616. * @method renderComponents
  617. */
  618. renderComponents: function (masterComponents) {
  619. var installedServices = App.StackService.find().filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'); //list of shown services
  620. var result = [];
  621. var serviceComponentId, previousComponentName;
  622. masterComponents.forEach(function (item) {
  623. var serviceComponent = App.StackServiceComponent.find().findProperty('componentName', item.component_name);
  624. var showRemoveControl = installedServices.contains(serviceComponent.get('stackService.serviceName')) &&
  625. (masterComponents.filterProperty('component_name', item.component_name).length > 1);
  626. var componentObj = Em.Object.create(item);
  627. console.log("TRACE: render master component name is: " + item.component_name);
  628. var masterComponent = App.StackServiceComponent.find().findProperty('componentName', item.component_name);
  629. if (masterComponent.get('isMasterWithMultipleInstances')) {
  630. previousComponentName = item.component_name;
  631. componentObj.set('serviceComponentId', result.filterProperty('component_name', item.component_name).length + 1);
  632. componentObj.set("showRemoveControl", showRemoveControl);
  633. }
  634. componentObj.set('isHostNameValid', true);
  635. result.push(componentObj);
  636. }, this);
  637. result = this.sortComponentsByServiceName(result);
  638. this.set("selectedServicesMasters", result);
  639. if (this.get('isReassignWizard')) {
  640. var components = result.filterProperty('component_name', this.get('content.reassign.component_name'));
  641. components.setEach('isInstalled', false);
  642. this.set('servicesMasters', components);
  643. } else {
  644. this.set('servicesMasters', result);
  645. }
  646. },
  647. sortComponentsByServiceName: function(components) {
  648. var displayOrder = App.StackService.displayOrder;
  649. return components.sort(function (a, b) {
  650. var aValue = displayOrder.indexOf(a.serviceId) != -1 ? displayOrder.indexOf(a.serviceId) : components.length;
  651. var bValue = displayOrder.indexOf(b.serviceId) != -1 ? displayOrder.indexOf(b.serviceId) : components.length;
  652. return aValue - bValue;
  653. });
  654. },
  655. /**
  656. * Update dependent co-hosted components according to the change in the component host
  657. * @method updateCoHosts
  658. */
  659. updateCoHosts: function () {
  660. var components = App.StackServiceComponent.find().filterProperty('isOtherComponentCoHosted');
  661. var selectedServicesMasters = this.get('selectedServicesMasters');
  662. components.forEach(function (component) {
  663. var componentName = component.get('componentName');
  664. var hostComponent = selectedServicesMasters.findProperty('component_name', componentName);
  665. var dependentCoHosts = component.get('coHostedComponents');
  666. dependentCoHosts.forEach(function (coHostedComponent) {
  667. var dependentHostComponent = selectedServicesMasters.findProperty('component_name', coHostedComponent);
  668. if (hostComponent && dependentHostComponent) dependentHostComponent.set('selectedHost', hostComponent.get('selectedHost'));
  669. }, this);
  670. }, this);
  671. }.observes('selectedServicesMasters.@each.selectedHost'),
  672. /**
  673. * select and return host for component by scheme
  674. * Scheme is an object that has keys which compared to number of hosts,
  675. * if key more that number of hosts, then return value of that key.
  676. * Value is index of host in hosts array.
  677. *
  678. * @param {object} componentName
  679. * @param {object} hosts
  680. * @return {string}
  681. * @method getHostForComponent
  682. */
  683. getHostForComponent: function (componentName, hosts) {
  684. var component = App.StackServiceComponent.find().findProperty('componentName', componentName);
  685. if (component) {
  686. var selectionScheme = App.StackServiceComponent.find().findProperty('componentName', componentName).get('selectionSchemeForMasterComponent');
  687. } else {
  688. return hosts[0];
  689. }
  690. if (hosts.length === 1 || $.isEmptyObject(selectionScheme)) {
  691. return hosts[0];
  692. } else {
  693. for (var i in selectionScheme) {
  694. if (window.isFinite(i)) {
  695. if (hosts.length < window.parseInt(i)) {
  696. return hosts[selectionScheme[i]];
  697. }
  698. }
  699. }
  700. return hosts[selectionScheme['else']]
  701. }
  702. },
  703. /**
  704. * Get list of host names for master component with multiple instances
  705. * @param {Object} component
  706. * @param {Object} hosts
  707. * @returns {string[]}
  708. * @method getHostsForComponent
  709. */
  710. getHostsForComponent: function (component, hosts) {
  711. var defaultNoOfMasterHosts = component.get('defaultNoOfMasterHosts');
  712. var masterHosts = [];
  713. if (hosts.length < defaultNoOfMasterHosts) {
  714. defaultNoOfMasterHosts = hosts.length;
  715. }
  716. for (var index = 0; index < defaultNoOfMasterHosts; index++) {
  717. masterHosts.push(hosts[index]);
  718. }
  719. return masterHosts;
  720. },
  721. /**
  722. * Return hostName of masterNode for specified service
  723. * @param componentName
  724. * @return {string|string[]}
  725. * @method selectHostLocally
  726. */
  727. selectHostLocally: function (componentName) {
  728. var component = App.StackServiceComponent.find().findProperty('componentName', componentName);
  729. var hostNames = this.get('hosts').mapProperty('host_name');
  730. if (hostNames.length > 1 && App.StackServiceComponent.find().filterProperty('isNotPreferableOnAmbariServerHost').mapProperty('componentName').contains(componentName)) {
  731. hostNames = this.get('hosts').mapProperty('host_name').filter(function (item) {
  732. return item !== location.hostname;
  733. }, this);
  734. }
  735. if (this.get('multipleComponents').contains(componentName)) {
  736. if (component.get('defaultNoOfMasterHosts') > 1) {
  737. return this.getHostsForComponent(component, hostNames);
  738. } else {
  739. return [this.getHostForComponent(componentName, hostNames)];
  740. }
  741. } else {
  742. return this.getHostForComponent(componentName, hostNames);
  743. }
  744. },
  745. /**
  746. * On change callback for inputs
  747. * @param {string} componentName
  748. * @param {string} selectedHost
  749. * @param {number} serviceComponentId
  750. * @method assignHostToMaster
  751. */
  752. assignHostToMaster: function (componentName, selectedHost, serviceComponentId) {
  753. var flag = this.isHostNameValid(componentName, selectedHost);
  754. this.updateIsHostNameValidFlag(componentName, serviceComponentId, flag);
  755. if (serviceComponentId) {
  756. this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("serviceComponentId", serviceComponentId).set("selectedHost", selectedHost);
  757. }
  758. else {
  759. this.get('selectedServicesMasters').findProperty("component_name", componentName).set("selectedHost", selectedHost);
  760. }
  761. },
  762. /**
  763. * Determines if hostName is valid for component:
  764. * <ul>
  765. * <li>host name shouldn't be empty</li>
  766. * <li>host should exist</li>
  767. * <li>host should have only one component with <code>componentName</code></li>
  768. * </ul>
  769. * @param {string} componentName
  770. * @param {string} selectedHost
  771. * @returns {boolean} true - valid, false - invalid
  772. * @method isHostNameValid
  773. */
  774. isHostNameValid: function (componentName, selectedHost) {
  775. return (selectedHost.trim() !== '') &&
  776. this.get('hosts').mapProperty('host_name').contains(selectedHost) &&
  777. (this.get('selectedServicesMasters').
  778. filterProperty('component_name', componentName).
  779. mapProperty('selectedHost').
  780. filter(function (h) {
  781. return h === selectedHost;
  782. }).length <= 1);
  783. },
  784. /**
  785. * Update <code>isHostNameValid</code> property with <code>flag</code> value
  786. * for component with name <code>componentName</code> and
  787. * <code>serviceComponentId</code>-property equal to <code>serviceComponentId</code>-parameter value
  788. * @param {string} componentName
  789. * @param {number} serviceComponentId
  790. * @param {bool} flag
  791. * @method updateIsHostNameValidFlag
  792. */
  793. updateIsHostNameValidFlag: function (componentName, serviceComponentId, flag) {
  794. if (componentName) {
  795. if (serviceComponentId) {
  796. this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("serviceComponentId", serviceComponentId).set("isHostNameValid", flag);
  797. } else {
  798. this.get('selectedServicesMasters').findProperty("component_name", componentName).set("isHostNameValid", flag);
  799. }
  800. }
  801. },
  802. /**
  803. * Returns last component of selected type
  804. * @param {string} componentName
  805. * @return {Em.Object|null}
  806. * @method last
  807. */
  808. last: function (componentName) {
  809. return this.get("selectedServicesMasters").filterProperty("component_name", componentName).get("lastObject");
  810. },
  811. /**
  812. * Add new component to ZooKeeper Server and Hbase master
  813. * @param {string} componentName
  814. * @return {bool} true - added, false - not added
  815. * @method addComponent
  816. */
  817. addComponent: function (componentName) {
  818. /*
  819. * Logic: If ZooKeeper or Hbase service is selected then there can be
  820. * minimum 1 ZooKeeper or Hbase master in total, and
  821. * maximum 1 ZooKeeper or Hbase on every host
  822. */
  823. var maxNumMasters = this.get("hosts.length"),
  824. currentMasters = this.get("selectedServicesMasters").filterProperty("component_name", componentName),
  825. newMaster = null,
  826. masterHosts = null,
  827. suggestedHost = null,
  828. i = 0,
  829. lastMaster = null;
  830. if (!currentMasters.length) {
  831. console.log('ALERT: Zookeeper service was not selected');
  832. return false;
  833. }
  834. if (currentMasters.get("length") < maxNumMasters) {
  835. currentMasters.set("lastObject.showAddControl", false);
  836. currentMasters.set("lastObject.showRemoveControl", true);
  837. //create a new master component host based on an existing one
  838. newMaster = Em.Object.create({});
  839. lastMaster = currentMasters.get("lastObject");
  840. newMaster.set("display_name", lastMaster.get("display_name"));
  841. newMaster.set("component_name", lastMaster.get("component_name"));
  842. newMaster.set("selectedHost", lastMaster.get("selectedHost"));
  843. newMaster.set("serviceId", lastMaster.get("serviceId"));
  844. newMaster.set("isInstalled", false);
  845. if (currentMasters.get("length") === (maxNumMasters - 1)) {
  846. newMaster.set("showAddControl", false);
  847. } else {
  848. newMaster.set("showAddControl", true);
  849. }
  850. newMaster.set("showRemoveControl", true);
  851. //get recommended host for the new Zookeeper server
  852. masterHosts = currentMasters.mapProperty("selectedHost").uniq();
  853. for (i = 0; i < this.get("hosts.length"); i++) {
  854. if (!(masterHosts.contains(this.get("hosts")[i].get("host_name")))) {
  855. suggestedHost = this.get("hosts")[i].get("host_name");
  856. break;
  857. }
  858. }
  859. newMaster.set("selectedHost", suggestedHost);
  860. newMaster.set("serviceComponentId", (currentMasters.get("lastObject.serviceComponentId") + 1));
  861. this.get("selectedServicesMasters").insertAt(this.get("selectedServicesMasters").indexOf(lastMaster) + 1, newMaster);
  862. this.set('componentToRebalance', componentName);
  863. this.incrementProperty('rebalanceComponentHostsCounter');
  864. this.toggleProperty('hostNameCheckTrigger');
  865. return true;
  866. }
  867. return false;//if no more zookeepers can be added
  868. },
  869. /**
  870. * Remove component from ZooKeeper server or Hbase Master
  871. * @param {string} componentName
  872. * @param {number} serviceComponentId
  873. * @return {bool} true - removed, false - no
  874. * @method removeComponent
  875. */
  876. removeComponent: function (componentName, serviceComponentId) {
  877. var currentMasters = this.get("selectedServicesMasters").filterProperty("component_name", componentName);
  878. //work only if the multiple master service is selected in previous step
  879. if (currentMasters.length <= 1) {
  880. return false;
  881. }
  882. this.get("selectedServicesMasters").removeAt(this.get("selectedServicesMasters").indexOf(currentMasters.findProperty("serviceComponentId", serviceComponentId)));
  883. currentMasters = this.get("selectedServicesMasters").filterProperty("component_name", componentName);
  884. if (currentMasters.get("length") < this.get("hosts.length")) {
  885. currentMasters.set("lastObject.showAddControl", true);
  886. }
  887. if (currentMasters.get("length") === 1) {
  888. currentMasters.set("lastObject.showRemoveControl", false);
  889. }
  890. this.set('componentToRebalance', componentName);
  891. this.incrementProperty('rebalanceComponentHostsCounter');
  892. this.toggleProperty('hostNameCheckTrigger');
  893. return true;
  894. },
  895. recommendAndValidate: function(callback) {
  896. var self = this;
  897. // load recommendations with partial request
  898. self.loadComponentsRecommendationsFromServer(function() {
  899. // For validation use latest received recommendations because ir contains current master layout and recommended slave/client layout
  900. self.validate(App.router.get('installerController.recommendations'), function() {
  901. if (callback) {
  902. callback();
  903. }
  904. });
  905. }, true);
  906. },
  907. /**
  908. * Submit button click handler
  909. * @metohd submit
  910. */
  911. submit: function () {
  912. var self = this;
  913. var goNextStepIfValid = function() {
  914. if (!self.get('submitDisabled')) {
  915. App.router.send('next');
  916. }
  917. };
  918. if (App.supports.serverRecommendValidate ) {
  919. self.recommendAndValidate(function() {
  920. goNextStepIfValid();
  921. });
  922. } else {
  923. self.updateIsSubmitDisabled();
  924. goNextStepIfValid();
  925. }
  926. }
  927. });