host_progress_popup.js 45 KB

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