progress_controller.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 installedComponents = [];
  20. App.HighAvailabilityProgressPageController = App.HighAvailabilityWizardController.extend({
  21. name: 'highAvailabilityProgressPageController',
  22. status: 'IN_PROGRESS',
  23. clusterDeployState: 'HIGH_AVAILABILITY_DEPLOY',
  24. tasks: [],
  25. commands: [],
  26. currentRequestIds: [],
  27. logs: [],
  28. currentTaskId: null,
  29. POLL_INTERVAL: 4000,
  30. isSubmitDisabled: true,
  31. serviceTimestamp: null,
  32. isRollback: false,
  33. loadStep: function () {
  34. console.warn('func: loadStep');
  35. this.clearStep();
  36. this.initializeTasks();
  37. this.loadTasks();
  38. this.addObserver('tasks.@each.status', this, 'onTaskStatusChange');
  39. this.onTaskStatusChange();
  40. },
  41. clearStep: function () {
  42. console.warn('func: clearStep');
  43. this.set('isSubmitDisabled', true);
  44. this.set('tasks', []);
  45. this.set('logs', []);
  46. this.set('currentRequestIds', []);
  47. },
  48. initializeTasks: function() {
  49. console.warn('func: initializeTasks');
  50. var commands = this.get('commands');
  51. var currentStep = App.router.get('highAvailabilityWizardController.currentStep');
  52. for (var i = 0; i < commands.length; i++) {
  53. this.get('tasks').pushObject(Ember.Object.create({
  54. title: Em.I18n.t('admin.highAvailability.wizard.step' + currentStep + '.task' + i + '.title'),
  55. status: 'PENDING',
  56. id: i,
  57. command: commands[i],
  58. showRetry: false,
  59. showRollback: false,
  60. name: Em.I18n.t('admin.highAvailability.wizard.step' + currentStep + '.task' + i + '.title'),
  61. displayName: Em.I18n.t('admin.highAvailability.wizard.step' + currentStep + '.task' + i + '.title'),
  62. progress: 0,
  63. isRunning: false,
  64. hosts: []
  65. }));
  66. }
  67. },
  68. services: function(){
  69. return this.get('tasks');
  70. }.property('tasks'),
  71. loadTasks: function () {
  72. console.warn('func: loadTasks');
  73. var self = this;
  74. var loadedStauses = this.get('content.tasksStatuses');
  75. var loadedLogs = this.get('content.logs');
  76. if (loadedStauses && loadedLogs && loadedStauses.length === this.get('tasks').length) {
  77. this.get('tasks').forEach(function(task,i){
  78. self.setTaskStatus(task.get('id'), loadedStauses[i]);
  79. self.restoreTaskLog(task.get('id'), loadedLogs[i]);
  80. });
  81. if (loadedStauses.contains('IN_PROGRESS')) {
  82. var curTaskId = this.get('tasks')[loadedStauses.indexOf('IN_PROGRESS')].get('id');
  83. this.set('currentRequestIds', this.get('content.requestIds'));
  84. this.set('currentTaskId', curTaskId);
  85. this.doPolling();
  86. }else if (loadedStauses.contains('QUEUED')){
  87. var curTaskId = this.get('tasks')[loadedStauses.indexOf('QUEUED')].get('id');
  88. this.set('currentTaskId', curTaskId);
  89. this.runTask(curTaskId);
  90. }
  91. }
  92. },
  93. setTaskStatus: function (taskId, status) {
  94. console.warn('func: setTaskStatus');
  95. this.get('tasks').findProperty('id', taskId).set('status', status);
  96. },
  97. restoreTaskLog: function (taskId, log) {
  98. console.warn('func: restoreTaskLog');
  99. this.get('tasks').findProperty('id', taskId).set('hosts', log);
  100. },
  101. setTaskLogs: function (taskId, tasks) {
  102. console.warn('func: setTaskLogs');
  103. var hosts = [];
  104. var uniqHosts = tasks.mapProperty('Tasks.host_name').uniq();
  105. uniqHosts.forEach(function (host) {
  106. var curHostTasks = tasks.filterProperty('Tasks.host_name', host);
  107. hosts.push(
  108. {
  109. name: host,
  110. publicName: host,
  111. logTasks: curHostTasks
  112. }
  113. );
  114. });
  115. this.get('tasks').findProperty('id', taskId).set('hosts', hosts);
  116. this.set('serviceTimestamp', App.dateTime());
  117. },
  118. retryTask: function () {
  119. console.warn('func: retryTask');
  120. var task = this.get('tasks').findProperty('status', 'FAILED');
  121. task.set('showRetry', false);
  122. task.set('showRollback', false);
  123. task.set('status', 'PENDING');
  124. },
  125. manualRollback: function () {
  126. console.warn('func: manualRollback');
  127. App.ModalPopup.show({
  128. header: Em.I18n.t('admin.highAvailability.confirmRollbackHeader'),
  129. primary: Em.I18n.t('yes'),
  130. showCloseButton: false,
  131. onPrimary: function () {
  132. var controller = App.router.get('highAvailabilityWizardController');
  133. controller.clearTasksData();
  134. controller.clearStorageData();
  135. controller.setCurrentStep('1');
  136. App.router.get('updateController').set('isWorking', true);
  137. App.clusterStatus.setClusterStatus({
  138. clusterName: App.router.get('content.cluster.name'),
  139. clusterState: 'DEFAULT',
  140. wizardControllerName: App.router.get('highAvailabilityRollbackController.name'),
  141. localdb: App.db.data
  142. });
  143. this.hide();
  144. App.router.transitionTo('main.admin.index');
  145. location.reload();
  146. },
  147. secondary : Em.I18n.t('no'),
  148. onSecondary: function(){
  149. this.hide();
  150. },
  151. bodyClass: Ember.View.extend({
  152. template: Ember.Handlebars.compile( Em.I18n.t('admin.highAvailability.confirmManualRollbackBody'))
  153. })
  154. });
  155. },
  156. rollback: function () {
  157. console.warn('func: rollback');
  158. var task = this.get('tasks').findProperty('status', 'FAILED');
  159. App.router.get(this.get('content.controllerName')).saveFailedTask(task);
  160. App.ModalPopup.show({
  161. header: Em.I18n.t('admin.highAvailability.confirmRollbackHeader'),
  162. primary: Em.I18n.t('common.confirm'),
  163. showCloseButton: false,
  164. onPrimary: function () {
  165. App.router.get('highAvailabilityWizardController').clearTasksData();
  166. App.router.transitionTo('main.admin.highAvailabilityRollback');
  167. this.hide();
  168. },
  169. secondary : Em.I18n.t('common.cancel'),
  170. body: Em.I18n.t('admin.highAvailability.confirmRollbackBody')
  171. });
  172. },
  173. onTaskStatusChange: function () {
  174. console.warn('func: onTaskStatusChange1');
  175. var statuses = this.get('tasks').mapProperty('status');
  176. var logs = this.get('tasks').mapProperty('hosts');
  177. var requestIds = this.get('currentRequestIds');
  178. console.warn('func: onTaskStatusChange5',statuses, logs, requestIds);
  179. // save task info
  180. App.router.get(this.get('content.controllerName')).saveTasksStatuses(statuses);
  181. App.router.get(this.get('content.controllerName')).saveRequestIds(requestIds);
  182. App.router.get(this.get('content.controllerName')).saveLogs(logs);
  183. // call saving of cluster status asynchronous
  184. // synchronous executing cause problems in Firefox
  185. App.clusterStatus.setClusterStatus({
  186. clusterName: this.get('content.cluster.name'),
  187. clusterState: this.get('clusterDeployState'),
  188. wizardControllerName: this.get('content.controllerName'),
  189. localdb: App.db.data
  190. }, { async: true, success: 'statusChangeCallback', sender: this });
  191. },
  192. /**
  193. * Method that called after saving persist data to server.
  194. * Switch task according its status.
  195. */
  196. statusChangeCallback: function() {
  197. if (!this.get('tasks').someProperty('status', 'IN_PROGRESS') && !this.get('tasks').someProperty('status', 'QUEUED') && !this.get('tasks').someProperty('status', 'FAILED')) {
  198. var nextTask = this.get('tasks').findProperty('status', 'PENDING');
  199. if (nextTask) {
  200. console.warn('func: onTaskStatusChange2');
  201. this.set('status', 'IN_PROGRESS');
  202. this.setTaskStatus(nextTask.get('id'), 'QUEUED');
  203. this.set('currentTaskId', nextTask.get('id'));
  204. this.runTask(nextTask.get('id'));
  205. } else {
  206. console.warn('func: onTaskStatusChange3');
  207. this.set('status', 'COMPLETED');
  208. this.set('isSubmitDisabled', false);
  209. }
  210. } else if (this.get('tasks').someProperty('status', 'FAILED')) {
  211. console.warn('func: onTaskStatusChange4');
  212. this.set('status', 'FAILED');
  213. this.get('tasks').findProperty('status', 'FAILED').set('showRetry', true);
  214. if(App.supports.autoRollbackHA){
  215. this.get('tasks').findProperty('status', 'FAILED').set('showRollback', true);
  216. }
  217. }
  218. this.get('tasks').filterProperty('status','COMPLETED').setEach('showRetry', false);
  219. this.get('tasks').filterProperty('status','COMPLETED').setEach('showRollback', false);
  220. },
  221. /**
  222. * Run command of appropriate task
  223. */
  224. runTask: function (taskId) {
  225. console.warn('func: runTask',taskId);
  226. this[this.get('tasks').findProperty('id', taskId).get('command')]();
  227. },
  228. onTaskError: function () {
  229. console.warn('func: onTaskError');
  230. this.setTaskStatus(this.get('currentTaskId'), 'FAILED');
  231. },
  232. onTaskCompleted: function () {
  233. console.warn('func: onTaskCompleted');
  234. this.setTaskStatus(this.get('currentTaskId'), 'COMPLETED');
  235. },
  236. /**
  237. * check whether component installed on specified hosts
  238. * @param componentName
  239. * @param hostNames
  240. * @return {Array}
  241. */
  242. checkInstalledComponents: function (componentName, hostNames) {
  243. var result = [];
  244. App.ajax.send({
  245. name: 'host_component.installed.on_hosts',
  246. sender: this,
  247. data: {
  248. componentName: componentName,
  249. hostNames: hostNames.join(',')
  250. },
  251. success: 'checkInstalledComponentsSuccessCallback'
  252. });
  253. hostNames.forEach(function (hostName) {
  254. result.push({
  255. componentName: componentName,
  256. hostName: hostName,
  257. hasComponent: installedComponents.someProperty('HostRoles.host_name', hostName)
  258. });
  259. });
  260. return result;
  261. },
  262. checkInstalledComponentsSuccessCallback: function (data, opt, params) {
  263. installedComponents = data.items;
  264. },
  265. createComponent: function (componentName, hostName) {
  266. var hostNames = (Array.isArray(hostName)) ? hostName : [hostName];
  267. this.checkInstalledComponents(componentName, hostNames).forEach(function (host, index, array) {
  268. if (!host.hasComponent) {
  269. App.ajax.send({
  270. name: 'admin.high_availability.create_component',
  271. sender: this,
  272. data: {
  273. hostName: host.hostName,
  274. componentName: host.componentName,
  275. taskNum: array.length
  276. },
  277. success: 'onCreateComponent',
  278. error: 'onCreateComponentError'
  279. });
  280. } else {
  281. // Simulates format returned from ajax.send
  282. this.onCreateComponent(null, null, {hostName: host.hostName, componentName: host.componentName, taskNum: array.length});
  283. }
  284. }, this)
  285. },
  286. onCreateComponent: function () {
  287. console.warn('func: onCreateComponent');
  288. var hostName = arguments[2].hostName;
  289. var componentName = arguments[2].componentName;
  290. var taskNum = arguments[2].taskNum;
  291. this.installComponent(componentName, hostName, taskNum);
  292. },
  293. onCreateComponentError: function (error) {
  294. if (error.responseText.indexOf('org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException') !== -1) {
  295. this.onCreateComponent();
  296. } else {
  297. this.onTaskError();
  298. }
  299. },
  300. installComponent: function (componentName, hostName, taskNum) {
  301. console.warn('func: installComponent');
  302. if (!(hostName instanceof Array)) {
  303. hostName = [hostName];
  304. }
  305. for (var i = 0; i < hostName.length; i++) {
  306. App.ajax.send({
  307. name: 'admin.high_availability.install_component',
  308. sender: this,
  309. data: {
  310. hostName: hostName[i],
  311. componentName: componentName,
  312. displayName: App.format.role(componentName),
  313. taskNum: taskNum || hostName.length
  314. },
  315. success: 'startPolling',
  316. error: 'onTaskError'
  317. });
  318. }
  319. },
  320. startComponent: function (componentName, hostName) {
  321. console.warn('func: startComponent');
  322. if (!(hostName instanceof Array)) {
  323. hostName = [hostName];
  324. }
  325. for (var i = 0; i < hostName.length; i++) {
  326. App.ajax.send({
  327. name: 'admin.high_availability.start_component',
  328. sender: this,
  329. data: {
  330. hostName: hostName[i],
  331. componentName: componentName,
  332. displayName: App.format.role(componentName),
  333. taskNum: hostName.length
  334. },
  335. success: 'startPolling',
  336. error: 'onTaskError'
  337. });
  338. }
  339. },
  340. stopComponent: function (componentName, hostName) {
  341. console.warn('func: stopComponent');
  342. if (!(hostName instanceof Array)) {
  343. hostName = [hostName];
  344. }
  345. for (var i = 0; i < hostName.length; i++) {
  346. App.ajax.send({
  347. name: 'admin.high_availability.stop_component',
  348. sender: this,
  349. data: {
  350. hostName: hostName[i],
  351. componentName: componentName,
  352. displayName: App.format.role(componentName),
  353. taskNum: hostName.length
  354. },
  355. success: 'startPolling',
  356. error: 'onTaskError'
  357. });
  358. }
  359. },
  360. startPolling: function (data) {
  361. if (data) {
  362. console.warn('func: startPolling1');
  363. this.get('currentRequestIds').push(data.Requests.id);
  364. var tasksCount = arguments[2].taskNum || 1;
  365. if (tasksCount === this.get('currentRequestIds').length) {
  366. console.warn('func: startPolling2');
  367. this.doPolling();
  368. }
  369. } else {
  370. console.warn('func: startPolling3');
  371. this.setTaskLogs(this.get('currentTaskId'), []);
  372. this.onTaskCompleted();
  373. }
  374. },
  375. doPolling: function () {
  376. console.warn('func: doPolling');
  377. this.setTaskStatus(this.get('currentTaskId'), 'IN_PROGRESS');
  378. var requestIds = this.get('currentRequestIds');
  379. this.set('logs', []);
  380. for (var i = 0; i < requestIds.length; i++) {
  381. App.ajax.send({
  382. name: 'admin.high_availability.polling',
  383. sender: this,
  384. data: {
  385. requestId: requestIds[i]
  386. },
  387. success: 'parseLogs',
  388. error: 'onTaskError'
  389. });
  390. }
  391. },
  392. parseLogs: function (logs) {
  393. console.warn('func: parseLogs');
  394. this.get('logs').push(logs.tasks);
  395. if (this.get('currentRequestIds').length === this.get('logs').length) {
  396. var tasks = [];
  397. this.get('logs').forEach(function (logs) {
  398. tasks.pushObjects(logs);
  399. }, this);
  400. var self = this;
  401. var currentTaskId = this.get('currentTaskId');
  402. this.setTaskLogs(currentTaskId, tasks);
  403. if (!tasks.someProperty('Tasks.status', 'PENDING') && !tasks.someProperty('Tasks.status', 'QUEUED') && !tasks.someProperty('Tasks.status', 'IN_PROGRESS')) {
  404. this.set('currentRequestIds', []);
  405. if (tasks.someProperty('Tasks.status', 'FAILED') || tasks.someProperty('Tasks.status', 'TIMEDOUT') || tasks.someProperty('Tasks.status', 'ABORTED')) {
  406. this.setTaskStatus(currentTaskId, 'FAILED');
  407. } else {
  408. this.setTaskStatus(currentTaskId, 'COMPLETED');
  409. }
  410. } else {
  411. var actionsPerHost = tasks.length;
  412. var completedActions = tasks.filterProperty('Tasks.status', 'COMPLETED').length
  413. + tasks.filterProperty('Tasks.status', 'FAILED').length
  414. + tasks.filterProperty('Tasks.status', 'ABORTED').length
  415. + tasks.filterProperty('Tasks.status', 'TIMEDOUT').length;
  416. var queuedActions = tasks.filterProperty('Tasks.status', 'QUEUED').length;
  417. var inProgressActions = tasks.filterProperty('Tasks.status', 'IN_PROGRESS').length;
  418. var progress = Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / actionsPerHost * 100);
  419. this.get('tasks').findProperty('id', currentTaskId).set('progress', progress);
  420. window.setTimeout(function () {
  421. self.doPolling()
  422. }, self.POLL_INTERVAL);
  423. }
  424. }
  425. },
  426. done: function () {
  427. if (!this.get('isSubmitDisabled')) {
  428. this.removeObserver('tasks.@each.status', this, 'onTaskStatusChange');
  429. App.router.send('next');
  430. }
  431. }
  432. });