step6_controller.js 18 KB

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