step4_controller.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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 stringUtils = require('utils/string_utils');
  20. App.WizardStep4Controller = Em.ArrayController.extend({
  21. name: 'wizardStep4Controller',
  22. /**
  23. * List of Services
  24. * @type {Object[]}
  25. */
  26. content: [],
  27. /**
  28. * Check / Uncheck 'Select All' checkbox with one argument; Check / Uncheck all other checkboxes with more arguments
  29. * @type {bool}
  30. */
  31. isAllChecked: function(key, value) {
  32. if (arguments.length > 1) {
  33. this.filterProperty('isInstalled', false).setEach('isSelected', value);
  34. return value;
  35. } else {
  36. return this.filterProperty('isInstalled', false).
  37. filterProperty('isHiddenOnSelectServicePage', false).
  38. everyProperty('isSelected', true);
  39. }
  40. }.property('@each.isSelected'),
  41. /**
  42. * Is Submit button disabled
  43. * @type {bool}
  44. */
  45. isSubmitDisabled: function () {
  46. return this.filterProperty('isSelected', true).filterProperty('isInstalled', false).length === 0;
  47. }.property("@each.isSelected"),
  48. /**
  49. * List of validation errors. Look to #createError method for information
  50. * regarding object structure.
  51. *
  52. * @type {Object[]}
  53. */
  54. errorStack: [],
  55. /**
  56. * Drop errorStack content on selected state changes.
  57. **/
  58. clearErrors: function() {
  59. if (!this.get('errorStack').someProperty('isAccepted', false)) {
  60. this.set('errorStack', []);
  61. }
  62. }.observes('@each.isSelected'),
  63. /**
  64. * Check if multiple distributed file systems were selected
  65. * @return {bool}
  66. * @method multipleDFSs
  67. */
  68. multipleDFSs: function () {
  69. var dfsServices = this.filterProperty('isDFS',true).filterProperty('isSelected',true);
  70. return dfsServices.length > 1;
  71. },
  72. /**
  73. * Check whether user selected Ambari Metrics service to install and go to next step
  74. * @param callback {function}
  75. * @method ambariMetricsValidation
  76. */
  77. ambariMetricsValidation: function (callback) {
  78. var ambariMetricsService = this.findProperty('serviceName', 'AMBARI_METRICS');
  79. if (ambariMetricsService) {
  80. if(!ambariMetricsService.get('isSelected')) {
  81. this.addValidationError({
  82. id: 'ambariMetricsCheck',
  83. type: 'WARNING',
  84. callback: this.ambariMetricsCheckPopup,
  85. callbackParams: [callback]
  86. });
  87. }
  88. else {
  89. //metrics is selected, remove the metrics error from errorObject array
  90. var metricsError = this.get('errorStack').filterProperty('id',"ambariMetricsCheck");
  91. if(metricsError)
  92. {
  93. this.get('errorStack').removeObject(metricsError[0]);
  94. }
  95. }
  96. }
  97. },
  98. /**
  99. * Check whether Ranger is selected and show installation requirements if yes
  100. * @param {function} callback
  101. * @method rangerValidation
  102. */
  103. rangerValidation: function (callback) {
  104. var rangerService = this.findProperty('serviceName', 'RANGER');
  105. if (rangerService && !rangerService.get('isInstalled')) {
  106. if(rangerService.get('isSelected')) {
  107. this.addValidationError({
  108. id: 'rangerRequirements',
  109. type: 'WARNING',
  110. callback: this.rangerRequirementsPopup,
  111. callbackParams: [callback]
  112. });
  113. }
  114. else {
  115. //Ranger is selected, remove the Ranger error from errorObject array
  116. var rangerError = this.get('errorStack').filterProperty('id',"rangerRequirements");
  117. if(rangerError)
  118. {
  119. this.get('errorStack').removeObject(rangerError[0]);
  120. }
  121. }
  122. }
  123. },
  124. /**
  125. * Warn user if he tries to install Spark with HDP 2.2
  126. * @param {function} callback
  127. * @method sparkValidation
  128. */
  129. sparkValidation: function (callback) {
  130. var sparkService = this.findProperty('serviceName', 'SPARK');
  131. if (sparkService && !sparkService.get('isInstalled') &&
  132. App.get('currentStackName') == 'HDP' && App.get('currentStackVersionNumber') == '2.2') {
  133. if(sparkService.get('isSelected')) {
  134. this.addValidationError({
  135. id: 'sparkWarning',
  136. type: 'WARNING',
  137. callback: this.sparkWarningPopup,
  138. callbackParams: [callback]
  139. });
  140. }
  141. else {
  142. //Spark is selected, remove the Spark error from errorObject array
  143. var sparkError = this.get('errorStack').filterProperty('id',"sparkWarning");
  144. if(sparkError)
  145. {
  146. this.get('errorStack').removeObject(sparkError[0]);
  147. }
  148. }
  149. }
  150. },
  151. /**
  152. * Onclick handler for <code>Next</code> button.
  153. * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
  154. * @method submit
  155. */
  156. submit: function () {
  157. if(App.router.nextBtnClickInProgress){
  158. return;
  159. }
  160. if (!this.get('isSubmitDisabled')) {
  161. this.unSelectServices();
  162. this.setGroupedServices();
  163. if (this.validate()) {
  164. App.router.nextBtnClickInProgress = true;
  165. this.set('errorStack', []);
  166. App.router.send('next');
  167. }
  168. }
  169. },
  170. /**
  171. * Set isSelected based on property doNotShowAndInstall
  172. */
  173. unSelectServices: function () {
  174. this.filterProperty('isSelected',true).filterProperty('doNotShowAndInstall', true).setEach('isSelected', false);
  175. },
  176. /**
  177. * Check if validation passed:
  178. * - required file system services selected
  179. * - dependencies between services
  180. * - monitoring services selected (not required)
  181. *
  182. * @return {Boolean}
  183. * @method validate
  184. **/
  185. validate: function () {
  186. var result;
  187. var self = this;
  188. // callback function to reset `isAccepted` needs to be called everytime when a popup from errorStack is dismissed/proceed by user action
  189. var callback = function (id) {
  190. var check = self.get('errorStack').findProperty('id', id);
  191. if (check) {
  192. check.isAccepted = true;
  193. }
  194. };
  195. this.serviceDependencyValidation(callback);
  196. this.fileSystemServiceValidation(callback);
  197. if (this.get('wizardController.name') == 'installerController') {
  198. this.ambariMetricsValidation(callback);
  199. }
  200. this.rangerValidation(callback);
  201. this.sparkValidation(callback);
  202. if (!!this.get('errorStack').filterProperty('isShown', false).length) {
  203. var firstError = this.get('errorStack').findProperty('isShown', false);
  204. this.showError(firstError);
  205. result = false;
  206. } else {
  207. result = true;
  208. }
  209. return result;
  210. },
  211. /**
  212. * Create error and push it to stack.
  213. *
  214. * @param {Object} errorObject - look to #createError
  215. * @return {Boolean}
  216. * @method addValidationError
  217. **/
  218. addValidationError: function (errorObject) {
  219. if (!this.get('errorStack').someProperty('id', errorObject.id)) {
  220. this.get('errorStack').push(this.createError(errorObject));
  221. return true;
  222. } else {
  223. return false;
  224. }
  225. },
  226. /**
  227. * Show current error by passed error object.
  228. *
  229. * @param {Object} errorObject
  230. * @method showError
  231. **/
  232. showError: function (errorObject) {
  233. return errorObject.callback.apply(errorObject.callbackContext, errorObject.callbackParams.concat(errorObject.id));
  234. },
  235. /**
  236. * Default primary button("Ok") callback for warning popups.
  237. * Change isShown state for last shown error.
  238. * Call #submit() method.
  239. *
  240. * @param {function} callback
  241. * @param {string} id
  242. * @method onPrimaryPopupCallback
  243. **/
  244. onPrimaryPopupCallback: function(callback, id) {
  245. var firstError = this.get('errorStack').findProperty('isShown', false);
  246. if (firstError) {
  247. firstError.isShown = true;
  248. }
  249. if (callback) {
  250. callback(id);
  251. }
  252. this.submit();
  253. },
  254. /**
  255. * Create error object with passed options.
  256. * Available options:
  257. * id - {String}
  258. * type - {String}
  259. * isShowed - {Boolean}
  260. * callback - {Function}
  261. * callbackContext
  262. * callbackParams - {Array}
  263. *
  264. * @param {Object} opt
  265. * @return {Object}
  266. * @method createError
  267. **/
  268. createError: function(opt) {
  269. var options = {
  270. // {String} error identifier
  271. id: '',
  272. // {String} type of error CRITICAL|WARNING
  273. type: 'CRITICAL',
  274. // {Boolean} error was shown
  275. isShown: false,
  276. // {Boolean} error was accepted by user
  277. isAccepted: false,
  278. // {Function} callback to execute
  279. callback: null,
  280. // context which execute from
  281. callbackContext: this,
  282. // {Array} params applied to callback
  283. callbackParams: []
  284. };
  285. $.extend(options, opt);
  286. return options;
  287. },
  288. /**
  289. * Checks if a filesystem is present in the Stack
  290. *
  291. * @method isDFSStack
  292. */
  293. isDFSStack: function () {
  294. var bDFSStack = false;
  295. var dfsServices = ['HDFS', 'GLUSTERFS'];
  296. var availableServices = this.filterProperty('isInstalled',false);
  297. availableServices.forEach(function(service){
  298. if (dfsServices.contains(service.get('serviceName')) || service.get('serviceType') == 'HCFS' ) {
  299. bDFSStack=true;
  300. }
  301. },this);
  302. return bDFSStack;
  303. },
  304. /**
  305. * Checks if a filesystem is selected and only one filesystem is selected
  306. * @param {function} callback
  307. * @method isFileSystemCheckFailed
  308. */
  309. fileSystemServiceValidation: function(callback) {
  310. if(this.isDFSStack()){
  311. var primaryDFS = this.findProperty('isPrimaryDFS',true);
  312. if (primaryDFS) {
  313. var primaryDfsDisplayName = primaryDFS.get('displayNameOnSelectServicePage');
  314. var primaryDfsServiceName = primaryDFS.get('serviceName');
  315. if (this.multipleDFSs()) {
  316. var dfsServices = this.filterProperty('isDFS',true).filterProperty('isSelected',true).mapProperty('serviceName');
  317. var services = dfsServices.map(function (item){
  318. return {
  319. serviceName: item,
  320. selected: item === primaryDfsServiceName
  321. };
  322. });
  323. this.addValidationError({
  324. id: 'multipleDFS',
  325. callback: this.needToAddServicePopup,
  326. callbackParams: [services, 'multipleDFS', primaryDfsDisplayName, callback]
  327. });
  328. }
  329. else
  330. {
  331. //if multiple DFS are not selected, remove the related error from the error array
  332. var fsError = this.get('errorStack').filterProperty('id',"multipleDFS");
  333. if(fsError)
  334. {
  335. this.get('errorStack').removeObject(fsError[0]);
  336. }
  337. }
  338. }
  339. }
  340. },
  341. /**
  342. * Checks if a dependent service is selected without selecting the main service.
  343. * @param {function} callback
  344. * @method serviceDependencyValidation
  345. */
  346. serviceDependencyValidation: function(callback) {
  347. var selectedServices = this.filterProperty('isSelected',true);
  348. var missingDependencies = [];
  349. var missingDependenciesDisplayName = [];
  350. selectedServices.forEach(function(service){
  351. var requiredServices = service.get('requiredServices');
  352. if (!!requiredServices && requiredServices.length) {
  353. requiredServices.forEach(function(_requiredService){
  354. var requiredService = this.findProperty('serviceName', _requiredService);
  355. if (requiredService) {
  356. if(requiredService.get('isSelected') === false)
  357. {
  358. if(missingDependencies.indexOf(_requiredService) == -1 ) {
  359. missingDependencies.push(_requiredService);
  360. missingDependenciesDisplayName.push(requiredService.get('displayNameOnSelectServicePage'));
  361. }
  362. }
  363. else
  364. {
  365. //required service is selected, remove the service error from errorObject array
  366. var serviceName = requiredService.get('serviceName');
  367. var serviceError = this.get('errorStack').filterProperty('id',"serviceCheck_"+serviceName);
  368. if(serviceError)
  369. {
  370. this.get('errorStack').removeObject(serviceError[0]);
  371. }
  372. }
  373. }
  374. },this);
  375. }
  376. },this);
  377. //create a copy of the errorStack, reset it
  378. //and add the dependencies in the correct order
  379. var errorStackCopy = this.get('errorStack');
  380. this.set('errorStack', []);
  381. if (missingDependencies.length > 0) {
  382. for(var i = 0; i < missingDependencies.length; i++) {
  383. this.addValidationError({
  384. id: 'serviceCheck_' + missingDependencies[i],
  385. callback: this.needToAddServicePopup,
  386. callbackParams: [{serviceName: missingDependencies[i], selected: true}, 'serviceCheck', missingDependenciesDisplayName[i], callback]
  387. });
  388. }
  389. }
  390. //iterate through the errorStackCopy array and add to errorStack array, the error objects that have no matching entry in the errorStack
  391. //and that are not related to serviceChecks since serviceCheck errors have already been added when iterating through the missing dependencies list
  392. //Only add Ranger, Ambari Metrics, Spark and file system service validation errors if they exist in the errorStackCopy array
  393. var ctr = 0;
  394. while(ctr < errorStackCopy.length) {
  395. //no matching entry in errorStack array
  396. if (!this.get('errorStack').someProperty('id', errorStackCopy[ctr].id)) {
  397. //not serviceCheck error
  398. if(!errorStackCopy[ctr].id.startsWith('serviceCheck_')) {
  399. this.get('errorStack').push(this.createError(errorStackCopy[ctr]));
  400. }
  401. }
  402. ctr++;
  403. }
  404. },
  405. /**
  406. * Select co hosted services which not showed on UI.
  407. *
  408. * @method setGroupedServices
  409. **/
  410. setGroupedServices: function() {
  411. this.forEach(function(service){
  412. var coSelectedServices = service.get('coSelectedServices');
  413. coSelectedServices.forEach(function(groupedServiceName) {
  414. var groupedService = this.findProperty('serviceName', groupedServiceName);
  415. if (groupedService.get('isSelected') !== service.get('isSelected')) {
  416. groupedService.set('isSelected',service.get('isSelected'));
  417. }
  418. },this);
  419. },this);
  420. },
  421. /**
  422. * Select/deselect services
  423. * @param services array of objects
  424. * <code>
  425. * [
  426. * {
  427. * service: 'HDFS',
  428. * selected: true
  429. * },
  430. * ....
  431. * ]
  432. * </code>
  433. * @param {string} i18nSuffix
  434. * @param {string} serviceName
  435. * @param {function} callback
  436. * @param {string} id
  437. * @return {App.ModalPopup}
  438. * @method needToAddServicePopup
  439. */
  440. needToAddServicePopup: function (services, i18nSuffix, serviceName, callback, id) {
  441. if (!(services instanceof Array)) {
  442. services = [services];
  443. }
  444. var self = this;
  445. return App.ModalPopup.show({
  446. header: Em.I18n.t('installer.step4.' + i18nSuffix + '.popup.header').format(serviceName),
  447. body: Em.I18n.t('installer.step4.' + i18nSuffix + '.popup.body').format(serviceName),
  448. onPrimary: function () {
  449. services.forEach(function (service) {
  450. self.findProperty('serviceName', service.serviceName).set('isSelected', service.selected);
  451. });
  452. self.onPrimaryPopupCallback(callback, id);
  453. this.hide();
  454. },
  455. onSecondary: function () {
  456. if (callback) {
  457. callback(id);
  458. }
  459. this._super();
  460. },
  461. onClose: function () {
  462. if (callback) {
  463. callback(id);
  464. }
  465. this._super();
  466. }
  467. });
  468. },
  469. /**
  470. * Show popup with info about not selected Ambari Metrics service
  471. * @param {function} callback
  472. * @param {string} id
  473. * @return {App.ModalPopup}
  474. * @method ambariMetricsCheckPopup
  475. */
  476. ambariMetricsCheckPopup: function (callback, id) {
  477. var self = this;
  478. return App.ModalPopup.show({
  479. header: Em.I18n.t('installer.step4.ambariMetricsCheck.popup.header'),
  480. body: Em.I18n.t('installer.step4.ambariMetricsCheck.popup.body'),
  481. primary: Em.I18n.t('common.proceedAnyway'),
  482. onPrimary: function () {
  483. self.onPrimaryPopupCallback(callback);
  484. this.hide();
  485. },
  486. onSecondary: function () {
  487. if (callback) {
  488. callback(id);
  489. }
  490. this._super();
  491. },
  492. onClose: function () {
  493. if (callback) {
  494. callback(id);
  495. }
  496. this._super();
  497. }
  498. });
  499. },
  500. /**
  501. * Show popup with installation requirements for Ranger service
  502. * @param {function} callback
  503. * @param {string} id
  504. * @return {App.ModalPopup}
  505. * @method rangerRequirementsPopup
  506. */
  507. rangerRequirementsPopup: function (callback, id) {
  508. var self = this;
  509. return App.ModalPopup.show({
  510. header: Em.I18n.t('installer.step4.rangerRequirements.popup.header'),
  511. bodyClass: Em.View.extend({
  512. templateName: require('templates/wizard/step4/step4_ranger_requirements_popup')
  513. }),
  514. primary: Em.I18n.t('common.proceed'),
  515. isChecked: false,
  516. disablePrimary: function () {
  517. return !this.get('isChecked');
  518. }.property('isChecked'),
  519. onPrimary: function () {
  520. self.onPrimaryPopupCallback(callback);
  521. this.hide();
  522. },
  523. onSecondary: function () {
  524. if (callback) {
  525. callback(id);
  526. }
  527. this._super();
  528. },
  529. onClose: function () {
  530. if (callback) {
  531. callback(id);
  532. }
  533. this._super();
  534. }
  535. });
  536. },
  537. /**
  538. * Show popup with Spark installation warning
  539. * @param {function} callback
  540. * @param {string} id
  541. * @return {App.ModalPopup}
  542. * @method sparkWarningPopup
  543. */
  544. sparkWarningPopup: function (callback, id) {
  545. var self = this;
  546. return App.ModalPopup.show({
  547. header: Em.I18n.t('common.warning'),
  548. body: Em.I18n.t('installer.step4.sparkWarning.popup.body'),
  549. primary: Em.I18n.t('common.proceed'),
  550. onPrimary: function () {
  551. self.onPrimaryPopupCallback(callback);
  552. this.hide();
  553. },
  554. onSecondary: function () {
  555. if (callback) {
  556. callback(id);
  557. }
  558. this._super();
  559. },
  560. onClose: function () {
  561. if (callback) {
  562. callback(id);
  563. }
  564. this._super();
  565. }
  566. });
  567. }
  568. });