wizardProgressPageController.js 20 KB

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