step5_controller.js 40 KB

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