step6_controller.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. * Return whether service was selected or not
  168. * @param {string} name serviceName
  169. * @return {bool}
  170. * @method isServiceSelected
  171. */
  172. isServiceSelected: function (name) {
  173. var serviceName = this.get('content.services').findProperty('serviceName', name);
  174. if (!serviceName) {
  175. return !!serviceName;
  176. }
  177. return serviceName.get('isSelected') || serviceName.get('isInstalled');
  178. },
  179. /**
  180. * Checkbox check callback
  181. * Verify if all/none checkboxes for current component are checked
  182. * @param {String} component
  183. * @method checkCallback
  184. */
  185. checkCallback: function (component) {
  186. var header = this.get('headers').findProperty('name', component);
  187. if (header) {
  188. var hosts = this.get('hosts');
  189. var allTrue = true;
  190. var allFalse = true;
  191. hosts.forEach(function (host) {
  192. host.get('checkboxes').forEach(function (checkbox) {
  193. if (checkbox.get('component') === component && !checkbox.get('isInstalled')) {
  194. allTrue = allTrue && checkbox.get('checked');
  195. allFalse = allFalse && !checkbox.get('checked');
  196. }
  197. });
  198. });
  199. header.set('allChecked', allTrue);
  200. header.set('noChecked', allFalse);
  201. }
  202. this.clearError();
  203. },
  204. /**
  205. * Init step6 data
  206. * @method loadStep
  207. */
  208. loadStep: function () {
  209. var self = this;
  210. console.log("WizardStep6Controller: Loading step6: Assign Slaves");
  211. this.clearStep();
  212. var selectedServices = App.StackService.find().filterProperty('isSelected');
  213. var installedServices = App.StackService.find().filterProperty('isInstalled');
  214. var services;
  215. if (this.get('isInstallerWizard')) services = selectedServices;
  216. else if (this.get('isAddHostWizard')) services = installedServices;
  217. else if (this.get('isAddServiceWizard')) services = installedServices.concat(selectedServices);
  218. var headers = Em.A([]);
  219. services.forEach(function (stackService) {
  220. stackService.get('serviceComponents').forEach(function (serviceComponent) {
  221. if (serviceComponent.get('isShownOnInstallerSlaveClientPage')) {
  222. headers.pushObject(Em.Object.create({
  223. name: serviceComponent.get('componentName'),
  224. label: serviceComponent.get('displayName'),
  225. allChecked: false,
  226. isRequired: serviceComponent.get('isRequired'),
  227. noChecked: true
  228. }));
  229. }
  230. }, this);
  231. }, this);
  232. headers.pushObject(Em.Object.create({
  233. name: 'CLIENT',
  234. label: App.format.role('CLIENT'),
  235. allChecked: false,
  236. noChecked: true
  237. }));
  238. this.get('headers').pushObjects(headers);
  239. this.render();
  240. if (this.get('content.skipSlavesStep')) {
  241. App.router.send('next');
  242. }
  243. },
  244. /**
  245. * Get active host names
  246. * @return {string[]}
  247. * @method getHostNames
  248. */
  249. getHostNames: function () {
  250. var hostInfo = this.get('content.hosts');
  251. var hostNames = [];
  252. //flag identify whether get all hosts or only uninstalled(newly added) hosts
  253. var getUninstalledHosts = (this.get('content.controllerName') !== 'addServiceController');
  254. for (var index in hostInfo) {
  255. if (hostInfo.hasOwnProperty(index)) {
  256. if (hostInfo[index].bootStatus === 'REGISTERED') {
  257. if(!getUninstalledHosts || !hostInfo[index].isInstalled) {
  258. hostNames.push(hostInfo[index].name);
  259. }
  260. }
  261. }
  262. }
  263. return hostNames;
  264. },
  265. /**
  266. * Load all data needed for this module. Then it automatically renders in template
  267. * @method render
  268. */
  269. render: function () {
  270. var hostsObj = [],
  271. masterHosts = [],
  272. headers = this.get('headers'),
  273. masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq();
  274. this.getHostNames().forEach(function (_hostName) {
  275. var hasMaster = masterHostNames.contains(_hostName);
  276. var obj = Em.Object.create({
  277. hostName: _hostName,
  278. hasMaster: hasMaster,
  279. checkboxes: []
  280. });
  281. headers.forEach(function (header) {
  282. obj.checkboxes.pushObject(Em.Object.create({
  283. component: header.name,
  284. title: header.label,
  285. checked: false,
  286. isInstalled: false
  287. }));
  288. });
  289. if (hasMaster) {
  290. masterHosts.pushObject(obj)
  291. } else {
  292. hostsObj.pushObject(obj);
  293. }
  294. });
  295. //hosts with master components should be in the beginning of list
  296. hostsObj.unshift.apply(hostsObj, masterHosts);
  297. hostsObj = this.renderSlaves(hostsObj);
  298. this.set('hosts', hostsObj);
  299. headers.forEach(function (header) {
  300. this.checkCallback(header.get('name'));
  301. }, this);
  302. this.set('isLoaded', true);
  303. },
  304. /**
  305. * Set checked values for slaves checkboxes
  306. * @param {Array} hostsObj
  307. * @return {Array}
  308. * @method renderSlaves
  309. */
  310. renderSlaves: function (hostsObj) {
  311. var headers = this.get('headers');
  312. var slaveComponents = this.get('content.slaveComponentHosts');
  313. if (!slaveComponents) { // we are at this page for the first time
  314. if (!App.supports.serverRecommendValidate) {
  315. var client_is_set = false;
  316. hostsObj.forEach(function (host) {
  317. var checkboxes = host.get('checkboxes');
  318. checkboxes.setEach('checked', !host.hasMaster);
  319. checkboxes.setEach('isInstalled', false);
  320. checkboxes.findProperty('title', headers.findProperty('name', 'CLIENT').get('label')).set('checked', false);
  321. // First not Master should have Client (only first!)
  322. if (!client_is_set) {
  323. var dfs = App.StackService.find().findProperty('isPrimaryDFS');
  324. if (dfs.get('isSelected') || dfs.get('isInstalled')) {
  325. var checkboxServiceComponent = checkboxes.findProperty('title', headers.findProperty('name', dfs.get('serviceComponents').
  326. findProperty('isShownOnInstallerSlaveClientPage').get('componentName')).get('label'));
  327. if (checkboxServiceComponent && checkboxServiceComponent.get('checked')) {
  328. checkboxes.findProperty('title', headers.findProperty('name', 'CLIENT').get('label')).set('checked', true);
  329. client_is_set = true;
  330. }
  331. }
  332. }
  333. });
  334. if (this.get('isInstallerWizard') && hostsObj.everyProperty('hasMaster', true)) {
  335. var lastHost = hostsObj[hostsObj.length - 1];
  336. lastHost.get('checkboxes').setEach('checked', true);
  337. }
  338. } else {
  339. var recommendations = App.router.get('installerController.recommendations');
  340. // Get all host-component pairs from recommendations
  341. var componentHostPairs = recommendations.blueprint.host_groups.map(function(group) {
  342. return group.components.map(function(component) {
  343. return recommendations.blueprint_cluster_binding.host_groups.findProperty('name', group.name).hosts.map(function(host) {
  344. return { component: component.name, host: host.fqdn};
  345. });
  346. });
  347. });
  348. // Flatten results twice because of two map() call before
  349. componentHostPairs = [].concat.apply([], componentHostPairs);
  350. componentHostPairs = [].concat.apply([], componentHostPairs);
  351. var clientComponents = App.get('components.clients');
  352. hostsObj.forEach(function (host) {
  353. var checkboxes = host.get('checkboxes');
  354. checkboxes.forEach(function(checkbox) {
  355. var recommended = componentHostPairs.some(function(pair) {
  356. var componentMatch = pair.component === checkbox.component;
  357. if (checkbox.component === 'CLIENT' && !componentMatch) {
  358. componentMatch = clientComponents.contains(pair.component);
  359. }
  360. return pair.host === host.hostName && componentMatch;
  361. });
  362. checkbox.checked = recommended;
  363. });
  364. });
  365. }
  366. } else {
  367. this.get('headers').forEach(function (header) {
  368. var nodes = slaveComponents.findProperty('componentName', header.get('name'));
  369. if (nodes) {
  370. nodes.hosts.forEach(function (_node) {
  371. var node = hostsObj.findProperty('hostName', _node.hostName);
  372. if (node) {
  373. node.get('checkboxes').findProperty('title', header.get('label')).set('checked', true);
  374. node.get('checkboxes').findProperty('title', header.get('label')).set('isInstalled', _node.isInstalled);
  375. }
  376. });
  377. }
  378. });
  379. }
  380. return hostsObj;
  381. },
  382. /**
  383. * Select checkboxes which correspond to master components
  384. *
  385. * @param {Array} hostsObj
  386. * @return {Array}
  387. * @method selectMasterComponents
  388. */
  389. selectMasterComponents: function (hostsObj) {
  390. var masterComponentHosts = this.get('content.masterComponentHosts');
  391. console.log('Master components selected on:', masterComponentHosts.mapProperty('hostName').uniq().join(", "));
  392. if (masterComponentHosts) {
  393. masterComponentHosts.forEach(function (item) {
  394. var host = hostsObj.findProperty('hostName', item.hostName);
  395. if (host) {
  396. var checkbox = host.get('checkboxes').findProperty('component', item.component);
  397. if (checkbox) {
  398. checkbox.set('checked', true);
  399. }
  400. }
  401. });
  402. }
  403. return hostsObj;
  404. },
  405. /**
  406. * Return list of master components for specified <code>hostname</code>
  407. * @param {string} hostName
  408. * @return {string[]}
  409. * @method getMasterComponentsForHost
  410. */
  411. getMasterComponentsForHost: function (hostName) {
  412. return this.get('content.masterComponentHosts').filterProperty('hostName', hostName).mapProperty('component');
  413. },
  414. /**
  415. * Validate form. Return do we have errors or not
  416. * @return {bool}
  417. * @method validate
  418. */
  419. validate: function () {
  420. if (this.get('isAddHostWizard')) {
  421. return this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForHost'));
  422. }
  423. else {
  424. if (this.get('isInstallerWizard')) {
  425. return this.validateEachComponent() && this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForSlaveHost'));
  426. }
  427. else {
  428. if (this.get('isAddServiceWizard')) {
  429. return this.validateEachComponent();
  430. }
  431. return true;
  432. }
  433. }
  434. },
  435. /**
  436. * Validate all components for each host. Return do we have errors or not
  437. * @return {bool}
  438. * @method validateEachHost
  439. */
  440. validateEachHost: function (errorMsg) {
  441. var isError = false;
  442. var hosts = this.get('hosts');
  443. var headers = this.get('headers');
  444. for (var i = 0; i < hosts.length; i++) {
  445. if (this.get('isInstallerWizard') && this.get('content.masterComponentHosts').someProperty('hostName', hosts[i].hostName)) {
  446. continue;
  447. }
  448. var checkboxes = hosts[i].get('checkboxes');
  449. isError = false;
  450. headers.forEach(function (header) {
  451. isError = isError || checkboxes.findProperty('title', header.get('label')).checked;
  452. });
  453. isError = !isError;
  454. if (isError) {
  455. this.set('errorMessage', errorMsg);
  456. break;
  457. }
  458. }
  459. return !isError;
  460. },
  461. /**
  462. * Check for minimum required count of components to install.
  463. *
  464. * @return {bool}
  465. * @method validateEachComponent
  466. */
  467. validateEachComponent: function () {
  468. var isError = false;
  469. var hosts = this.get('hosts');
  470. var headers = this.get('headers');
  471. var componentsToInstall = [];
  472. headers.forEach(function (header) {
  473. var checkboxes = hosts.mapProperty('checkboxes').reduce(function(cItem, pItem) { return cItem.concat(pItem); });
  474. var selectedCount = checkboxes.filterProperty('component', header.get('name')).filterProperty('checked').length;
  475. if (header.get('name') == 'CLIENT') {
  476. var clientsMinCount = 0;
  477. var serviceNames = this.get('installedServiceNames').concat(this.get('content.selectedServiceNames'));
  478. // find max value for `minToInstall` property
  479. serviceNames.forEach(function(serviceName) {
  480. App.StackServiceComponent.find().filterProperty('stackService.serviceName', serviceName).filterProperty('isClient')
  481. .mapProperty('minToInstall').forEach(function(ctMinCount) { clientsMinCount = ctMinCount > clientsMinCount ? ctMinCount : clientsMinCount; });
  482. });
  483. if (selectedCount < clientsMinCount) {
  484. isError = true;
  485. var requiredQuantity = (clientsMinCount > hosts.length ? hosts.length : clientsMinCount) - selectedCount;
  486. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, Em.I18n.t('common.client')));
  487. }
  488. } else {
  489. var stackComponent = App.StackServiceComponent.find().findProperty('componentName', header.get('name'));
  490. if (selectedCount < stackComponent.get('minToInstall')) {
  491. isError = true;
  492. var requiredQuantity = (stackComponent.get('minToInstall') > hosts.length ? hosts.length : stackComponent.get('minToInstall')) - selectedCount;
  493. componentsToInstall.push(requiredQuantity + ' ' + stringUtils.pluralize(requiredQuantity, stackComponent.get('displayName')));
  494. }
  495. }
  496. }, this);
  497. if (componentsToInstall.length) {
  498. this.set('errorMessage', Em.I18n.t('installer.step6.error.mustSelectComponents').format(componentsToInstall.join(', ')));
  499. }
  500. return !isError;
  501. }
  502. });