step4_controller.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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 && !ambariMetricsService.get('isSelected')) {
  80. this.addValidationError({
  81. id: 'ambariMetricsCheck',
  82. type: 'WARNING',
  83. callback: this.ambariMetricsCheckPopup,
  84. callbackParams: [callback]
  85. });
  86. }
  87. },
  88. /**
  89. * Check whether Ranger is selected and show installation requirements if yes
  90. * @param {function} callback
  91. * @method rangerValidation
  92. */
  93. rangerValidation: function (callback) {
  94. var rangerService = this.findProperty('serviceName', 'RANGER');
  95. if (rangerService && rangerService.get('isSelected') && !rangerService.get('isInstalled')) {
  96. this.addValidationError({
  97. id: 'rangerRequirements',
  98. type: 'WARNING',
  99. callback: this.rangerRequirementsPopup,
  100. callbackParams: [callback]
  101. });
  102. }
  103. },
  104. /**
  105. * Warn user if he tries to install Spark with HDP 2.2
  106. * @param {function} callback
  107. * @method sparkValidation
  108. */
  109. sparkValidation: function (callback) {
  110. var sparkService = this.findProperty('serviceName', 'SPARK');
  111. if (sparkService && sparkService.get('isSelected') && !sparkService.get('isInstalled') &&
  112. App.get('currentStackName') == 'HDP' && App.get('currentStackVersionNumber') == '2.2') {
  113. this.addValidationError({
  114. id: 'sparkWarning',
  115. type: 'WARNING',
  116. callback: this.sparkWarningPopup,
  117. callbackParams: [callback]
  118. });
  119. }
  120. },
  121. /**
  122. * Onclick handler for <code>Next</code> button.
  123. * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
  124. * @method submit
  125. */
  126. submit: function () {
  127. if(App.router.nextBtnClickInProgress){
  128. return;
  129. }
  130. if (!this.get('isSubmitDisabled')) {
  131. this.unSelectServices();
  132. this.setGroupedServices();
  133. if (this.validate()) {
  134. App.router.nextBtnClickInProgress = true;
  135. this.set('errorStack', []);
  136. App.router.send('next');
  137. }
  138. }
  139. },
  140. /**
  141. * Set isSelected based on property doNotShowAndInstall
  142. */
  143. unSelectServices: function () {
  144. this.filterProperty('isSelected',true).filterProperty('doNotShowAndInstall', true).setEach('isSelected', false);
  145. },
  146. /**
  147. * Check if validation passed:
  148. * - required file system services selected
  149. * - dependencies between services
  150. * - monitoring services selected (not required)
  151. *
  152. * @return {Boolean}
  153. * @method validate
  154. **/
  155. validate: function () {
  156. var result;
  157. var self = this;
  158. // callback function to reset `isAccepted` needs to be called everytime when a popup from errorStack is dismissed/proceed by user action
  159. var callback = function (id) {
  160. var check = self.get('errorStack').findProperty('id', id);
  161. if (check) {
  162. check.isAccepted = true;
  163. }
  164. };
  165. this.serviceDependencyValidation(callback);
  166. this.fileSystemServiceValidation(callback);
  167. if (this.get('wizardController.name') == 'installerController') {
  168. this.ambariMetricsValidation(callback);
  169. }
  170. this.rangerValidation(callback);
  171. this.sparkValidation(callback);
  172. if (!!this.get('errorStack').filterProperty('isShown', false).length) {
  173. var firstError = this.get('errorStack').findProperty('isShown', false);
  174. this.showError(firstError);
  175. result = false;
  176. } else {
  177. result = true;
  178. }
  179. return result;
  180. },
  181. /**
  182. * Create error and push it to stack.
  183. *
  184. * @param {Object} errorObject - look to #createError
  185. * @return {Boolean}
  186. * @method addValidationError
  187. **/
  188. addValidationError: function (errorObject) {
  189. if (!this.get('errorStack').someProperty('id', errorObject.id)) {
  190. this.get('errorStack').push(this.createError(errorObject));
  191. return true;
  192. } else {
  193. return false;
  194. }
  195. },
  196. /**
  197. * Show current error by passed error object.
  198. *
  199. * @param {Object} errorObject
  200. * @method showError
  201. **/
  202. showError: function (errorObject) {
  203. return errorObject.callback.apply(errorObject.callbackContext, errorObject.callbackParams.concat(errorObject.id));
  204. },
  205. /**
  206. * Default primary button("Ok") callback for warning popups.
  207. * Change isShown state for last shown error.
  208. * Call #submit() method.
  209. *
  210. * @param {function} callback
  211. * @param {string} id
  212. * @method onPrimaryPopupCallback
  213. **/
  214. onPrimaryPopupCallback: function(callback, id) {
  215. var firstError = this.get('errorStack').findProperty('isShown', false);
  216. if (firstError) {
  217. firstError.isShown = true;
  218. }
  219. if (callback) {
  220. callback(id);
  221. }
  222. this.submit();
  223. },
  224. /**
  225. * Create error object with passed options.
  226. * Available options:
  227. * id - {String}
  228. * type - {String}
  229. * isShowed - {Boolean}
  230. * callback - {Function}
  231. * callbackContext
  232. * callbackParams - {Array}
  233. *
  234. * @param {Object} opt
  235. * @return {Object}
  236. * @method createError
  237. **/
  238. createError: function(opt) {
  239. var options = {
  240. // {String} error identifier
  241. id: '',
  242. // {String} type of error CRITICAL|WARNING
  243. type: 'CRITICAL',
  244. // {Boolean} error was shown
  245. isShown: false,
  246. // {Boolean} error was accepted by user
  247. isAccepted: false,
  248. // {Function} callback to execute
  249. callback: null,
  250. // context which execute from
  251. callbackContext: this,
  252. // {Array} params applied to callback
  253. callbackParams: []
  254. };
  255. $.extend(options, opt);
  256. return options;
  257. },
  258. /**
  259. * Checks if a filesystem is present in the Stack
  260. *
  261. * @method isDFSStack
  262. */
  263. isDFSStack: function () {
  264. var bDFSStack = false;
  265. var dfsServices = ['HDFS', 'GLUSTERFS'];
  266. var availableServices = this.filterProperty('isInstalled',false);
  267. availableServices.forEach(function(service){
  268. if (dfsServices.contains(service.get('serviceName')) || service.get('serviceType') == 'HCFS' ) {
  269. bDFSStack=true;
  270. }
  271. },this);
  272. return bDFSStack;
  273. },
  274. /**
  275. * Checks if a filesystem is selected and only one filesystem is selected
  276. * @param {function} callback
  277. * @method isFileSystemCheckFailed
  278. */
  279. fileSystemServiceValidation: function(callback) {
  280. if(this.isDFSStack()){
  281. var primaryDFS = this.findProperty('isPrimaryDFS',true);
  282. if (primaryDFS) {
  283. var primaryDfsDisplayName = primaryDFS.get('displayNameOnSelectServicePage');
  284. var primaryDfsServiceName = primaryDFS.get('serviceName');
  285. if (this.multipleDFSs()) {
  286. var dfsServices = this.filterProperty('isDFS',true).filterProperty('isSelected',true).mapProperty('serviceName');
  287. var services = dfsServices.map(function (item){
  288. return {
  289. serviceName: item,
  290. selected: item === primaryDfsServiceName
  291. };
  292. });
  293. this.addValidationError({
  294. id: 'multipleDFS',
  295. callback: this.needToAddServicePopup,
  296. callbackParams: [services, 'multipleDFS', primaryDfsDisplayName, callback]
  297. });
  298. }
  299. }
  300. }
  301. },
  302. /**
  303. * Checks if a dependent service is selected without selecting the main service.
  304. * @param {function} callback
  305. * @method serviceDependencyValidation
  306. */
  307. serviceDependencyValidation: function(callback) {
  308. var selectedServices = this.filterProperty('isSelected',true);
  309. var missingDependencies = [];
  310. var missingDependenciesDisplayName = [];
  311. selectedServices.forEach(function(service){
  312. var requiredServices = service.get('requiredServices');
  313. if (!!requiredServices && requiredServices.length) {
  314. requiredServices.forEach(function(_requiredService){
  315. var requiredService = this.findProperty('serviceName', _requiredService);
  316. if (requiredService && requiredService.get('isSelected') === false) {
  317. if(missingDependencies.indexOf(_requiredService) == -1 ) {
  318. missingDependencies.push(_requiredService);
  319. missingDependenciesDisplayName.push(requiredService.get('displayNameOnSelectServicePage'));
  320. }
  321. }
  322. },this);
  323. }
  324. },this);
  325. if (missingDependencies.length > 0) {
  326. for(var i = 0; i < missingDependencies.length; i++) {
  327. this.addValidationError({
  328. id: 'serviceCheck_' + missingDependencies[i],
  329. callback: this.needToAddServicePopup,
  330. callbackParams: [{serviceName: missingDependencies[i], selected: true}, 'serviceCheck', missingDependenciesDisplayName[i], callback]
  331. });
  332. }
  333. }
  334. },
  335. /**
  336. * Select co hosted services which not showed on UI.
  337. *
  338. * @method setGroupedServices
  339. **/
  340. setGroupedServices: function() {
  341. this.forEach(function(service){
  342. var coSelectedServices = service.get('coSelectedServices');
  343. coSelectedServices.forEach(function(groupedServiceName) {
  344. var groupedService = this.findProperty('serviceName', groupedServiceName);
  345. if (groupedService.get('isSelected') !== service.get('isSelected')) {
  346. groupedService.set('isSelected',service.get('isSelected'));
  347. }
  348. },this);
  349. },this);
  350. },
  351. /**
  352. * Select/deselect services
  353. * @param services array of objects
  354. * <code>
  355. * [
  356. * {
  357. * service: 'HDFS',
  358. * selected: true
  359. * },
  360. * ....
  361. * ]
  362. * </code>
  363. * @param {string} i18nSuffix
  364. * @param {string} serviceName
  365. * @param {function} callback
  366. * @param {string} id
  367. * @return {App.ModalPopup}
  368. * @method needToAddServicePopup
  369. */
  370. needToAddServicePopup: function (services, i18nSuffix, serviceName, callback, id) {
  371. if (!(services instanceof Array)) {
  372. services = [services];
  373. }
  374. var self = this;
  375. return App.ModalPopup.show({
  376. header: Em.I18n.t('installer.step4.' + i18nSuffix + '.popup.header').format(serviceName),
  377. body: Em.I18n.t('installer.step4.' + i18nSuffix + '.popup.body').format(serviceName),
  378. onPrimary: function () {
  379. services.forEach(function (service) {
  380. self.findProperty('serviceName', service.serviceName).set('isSelected', service.selected);
  381. });
  382. self.onPrimaryPopupCallback(callback, id);
  383. this.hide();
  384. },
  385. onSecondary: function () {
  386. if (callback) {
  387. callback(id);
  388. }
  389. this._super();
  390. },
  391. onClose: function () {
  392. if (callback) {
  393. callback(id);
  394. }
  395. this._super();
  396. }
  397. });
  398. },
  399. /**
  400. * Show popup with info about not selected Ambari Metrics service
  401. * @param {function} callback
  402. * @param {string} id
  403. * @return {App.ModalPopup}
  404. * @method ambariMetricsCheckPopup
  405. */
  406. ambariMetricsCheckPopup: function (callback, id) {
  407. var self = this;
  408. return App.ModalPopup.show({
  409. header: Em.I18n.t('installer.step4.ambariMetricsCheck.popup.header'),
  410. body: Em.I18n.t('installer.step4.ambariMetricsCheck.popup.body'),
  411. primary: Em.I18n.t('common.proceedAnyway'),
  412. onPrimary: function () {
  413. self.onPrimaryPopupCallback(callback);
  414. this.hide();
  415. },
  416. onSecondary: function () {
  417. if (callback) {
  418. callback(id);
  419. }
  420. this._super();
  421. },
  422. onClose: function () {
  423. if (callback) {
  424. callback(id);
  425. }
  426. this._super();
  427. }
  428. });
  429. },
  430. /**
  431. * Show popup with installation requirements for Ranger service
  432. * @param {function} callback
  433. * @param {string} id
  434. * @return {App.ModalPopup}
  435. * @method rangerRequirementsPopup
  436. */
  437. rangerRequirementsPopup: function (callback, id) {
  438. var self = this;
  439. return App.ModalPopup.show({
  440. header: Em.I18n.t('installer.step4.rangerRequirements.popup.header'),
  441. bodyClass: Em.View.extend({
  442. templateName: require('templates/wizard/step4/step4_ranger_requirements_popup')
  443. }),
  444. primary: Em.I18n.t('common.proceed'),
  445. isChecked: false,
  446. disablePrimary: function () {
  447. return !this.get('isChecked');
  448. }.property('isChecked'),
  449. onPrimary: function () {
  450. self.onPrimaryPopupCallback(callback);
  451. this.hide();
  452. },
  453. onSecondary: function () {
  454. if (callback) {
  455. callback(id);
  456. }
  457. this._super();
  458. },
  459. onClose: function () {
  460. if (callback) {
  461. callback(id);
  462. }
  463. this._super();
  464. }
  465. });
  466. },
  467. /**
  468. * Show popup with Spark installation warning
  469. * @param {function} callback
  470. * @param {string} id
  471. * @return {App.ModalPopup}
  472. * @method sparkWarningPopup
  473. */
  474. sparkWarningPopup: function (callback, id) {
  475. var self = this;
  476. return App.ModalPopup.show({
  477. header: Em.I18n.t('common.warning'),
  478. body: Em.I18n.t('installer.step4.sparkWarning.popup.body'),
  479. primary: Em.I18n.t('common.proceed'),
  480. onPrimary: function () {
  481. self.onPrimaryPopupCallback(callback);
  482. this.hide();
  483. },
  484. onSecondary: function () {
  485. if (callback) {
  486. callback(id);
  487. }
  488. this._super();
  489. },
  490. onClose: function () {
  491. if (callback) {
  492. callback(id);
  493. }
  494. this._super();
  495. }
  496. });
  497. }
  498. });