wizardProgressPageController.js 19 KB

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