step6_controller.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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. /**
  22. * By Step 6, we have the following information stored in App.db and set on this
  23. * controller by the router:
  24. *
  25. * hosts: App.db.hosts (list of all hosts the user selected in Step 3)
  26. * selectedServiceNames: App.db.selectedServiceNames (the services that the user selected in Step 4)
  27. * masterComponentHosts: App.db.masterComponentHosts (master-components-to-hosts mapping the user selected in Step 5)
  28. *
  29. * Step 6 will set the following information in App.db:
  30. * slaveComponentHosts: App.db.slaveComponentHosts (slave-components-to-hosts mapping the user selected in Step 6)
  31. *
  32. */
  33. App.WizardStep6Controller = Em.Controller.extend({
  34. /**
  35. * List of hosts
  36. * @type {object[]}
  37. */
  38. hosts: [],
  39. /**
  40. * List of components info about selecting/deselecting status for components.
  41. *
  42. * @type {Array}
  43. * @item {Em.Object}
  44. * @property name {String} - component name
  45. * @property label {String} - component display name
  46. * @property allChecked {bool} - all checkboxes are checked
  47. * @property noChecked {bool} - no checkboxes checked
  48. */
  49. headers: [],
  50. /**
  51. * @type {bool}
  52. */
  53. isLoaded: false,
  54. /**
  55. * Check if <code>addHostWizard</code> used
  56. * @type {bool}
  57. */
  58. isAddHostWizard: function () {
  59. return this.get('content.controllerName') === 'addHostController';
  60. }.property('content.controllerName'),
  61. /**
  62. * Check if <code>installerWizard</code> used
  63. * @type {bool}
  64. */
  65. isInstallerWizard: function () {
  66. return this.get('content.controllerName') === 'installerController';
  67. }.property('content.controllerName'),
  68. /**
  69. * Check if <code>addServiceWizard</code> used
  70. * @type {bool}
  71. */
  72. isAddServiceWizard: function () {
  73. return this.get('content.controllerName') === 'addServiceController';
  74. }.property('content.controllerName'),
  75. installedServiceNames: function () {
  76. return this.get('content.services').filterProperty('isInstalled').mapProperty('serviceName');
  77. }.property('content.services').cacheable(),
  78. /**
  79. * Verify condition that at least one checkbox of each component was checked
  80. * @method clearError
  81. */
  82. clearError: function () {
  83. var self = this;
  84. var isError = false;
  85. var err = false;
  86. var hosts = this.get('hosts');
  87. var headers = this.get('headers');
  88. var headersMap = {};
  89. headers.forEach(function (header) {
  90. headersMap[header.name] = true;
  91. });
  92. hosts.forEach(function (host) {
  93. host.get('checkboxes').forEach(function (checkbox) {
  94. if (headersMap[checkbox.get('component')]) {
  95. headersMap[checkbox.get('component')] = !checkbox.get('checked');
  96. }
  97. });
  98. });
  99. for (var i in headersMap) {
  100. err |= headersMap[i];
  101. }
  102. if (!err) {
  103. this.set('errorMessage', '');
  104. }
  105. if (this.get('isAddHostWizard')) {
  106. hosts.forEach(function (host) {
  107. isError = false;
  108. headers.forEach(function (header) {
  109. isError |= host.get('checkboxes').findProperty('title', header.get('label')).checked;
  110. });
  111. isError = !isError;
  112. if (!isError) {
  113. self.set('errorMessage', '');
  114. }
  115. });
  116. }
  117. },
  118. /**
  119. * Clear Step6 data like <code>hosts</code>, <code>headers</code> etc
  120. * @method clearStep
  121. */
  122. clearStep: function () {
  123. this.set('hosts', []);
  124. this.set('headers', []);
  125. this.clearError();
  126. this.set('isLoaded', false);
  127. },
  128. /**
  129. * Enable some service for all hosts
  130. * @param {object} event
  131. * @method selectAllNodes
  132. */
  133. selectAllNodes: function (event) {
  134. var name = Em.get(event, 'context.name');
  135. if (name) {
  136. this.setAllNodes(name, true);
  137. }
  138. },
  139. /**
  140. * Disable some services for all hosts
  141. * @param {object} event
  142. * @method deselectAllNodes
  143. */
  144. deselectAllNodes: function (event) {
  145. var name = Em.get(event, 'context.name');
  146. if (name) {
  147. this.setAllNodes(name, false);
  148. }
  149. },
  150. /**
  151. * Enable/disable some service for all hosts
  152. * @param {String} component - component name
  153. * @param {bool} checked - true - enable, false - disable
  154. * @method setAllNodes
  155. */
  156. setAllNodes: function (component, checked) {
  157. this.get('hosts').forEach(function (host) {
  158. host.get('checkboxes').filterProperty('isInstalled', false).forEach(function (checkbox) {
  159. if (checkbox.get('component') === component) {
  160. checkbox.set('checked', checked);
  161. }
  162. });
  163. });
  164. this.checkCallback(component);
  165. },
  166. /**
  167. * Checkbox check callback
  168. * Verify if all/none checkboxes for current component are checked
  169. * @param {String} component
  170. * @method checkCallback
  171. */
  172. checkCallback: function (component) {
  173. var header = this.get('headers').findProperty('name', component);
  174. if (header) {
  175. var hosts = this.get('hosts');
  176. var allTrue = true;
  177. var allFalse = true;
  178. hosts.forEach(function (host) {
  179. host.get('checkboxes').forEach(function (checkbox) {
  180. if (checkbox.get('component') === component && !checkbox.get('isInstalled')) {
  181. allTrue = allTrue && checkbox.get('checked');
  182. allFalse = allFalse && !checkbox.get('checked');
  183. }
  184. });
  185. });
  186. header.set('allChecked', allTrue);
  187. header.set('noChecked', allFalse);
  188. }
  189. this.clearError();
  190. },
  191. /**
  192. * Init step6 data
  193. * @method loadStep
  194. */
  195. loadStep: function () {
  196. console.log("WizardStep6Controller: Loading step6: Assign Slaves");
  197. this.clearStep();
  198. var selectedServices = App.StackService.find().filterProperty('isSelected');
  199. var installedServices = App.StackService.find().filterProperty('isInstalled');
  200. var services;
  201. if (this.get('isInstallerWizard')) services = selectedServices;
  202. else if (this.get('isAddHostWizard')) services = installedServices;
  203. else if (this.get('isAddServiceWizard')) services = installedServices.concat(selectedServices);
  204. var headers = Em.A([]);
  205. services.forEach(function (stackService) {
  206. stackService.get('serviceComponents').forEach(function (serviceComponent) {
  207. if (serviceComponent.get('isShownOnInstallerSlaveClientPage')) {
  208. headers.pushObject(Em.Object.create({
  209. name: serviceComponent.get('componentName'),
  210. label: serviceComponent.get('displayName'),
  211. allChecked: false,
  212. isRequired: serviceComponent.get('isRequired'),
  213. noChecked: true,
  214. isDisabled: installedServices.someProperty('serviceName',stackService.get('serviceName'))
  215. }));
  216. }
  217. }, this);
  218. }, this);
  219. if (this.get('content.clients') && !!this.get('content.clients').length) {
  220. headers.pushObject(Em.Object.create({
  221. name: 'CLIENT',
  222. label: App.format.role('CLIENT'),
  223. allChecked: false,
  224. noChecked: true,
  225. isDisabled: false
  226. }));
  227. }
  228. this.get('headers').pushObjects(headers);
  229. this.render();
  230. if (this.get('content.skipSlavesStep')) {
  231. App.router.send('next');
  232. }
  233. },
  234. /**
  235. * Get active host names
  236. * @return {string[]}
  237. * @method getHostNames
  238. */
  239. getHostNames: function () {
  240. var hostInfo = this.get('content.hosts');
  241. var hostNames = [];
  242. //flag identify whether get all hosts or only uninstalled(newly added) hosts
  243. var getUninstalledHosts = (this.get('content.controllerName') !== 'addServiceController');
  244. for (var index in hostInfo) {
  245. if (hostInfo.hasOwnProperty(index)) {
  246. if (hostInfo[index].bootStatus === 'REGISTERED') {
  247. if (!getUninstalledHosts || !hostInfo[index].isInstalled) {
  248. hostNames.push(hostInfo[index].name);
  249. }
  250. }
  251. }
  252. }
  253. return hostNames;
  254. },
  255. /**
  256. * Load all data needed for this module. Then it automatically renders in template
  257. * @method render
  258. */
  259. render: function () {
  260. var hostsObj = [],
  261. masterHosts = [],
  262. headers = this.get('headers'),
  263. masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq();
  264. this.getHostNames().forEach(function (_hostName) {
  265. var hasMaster = masterHostNames.contains(_hostName);
  266. var obj = Em.Object.create({
  267. hostName: _hostName,
  268. hasMaster: hasMaster,
  269. checkboxes: []
  270. });
  271. headers.forEach(function (header) {
  272. obj.checkboxes.pushObject(Em.Object.create({
  273. component: header.name,
  274. title: header.label,
  275. checked: false,
  276. isInstalled: false,
  277. isDisabled: header.get('isDisabled')
  278. }));
  279. });
  280. if (hasMaster) {
  281. masterHosts.pushObject(obj)
  282. } else {
  283. hostsObj.pushObject(obj);
  284. }
  285. });
  286. //hosts with master components should be in the beginning of list
  287. hostsObj.unshift.apply(hostsObj, masterHosts);
  288. hostsObj = this.renderSlaves(hostsObj);
  289. this.set('hosts', hostsObj);
  290. headers.forEach(function (header) {
  291. this.checkCallback(header.get('name'));
  292. }, this);
  293. this.set('isLoaded', true);
  294. },
  295. /**
  296. * Set checked values for slaves checkboxes
  297. * @param {Array} hostsObj
  298. * @return {Array}
  299. * @method renderSlaves
  300. */
  301. renderSlaves: function (hostsObj) {
  302. var headers = this.get('headers');
  303. var clientHeaders = headers.findProperty('name', 'CLIENT');
  304. var slaveComponents = this.get('content.slaveComponentHosts');
  305. if (!slaveComponents) { // we are at this page for the first time
  306. if (!App.supports.serverRecommendValidate) {
  307. hostsObj.forEach(function (host) {
  308. var checkboxes = host.get('checkboxes');
  309. checkboxes.setEach('checked', !host.hasMaster);
  310. checkboxes.setEach('isInstalled', false);
  311. if (clientHeaders) {
  312. checkboxes.findProperty('title', clientHeaders.get('label')).set('checked', false);
  313. }
  314. });
  315. this.selectClientHost(hostsObj);
  316. if (this.get('isInstallerWizard') && hostsObj.everyProperty('hasMaster', true)) {
  317. var lastHost = hostsObj[hostsObj.length - 1];
  318. lastHost.get('checkboxes').setEach('checked', true);
  319. }
  320. } else {
  321. var recommendations = App.router.get('installerController.recommendations');
  322. // Get all host-component pairs from recommendations
  323. var componentHostPairs = recommendations.blueprint.host_groups.map(function (group) {
  324. return group.components.map(function (component) {
  325. return recommendations.blueprint_cluster_binding.host_groups.findProperty('name', group.name).hosts.map(function (host) {
  326. return { component: component.name, host: host.fqdn};
  327. });
  328. });
  329. });
  330. // Flatten results twice because of two map() call before
  331. componentHostPairs = [].concat.apply([], componentHostPairs);
  332. componentHostPairs = [].concat.apply([], componentHostPairs);
  333. var clientComponents = App.get('components.clients');
  334. hostsObj.forEach(function (host) {
  335. var checkboxes = host.get('checkboxes');
  336. checkboxes.forEach(function (checkbox) {
  337. var recommended = componentHostPairs.some(function (pair) {
  338. var componentMatch = pair.component === checkbox.component;
  339. if (checkbox.component === 'CLIENT' && !componentMatch) {
  340. componentMatch = clientComponents.contains(pair.component);
  341. }
  342. return pair.host === host.hostName && componentMatch;
  343. });
  344. checkbox.checked = recommended;
  345. });
  346. });
  347. }
  348. } else {
  349. this.get('headers').forEach(function (header) {
  350. var nodes = slaveComponents.findProperty('componentName', header.get('name'));
  351. if (nodes) {
  352. nodes.hosts.forEach(function (_node) {
  353. var node = hostsObj.findProperty('hostName', _node.hostName);
  354. if (node) {
  355. node.get('checkboxes').findProperty('title', header.get('label')).set('checked', true);
  356. node.get('checkboxes').findProperty('title', header.get('label')).set('isInstalled', _node.isInstalled);
  357. }
  358. });
  359. }
  360. });
  361. }
  362. this.selectClientHost(hostsObj);
  363. return hostsObj;
  364. },
  365. /**
  366. *
  367. * @param hostsObj
  368. */
  369. selectClientHost: function (hostsObj) {
  370. var headers = this.get('headers');
  371. var clientHeaders = headers.findProperty('name', 'CLIENT');
  372. if (!clientHeaders) {
  373. return;
  374. }
  375. var client_is_set = false;
  376. hostsObj.forEach(function (host) {
  377. if (!client_is_set) {
  378. var checkboxes = host.get('checkboxes');
  379. var dfsService = App.StackService.find().findProperty('isPrimaryDFS');
  380. var checkboxServiceComponent = checkboxes.findProperty('title', headers.findProperty('name', dfsService.get('serviceComponents').
  381. findProperty('isShownOnInstallerSlaveClientPage').get('componentName')).get('label'));
  382. if (checkboxServiceComponent && checkboxServiceComponent.get('checked')) {
  383. checkboxes.findProperty('title', clientHeaders.get('label')).set('checked', true);
  384. client_is_set = true;
  385. }
  386. }
  387. }, this);
  388. },
  389. /**
  390. * Select checkboxes which correspond to master components
  391. *
  392. * @param {Array} hostsObj
  393. * @return {Array}
  394. * @method selectMasterComponents
  395. */
  396. selectMasterComponents: function (hostsObj) {
  397. var masterComponentHosts = this.get('content.masterComponentHosts');
  398. console.log('Master components selected on:', masterComponentHosts.mapProperty('hostName').uniq().join(", "));
  399. if (masterComponentHosts) {
  400. masterComponentHosts.forEach(function (item) {
  401. var host = hostsObj.findProperty('hostName', item.hostName);
  402. if (host) {
  403. var checkbox = host.get('checkboxes').findProperty('component', item.component);
  404. if (checkbox) {
  405. checkbox.set('checked', true);
  406. }
  407. }
  408. });
  409. }
  410. return hostsObj;
  411. },
  412. /**
  413. * Return list of master components for specified <code>hostname</code>
  414. * @param {string} hostName
  415. * @return {string[]}
  416. * @method getMasterComponentsForHost
  417. */
  418. getMasterComponentsForHost: function (hostName) {
  419. return this.get('content.masterComponentHosts').filterProperty('hostName', hostName).mapProperty('component');
  420. },
  421. /**
  422. * Validate form. Return do we have errors or not
  423. * @return {bool}
  424. * @method validate
  425. */
  426. validate: function () {
  427. if (this.get('isAddHostWizard')) {
  428. return this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForHost'));
  429. }
  430. else {
  431. if (this.get('isInstallerWizard')) {
  432. return this.validateEachComponent() && this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForSlaveHost'));
  433. }
  434. else {
  435. if (this.get('isAddServiceWizard')) {
  436. return this.validateEachComponent();
  437. }
  438. return true;
  439. }
  440. }
  441. },
  442. /**
  443. * Validate all components for each host. Return do we have errors or not
  444. * @return {bool}
  445. * @method validateEachHost
  446. */
  447. validateEachHost: function (errorMsg) {
  448. var isError = false;
  449. var hosts = this.get('hosts');
  450. var headers = this.get('headers');
  451. for (var i = 0; i < hosts.length; i++) {
  452. if (this.get('isInstallerWizard') && this.get('content.masterComponentHosts').someProperty('hostName', hosts[i].hostName)) {
  453. continue;
  454. }
  455. var checkboxes = hosts[i].get('checkboxes');
  456. isError = false;
  457. headers.forEach(function (header) {
  458. isError = isError || checkboxes.findProperty('title', header.get('label')).checked;
  459. });
  460. isError = !isError;
  461. if (isError) {
  462. this.set('errorMessage', errorMsg);
  463. break;
  464. }
  465. }
  466. return !isError;
  467. },
  468. /**
  469. * Check for minimum required count of components to install.
  470. *
  471. * @return {bool}
  472. * @method validateEachComponent
  473. */
  474. validateEachComponent: function () {
  475. var isError = false;
  476. var hosts = this.get('hosts');
  477. var headers = this.get('headers');
  478. var componentsToInstall = [];
  479. headers.forEach(function (header) {
  480. var checkboxes = hosts.mapProperty('checkboxes').reduce(function (cItem, pItem) {
  481. return cItem.concat(pItem);
  482. });
  483. var selectedCount = checkboxes.filterProperty('component', header.get('name')).filterProperty('checked').length;
  484. if (header.get('name') == 'CLIENT') {
  485. var clientsMinCount = 0;
  486. var serviceNames = this.get('installedServiceNames').concat(this.get('content.selectedServiceNames'));
  487. // find max value for `minToInstall` property
  488. serviceNames.forEach(function (serviceName) {
  489. App.StackServiceComponent.find().filterProperty('stackService.serviceName', serviceName).filterProperty('isClient')
  490. .mapProperty('minToInstall').forEach(function (ctMinCount) {
  491. clientsMinCount = ctMinCount > clientsMinCount ? ctMinCount : clientsMinCount;
  492. });
  493. });
  494. if (selectedCount < clientsMinCount) {
  495. isError = true;
  496. var requiredQuantity = (clientsMinCount > hosts.length ? hosts.length : clientsMinCount) - selectedCount;
  497. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, Em.I18n.t('common.client')));
  498. }
  499. } else {
  500. var stackComponent = App.StackServiceComponent.find().findProperty('componentName', header.get('name'));
  501. if (selectedCount < stackComponent.get('minToInstall')) {
  502. isError = true;
  503. var requiredQuantity = (stackComponent.get('minToInstall') > hosts.length ? hosts.length : stackComponent.get('minToInstall')) - selectedCount;
  504. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, stackComponent.get('displayName')));
  505. }
  506. }
  507. }, this);
  508. if (componentsToInstall.length) {
  509. this.set('errorMessage', Em.I18n.t('installer.step6.error.mustSelectComponents').format(componentsToInstall.join(', ')));
  510. }
  511. return !isError;
  512. }
  513. });