step4_controller.js 15 KB

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