host_progress_popup.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  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 batchUtils = require('utils/batch_scheduled_requests');
  20. var date = require('utils/date');
  21. /**
  22. * App.HostPopup is for the popup that shows up upon clicking already-performed or currently-in-progress operations
  23. */
  24. App.HostPopup = Em.Object.create({
  25. name: 'hostPopup',
  26. servicesInfo: [],
  27. hosts: null,
  28. inputData: null,
  29. /**
  30. * @type {string}
  31. */
  32. serviceName: "",
  33. /**
  34. * @type {Number}
  35. */
  36. currentServiceId: null,
  37. previousServiceId: null,
  38. /**
  39. * @type {string}
  40. */
  41. popupHeaderName: "",
  42. operationInfo: null,
  43. /**
  44. * @type {App.Controller}
  45. */
  46. dataSourceController: null,
  47. /**
  48. * @type {bool}
  49. */
  50. isBackgroundOperations: false,
  51. /**
  52. * @type {string}
  53. */
  54. currentHostName: null,
  55. /**
  56. * @type {App.ModalPopup}
  57. */
  58. isPopup: null,
  59. abortIcon: Em.View.extend({
  60. tagName: 'i',
  61. classNames: ['abort-icon', 'icon-remove-circle', 'pointer'],
  62. click: function () {
  63. this.get('controller').abortRequest(this.get('servicesInfo'));
  64. return false;
  65. },
  66. didInsertElement: function () {
  67. App.tooltip($(this.get('element')), {
  68. placement: "top",
  69. title: Em.I18n.t('hostPopup.bgop.abortRequest.title')
  70. });
  71. }
  72. }),
  73. statusIcon: Em.View.extend({
  74. tagName: 'i',
  75. classNames: ["service-status"],
  76. classNameBindings: ['servicesInfo.status', 'servicesInfo.icon', 'additionalClass'],
  77. attributeBindings: ['data-original-title'],
  78. 'data-original-title': function() {
  79. return this.get('servicesInfo.status');
  80. }.property('servicesInfo.status'),
  81. didInsertElement: function () {
  82. App.tooltip($(this.get('element')));
  83. }
  84. }),
  85. /**
  86. * Determines if background operation can be aborted depending on its status
  87. * @param status
  88. * @returns {boolean}
  89. */
  90. isAbortableByStatus: function (status) {
  91. var statuses = this.get('statusesStyleMap');
  92. return !Em.keys(statuses).contains(status) || status == 'IN_PROGRESS';
  93. },
  94. /**
  95. * Send request to abort operation
  96. */
  97. abortRequest: function (serviceInfo) {
  98. var requestName = serviceInfo.get('name');
  99. var self = this;
  100. App.showConfirmationPopup(function () {
  101. serviceInfo.set('isAbortable', false);
  102. App.ajax.send({
  103. name: 'background_operations.abort_request',
  104. sender: self,
  105. data: {
  106. requestId: serviceInfo.get('id'),
  107. requestName: requestName,
  108. serviceInfo: serviceInfo
  109. },
  110. success: 'abortRequestSuccessCallback',
  111. error: 'abortRequestErrorCallback'
  112. });
  113. }, Em.I18n.t('hostPopup.bgop.abortRequest.confirmation.body').format(requestName));
  114. return false;
  115. },
  116. /**
  117. * Method called on successful sending request to abort operation
  118. */
  119. abortRequestSuccessCallback: function (response, request, data) {
  120. App.ModalPopup.show({
  121. header: Em.I18n.t('hostPopup.bgop.abortRequest.modal.header'),
  122. bodyClass: Em.View.extend({
  123. template: Em.Handlebars.compile(Em.I18n.t('hostPopup.bgop.abortRequest.modal.body').format(data.requestName))
  124. }),
  125. secondary: null
  126. });
  127. },
  128. /**
  129. * Method called on unsuccessful sending request to abort operation
  130. */
  131. abortRequestErrorCallback: function (xhr, textStatus, error, opt, data) {
  132. data.serviceInfo.set('isAbortable', this.isAbortableByStatus(data.serviceInfo.status));
  133. App.ajax.defaultErrorHandler(xhr, opt.url, 'PUT', xhr.status);
  134. },
  135. /**
  136. * Entering point of this component
  137. * @param {String} serviceName
  138. * @param {Object} controller
  139. * @param {Boolean} isBackgroundOperations
  140. * @param {Integer} requestId
  141. */
  142. initPopup: function (serviceName, controller, isBackgroundOperations, requestId) {
  143. if (!isBackgroundOperations) {
  144. this.clearHostPopup();
  145. this.set("popupHeaderName", serviceName);
  146. }
  147. this.set('currentServiceId', requestId);
  148. this.set("serviceName", serviceName);
  149. this.set("dataSourceController", controller);
  150. this.set("isBackgroundOperations", isBackgroundOperations);
  151. this.set("inputData", this.get("dataSourceController.services"));
  152. if(isBackgroundOperations){
  153. this.onServiceUpdate();
  154. } else {
  155. this.onHostUpdate();
  156. }
  157. return this.createPopup();
  158. },
  159. /**
  160. * clear info popup data
  161. */
  162. clearHostPopup: function () {
  163. this.set('servicesInfo', []);
  164. this.set('hosts', null);
  165. this.set('inputData', null);
  166. this.set('serviceName', "");
  167. this.set('currentServiceId', null);
  168. this.set('previousServiceId', null);
  169. this.set('popupHeaderName', "");
  170. this.set('dataSourceController', null);
  171. this.set('currentHostName', null);
  172. this.get('isPopup') ? this.get('isPopup').remove() : null;
  173. },
  174. /**
  175. * Depending on tasks status
  176. * @param {Array} tasks
  177. * @return {Array} [Status, Icon type, Progressbar color, is IN_PROGRESS]
  178. */
  179. getStatus: function(tasks){
  180. var isCompleted = true;
  181. var status;
  182. var tasksLength = tasks.length;
  183. var isFailed = false;
  184. var isAborted = false;
  185. var isTimedout = false;
  186. var isInProgress = false;
  187. for (var i = 0; i < tasksLength; i++) {
  188. if (tasks[i].Tasks.status !== 'COMPLETED') {
  189. isCompleted = false;
  190. }
  191. if(tasks[i].Tasks.status === 'FAILED'){
  192. isFailed = true;
  193. }
  194. if (tasks[i].Tasks.status === 'ABORTED') {
  195. isAborted = true;
  196. }
  197. if (tasks[i].Tasks.status === 'TIMEDOUT') {
  198. isTimedout = true;
  199. }
  200. if (tasks[i].Tasks.status === 'IN_PROGRESS') {
  201. isInProgress = true;
  202. }
  203. }
  204. if (isFailed) {
  205. status = ['FAILED', 'icon-exclamation-sign', 'progress-danger', false];
  206. } else if (isAborted) {
  207. status = ['ABORTED', 'icon-minus', 'progress-warning', false];
  208. } else if (isTimedout) {
  209. status = ['TIMEDOUT', 'icon-time', 'progress-warning', false];
  210. } else if (isInProgress) {
  211. status = ['IN_PROGRESS', 'icon-cogs', 'progress-info', true];
  212. }
  213. if(status){
  214. return status;
  215. } else if(isCompleted){
  216. return ['SUCCESS', 'icon-ok', 'progress-success', false];
  217. } else {
  218. return ['PENDING', 'icon-cog', 'progress-info', true];
  219. }
  220. },
  221. /**
  222. * Progress of host or service depending on tasks status
  223. * @param {Array} tasks
  224. * @return {Number} percent of completion
  225. */
  226. getProgress: function (tasks) {
  227. if (!tasks || tasks.length === 0) return 0;
  228. var completedActions = 0;
  229. var queuedActions = 0;
  230. var inProgressActions = 0;
  231. tasks.forEach(function (task) {
  232. if (['COMPLETED', 'FAILED', 'ABORTED', 'TIMEDOUT'].contains(task.Tasks.status)) {
  233. completedActions++;
  234. } else if (task.Tasks.status === 'QUEUED') {
  235. queuedActions++;
  236. } else if (task.Tasks.status === 'IN_PROGRESS') {
  237. inProgressActions++;
  238. }
  239. });
  240. return Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / tasks.length * 100);
  241. },
  242. /**
  243. * Count number of operations for select box options
  244. * @param {Object[]} obj
  245. * @param {Object[]} categories
  246. */
  247. setSelectCount: function (obj, categories) {
  248. if (!obj) return;
  249. var countAll = obj.length;
  250. var countPending = 0;
  251. var countInProgress = 0;
  252. var countFailed = 0;
  253. var countCompleted = 0;
  254. var countAborted = 0;
  255. var countTimedout = 0;
  256. obj.forEach(function(item){
  257. switch (item.status){
  258. case 'pending':
  259. countPending++;
  260. break;
  261. case 'queued':
  262. countPending++;
  263. break;
  264. case 'in_progress':
  265. countInProgress++;
  266. break;
  267. case 'failed':
  268. countFailed++;
  269. break;
  270. case 'success':
  271. countCompleted++;
  272. break;
  273. case 'completed':
  274. countCompleted++;
  275. break;
  276. case 'aborted':
  277. countAborted++;
  278. break;
  279. case 'timedout':
  280. countTimedout++;
  281. break;
  282. }
  283. });
  284. categories.findProperty("value", 'all').set("count", countAll);
  285. categories.findProperty("value", 'pending').set("count", countPending);
  286. categories.findProperty("value", 'in_progress').set("count", countInProgress);
  287. categories.findProperty("value", 'failed').set("count", countFailed);
  288. categories.findProperty("value", 'completed').set("count", countCompleted);
  289. categories.findProperty("value", 'aborted').set("count", countAborted);
  290. categories.findProperty("value", 'timedout').set("count", countTimedout);
  291. },
  292. /**
  293. * For Background operation popup calculate number of running Operations, and set popup header
  294. * @param {bool} isServiceListHidden
  295. */
  296. setBackgroundOperationHeader: function (isServiceListHidden) {
  297. if (this.get('isBackgroundOperations') && !isServiceListHidden) {
  298. var numRunning = App.router.get('backgroundOperationsController.allOperationsCount');
  299. this.set("popupHeaderName", numRunning + Em.I18n.t('hostPopup.header.postFix').format(numRunning == 1 ? "" : "s"));
  300. }
  301. },
  302. // map to get css class with styles by service status
  303. statusesStyleMap: {
  304. 'FAILED': ['FAILED', 'icon-exclamation-sign', 'progress-danger', false],
  305. 'ABORTED': ['ABORTED', 'icon-minus', 'progress-warning', false],
  306. 'TIMEDOUT': ['TIMEDOUT', 'icon-time', 'progress-warning', false],
  307. 'IN_PROGRESS': ['IN_PROGRESS', 'icon-cogs', 'progress-info', true],
  308. 'COMPLETED': ['SUCCESS', 'icon-ok', 'progress-success', false]
  309. },
  310. /**
  311. * Create services obj data structure for popup
  312. * Set data for services
  313. * @param {bool} isServiceListHidden
  314. */
  315. onServiceUpdate: function (isServiceListHidden) {
  316. if (this.get('isBackgroundOperations') && this.get("inputData")) {
  317. var statuses = this.get('statusesStyleMap');
  318. var servicesInfo = this.get("servicesInfo");
  319. var currentServices = [];
  320. this.get("inputData").forEach(function (service, index) {
  321. var updatedService;
  322. var id = service.id;
  323. currentServices.push(id);
  324. var existedService = servicesInfo.findProperty('id', id);
  325. updatedService = existedService;
  326. if (existedService) {
  327. updatedService = this.updateService(existedService, service);
  328. } else {
  329. updatedService = this.createService(service);
  330. servicesInfo.insertAt(index, updatedService);
  331. }
  332. updatedService.set('isAbortable', App.isAccessible('MANAGER') && this.isAbortableByStatus(service.status));
  333. }, this);
  334. this.removeOldServices(servicesInfo, currentServices);
  335. this.setBackgroundOperationHeader(isServiceListHidden);
  336. }
  337. },
  338. /**
  339. * Create service object from transmitted data
  340. * @param service
  341. */
  342. createService: function (service) {
  343. var statuses = this.get('statusesStyleMap');
  344. var pendingStatus = ['PENDING', 'icon-cog', 'progress-info', true];
  345. var status = statuses[service.status] || pendingStatus;
  346. return Ember.Object.create({
  347. id: service.id,
  348. displayName: service.displayName,
  349. progress: service.progress,
  350. status: App.format.taskStatus(status[0]),
  351. isRunning: service.isRunning,
  352. name: service.name,
  353. isVisible: true,
  354. startTime: date.startTime(service.startTime),
  355. duration: date.durationSummary(service.startTime, service.endTime),
  356. icon: status[1],
  357. barColor: status[2],
  358. isInProgress: status[3],
  359. barWidth: "width:" + service.progress + "%;",
  360. sourceRequestScheduleId: service.get('sourceRequestScheduleId'),
  361. contextCommand: service.get('contextCommand')
  362. });
  363. },
  364. /**
  365. * Update properties of existed service with new data
  366. * @param service
  367. * @param newData
  368. * @returns {Ember.Object}
  369. */
  370. updateService: function (service, newData) {
  371. var statuses = this.get('statusesStyleMap');
  372. var pendingStatus = ['PENDING', 'icon-cog', 'progress-info', true];
  373. var status = statuses[newData.status] || pendingStatus;
  374. return service.setProperties({
  375. progress: newData.progress,
  376. status: App.format.taskStatus(status[0]),
  377. isRunning: newData.isRunning,
  378. startTime: date.startTime(newData.startTime),
  379. duration: date.durationSummary(newData.startTime, newData.endTime),
  380. icon: status[1],
  381. barColor: status[2],
  382. isInProgress: status[3],
  383. barWidth: "width:" + newData.progress + "%;",
  384. sourceRequestScheduleId: newData.get('sourceRequestScheduleId'),
  385. contextCommand: newData.get('contextCommand')
  386. });
  387. },
  388. /**
  389. * remove old requests
  390. * as API returns 10, or 20 , or 30 ...etc latest request, the requests that absent in response should be removed
  391. * @param services
  392. * @param currentServicesIds
  393. */
  394. removeOldServices: function (services, currentServicesIds) {
  395. services.forEach(function (service, index, services) {
  396. if (!currentServicesIds.contains(service.id)) {
  397. services.removeAt(index, 1);
  398. }
  399. });
  400. },
  401. /**
  402. * create task Ember object
  403. * @param {Object} _task
  404. * @return {Em.Object}
  405. */
  406. createTask: function (_task) {
  407. return Em.Object.create({
  408. id: _task.Tasks.id,
  409. hostName: _task.Tasks.host_name,
  410. command: ( _task.Tasks.command.toLowerCase() != 'service_check') ? _task.Tasks.command.toLowerCase() : '',
  411. commandDetail: App.format.commandDetail(_task.Tasks.command_detail, _task.Tasks.request_inputs),
  412. status: App.format.taskStatus(_task.Tasks.status),
  413. role: App.format.role(_task.Tasks.role),
  414. stderr: _task.Tasks.stderr,
  415. stdout: _task.Tasks.stdout,
  416. isVisible: true,
  417. startTime: date.startTime(_task.Tasks.start_time),
  418. duration: date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time),
  419. icon: function () {
  420. var statusIconMap = {
  421. 'pending': 'icon-cog',
  422. 'queued': 'icon-cog',
  423. 'in_progress': 'icon-cogs',
  424. 'completed': 'icon-ok',
  425. 'failed': 'icon-exclamation-sign',
  426. 'aborted': 'icon-minus',
  427. 'timedout': 'icon-time'
  428. };
  429. return statusIconMap[this.get('status')] || 'icon-cog';
  430. }.property('status')
  431. });
  432. },
  433. /**
  434. * Create hosts and tasks data structure for popup
  435. * Set data for hosts and tasks
  436. */
  437. onHostUpdate: function () {
  438. var self = this;
  439. var inputData = this.get("inputData");
  440. if (inputData) {
  441. var hostsArr = [];
  442. var hostsData;
  443. var hostsMap = {};
  444. if(this.get('isBackgroundOperations') && this.get("currentServiceId")){
  445. //hosts popup for Background Operations
  446. hostsData = inputData.findProperty("id", this.get("currentServiceId"));
  447. } else if (this.get("serviceName")) {
  448. //hosts popup for Wizards
  449. hostsData = inputData.findProperty("name", this.get("serviceName"));
  450. }
  451. if (hostsData) {
  452. if (hostsData.hostsMap) {
  453. //hosts data come from Background Operations as object map
  454. hostsMap = hostsData.hostsMap;
  455. } else if (hostsData.hosts) {
  456. //hosts data come from Wizard as array
  457. hostsData.hosts.forEach(function (_host) {
  458. hostsMap[_host.name] = _host;
  459. });
  460. }
  461. }
  462. var existedHosts = self.get('hosts');
  463. if (existedHosts && (existedHosts.length > 0) && this.get('currentServiceId') === this.get('previousServiceId')) {
  464. existedHosts.forEach(function (host) {
  465. var newHostInfo = hostsMap[host.get('name')];
  466. //update only hosts with changed tasks or currently opened tasks of host
  467. if (newHostInfo && (!this.get('isBackgroundOperations') || newHostInfo.isModified || this.get('currentHostName') === host.get('name'))) {
  468. var hostStatus = self.getStatus(newHostInfo.logTasks);
  469. var hostProgress = self.getProgress(newHostInfo.logTasks);
  470. host.set('status', App.format.taskStatus(hostStatus[0]));
  471. host.set('icon', hostStatus[1]);
  472. host.set('barColor', hostStatus[2]);
  473. host.set('isInProgress', hostStatus[3]);
  474. host.set('progress', hostProgress);
  475. host.set('barWidth', "width:" + hostProgress + "%;");
  476. host.set('logTasks', newHostInfo.logTasks);
  477. var existTasks = host.get('tasks');
  478. if (existTasks) {
  479. newHostInfo.logTasks.forEach(function (_task) {
  480. var existTask = existTasks.findProperty('id', _task.Tasks.id);
  481. if (existTask) {
  482. var status = _task.Tasks.status;
  483. existTask.set('status', App.format.taskStatus(status));
  484. existTask.set('stdout', _task.Tasks.stdout);
  485. existTask.set('stderr', _task.Tasks.stderr);
  486. // Verified that this is needed.
  487. existTask.set('outputLog', _task.Tasks.output_log);
  488. existTask.set('errorLog', _task.Tasks.error_log);
  489. existTask.set('startTime', date.startTime(_task.Tasks.start_time));
  490. existTask.set('duration', date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time));
  491. // Puts some command information to render it
  492. var isRebalanceHDFSTask = (_task.Tasks.command === 'CUSTOM_COMMAND' && _task.Tasks.custom_command_name === 'REBALANCEHDFS');
  493. existTask.set('isRebalanceHDFSTask', isRebalanceHDFSTask);
  494. if(isRebalanceHDFSTask){
  495. var structuredOut = _task.Tasks.structured_out;
  496. if (!structuredOut || structuredOut === 'null') {
  497. structuredOut = {};
  498. }
  499. var barColorMap = {
  500. 'FAILED': 'progress-danger',
  501. 'ABORTED': 'progress-warning',
  502. 'TIMEDOUT': 'progress-warning',
  503. 'IN_PROGRESS': 'progress-info',
  504. 'COMPLETED': 'progress-success'
  505. };
  506. existTask.set('dataMoved', structuredOut['dataMoved'] || '0');
  507. existTask.set('dataLeft', structuredOut['dataLeft'] || '0');
  508. existTask.set('dataBeingMoved', structuredOut['dataBeingMoved'] || '0');
  509. existTask.set('barColor', barColorMap[status]);
  510. existTask.set('isInProgress', status == 'IN_PROGRESS');
  511. existTask.set('isNotComplete', ['QUEUED', 'IN_PROGRESS'].contains(status));
  512. existTask.set('completionProgressStyle', 'width:' + (structuredOut['completePercent'] || 0) * 100 + '%;');
  513. existTask.set('command', _task.Tasks.command);
  514. existTask.set('custom_command_name', _task.Tasks.custom_command_name);
  515. }
  516. } else {
  517. existTasks.pushObject(this.createTask(_task));
  518. }
  519. }, this);
  520. }
  521. }
  522. }, this);
  523. } else {
  524. for (var hostName in hostsMap) {
  525. var _host = hostsMap[hostName];
  526. var tasks = _host.logTasks;
  527. var hostInfo = Ember.Object.create({
  528. name: hostName,
  529. publicName: _host.publicName,
  530. displayName: function () {
  531. return this.get('name').length < 43 ? this.get('name') : (this.get('name').substr(0, 40) + '...');
  532. }.property('name'),
  533. progress: 0,
  534. status: App.format.taskStatus("PENDING"),
  535. serviceName: _host.serviceName,
  536. isVisible: true,
  537. icon: "icon-cog",
  538. barColor: "progress-info",
  539. barWidth: "width:0%;"
  540. });
  541. if (tasks.length) {
  542. tasks = tasks.sortProperty('Tasks.id');
  543. var hostStatus = self.getStatus(tasks);
  544. var hostProgress = self.getProgress(tasks);
  545. hostInfo.set('status', App.format.taskStatus(hostStatus[0]));
  546. hostInfo.set('icon', hostStatus[1]);
  547. hostInfo.set('barColor', hostStatus[2]);
  548. hostInfo.set('isInProgress', hostStatus[3]);
  549. hostInfo.set('progress', hostProgress);
  550. hostInfo.set('barWidth', "width:" + hostProgress + "%;");
  551. }
  552. hostInfo.set('logTasks', tasks);
  553. hostsArr.push(hostInfo);
  554. }
  555. hostsArr = hostsArr.sortProperty('name');
  556. hostsArr.setEach("serviceName", this.get("serviceName"));
  557. self.set("hosts", hostsArr);
  558. self.set('previousServiceId', this.get('currentServiceId'));
  559. }
  560. }
  561. var operation = this.get('servicesInfo').findProperty('name', this.get('serviceName'));
  562. if (!operation || (operation && operation.get('progress') == 100)) {
  563. this.set('operationInfo', null);
  564. } else {
  565. this.set('operationInfo', operation);
  566. }
  567. },
  568. /**
  569. * Show popup
  570. * @return {App.ModalPopup} PopupObject For testing purposes
  571. */
  572. createPopup: function () {
  573. var self = this;
  574. var servicesInfo = this.get("servicesInfo");
  575. var isBackgroundOperations = this.get('isBackgroundOperations');
  576. var categoryObject = Em.Object.extend({
  577. value: '',
  578. count: 0,
  579. labelPath: '',
  580. label: function(){
  581. return Em.I18n.t(this.get('labelPath')).format(this.get('count'));
  582. }.property('count')
  583. });
  584. self.set('isPopup', App.ModalPopup.show({
  585. isLogWrapHidden: true,
  586. isTaskListHidden: true,
  587. isHostListHidden: true,
  588. isServiceListHidden: false,
  589. isHideBodyScroll: true,
  590. /**
  591. * no need to track is it loaded when popup contain only list of hosts
  592. * @type {bool}
  593. */
  594. isLoaded: !isBackgroundOperations,
  595. /**
  596. * is BG-popup opened
  597. * @type {bool}
  598. */
  599. isOpen: false,
  600. didInsertElement: function(){
  601. this._super();
  602. this.set('isOpen', true);
  603. },
  604. /**
  605. * @type {Em.View}
  606. */
  607. headerClass: Em.View.extend({
  608. controller: this,
  609. template: Ember.Handlebars.compile('{{popupHeaderName}} ' +
  610. '{{#unless view.parentView.isHostListHidden}}{{#if controller.operationInfo.isAbortable}}' +
  611. '{{view controller.abortIcon servicesInfoBinding="controller.operationInfo"}}' +
  612. '{{/if}}{{/unless}}')
  613. }),
  614. /**
  615. * @type {String[]}
  616. */
  617. classNames: ['sixty-percent-width-modal', 'host-progress-popup'],
  618. /**
  619. * for the checkbox: do not show this dialog again
  620. * @type {bool}
  621. */
  622. hasFooterCheckbox: true,
  623. /**
  624. * Auto-display BG-popup
  625. * @type {bool}
  626. */
  627. isNotShowBgChecked : null,
  628. /**
  629. * Save user pref about auto-display BG-popup
  630. */
  631. updateNotShowBgChecked: function () {
  632. var curVal = !this.get('isNotShowBgChecked');
  633. var key = App.router.get('applicationController').persistKey();
  634. if (!App.get('testMode')) {
  635. App.router.get('applicationController').postUserPref(key, curVal);
  636. }
  637. }.observes('isNotShowBgChecked'),
  638. autoHeight: false,
  639. closeModelPopup: function () {
  640. this.set('isOpen', false);
  641. if(isBackgroundOperations){
  642. $(this.get('element')).detach();
  643. App.router.get('backgroundOperationsController').set('levelInfo.name', 'REQUESTS_LIST');
  644. } else {
  645. this.hide();
  646. self.set('isPopup', null);
  647. }
  648. },
  649. onPrimary: function () {
  650. this.closeModelPopup();
  651. },
  652. onClose: function () {
  653. this.closeModelPopup();
  654. },
  655. secondary: null,
  656. bodyClass: App.TableView.extend({
  657. templateName: require('templates/common/host_progress_popup'),
  658. showTextArea: false,
  659. isServiceEmptyList: true,
  660. isTasksEmptyList: true,
  661. controller: this,
  662. sourceRequestScheduleId: -1,
  663. sourceRequestScheduleRunning: false,
  664. sourceRequestScheduleAborted: false,
  665. sourceRequestScheduleCommand: null,
  666. hosts: self.get('hosts'),
  667. services: self.get('servicesInfo'),
  668. filterMap: {
  669. pending: ["pending", "queued"],
  670. in_progress: ["in_progress", "upgrading"],
  671. failed: ["failed"],
  672. completed: ["completed", "success"],
  673. aborted: ["aborted"],
  674. timedout: ["timedout"]
  675. },
  676. pagination: true,
  677. isPaginate: false,
  678. /**
  679. * Select box, display names and values
  680. */
  681. categories: [
  682. categoryObject.create({value: 'all', labelPath: 'hostPopup.status.category.all'}),
  683. categoryObject.create({value: 'pending', labelPath: 'hostPopup.status.category.pending'}),
  684. categoryObject.create({value: 'in_progress', labelPath: 'hostPopup.status.category.inProgress'}),
  685. categoryObject.create({value: 'failed', labelPath: 'hostPopup.status.category.failed'}),
  686. categoryObject.create({value: 'completed', labelPath: 'hostPopup.status.category.success'}),
  687. categoryObject.create({value: 'aborted', labelPath: 'hostPopup.status.category.aborted'}),
  688. categoryObject.create({value: 'timedout', labelPath: 'hostPopup.status.category.timedout'})
  689. ],
  690. /**
  691. * Selected option is bound to this values
  692. */
  693. serviceCategory: null,
  694. hostCategory: null,
  695. taskCategory: null,
  696. /**
  697. * flag to indicate whether level data has already been loaded
  698. * applied only to HOSTS_LIST and TASK_DETAILS levels, whereas async query used to obtain data
  699. */
  700. isLevelLoaded: true,
  701. isHostEmptyList: function() {
  702. return !this.get('pageContent.length');
  703. }.property('pageContent.length'),
  704. currentHost: function () {
  705. return this.get('hosts') && this.get('hosts').findProperty('name', this.get('controller.currentHostName'));
  706. }.property('controller.currentHostName'),
  707. tasks: function () {
  708. var currentHost = this.get('currentHost');
  709. if (currentHost) {
  710. return currentHost.get('tasks');
  711. }
  712. return [];
  713. }.property('currentHost.tasks', 'currentHost.tasks.@each.status'),
  714. /**
  715. * Preset values on init
  716. */
  717. setOnStart: function () {
  718. this.set('serviceCategory', this.get('categories').findProperty('value','all'));
  719. if (this.get("controller.isBackgroundOperations")) {
  720. this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
  721. this.updateHostInfo();
  722. } else {
  723. this.set("parentView.isHostListHidden", false);
  724. this.set("parentView.isServiceListHidden", true);
  725. }
  726. },
  727. /**
  728. * force popup to show list of operations
  729. */
  730. resetState: function(){
  731. if(this.get('parentView.isOpen')){
  732. this.set('parentView.isLogWrapHidden', true);
  733. this.set('parentView.isTaskListHidden', true);
  734. this.set('parentView.isHostListHidden', true);
  735. this.set('parentView.isServiceListHidden', false);
  736. this.get("controller").setBackgroundOperationHeader(false);
  737. this.setOnStart();
  738. }
  739. }.observes('parentView.isOpen'),
  740. /**
  741. * When popup is opened, and data after polling has changed, update this data in component
  742. */
  743. updateHostInfo: function () {
  744. if(!this.get('parentView.isOpen')) return;
  745. this.set('parentView.isLoaded', false);
  746. this.get("controller").set("inputData", this.get("controller.dataSourceController.services"));
  747. this.get("controller").onServiceUpdate(this.get('parentView.isServiceListHidden'));
  748. this.get("controller").onHostUpdate();
  749. this.set('parentView.isLoaded', true);
  750. this.set("hosts", this.get("controller.hosts"));
  751. this.set("services", this.get("controller.servicesInfo"));
  752. this.set('isLevelLoaded', true);
  753. }.observes("controller.dataSourceController.serviceTimestamp"),
  754. /**
  755. * Depending on service filter, set which services should be shown
  756. */
  757. visibleServices: function () {
  758. if (this.get("services")) {
  759. this.set("isServiceEmptyList", true);
  760. if (this.get('serviceCategory.value')) {
  761. var filter = this.get('serviceCategory.value');
  762. var services = this.get('services');
  763. this.set("isServiceEmptyList", this.setVisibility(filter, services));
  764. }
  765. }
  766. }.observes('serviceCategory', 'services', 'services.@each.status'),
  767. /**
  768. * Depending on hosts filter, set which hosts should be shown
  769. */
  770. filter: function() {
  771. var _this = this,
  772. filter = this.get('hostCategory.value'),
  773. hosts = this.get('hosts') || [];
  774. if (!filter || !hosts.length) return;
  775. if (filter === 'all') {
  776. this.set('filteredContent', hosts);
  777. } else {
  778. this.set('filteredContent', hosts.filter(function(item) {
  779. return _this.get('filterMap')[filter].contains(item.status);
  780. }));
  781. }
  782. }.observes('hosts.length', 'hostCategory.value'),
  783. /**
  784. * Reset startIndex property back to 1 when filter type has been changed.
  785. */
  786. resetIndex: function() {
  787. if (this.get('hostCategory.value')) this.set('startIndex', 1)
  788. }.observes('hostCategory.value'),
  789. /**
  790. * Depending on tasks filter, set which tasks should be shown
  791. */
  792. visibleTasks: function () {
  793. this.set("isTasksEmptyList", true);
  794. if (this.get('taskCategory.value') && this.get('tasks')) {
  795. var filter = this.get('taskCategory.value');
  796. var tasks = this.get('tasks');
  797. this.set("isTasksEmptyList", this.setVisibility(filter, tasks));
  798. }
  799. }.observes('taskCategory', 'tasks', 'tasks.@each.status'),
  800. /**
  801. * Depending on selected filter type, set object visibility value
  802. * @param filter
  803. * @param obj
  804. * @return {bool} isEmptyList
  805. */
  806. setVisibility: function (filter, obj) {
  807. var isEmptyList = true;
  808. if (filter == "all") {
  809. obj.setEach("isVisible", true);
  810. isEmptyList = !(obj.length > 0);
  811. } else {
  812. obj.forEach(function(item){
  813. item.set('isVisible', this.get('filterMap')[filter].contains(item.status));
  814. isEmptyList = (isEmptyList) ? !item.get('isVisible') : false;
  815. }, this)
  816. }
  817. return isEmptyList;
  818. },
  819. /**
  820. * Depending on currently viewed tab, call setSelectCount function
  821. */
  822. updateSelectView: function () {
  823. var isPaginate;
  824. if (!this.get('parentView.isHostListHidden')) {
  825. //since lazy loading used for hosts, we need to get hosts info directly from controller, that always contains entire array of data
  826. this.get('controller').setSelectCount(this.get("controller.hosts"), this.get('categories'));
  827. isPaginate = true;
  828. } else if (!this.get('parentView.isTaskListHidden')) {
  829. this.get('controller').setSelectCount(this.get("tasks"), this.get('categories'));
  830. } else if (!this.get('parentView.isServiceListHidden')) {
  831. this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
  832. }
  833. this.set('isPaginate', !!isPaginate);
  834. }.observes('tasks.@each.status', 'hosts.@each.status', 'parentView.isTaskListHidden', 'parentView.isHostListHidden', 'services.length', 'services.@each.status'),
  835. /**
  836. * control data uploading, depending on which display level is showed
  837. * @param levelName
  838. */
  839. switchLevel: function (levelName) {
  840. var dataSourceController = this.get('controller.dataSourceController');
  841. var securityControllers = [
  842. 'mainAdminSecurityDisableController',
  843. 'mainAdminSecurityAddStep4Controller'
  844. ];
  845. if (this.get("controller.isBackgroundOperations")) {
  846. var levelInfo = dataSourceController.get('levelInfo');
  847. levelInfo.set('taskId', this.get('openedTaskId'));
  848. levelInfo.set('requestId', this.get('controller.currentServiceId'));
  849. levelInfo.set('name', levelName);
  850. if (levelName === 'HOSTS_LIST') {
  851. this.set('isLevelLoaded', dataSourceController.requestMostRecent());
  852. this.set('hostCategory', this.get('categories').findProperty('value','all'));
  853. } else if (levelName === 'TASK_DETAILS') {
  854. dataSourceController.requestMostRecent();
  855. this.set('isLevelLoaded', false);
  856. } else if (levelName === 'REQUESTS_LIST') {
  857. this.set('serviceCategory', this.get('categories').findProperty('value','all'));
  858. this.get('controller.hosts').clear();
  859. dataSourceController.requestMostRecent();
  860. } else {
  861. this.set('taskCategory', this.get('categories').findProperty('value','all'));
  862. }
  863. } else if (securityControllers.contains(dataSourceController.get('name'))) {
  864. if (levelName === 'TASK_DETAILS') {
  865. this.set('isLevelLoaded', false);
  866. dataSourceController.startUpdatingTask(this.get('controller.currentServiceId'), this.get('openedTaskId'));
  867. } else {
  868. dataSourceController.stopUpdatingTask(this.get('controller.currentServiceId'));
  869. }
  870. }
  871. },
  872. /**
  873. * Onclick handler for button <-Tasks
  874. */
  875. backToTaskList: function () {
  876. this.destroyClipBoard();
  877. this.set("openedTaskId", 0);
  878. this.set("parentView.isLogWrapHidden", true);
  879. this.set("parentView.isTaskListHidden", false);
  880. this.switchLevel("TASKS_LIST");
  881. },
  882. /**
  883. * Onclick handler for button <-Hosts
  884. */
  885. backToHostList: function () {
  886. this.set("parentView.isHostListHidden", false);
  887. this.set("parentView.isTaskListHidden", true);
  888. this.get("controller").set("popupHeaderName", this.get("controller.serviceName"));
  889. this.get("controller").set("operationInfo", this.get('controller.servicesInfo').findProperty('name', this.get('controller.serviceName')));
  890. this.switchLevel("HOSTS_LIST");
  891. },
  892. /**
  893. * Onclick handler for button <-Services
  894. */
  895. backToServiceList: function () {
  896. this.get("controller").set("serviceName", "");
  897. this.set("parentView.isHostListHidden", true);
  898. this.set("parentView.isServiceListHidden", false);
  899. this.set("parentView.isTaskListHidden", true);
  900. this.set("parentView.isLogWrapHidden", true);
  901. this.set("hosts", null);
  902. this.get("controller").setBackgroundOperationHeader(false);
  903. this.switchLevel("REQUESTS_LIST");
  904. },
  905. /**
  906. * Onclick handler for Show more ..
  907. */
  908. requestMoreOperations: function () {
  909. var BGOController = App.router.get('backgroundOperationsController');
  910. var count = BGOController.get('operationsCount');
  911. BGOController.set('operationsCount', (count + 10));
  912. BGOController.requestMostRecent();
  913. },
  914. setShowMoreAvailable: function () {
  915. if (this.get('parentView.isOpen')) {
  916. this.set('isShowMore', App.router.get("backgroundOperationsController.isShowMoreAvailable"));
  917. }
  918. }.observes('parentView.isOpen', 'App.router.backgroundOperationsController.isShowMoreAvailable'),
  919. isShowMore: true,
  920. /**
  921. * Onclick handler for selected Service
  922. * @param {Object} event
  923. */
  924. gotoHosts: function (event) {
  925. this.get("controller").set("serviceName", event.context.get("name"));
  926. this.get("controller").set("currentServiceId", event.context.get("id"));
  927. this.get("controller").set("currentHostName", null);
  928. this.get("controller").onHostUpdate();
  929. this.switchLevel("HOSTS_LIST");
  930. var servicesInfo = this.get("controller.hosts");
  931. this.set("controller.popupHeaderName", event.context.get("name"));
  932. this.set("controller.operationInfo", event.context);
  933. //apply lazy loading on cluster with more than 100 nodes
  934. if (servicesInfo.length > 100) {
  935. this.set('hosts', servicesInfo.slice(0, 50));
  936. } else {
  937. this.set('hosts', servicesInfo);
  938. }
  939. this.set("parentView.isServiceListHidden", true);
  940. this.set("parentView.isHostListHidden", false);
  941. this.set("parentView.isTaskListHidden", true);
  942. $(".modal").scrollTop(0);
  943. $(".modal-body").scrollTop(0);
  944. if (servicesInfo.length > 100) {
  945. Em.run.next(this, function(){
  946. this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
  947. });
  948. }
  949. // Determine if source request schedule is present
  950. this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
  951. this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
  952. this.refreshRequestScheduleInfo();
  953. },
  954. isRequestSchedule : function() {
  955. var id = this.get('sourceRequestScheduleId');
  956. return id != null && !isNaN(id) && id > -1;
  957. }.property('sourceRequestScheduleId'),
  958. refreshRequestScheduleInfo : function() {
  959. var self = this;
  960. var id = this.get('sourceRequestScheduleId');
  961. batchUtils.getRequestSchedule(id, function(data) {
  962. if (data != null && data.RequestSchedule != null &&
  963. data.RequestSchedule.status != null) {
  964. switch (data.RequestSchedule.status) {
  965. case 'DISABLED':
  966. self.set('sourceRequestScheduleRunning', false);
  967. self.set('sourceRequestScheduleAborted', true);
  968. break;
  969. case 'COMPLETED':
  970. self.set('sourceRequestScheduleRunning', false);
  971. self.set('sourceRequestScheduleAborted', false);
  972. break;
  973. case 'SCHEDULED':
  974. self.set('sourceRequestScheduleRunning', true);
  975. self.set('sourceRequestScheduleAborted', false);
  976. break;
  977. }
  978. } else {
  979. self.set('sourceRequestScheduleRunning', false);
  980. self.set('sourceRequestScheduleAborted', false);
  981. }
  982. }, function(xhr, textStatus, error, opt) {
  983. console.log("Error getting request schedule information: ", textStatus, error, opt);
  984. self.set('sourceRequestScheduleRunning', false);
  985. self.set('sourceRequestScheduleAborted', false);
  986. });
  987. }.observes('sourceRequestScheduleId'),
  988. /**
  989. * Attempts to abort the current request schedule
  990. */
  991. doAbortRequestSchedule: function(event){
  992. var self = this;
  993. var id = event.context;
  994. console.log("Aborting request schedule: ", id);
  995. batchUtils.doAbortRequestSchedule(id, function(){
  996. self.refreshRequestScheduleInfo();
  997. });
  998. },
  999. requestScheduleAbortLabel : function() {
  1000. var label = Em.I18n.t("common.abort");
  1001. var command = this.get('sourceRequestScheduleCommand');
  1002. if (command != null && "ROLLING-RESTART" == command) {
  1003. label = Em.I18n.t("hostPopup.bgop.abort.rollingRestart");
  1004. }
  1005. return label;
  1006. }.property('sourceRequestScheduleCommand'),
  1007. /**
  1008. * Onclick handler for selected Host
  1009. * @param {Object} event
  1010. */
  1011. gotoTasks: function (event) {
  1012. var tasksInfo = [];
  1013. event.context.logTasks.forEach(function (_task) {
  1014. tasksInfo.pushObject(this.get("controller").createTask(_task));
  1015. }, this);
  1016. if (tasksInfo.length) {
  1017. this.get("controller").set("popupHeaderName", event.context.publicName);
  1018. this.get("controller").set("currentHostName", event.context.publicName);
  1019. }
  1020. this.switchLevel("TASKS_LIST");
  1021. this.set('currentHost.tasks', tasksInfo);
  1022. this.set("parentView.isHostListHidden", true);
  1023. this.set("parentView.isTaskListHidden", false);
  1024. $(".modal").scrollTop(0);
  1025. $(".modal-body").scrollTop(0);
  1026. },
  1027. stopRebalanceHDFS: function () {
  1028. var hostPopup = this;
  1029. return App.showConfirmationPopup(function () {
  1030. App.ajax.send({
  1031. name : 'cancel.background.operation',
  1032. sender : hostPopup,
  1033. data : {
  1034. requestId : hostPopup.get('controller.currentServiceId')
  1035. }
  1036. });
  1037. hostPopup.backToServiceList();
  1038. });
  1039. },
  1040. /**
  1041. * Onclick handler for selected Task
  1042. */
  1043. openTaskLogInDialog: function () {
  1044. if ($(".task-detail-log-clipboard").length > 0) {
  1045. this.destroyClipBoard();
  1046. }
  1047. var newWindow = window.open();
  1048. var newDocument = newWindow.document;
  1049. newDocument.write($(".task-detail-log-info").html());
  1050. newDocument.close();
  1051. },
  1052. openedTaskId: 0,
  1053. /**
  1054. * Return task detail info of opened task
  1055. */
  1056. openedTask: function () {
  1057. if (!(this.get('openedTaskId') && this.get('tasks'))) {
  1058. return Ember.Object.create();
  1059. }
  1060. return this.get('tasks').findProperty('id', this.get('openedTaskId'));
  1061. }.property('tasks', 'tasks.@each.stderr', 'tasks.@each.stdout', 'openedTaskId'),
  1062. /**
  1063. * Onclick event for show task detail info
  1064. * @param {Object} event
  1065. */
  1066. toggleTaskLog: function (event) {
  1067. var taskInfo = event.context;
  1068. this.set("parentView.isLogWrapHidden", false);
  1069. if ($(".task-detail-log-clipboard").length > 0) {
  1070. this.destroyClipBoard();
  1071. }
  1072. this.set("parentView.isHostListHidden", true);
  1073. this.set("parentView.isTaskListHidden", true);
  1074. this.set('openedTaskId', taskInfo.id);
  1075. this.switchLevel("TASK_DETAILS");
  1076. $(".modal").scrollTop(0);
  1077. $(".modal-body").scrollTop(0);
  1078. },
  1079. /**
  1080. * Onclick event for copy to clipboard button
  1081. */
  1082. textTrigger: function () {
  1083. $(".task-detail-log-clipboard").length > 0 ? this.destroyClipBoard() : this.createClipBoard();
  1084. },
  1085. /**
  1086. * Create Clip Board
  1087. */
  1088. createClipBoard: function () {
  1089. var logElement = $(".task-detail-log-maintext");
  1090. $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
  1091. $(".task-detail-log-clipboard")
  1092. .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
  1093. .css("display", "block")
  1094. .width(logElement.width())
  1095. .height(logElement.height())
  1096. .select();
  1097. logElement.css("display", "none")
  1098. },
  1099. /**
  1100. * Destroy Clip Board
  1101. */
  1102. destroyClipBoard: function () {
  1103. $(".task-detail-log-clipboard").remove();
  1104. $(".task-detail-log-maintext").css("display", "block");
  1105. }
  1106. })
  1107. }));
  1108. return self.get('isPopup');
  1109. }
  1110. });