step5_controller.js 41 KB

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