wizardProgressPageController.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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. /**
  20. * Mixin for wizard controller for showing command progress on wizard pages
  21. * This should
  22. * @type {Ember.Mixin}
  23. */
  24. App.wizardProgressPageControllerMixin = Em.Mixin.create({
  25. controllerName: '',
  26. clusterDeployState: 'WIZARD_DEPLOY',
  27. status: 'IN_PROGRESS',
  28. tasks: [],
  29. commands: [],
  30. currentRequestIds: [], //todo: replace with using requestIds from tasks
  31. logs: [],
  32. currentTaskId: null,
  33. POLL_INTERVAL: 4000,
  34. isSubmitDisabled: true,
  35. isBackButtonDisabled: true,
  36. stages: [],
  37. currentPageRequestId: null,
  38. isSingleRequestPage: false,
  39. isCommandLevelRetry: function () {
  40. return !this.get('isSingleRequestPage');
  41. }.property('isSingleRequestPage'),
  42. showRetry: false,
  43. /**
  44. * Show whether tasks data was loaded
  45. * @type {Boolean}
  46. */
  47. isLoading: false,
  48. k: Em.K,
  49. /**
  50. * tasksMessagesPrefix should be overloaded by any controller including the mixin
  51. */
  52. tasksMessagesPrefix: '',
  53. loadStep: function () {
  54. this.clearStep();
  55. var self = this;
  56. if (!self.isSingleRequestPage) {
  57. this.initStep();
  58. } else {
  59. var requestIds = this.get('content.tasksRequestIds');
  60. var currentRequestId = requestIds && requestIds[0][0];
  61. if (!currentRequestId) {
  62. this.submitRequest();
  63. } else {
  64. self.set('currentPageRequestId', currentRequestId);
  65. self.doPollingForPageRequest();
  66. }
  67. }
  68. },
  69. initStep: function () {
  70. this.initializeTasks();
  71. if (!this.isSingleRequestPage) {
  72. this.loadTasks();
  73. }
  74. this.addObserver('tasks.@each.status', this, 'onTaskStatusChange');
  75. if (this.isSingleRequestPage) {
  76. var dfd = $.Deferred();
  77. var dfdObject = {
  78. deferred: dfd,
  79. isJqueryPromise: true
  80. };
  81. this.onTaskStatusChange(dfdObject);
  82. return dfd.promise();
  83. } else {
  84. this.onTaskStatusChange();
  85. }
  86. },
  87. clearStep: function () {
  88. this.removeObserver('tasks.@each.status', this, 'onTaskStatusChange');
  89. this.set('isSubmitDisabled', true);
  90. this.set('isBackButtonDisabled', true);
  91. this.set('tasks', []);
  92. this.set('currentRequestIds', []);
  93. this.set('isLoaded', false);
  94. },
  95. /**
  96. * Clear stages info for single page request.
  97. */
  98. clearStage: function() {
  99. this.setDBProperty('tasksRequestIds', null);
  100. this.setDBProperty('tasksStatuses', null);
  101. this.set('showRetry', false);
  102. this.set('content.tasksRequestIds', null);
  103. this.set('content.tasksStatuses', null);
  104. this.set('content.currentTaskId', null);
  105. this.get('stages').clear();
  106. },
  107. retry: function () {
  108. this.set('showRetry', false);
  109. this.get('tasks').setEach('status','PENDING');
  110. this.loadStep();
  111. },
  112. submitRequest: function () {
  113. return App.ajax.send({
  114. name: this.get('request.ajaxName'),
  115. data: this.get('request.ajaxData'),
  116. sender: this,
  117. success: 'submitRequestSuccess',
  118. kdcCancelHandler: 'failTaskOnKdcCheck'
  119. });
  120. },
  121. submitRequestSuccess: function(data, result, request) {
  122. if (data) {
  123. this.set('currentPageRequestId', data.Requests.id);
  124. this.doPollingForPageRequest();
  125. } else {
  126. //Step has been successfully completed
  127. if (request.status === 200) {
  128. this.set('status', 'COMPLETED');
  129. this.set('isSubmitDisabled', false);
  130. this.set('isLoaded', true);
  131. }
  132. }
  133. },
  134. failTaskOnKdcCheck: function() {
  135. App.router.send('back');
  136. },
  137. doPollingForPageRequest: function () {
  138. App.ajax.send({
  139. name: 'admin.poll.kerberize.cluster.request',
  140. sender: this,
  141. data: {
  142. requestId: this.get('currentPageRequestId')
  143. },
  144. success: 'initializeStages'
  145. });
  146. },
  147. initializeStages: function (data) {
  148. var self = this;
  149. var stages = [];
  150. this.set('logs', []);
  151. data.stages.forEach(function (_stage) {
  152. stages.pushObject(Em.Object.create(_stage.Stage));
  153. }, this);
  154. if (!this.get('stages').length) {
  155. this.get('stages').pushObjects(stages);
  156. this.initStep().done(function(){
  157. self.updatePageWithPolledData(data);
  158. });
  159. } else {
  160. this.updatePageWithPolledData(data);
  161. }
  162. },
  163. updatePageWithPolledData: function(data) {
  164. // If all tasks completed no need to update each task status.
  165. // Preferable to skip polling of data for completed tasks after page refresh.
  166. if (this.get('status') === 'COMPLETED') return;
  167. var self = this;
  168. var tasks = [];
  169. var currentPageRequestId = this.get('currentPageRequestId');
  170. var currentTaskId = this.get('currentTaskId');
  171. var currentTask = this.get('tasks').findProperty('id', currentTaskId);
  172. var currentStage = data.stages.findProperty('Stage.stage_id', currentTask.get('stageId'));
  173. var tasksInCurrentStage = currentStage.tasks;
  174. this.set('logs',tasksInCurrentStage);
  175. this.setRequestIds(this.get('currentTaskId'), [this.get('currentPageRequestId')]);
  176. if (!tasksInCurrentStage.someProperty('Tasks.status', 'PENDING') && !tasksInCurrentStage.someProperty('Tasks.status', 'QUEUED') && !tasksInCurrentStage.someProperty('Tasks.status', 'IN_PROGRESS')) {
  177. this.set('currentRequestIds', []);
  178. if (tasksInCurrentStage.someProperty('Tasks.status', 'FAILED') || tasksInCurrentStage.someProperty('Tasks.status', 'TIMEDOUT') || tasksInCurrentStage.someProperty('Tasks.status', 'ABORTED')) {
  179. this.setTaskStatus(currentTaskId, 'FAILED');
  180. } else {
  181. this.setTaskStatus(currentTaskId, 'COMPLETED');
  182. }
  183. } else {
  184. var completedActions = tasksInCurrentStage.filterProperty('Tasks.status', 'COMPLETED').length
  185. + tasksInCurrentStage.filterProperty('Tasks.status', 'FAILED').length
  186. + tasksInCurrentStage.filterProperty('Tasks.status', 'ABORTED').length
  187. + tasksInCurrentStage.filterProperty('Tasks.status', 'TIMEDOUT').length;
  188. var queuedActions = tasksInCurrentStage.filterProperty('Tasks.status', 'QUEUED').length;
  189. var inProgressActions = tasksInCurrentStage.filterProperty('Tasks.status', 'IN_PROGRESS').length;
  190. var progress = Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / tasksInCurrentStage.length * 100);
  191. this.get('tasks').findProperty('id', currentTaskId).set('progress', progress);
  192. }
  193. // start polling if current step status not completed or failed
  194. if (!(this.get('status') === 'COMPLETED' && this.get('status') === 'FAILED')) {
  195. window.setTimeout(function () {
  196. self.doPollingForPageRequest();
  197. }, self.POLL_INTERVAL);
  198. }
  199. },
  200. initializeTasks: function () {
  201. var self = this;
  202. var commands = this.isSingleRequestPage ? this.get('stages') : this.get('commands');
  203. var currentStep = App.router.get(this.get('content.controllerName') + '.currentStep');
  204. var tasksMessagesPrefix = this.get('tasksMessagesPrefix');
  205. // check that all stages have been completed for single request type
  206. var allStagesCompleted = commands.everyProperty('status', 'COMPLETED');
  207. for (var i = 0; i < commands.length; i++) {
  208. this.get('tasks').pushObject(Ember.Object.create({
  209. title: self.isSingleRequestPage ? commands[i].get('context') : Em.I18n.t(tasksMessagesPrefix + currentStep + '.task' + i + '.title'),
  210. // set COMPLETED status for task if all stages completed successfully
  211. status: allStagesCompleted ? 'COMPLETED' : 'PENDING',
  212. id: i,
  213. stageId: self.isSingleRequestPage ? commands[i].get('stage_id') : null,
  214. command: self.isSingleRequestPage ? 'k' : commands[i],
  215. showRetry: false,
  216. showRollback: false,
  217. name: self.isSingleRequestPage ? commands[i].get('context') : Em.I18n.t(tasksMessagesPrefix + currentStep + '.task' + i + '.title'),
  218. displayName: self.isSingleRequestPage ? commands[i].get('context') : Em.I18n.t(tasksMessagesPrefix + currentStep + '.task' + i + '.title'),
  219. progress: 0,
  220. isRunning: false,
  221. requestIds: self.isSingleRequestPage ? [this.get('stages')[0].request_id] : []
  222. }));
  223. }
  224. this.set('isLoaded', true);
  225. },
  226. loadTasks: function () {
  227. var self = this;
  228. var loadedStatuses = this.get('content.tasksStatuses');
  229. var loadedRequestIds = this.get('content.tasksRequestIds');
  230. if (loadedStatuses && loadedStatuses.length === this.get('tasks').length) {
  231. this.get('tasks').forEach(function (task, i) {
  232. self.setTaskStatus(task.get('id'), loadedStatuses[i]);
  233. self.setRequestIds(task.get('id'), loadedRequestIds[i]);
  234. });
  235. if (loadedStatuses.contains('IN_PROGRESS')) {
  236. var curTaskId = this.get('tasks')[loadedStatuses.indexOf('IN_PROGRESS')].get('id');
  237. this.set('currentRequestIds', this.get('content.requestIds'));
  238. this.set('currentTaskId', curTaskId);
  239. this.doPolling();
  240. } else if (loadedStatuses.contains('QUEUED')) {
  241. var curTaskId = this.get('tasks')[loadedStatuses.indexOf('QUEUED')].get('id');
  242. this.set('currentTaskId', curTaskId);
  243. this.runTask(curTaskId);
  244. }
  245. }
  246. },
  247. setTaskStatus: function (taskId, status) {
  248. this.get('tasks').findProperty('id', taskId).set('status', status);
  249. },
  250. setRequestIds: function (taskId, requestIds) {
  251. this.get('tasks').findProperty('id', taskId).set('requestIds', requestIds);
  252. },
  253. retryTask: function () {
  254. var task = this.get('tasks').findProperty('status', 'FAILED');
  255. task.set('showRetry', false);
  256. task.set('showRollback', false);
  257. task.set('status', 'PENDING');
  258. },
  259. onTaskStatusChange: function (dfdObject) {
  260. var statuses = this.get('tasks').mapProperty('status');
  261. var tasksRequestIds = this.get('tasks').mapProperty('requestIds');
  262. var requestIds = this.get('currentRequestIds');
  263. // save task info
  264. App.router.get(this.get('content.controllerName')).saveTasksStatuses(statuses);
  265. App.router.get(this.get('content.controllerName')).saveTasksRequestIds(tasksRequestIds);
  266. App.router.get(this.get('content.controllerName')).saveRequestIds(requestIds);
  267. // call saving of cluster status asynchronous
  268. // synchronous executing cause problems in Firefox
  269. var successCallbackData;
  270. if (dfdObject && dfdObject.isJqueryPromise) {
  271. successCallbackData = {deferred: dfdObject.deferred};
  272. }
  273. App.clusterStatus.setClusterStatus({
  274. clusterName: App.router.getClusterName(),
  275. clusterState: this.get('clusterDeployState'),
  276. wizardControllerName: this.get('content.controllerName'),
  277. localdb: App.db.data
  278. }, {successCallback: this.statusChangeCallback, sender: this, successCallbackData: successCallbackData});
  279. },
  280. /**
  281. * Method that called after saving persist data to server.
  282. * Switch task according its status.
  283. */
  284. statusChangeCallback: function (data) {
  285. if (!this.get('tasks').someProperty('status', 'IN_PROGRESS') && !this.get('tasks').someProperty('status', 'QUEUED') && !this.get('tasks').someProperty('status', 'FAILED')) {
  286. var nextTask = this.get('tasks').findProperty('status', 'PENDING');
  287. if (nextTask) {
  288. this.set('status', 'IN_PROGRESS');
  289. var taskStatus = this.isSingleRequestPage ? 'IN_PROGRESS' : 'QUEUED';
  290. this.setTaskStatus(nextTask.get('id'), taskStatus);
  291. this.set('currentTaskId', nextTask.get('id'));
  292. this.runTask(nextTask.get('id'));
  293. } else {
  294. this.set('status', 'COMPLETED');
  295. this.set('isSubmitDisabled', false);
  296. this.set('isBackButtonDisabled', false);
  297. }
  298. } else if (this.get('tasks').someProperty('status', 'FAILED')) {
  299. this.set('status', 'FAILED');
  300. this.set('isBackButtonDisabled', false);
  301. if (this.get('isCommandLevelRetry')) {
  302. this.get('tasks').findProperty('status', 'FAILED').set('showRetry', true);
  303. } else {
  304. this.set('showRetry', true);
  305. }
  306. if (App.supports.autoRollbackHA) {
  307. this.get('tasks').findProperty('status', 'FAILED').set('showRollback', true);
  308. }
  309. }
  310. this.get('tasks').filterProperty('status', 'COMPLETED').setEach('showRetry', false);
  311. this.get('tasks').filterProperty('status', 'COMPLETED').setEach('showRollback', false);
  312. if (data && data.deferred) {
  313. data.deferred.resolve();
  314. }
  315. },
  316. /**
  317. * Run command of appropriate task
  318. */
  319. runTask: function (taskId) {
  320. this[this.get('tasks').findProperty('id', taskId).get('command')]();
  321. },
  322. onTaskError: function () {
  323. this.setTaskStatus(this.get('currentTaskId'), 'FAILED');
  324. },
  325. onTaskCompleted: function () {
  326. this.setTaskStatus(this.get('currentTaskId'), 'COMPLETED');
  327. },
  328. /**
  329. * check whether component installed on specified hosts
  330. * @param {string} componentName
  331. * @param {string[]} hostNames
  332. * @return {$.ajax}
  333. */
  334. checkInstalledComponents: function (componentName, hostNames) {
  335. return App.ajax.send({
  336. name: 'host_component.installed.on_hosts',
  337. sender: this,
  338. data: {
  339. componentName: componentName,
  340. hostNames: hostNames.join(',')
  341. }
  342. });
  343. },
  344. /**
  345. * Create component on single or multiple hosts.
  346. *
  347. * @method createComponent
  348. * @param {string} componentName - name of the component
  349. * @param {(string|string[])} hostName - host/hosts where components should be installed
  350. * @param {string} serviceName - name of the services
  351. */
  352. createComponent: function (componentName, hostName, serviceName) {
  353. var hostNames = (Array.isArray(hostName)) ? hostName : [hostName];
  354. var self = this;
  355. this.checkInstalledComponents(componentName, hostNames).then(function (data) {
  356. var hostsWithComponents = data.items.mapProperty('HostRoles.host_name');
  357. var result = hostNames.map(function(item) {
  358. return {
  359. componentName: componentName,
  360. hostName: item,
  361. hasComponent: hostsWithComponents.contains(item)
  362. };
  363. });
  364. var hostsWithoutComponents = result.filterProperty('hasComponent', false).mapProperty('hostName');
  365. var taskNum = 1;
  366. var requestData = {
  367. "RequestInfo": {
  368. "query": hostsWithoutComponents.map(function(item) {
  369. return 'Hosts/host_name=' + item;
  370. }).join('|')
  371. },
  372. "Body": {
  373. "host_components": [
  374. {
  375. "HostRoles": {
  376. "component_name": componentName
  377. }
  378. }
  379. ]
  380. }
  381. };
  382. if (!!hostsWithoutComponents.length) {
  383. App.ajax.send({
  384. name: 'wizard.step8.register_host_to_component',
  385. sender: self,
  386. data: {
  387. data: JSON.stringify(requestData),
  388. hostName: result.mapProperty('hostName'),
  389. componentName: componentName,
  390. serviceName: serviceName,
  391. taskNum: taskNum,
  392. cluster: App.get('clusterName')
  393. },
  394. success: 'onCreateComponent',
  395. error: 'onCreateComponent'
  396. });
  397. } else {
  398. self.onCreateComponent(null, null, {
  399. hostName: result.mapProperty('hostName'),
  400. componentName: componentName,
  401. serviceName: serviceName,
  402. taskNum: taskNum
  403. }, self);
  404. }
  405. });
  406. },
  407. onCreateComponent: function () {
  408. var hostName = arguments[2].hostName;
  409. var componentName = arguments[2].componentName;
  410. var taskNum = arguments[2].taskNum;
  411. var serviceName = arguments[2].serviceName;
  412. this.updateComponent(componentName, hostName, serviceName, "Install", taskNum);
  413. },
  414. onCreateComponentError: function (error) {
  415. if (error.responseText.indexOf('org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException') !== -1) {
  416. this.onCreateComponent();
  417. } else {
  418. this.onTaskError();
  419. }
  420. },
  421. /**
  422. * Update component status on selected hosts.
  423. *
  424. * @param {string} componentName
  425. * @param {(string|string[])} hostName
  426. * @param {string} serviceName
  427. * @param {string} context
  428. * @param {number} taskNum
  429. * @returns {$.ajax}
  430. */
  431. updateComponent: function (componentName, hostName, serviceName, context, taskNum) {
  432. if (!(hostName instanceof Array)) {
  433. hostName = [hostName];
  434. }
  435. var state = context.toLowerCase() == "start" ? "STARTED" : "INSTALLED";
  436. return App.ajax.send({
  437. name: 'common.host_components.update',
  438. sender: this,
  439. data: {
  440. HostRoles: {
  441. state: state
  442. },
  443. query: 'HostRoles/component_name=' + componentName + '&HostRoles/host_name.in(' + hostName.join(',') + ')&HostRoles/maintenance_state=OFF',
  444. context: context + " " + App.format.role(componentName),
  445. hostName: hostName,
  446. taskNum: taskNum || 1,
  447. componentName: componentName,
  448. serviceName: serviceName
  449. },
  450. success: 'startPolling',
  451. error: 'onTaskError'
  452. });
  453. },
  454. startPolling: function (data) {
  455. if (data) {
  456. this.get('currentRequestIds').push(data.Requests.id);
  457. var tasksCount = arguments[2].taskNum || 1;
  458. if (tasksCount === this.get('currentRequestIds').length) {
  459. this.setRequestIds(this.get('currentTaskId'), this.get('currentRequestIds'));
  460. this.doPolling();
  461. }
  462. } else {
  463. this.onTaskCompleted();
  464. }
  465. },
  466. doPolling: function () {
  467. this.setTaskStatus(this.get('currentTaskId'), 'IN_PROGRESS');
  468. var requestIds = this.get('currentRequestIds');
  469. this.set('logs', []);
  470. for (var i = 0; i < requestIds.length; i++) {
  471. App.ajax.send({
  472. name: 'admin.high_availability.polling',
  473. sender: this,
  474. data: {
  475. requestId: requestIds[i]
  476. },
  477. success: 'parseLogs',
  478. error: 'onTaskError'
  479. });
  480. }
  481. },
  482. parseLogs: function (logs) {
  483. this.get('logs').pushObject(logs.tasks);
  484. if (this.get('currentRequestIds').length === this.get('logs').length) {
  485. var tasks = [];
  486. this.get('logs').forEach(function (logs) {
  487. tasks.pushObjects(logs);
  488. }, this);
  489. var self = this;
  490. var currentTaskId = this.get('currentTaskId');
  491. if (!tasks.someProperty('Tasks.status', 'PENDING') && !tasks.someProperty('Tasks.status', 'QUEUED') && !tasks.someProperty('Tasks.status', 'IN_PROGRESS')) {
  492. this.set('currentRequestIds', []);
  493. if (tasks.someProperty('Tasks.status', 'FAILED') || tasks.someProperty('Tasks.status', 'TIMEDOUT') || tasks.someProperty('Tasks.status', 'ABORTED')) {
  494. this.setTaskStatus(currentTaskId, 'FAILED');
  495. } else {
  496. this.setTaskStatus(currentTaskId, 'COMPLETED');
  497. }
  498. } else {
  499. var actionsPerHost = tasks.length;
  500. var completedActions = tasks.filterProperty('Tasks.status', 'COMPLETED').length
  501. + tasks.filterProperty('Tasks.status', 'FAILED').length
  502. + tasks.filterProperty('Tasks.status', 'ABORTED').length
  503. + tasks.filterProperty('Tasks.status', 'TIMEDOUT').length;
  504. var queuedActions = tasks.filterProperty('Tasks.status', 'QUEUED').length;
  505. var inProgressActions = tasks.filterProperty('Tasks.status', 'IN_PROGRESS').length;
  506. var progress = Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / actionsPerHost * 100);
  507. this.get('tasks').findProperty('id', currentTaskId).set('progress', progress);
  508. window.setTimeout(function () {
  509. self.doPolling();
  510. }, self.POLL_INTERVAL);
  511. }
  512. }
  513. },
  514. showHostProgressPopup: function (event) {
  515. var popupTitle = event.contexts[0].title;
  516. var requestIds = event.contexts[0].requestIds;
  517. var stageId = event.contexts[0].stageId;
  518. var hostProgressPopupController = App.router.get('highAvailabilityProgressPopupController');
  519. hostProgressPopupController.initPopup(popupTitle, requestIds, this, true, stageId);
  520. },
  521. done: function () {
  522. if (!this.get('isSubmitDisabled')) {
  523. this.removeObserver('tasks.@each.status', this, 'onTaskStatusChange');
  524. App.router.send('next');
  525. }
  526. },
  527. back: function () {
  528. if (!this.get('isBackButtonDisabled')) {
  529. this.removeObserver('tasks.@each.status', this, 'onTaskStatusChange');
  530. App.router.send('back');
  531. }
  532. }
  533. });