step6_controller.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  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 db = require('utils/db');
  20. var stringUtils = require('utils/string_utils');
  21. var blueprintUtils = require('utils/blueprint');
  22. var validationUtils = require('utils/validator');
  23. /**
  24. * By Step 6, we have the following information stored in App.db and set on this
  25. * controller by the router:
  26. *
  27. * hosts: App.db.hosts (list of all hosts the user selected in Step 3)
  28. * selectedServiceNames: App.db.selectedServiceNames (the services that the user selected in Step 4)
  29. * masterComponentHosts: App.db.masterComponentHosts (master-components-to-hosts mapping the user selected in Step 5)
  30. *
  31. * Step 6 will set the following information in App.db:
  32. * slaveComponentHosts: App.db.slaveComponentHosts (slave-components-to-hosts mapping the user selected in Step 6)
  33. *
  34. */
  35. App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
  36. /**
  37. * List of hosts
  38. * @type {object[]}
  39. */
  40. hosts: [],
  41. /**
  42. * List of components info about selecting/deselecting status for components.
  43. *
  44. * @type {Array}
  45. * @item {Em.Object}
  46. * @property name {String} - component name
  47. * @property label {String} - component display name
  48. * @property allChecked {bool} - all checkboxes are checked
  49. * @property noChecked {bool} - no checkboxes checked
  50. */
  51. headers: [],
  52. /**
  53. * @type {bool}
  54. */
  55. isLoaded: false,
  56. /**
  57. * Indication if user has chosen hosts to install clients
  58. * @type {bool}
  59. */
  60. isClientsSet: false,
  61. /**
  62. * Define state for submit button
  63. * @type {bool}
  64. */
  65. submitDisabled: true,
  66. /**
  67. * Check if <code>addHostWizard</code> used
  68. * @type {bool}
  69. */
  70. isAddHostWizard: function () {
  71. return this.get('content.controllerName') === 'addHostController';
  72. }.property('content.controllerName'),
  73. /**
  74. * Check if <code>installerWizard</code> used
  75. * @type {bool}
  76. */
  77. isInstallerWizard: function () {
  78. return this.get('content.controllerName') === 'installerController';
  79. }.property('content.controllerName'),
  80. /**
  81. * Check if <code>addServiceWizard</code> used
  82. * @type {bool}
  83. */
  84. isAddServiceWizard: function () {
  85. return this.get('content.controllerName') === 'addServiceController';
  86. }.property('content.controllerName'),
  87. installedServiceNames: function () {
  88. return this.get('content.services').filterProperty('isInstalled').mapProperty('serviceName');
  89. }.property('content.services').cacheable(),
  90. /**
  91. * Validation error messages which don't related with any master
  92. */
  93. generalErrorMessages: [],
  94. /**
  95. * Validation warning messages which don't related with any master
  96. */
  97. generalWarningMessages: [],
  98. /**
  99. * true if validation has any general (which is not related with concrete host) error message
  100. */
  101. anyGeneralErrors: function() {
  102. var messages = this.get('generalErrorMessages');
  103. return this.get('errorMessage') || (messages && messages.length > 0);
  104. }.property('generalErrorMessages', 'generalErrorMessages.@each', 'errorMessage'),
  105. /**
  106. * true if validation has any general (which is not related with concrete host) warning message
  107. */
  108. anyGeneralWarnings: function() {
  109. var messages = this.get('generalWarningMessages');
  110. return messages && messages.length > 0;
  111. }.property('generalWarningMessages', 'generalWarningMessages.@each'),
  112. /**
  113. * true if validation has any error message (general or host specific)
  114. */
  115. anyErrors: function() {
  116. return this.get('anyGeneralErrors') || this.get('hosts').some(function(h) { return h.get('errorMessages').length > 0; });
  117. }.property('anyGeneralErrors', 'hosts.@each.errorMessages'),
  118. /**
  119. * true if validation has any warning message (general or host specific)
  120. */
  121. anyWarnings: function() {
  122. return this.get('anyGeneralWarnings') || this.get('hosts').some(function(h) { return h.get('warnMessages').length > 0; });
  123. }.property('anyGeneralWarnings', 'hosts.@each.warnMessages'),
  124. /**
  125. * Verify condition that at least one checkbox of each component was checked
  126. * @method clearError
  127. */
  128. clearError: function () {
  129. var self = this;
  130. var isError = false;
  131. var err = false;
  132. var hosts = this.get('hosts');
  133. var headers = this.get('headers');
  134. var headersMap = {};
  135. headers.forEach(function (header) {
  136. headersMap[header.name] = true;
  137. });
  138. hosts.forEach(function (host) {
  139. host.get('checkboxes').forEach(function (checkbox) {
  140. if (headersMap[checkbox.get('component')]) {
  141. headersMap[checkbox.get('component')] = !checkbox.get('checked');
  142. }
  143. });
  144. });
  145. for (var i in headersMap) {
  146. err |= headersMap[i];
  147. }
  148. if (!err) {
  149. this.set('errorMessage', '');
  150. }
  151. if (this.get('isAddHostWizard')) {
  152. hosts.forEach(function (host) {
  153. isError = false;
  154. headers.forEach(function (header) {
  155. isError |= host.get('checkboxes').findProperty('title', header.get('label')).checked;
  156. });
  157. isError = !isError;
  158. if (!isError) {
  159. self.set('errorMessage', '');
  160. }
  161. });
  162. }
  163. },
  164. /**
  165. * Clear Step6 data like <code>hosts</code>, <code>headers</code> etc
  166. * @method clearStep
  167. */
  168. clearStep: function () {
  169. this.set('hosts', []);
  170. this.set('headers', []);
  171. this.clearError();
  172. this.set('isLoaded', false);
  173. },
  174. /**
  175. * Enable some service for all hosts
  176. * @param {object} event
  177. * @method selectAllNodes
  178. */
  179. selectAllNodes: function (event) {
  180. var name = Em.get(event, 'context.name');
  181. if (name) {
  182. this.setAllNodes(name, true);
  183. this.callValidation();
  184. }
  185. },
  186. /**
  187. * Disable some services for all hosts
  188. * @param {object} event
  189. * @method deselectAllNodes
  190. */
  191. deselectAllNodes: function (event) {
  192. var name = Em.get(event, 'context.name');
  193. if (name) {
  194. this.setAllNodes(name, false);
  195. this.callValidation();
  196. }
  197. },
  198. /**
  199. * Enable/disable some service for all hosts
  200. * @param {String} component - component name
  201. * @param {bool} checked - true - enable, false - disable
  202. * @method setAllNodes
  203. */
  204. setAllNodes: function (component, checked) {
  205. this.get('hosts').forEach(function (host) {
  206. host.get('checkboxes').filterProperty('isInstalled', false).forEach(function (checkbox) {
  207. if (checkbox.get('component') === component) {
  208. checkbox.set('checked', checked);
  209. }
  210. });
  211. });
  212. this.checkCallback(component);
  213. },
  214. /**
  215. * Checkbox check callback
  216. * Verify if all/none checkboxes for current component are checked
  217. * @param {String} component
  218. * @method checkCallback
  219. */
  220. checkCallback: function (component) {
  221. var header = this.get('headers').findProperty('name', component);
  222. if (header) {
  223. var hosts = this.get('hosts');
  224. var allTrue = true;
  225. var allFalse = true;
  226. hosts.forEach(function (host) {
  227. host.get('checkboxes').forEach(function (checkbox) {
  228. if (checkbox.get('component') === component && !checkbox.get('isInstalled')) {
  229. allTrue = allTrue && checkbox.get('checked');
  230. allFalse = allFalse && !checkbox.get('checked');
  231. }
  232. });
  233. });
  234. header.set('allChecked', allTrue);
  235. header.set('noChecked', allFalse);
  236. }
  237. this.clearError();
  238. },
  239. /**
  240. * Init step6 data
  241. * @method loadStep
  242. */
  243. loadStep: function () {
  244. console.log("WizardStep6Controller: Loading step6: Assign Slaves");
  245. this.clearStep();
  246. var selectedServices = App.StackService.find().filterProperty('isSelected');
  247. var installedServices = App.StackService.find().filterProperty('isInstalled');
  248. var services;
  249. if (this.get('isInstallerWizard')) services = selectedServices;
  250. else if (this.get('isAddHostWizard')) services = installedServices;
  251. else if (this.get('isAddServiceWizard')) services = installedServices.concat(selectedServices);
  252. var headers = Em.A([]);
  253. services.forEach(function (stackService) {
  254. stackService.get('serviceComponents').forEach(function (serviceComponent) {
  255. if (serviceComponent.get('isShownOnInstallerSlaveClientPage')) {
  256. headers.pushObject(Em.Object.create({
  257. name: serviceComponent.get('componentName'),
  258. label: serviceComponent.get('displayName'),
  259. allChecked: false,
  260. isRequired: serviceComponent.get('isRequired'),
  261. noChecked: true,
  262. isDisabled: installedServices.someProperty('serviceName', stackService.get('serviceName')) && this.get('isAddServiceWizard')
  263. }));
  264. }
  265. }, this);
  266. }, this);
  267. if (this.get('content.clients') && !!this.get('content.clients').length) {
  268. headers.pushObject(Em.Object.create({
  269. name: 'CLIENT',
  270. label: App.format.role('CLIENT'),
  271. allChecked: false,
  272. noChecked: true,
  273. isDisabled: false
  274. }));
  275. }
  276. this.get('headers').pushObjects(headers);
  277. this.render();
  278. if (this.get('content.skipSlavesStep')) {
  279. App.router.send('next');
  280. } else {
  281. this.callValidation();
  282. }
  283. },
  284. /**
  285. * Get active host names
  286. * @return {string[]}
  287. * @method getHostNames
  288. */
  289. getHostNames: function () {
  290. var hostInfo = this.get('content.hosts');
  291. var hostNames = [];
  292. //flag identify whether get all hosts or only uninstalled(newly added) hosts
  293. var getUninstalledHosts = (this.get('content.controllerName') !== 'addServiceController');
  294. for (var index in hostInfo) {
  295. if (hostInfo.hasOwnProperty(index)) {
  296. if (hostInfo[index].bootStatus === 'REGISTERED') {
  297. if (!getUninstalledHosts || !hostInfo[index].isInstalled) {
  298. hostNames.push(hostInfo[index].name);
  299. }
  300. }
  301. }
  302. }
  303. return hostNames;
  304. },
  305. /**
  306. * Load all data needed for this module. Then it automatically renders in template
  307. * @method render
  308. */
  309. render: function () {
  310. var hostsObj = [],
  311. masterHosts = [],
  312. headers = this.get('headers'),
  313. masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq();
  314. this.getHostNames().forEach(function (_hostName) {
  315. var hasMaster = masterHostNames.contains(_hostName);
  316. var obj = Em.Object.create({
  317. hostName: _hostName,
  318. hasMaster: hasMaster,
  319. checkboxes: []
  320. });
  321. headers.forEach(function (header) {
  322. obj.checkboxes.pushObject(Em.Object.create({
  323. component: header.name,
  324. title: header.label,
  325. checked: false,
  326. isInstalled: false,
  327. isDisabled: header.get('isDisabled')
  328. }));
  329. });
  330. if (hasMaster) {
  331. masterHosts.pushObject(obj)
  332. } else {
  333. hostsObj.pushObject(obj);
  334. }
  335. });
  336. //hosts with master components should be in the beginning of list
  337. hostsObj.unshift.apply(hostsObj, masterHosts);
  338. hostsObj = this.renderSlaves(hostsObj);
  339. this.set('hosts', hostsObj);
  340. headers.forEach(function (header) {
  341. this.checkCallback(header.get('name'));
  342. }, this);
  343. this.set('isLoaded', true);
  344. },
  345. /**
  346. * Set checked values for slaves checkboxes
  347. * @param {Array} hostsObj
  348. * @return {Array}
  349. * @method renderSlaves
  350. */
  351. renderSlaves: function (hostsObj) {
  352. var headers = this.get('headers');
  353. var clientHeaders = headers.findProperty('name', 'CLIENT');
  354. var slaveComponents = this.get('content.slaveComponentHosts');
  355. if (!slaveComponents) { // we are at this page for the first time
  356. if (!App.get('supports.serverRecommendValidate')) {
  357. hostsObj.forEach(function (host) {
  358. var checkboxes = host.get('checkboxes');
  359. checkboxes.setEach('checked', !host.hasMaster);
  360. checkboxes.setEach('isInstalled', false);
  361. if (clientHeaders) {
  362. checkboxes.findProperty('title', clientHeaders.get('label')).set('checked', false);
  363. }
  364. });
  365. this.selectClientHost(hostsObj);
  366. if (this.get('isInstallerWizard') && hostsObj.everyProperty('hasMaster', true)) {
  367. var lastHost = hostsObj[hostsObj.length - 1];
  368. lastHost.get('checkboxes').setEach('checked', true);
  369. }
  370. } else {
  371. var recommendations = this.get('content.recommendations');
  372. // Get all host-component pairs from recommendations
  373. var componentHostPairs = recommendations.blueprint.host_groups.map(function (group) {
  374. return group.components.map(function (component) {
  375. return recommendations.blueprint_cluster_binding.host_groups.findProperty('name', group.name).hosts.map(function (host) {
  376. return { component: component.name, host: host.fqdn};
  377. });
  378. });
  379. });
  380. // Flatten results twice because of two map() call before
  381. componentHostPairs = [].concat.apply([], componentHostPairs);
  382. componentHostPairs = [].concat.apply([], componentHostPairs);
  383. var clientComponents = App.get('components.clients');
  384. hostsObj.forEach(function (host) {
  385. var checkboxes = host.get('checkboxes');
  386. checkboxes.forEach(function (checkbox) {
  387. var recommended = componentHostPairs.some(function (pair) {
  388. var componentMatch = pair.component === checkbox.component;
  389. if (checkbox.component === 'CLIENT' && !componentMatch) {
  390. componentMatch = clientComponents.contains(pair.component);
  391. }
  392. return pair.host === host.hostName && componentMatch;
  393. });
  394. checkbox.checked = recommended;
  395. });
  396. });
  397. }
  398. } else {
  399. this.get('headers').forEach(function (header) {
  400. var nodes = slaveComponents.findProperty('componentName', header.get('name'));
  401. if (nodes) {
  402. nodes.hosts.forEach(function (_node) {
  403. var node = hostsObj.findProperty('hostName', _node.hostName);
  404. if (node) {
  405. node.get('checkboxes').findProperty('title', header.get('label')).set('checked', true);
  406. node.get('checkboxes').findProperty('title', header.get('label')).set('isInstalled', _node.isInstalled);
  407. }
  408. });
  409. }
  410. });
  411. }
  412. this.selectClientHost(hostsObj);
  413. return hostsObj;
  414. },
  415. /**
  416. *
  417. * @param hostsObj
  418. */
  419. selectClientHost: function (hostsObj) {
  420. if (!this.get('isClientsSet')) {
  421. var nonMasterHost = hostsObj.findProperty('hasMaster', false);
  422. var clientHost = !!nonMasterHost ? nonMasterHost : hostsObj[hostsObj.length - 1]; // last host
  423. var clientCheckBox = clientHost.get('checkboxes').findProperty('component', 'CLIENT');
  424. if (clientCheckBox) {
  425. clientCheckBox.set('checked', true);
  426. }
  427. this.set('isClientsSet', true);
  428. }
  429. },
  430. /**
  431. * Select checkboxes which correspond to master components
  432. *
  433. * @param {Array} hostsObj
  434. * @return {Array}
  435. * @method selectMasterComponents
  436. */
  437. selectMasterComponents: function (hostsObj) {
  438. var masterComponentHosts = this.get('content.masterComponentHosts');
  439. console.log('Master components selected on:', masterComponentHosts.mapProperty('hostName').uniq().join(", "));
  440. if (masterComponentHosts) {
  441. masterComponentHosts.forEach(function (item) {
  442. var host = hostsObj.findProperty('hostName', item.hostName);
  443. if (host) {
  444. var checkbox = host.get('checkboxes').findProperty('component', item.component);
  445. if (checkbox) {
  446. checkbox.set('checked', true);
  447. }
  448. }
  449. });
  450. }
  451. return hostsObj;
  452. },
  453. /**
  454. * Return list of master components for specified <code>hostname</code>
  455. * @param {string} hostName
  456. * @return {string[]}
  457. * @method getMasterComponentsForHost
  458. */
  459. getMasterComponentsForHost: function (hostName) {
  460. return this.get('content.masterComponentHosts').filterProperty('hostName', hostName).mapProperty('component');
  461. },
  462. callValidation: function (successCallback) {
  463. var self = this;
  464. if (App.get('supports.serverRecommendValidate')) {
  465. self.callServerSideValidation(successCallback);
  466. } else {
  467. var res = self.callClientSideValidation();
  468. self.set('submitDisabled', !res);
  469. if (res && successCallback) {
  470. successCallback();
  471. }
  472. }
  473. },
  474. /**
  475. * Update submit button status
  476. * @metohd callServerSideValidation
  477. */
  478. callServerSideValidation: function (successCallback) {
  479. var self = this;
  480. self.set('submitDisabled', true);
  481. var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName');
  482. var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName');
  483. var services = installedServices.concat(selectedServices).uniq();
  484. var hostNames = self.get('hosts').mapProperty('hostName');
  485. var slaveBlueprint = self.getCurrentBlueprint();
  486. var masterBlueprint = null;
  487. var invisibleSlaves = App.StackServiceComponent.find().filterProperty("isSlave").filterProperty("isShownOnInstallerSlaveClientPage", false).mapProperty("componentName");
  488. if (this.get('isInstallerWizard') || this.get('isAddServiceWizard')) {
  489. masterBlueprint = App.router.get('wizardStep5Controller').getCurrentBlueprint();
  490. var invisibleMasters = [];
  491. if (this.get('isInstallerWizard')) {
  492. invisibleMasters = App.StackServiceComponent.find().filterProperty("isMaster").filterProperty("isShownOnInstallerAssignMasterPage", false).mapProperty("componentName");
  493. } else if (this.get('isAddServiceWizard')) {
  494. invisibleMasters = App.StackServiceComponent.find().filterProperty("isMaster").filterProperty("isShownOnAddServiceAssignMasterPage", false).mapProperty("componentName");
  495. }
  496. var selectedClientComponents = self.get('content.clients').mapProperty('component_name');
  497. var alreadyInstalledClients = App.get('components.clients').reject(function (c) {
  498. return selectedClientComponents.contains(c);
  499. });
  500. var invisibleComponents = invisibleMasters.concat(invisibleSlaves).concat(alreadyInstalledClients);
  501. var invisibleBlueprint = blueprintUtils.filterByComponents(this.get('content.recommendations'), invisibleComponents);
  502. masterBlueprint = blueprintUtils.mergeBlueprints(masterBlueprint, invisibleBlueprint);
  503. } else if (this.get('isAddHostWizard')) {
  504. masterBlueprint = self.getCurrentMasterSlaveBlueprint();
  505. hostNames = hostNames.concat(App.Host.find().mapProperty("hostName")).uniq();
  506. slaveBlueprint = blueprintUtils.addComponentsToBlueprint(slaveBlueprint, invisibleSlaves);
  507. }
  508. var bluePrintsForValidation = blueprintUtils.mergeBlueprints(masterBlueprint, slaveBlueprint);
  509. this.set('content.recommendationsHostGroups', bluePrintsForValidation);
  510. App.ajax.send({
  511. name: 'config.validations',
  512. sender: self,
  513. data: {
  514. stackVersionUrl: App.get('stackVersionURL'),
  515. hosts: hostNames,
  516. services: services,
  517. validate: 'host_groups',
  518. recommendations: bluePrintsForValidation
  519. },
  520. success: 'updateValidationsSuccessCallback'
  521. }).
  522. retry({
  523. times: App.maxRetries,
  524. timeout: App.timeout
  525. }).
  526. then(function () {
  527. if (!self.get('submitDisabled') && successCallback) {
  528. successCallback();
  529. }
  530. }, function () {
  531. App.showReloadPopup();
  532. console.log('Load validations failed');
  533. }
  534. );
  535. },
  536. /**
  537. * Success-callback for validations request
  538. * @param {object} data
  539. * @method updateValidationsSuccessCallback
  540. */
  541. updateValidationsSuccessCallback: function (data) {
  542. var self = this;
  543. //data = JSON.parse(data); // temporary fix
  544. var clientComponents = App.get('components.clients');
  545. this.set('generalErrorMessages', []);
  546. this.set('generalWarningMessages', []);
  547. this.get('hosts').setEach('warnMessages', []);
  548. this.get('hosts').setEach('errorMessages', []);
  549. this.get('hosts').setEach('anyMessage', false);
  550. this.get('hosts').forEach(function (host) {
  551. host.checkboxes.setEach('hasWarnMessage', false);
  552. host.checkboxes.setEach('hasErrorMessage', false);
  553. });
  554. var anyErrors = false;
  555. var anyGeneralClientErrors = false; // any error/warning for any client component (under "CLIENT" alias)
  556. var validationData = validationUtils.filterNotInstalledComponents(data);
  557. validationData.filterProperty('type', 'host-component').forEach(function (item) {
  558. var checkboxWithIssue = null;
  559. var isGeneralClientValidationItem = clientComponents.contains(item['component-name']); // it is an error/warning for any client component (under "CLIENT" alias)
  560. var host = self.get('hosts').find(function (h) {
  561. return h.hostName === item.host && h.checkboxes.some(function (checkbox) {
  562. var isClientComponent = checkbox.component === "CLIENT" && isGeneralClientValidationItem;
  563. if (checkbox.component === item['component-name'] || isClientComponent) {
  564. checkboxWithIssue = checkbox;
  565. return true;
  566. } else {
  567. return false;
  568. }
  569. });
  570. });
  571. if (host) {
  572. host.set('anyMessage', true);
  573. if (item.level === 'ERROR') {
  574. anyErrors = true;
  575. host.get('errorMessages').push(item.message);
  576. checkboxWithIssue.set('hasErrorMessage', true);
  577. } else if (item.level === 'WARN') {
  578. host.get('warnMessages').push(item.message);
  579. checkboxWithIssue.set('hasWarnMessage', true);
  580. }
  581. } else {
  582. var component;
  583. if (isGeneralClientValidationItem) {
  584. if (!anyGeneralClientErrors) {
  585. anyGeneralClientErrors = true;
  586. component = "Client";
  587. }
  588. } else {
  589. component = item['component-name'];
  590. }
  591. if (component || !item['component-name']) {
  592. var details = "";
  593. if (component) {
  594. details += " for " + component + " component";
  595. }
  596. if (item.host) {
  597. details += " " + item.host;
  598. }
  599. if (item.level === 'ERROR') {
  600. anyErrors = true;
  601. self.get('generalErrorMessages').push(item.message + details);
  602. } else if (item.level === 'WARN') {
  603. self.get('generalWarningMessages').push(item.message + details);
  604. }
  605. }
  606. }
  607. });
  608. // use this.set('submitDisabled', anyErrors); is validation results should block next button
  609. // It's because showValidationIssuesAcceptBox allow use accept validation issues and continue
  610. this.set('submitDisabled', false);
  611. },
  612. /**
  613. * Composes selected values of comboboxes into blueprint format
  614. */
  615. getCurrentBlueprint: function () {
  616. var self = this;
  617. var res = {
  618. blueprint: { host_groups: [] },
  619. blueprint_cluster_binding: { host_groups: [] }
  620. };
  621. var clientComponents = self.get('content.clients').mapProperty('component_name');
  622. var mapping = self.get('hosts');
  623. var i = 0;
  624. mapping.forEach(function (item) {
  625. i += 1;
  626. var group_name = 'host-group-' + i;
  627. var host_group = {
  628. name: group_name,
  629. components: item.checkboxes.filterProperty('checked', true).map(function (checkbox) {
  630. if (checkbox.component === "CLIENT") {
  631. return clientComponents.map(function (client) {
  632. return { name: client };
  633. });
  634. } else {
  635. return { name: checkbox.component };
  636. }
  637. })
  638. };
  639. host_group.components = [].concat.apply([], host_group.components);
  640. var binding = {
  641. name: group_name,
  642. hosts: [
  643. { fqdn: item.hostName }
  644. ]
  645. }
  646. res.blueprint.host_groups.push(host_group);
  647. res.blueprint_cluster_binding.host_groups.push(binding);
  648. });
  649. return res;
  650. },
  651. /**
  652. * callClientSideValidation form. Return do we have errors or not
  653. * @return {bool}
  654. * @method callClientSideValidation
  655. */
  656. callClientSideValidation: function () {
  657. if (this.get('isAddHostWizard')) {
  658. return this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForHost'));
  659. }
  660. else {
  661. if (this.get('isInstallerWizard')) {
  662. return this.validateEachComponent() && this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForSlaveHost'));
  663. }
  664. else {
  665. if (this.get('isAddServiceWizard')) {
  666. return this.validateEachComponent();
  667. }
  668. return true;
  669. }
  670. }
  671. },
  672. /**
  673. * Validate all components for each host. Return do we have errors or not
  674. * @return {bool}
  675. * @method validateEachHost
  676. */
  677. validateEachHost: function (errorMsg) {
  678. var isError = false;
  679. var hosts = this.get('hosts');
  680. var headers = this.get('headers');
  681. for (var i = 0; i < hosts.length; i++) {
  682. if (this.get('isInstallerWizard') && this.get('content.masterComponentHosts').someProperty('hostName', hosts[i].hostName)) {
  683. continue;
  684. }
  685. var checkboxes = hosts[i].get('checkboxes');
  686. isError = false;
  687. headers.forEach(function (header) {
  688. isError = isError || checkboxes.findProperty('title', header.get('label')).checked;
  689. });
  690. isError = !isError;
  691. if (isError) {
  692. this.set('errorMessage', errorMsg);
  693. break;
  694. }
  695. }
  696. return !isError;
  697. },
  698. /**
  699. * Check for minimum required count of components to install.
  700. *
  701. * @return {bool}
  702. * @method validateEachComponent
  703. */
  704. validateEachComponent: function () {
  705. var isError = false;
  706. var hosts = this.get('hosts');
  707. var headers = this.get('headers');
  708. var componentsToInstall = [];
  709. headers.forEach(function (header) {
  710. var checkboxes = hosts.mapProperty('checkboxes').reduce(function (cItem, pItem) {
  711. return cItem.concat(pItem);
  712. });
  713. var selectedCount = checkboxes.filterProperty('component', header.get('name')).filterProperty('checked').length;
  714. if (header.get('name') == 'CLIENT') {
  715. var clientsMinCount = 0;
  716. var serviceNames = this.get('installedServiceNames').concat(this.get('content.selectedServiceNames'));
  717. // find max value for `minToInstall` property
  718. serviceNames.forEach(function (serviceName) {
  719. App.StackServiceComponent.find().filterProperty('stackService.serviceName', serviceName).filterProperty('isClient')
  720. .mapProperty('minToInstall').forEach(function (ctMinCount) {
  721. clientsMinCount = ctMinCount > clientsMinCount ? ctMinCount : clientsMinCount;
  722. });
  723. });
  724. if (selectedCount < clientsMinCount) {
  725. isError = true;
  726. var requiredQuantity = (clientsMinCount > hosts.length ? hosts.length : clientsMinCount) - selectedCount;
  727. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, Em.I18n.t('common.client')));
  728. }
  729. } else {
  730. var stackComponent = App.StackServiceComponent.find().findProperty('componentName', header.get('name'));
  731. if (selectedCount < stackComponent.get('minToInstall')) {
  732. isError = true;
  733. var requiredQuantity = (stackComponent.get('minToInstall') > hosts.length ? hosts.length : stackComponent.get('minToInstall')) - selectedCount;
  734. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, stackComponent.get('displayName')));
  735. }
  736. }
  737. }, this);
  738. if (componentsToInstall.length) {
  739. this.set('errorMessage', Em.I18n.t('installer.step6.error.mustSelectComponents').format(componentsToInstall.join(', ')));
  740. }
  741. return !isError;
  742. },
  743. /**
  744. * In case of any validation issues shows accept dialog box for user which allow cancel and fix issues or continue anyway
  745. * @metohd submit
  746. */
  747. showValidationIssuesAcceptBox: function(callback) {
  748. var self = this;
  749. if (App.get('supports.serverRecommendValidate') && (self.get('anyWarnings') || self.get('anyErrors'))) {
  750. App.ModalPopup.show({
  751. primary: Em.I18n.t('common.continueAnyway'),
  752. header: Em.I18n.t('installer.step6.validationIssuesAttention.header'),
  753. body: Em.I18n.t('installer.step6.validationIssuesAttention'),
  754. onPrimary: function () {
  755. this.hide();
  756. callback();
  757. }
  758. });
  759. } else {
  760. callback();
  761. }
  762. }
  763. });