host_progress_popup_body_view.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  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/date');
  21. var fileUtils = require('utils/file_utils');
  22. /**
  23. * @typedef {object} TaskRelationObject
  24. * @property {string} type relation type 'service', 'component'
  25. * @property {string} [value] optional value of relation e.g. name of component or service
  26. */
  27. /**
  28. * Option for "filter by state" dropdown
  29. * @typedef {object} progressPopupCategoryObject
  30. * @property {string} value "all|pending|in progress|failed|completed|aborted|timedout"
  31. * @property {number} count number of items with <code>state</code> equal to <code>this.value</code>
  32. * @property {string} labelPath key in the messages.js
  33. * @property {string} label localized label
  34. */
  35. var categoryObject = Em.Object.extend({
  36. value: '',
  37. count: 0,
  38. labelPath: '',
  39. label: function () {
  40. return Em.I18n.t(this.get('labelPath')).format(this.get('count'));
  41. }.property('count', 'labelPath')
  42. });
  43. /**
  44. * @class HostProgressPopupBodyView
  45. * @type {Em.View}
  46. */
  47. App.HostProgressPopupBodyView = App.TableView.extend({
  48. templateName: require('templates/common/host_progress_popup'),
  49. /**
  50. * @type {boolean}
  51. */
  52. showTextArea: false,
  53. /**
  54. * @type {boolean}
  55. */
  56. isServiceEmptyList: true,
  57. /**
  58. * @type {boolean}
  59. */
  60. isTasksEmptyList: true,
  61. /**
  62. * @type {number}
  63. */
  64. sourceRequestScheduleId: -1,
  65. /**
  66. * @type {boolean}
  67. */
  68. sourceRequestScheduleRunning: false,
  69. /**
  70. * @type {boolean}
  71. */
  72. sourceRequestScheduleAborted: false,
  73. /**
  74. * @type {?string}
  75. */
  76. sourceRequestScheduleCommand: null,
  77. /**
  78. * @type {string}
  79. */
  80. clipBoardContent: null,
  81. isClipBoardActive: false,
  82. /**
  83. * Alias for <code>controller.hosts</code>
  84. *
  85. * @type {wrappedHost[]}
  86. */
  87. hosts: function () {
  88. return this.get('controller.hosts')
  89. }.property('controller.hosts.[]'),
  90. /**
  91. * Alias for <code>controller.servicesInfo</code>
  92. *
  93. * @type {wrappedService[]}
  94. */
  95. services: function () {
  96. return this.get('controller.servicesInfo');
  97. }.property('controller.servicesInfo.[]'),
  98. /**
  99. * @type {number}
  100. */
  101. openedTaskId: 0,
  102. /**
  103. * Return task detail info of opened task
  104. *
  105. * @type {wrappedTask}
  106. */
  107. openedTask: function () {
  108. if (!(this.get('openedTaskId') && this.get('tasks'))) {
  109. return Em.Object.create();
  110. }
  111. return this.get('tasks').findProperty('id', this.get('openedTaskId'));
  112. }.property('tasks', 'tasks.@each.stderr', 'tasks.@each.stdout', 'openedTaskId'),
  113. /**
  114. * @type {object}
  115. */
  116. filterMap: {
  117. pending: ["pending", "queued"],
  118. in_progress: ["in_progress", "upgrading"],
  119. failed: ["failed"],
  120. completed: ["completed", "success"],
  121. aborted: ["aborted"],
  122. timedout: ["timedout"]
  123. },
  124. /**
  125. * Determines if "Show More ..."-link should be shown
  126. * @type {boolean}
  127. */
  128. isShowMore: true,
  129. /**
  130. * @type {boolean}
  131. */
  132. pagination: true,
  133. /**
  134. * @type {boolean}
  135. */
  136. isPaginate: false,
  137. /**
  138. * Select box, display names and values
  139. *
  140. * @type {progressPopupCategoryObject[]}
  141. */
  142. categories: [
  143. categoryObject.create({value: 'all', labelPath: 'hostPopup.status.category.all'}),
  144. categoryObject.create({value: 'pending', labelPath: 'hostPopup.status.category.pending'}),
  145. categoryObject.create({value: 'in_progress', labelPath: 'hostPopup.status.category.inProgress'}),
  146. categoryObject.create({value: 'failed', labelPath: 'hostPopup.status.category.failed'}),
  147. categoryObject.create({value: 'completed', labelPath: 'hostPopup.status.category.success'}),
  148. categoryObject.create({value: 'aborted', labelPath: 'hostPopup.status.category.aborted'}),
  149. categoryObject.create({value: 'timedout', labelPath: 'hostPopup.status.category.timedout'})
  150. ],
  151. /**
  152. * Selected option is bound to this values
  153. * @type {?progressPopupCategoryObject}
  154. */
  155. serviceCategory: null,
  156. /**
  157. * @type {?progressPopupCategoryObject}
  158. */
  159. hostCategory: null,
  160. /**
  161. * @type {?progressPopupCategoryObject}
  162. */
  163. taskCategory: null,
  164. /**
  165. * flag to indicate whether level data has already been loaded
  166. * applied only to HOSTS_LIST and TASK_DETAILS levels, whereas async query used to obtain data
  167. *
  168. * @type {boolean}
  169. */
  170. isLevelLoaded: true,
  171. /**
  172. * <code>switchLevel</code> for some <code>controller.dataSourceController</code> should be customized
  173. * So, this map contains information about customize-methods
  174. * Format: key - <code>dataSourceController.name</code>, value - method name in this view
  175. * Method is called with same arguments as <code>switchLevel</code> is
  176. *
  177. * @type {object}
  178. */
  179. customControllersSwitchLevelMap: {
  180. highAvailabilityProgressPopupController: '_switchLevelForHAProgressPopupController'
  181. },
  182. /**
  183. * @type {boolean}
  184. */
  185. isHostEmptyList: Em.computed.empty('pageContent'),
  186. /**
  187. * @type {wrappedHost}
  188. */
  189. currentHost: function () {
  190. return this.get('hosts') && this.get('hosts').findProperty('name', this.get('controller.currentHostName'));
  191. }.property('controller.currentHostName'),
  192. /**
  193. * Tasks for current shown host (<code>currentHost</code>)
  194. *
  195. * @type {wrappedTask[]}
  196. */
  197. tasks: function () {
  198. var currentHost = this.get('currentHost');
  199. return currentHost ? currentHost.get('tasks') : [];
  200. }.property('currentHost.tasks', 'currentHost.tasks.@each.status'),
  201. /**
  202. * Message about aborting operation
  203. * Custom for Rolling Restart
  204. *
  205. * @type {string}
  206. */
  207. requestScheduleAbortLabel: function () {
  208. return 'ROLLING-RESTART' == this.get('sourceRequestScheduleCommand') ?
  209. Em.I18n.t("hostPopup.bgop.abort.rollingRestart"):
  210. Em.I18n.t("common.abort");
  211. }.property('sourceRequestScheduleCommand'),
  212. didInsertElement: function () {
  213. this.updateHostInfo();
  214. this.subscribeResize();
  215. },
  216. willDestroyElement: function () {
  217. if (this.get('controller.dataSourceController.name') == 'highAvailabilityProgressPopupController') {
  218. this.set('controller.dataSourceController.isTaskPolling', false);
  219. }
  220. this.unsubscribeResize();
  221. },
  222. /**
  223. * Subscribe for window <code>resize</code> event.
  224. *
  225. * @method subscribeResize
  226. */
  227. subscribeResize: function() {
  228. var self = this;
  229. $(window).on('resize', this.resizeHandler.bind(this));
  230. Em.run.next(this, function() {
  231. self.resizeHandler();
  232. });
  233. },
  234. /**
  235. * Remove event listener for window <code>resize</code> event.
  236. */
  237. unsubscribeResize: function() {
  238. $(window).off('resize', this.resizeHandler.bind(this));
  239. },
  240. /**
  241. * This method handles window resize and fit modal body content according to visible items for each <code>level</code>.
  242. *
  243. * @method resizeHandler
  244. */
  245. resizeHandler: function() {
  246. if (this.get('state') === 'destroyed' || !this.get('parentView.isOpen')) return;
  247. var headerHeight = 48,
  248. modalFooterHeight = 60,
  249. taskTopWrapHeight = 40,
  250. modalTopOffset = $('.modal').offset().top,
  251. contentPaddingBottom = 40,
  252. hostsPageBarHeight = 45,
  253. tabbedContentNavHeight = 68,
  254. logComponentFileNameHeight = 30,
  255. levelName = this.get('currentLevelName'),
  256. boLevelHeightMap = {
  257. 'REQUESTS_LIST': {
  258. height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
  259. target: '#service-info'
  260. },
  261. 'HOSTS_LIST': {
  262. height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom - hostsPageBarHeight,
  263. target: '#host-info'
  264. },
  265. 'TASKS_LIST': {
  266. height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
  267. target: '#host-log'
  268. },
  269. 'TASK_DETAILS': {
  270. height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
  271. target: ['.task-detail-log-info', '.log-tail-content.pre-styled']
  272. }
  273. },
  274. currentLevelHeight,
  275. resizeTarget;
  276. if (levelName && levelName in boLevelHeightMap) {
  277. resizeTarget = boLevelHeightMap[levelName].target;
  278. currentLevelHeight = boLevelHeightMap[levelName].height;
  279. if (levelName === 'TASK_DETAILS' && $('.task-detail-info').hasClass('task-detail-info-tabbed')) {
  280. currentLevelHeight -= tabbedContentNavHeight;
  281. }
  282. if (!Em.isArray(resizeTarget)) {
  283. resizeTarget = [resizeTarget];
  284. }
  285. resizeTarget.forEach(function(target) {
  286. if (target === '.log-tail-content.pre-styled') {
  287. currentLevelHeight -= logComponentFileNameHeight;
  288. }
  289. $(target).css('maxHeight', currentLevelHeight + 'px');
  290. });
  291. }
  292. },
  293. currentLevelName: function() {
  294. return this.get('controller.dataSourceController.levelInfo.name');
  295. }.property('controller.dataSourceController.levelInfo.name'),
  296. /**
  297. * Preset values on init
  298. *
  299. * @method setOnStart
  300. */
  301. setOnStart: function () {
  302. this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
  303. if (this.get("controller.isBackgroundOperations")) {
  304. this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
  305. this.updateHostInfo();
  306. }
  307. else {
  308. this.set("parentView.isHostListHidden", false);
  309. this.set("parentView.isServiceListHidden", true);
  310. }
  311. },
  312. /**
  313. * force popup to show list of operations
  314. *
  315. * @method resetState
  316. */
  317. resetState: function () {
  318. if (this.get('parentView.isOpen')) {
  319. this.get('parentView').setProperties({
  320. isLogWrapHidden: true,
  321. isTaskListHidden: true,
  322. isHostListHidden: true,
  323. isServiceListHidden: false
  324. });
  325. this.get("controller").setBackgroundOperationHeader(false);
  326. this.setOnStart();
  327. this.rerender();
  328. }
  329. }.observes('parentView.isOpen'),
  330. /**
  331. * When popup is opened, and data after polling has changed, update this data in component
  332. *
  333. * @method updateHostInfo
  334. */
  335. updateHostInfo: function () {
  336. if (!this.get('parentView.isOpen')) {
  337. return;
  338. }
  339. this.set('parentView.isLoaded', false);
  340. this.get("controller").set("inputData", this.get("controller.dataSourceController.services"));
  341. this.get("controller").onServiceUpdate(this.get('parentView.isServiceListHidden'));
  342. this.get("controller").onHostUpdate();
  343. this.set('parentView.isLoaded', true);
  344. this.set("hosts", this.get("controller.hosts"));
  345. this.set("services", this.get("controller.servicesInfo"));
  346. this.set('isLevelLoaded', true);
  347. }.observes("controller.dataSourceController.serviceTimestamp"),
  348. /**
  349. * Depending on service filter, set which services should be shown
  350. *
  351. * @method visibleServices
  352. */
  353. visibleServices: function () {
  354. if (this.get("services")) {
  355. this.set("isServiceEmptyList", true);
  356. if (this.get('serviceCategory.value')) {
  357. var filter = this.get('serviceCategory.value');
  358. var services = this.get('services');
  359. this.set("isServiceEmptyList", this.setVisibility(filter, services));
  360. }
  361. }
  362. }.observes('serviceCategory', 'services', 'services.@each.status'),
  363. /**
  364. * Depending on hosts filter, set which hosts should be shown
  365. *
  366. * @method filter
  367. */
  368. filter: function () {
  369. var filter = this.get('hostCategory.value');
  370. var hosts = this.get('hosts') || [];
  371. var filterMap = this.get('filterMap');
  372. if (!filter || !hosts.length) {
  373. return;
  374. }
  375. if (filter === 'all') {
  376. this.set('filteredContent', hosts);
  377. }
  378. else {
  379. this.set('filteredContent', hosts.filter(function (item) {
  380. return filterMap[filter].contains(item.status);
  381. }));
  382. }
  383. }.observes('hosts.length', 'hostCategory.value'),
  384. /**
  385. * Reset startIndex property back to 1 when filter type has been changed.
  386. *
  387. * @method resetIndex
  388. */
  389. resetIndex: function () {
  390. if (this.get('hostCategory.value')) {
  391. this.set('startIndex', 1);
  392. }
  393. }.observes('hostCategory.value'),
  394. /**
  395. * Depending on tasks filter, set which tasks should be shown
  396. *
  397. * @method visibleTasks
  398. */
  399. visibleTasks: function () {
  400. this.set("isTasksEmptyList", true);
  401. if (this.get('taskCategory.value') && this.get('tasks')) {
  402. var filter = this.get('taskCategory.value');
  403. var tasks = this.get('tasks');
  404. this.set("isTasksEmptyList", this.setVisibility(filter, tasks));
  405. }
  406. }.observes('taskCategory', 'tasks', 'tasks.@each.status'),
  407. /**
  408. * Depending on selected filter type, set object visibility value
  409. *
  410. * @param filter
  411. * @param obj
  412. * @return {bool} isEmptyList
  413. * @method setVisibility
  414. */
  415. setVisibility: function (filter, obj) {
  416. var isEmptyList = true;
  417. var filterMap = this.get('filterMap');
  418. if (filter == "all") {
  419. obj.setEach("isVisible", true);
  420. isEmptyList = !obj.length;
  421. }
  422. else {
  423. obj.forEach(function (item) {
  424. item.set('isVisible', filterMap[filter].contains(item.status));
  425. isEmptyList = (isEmptyList) ? !item.get('isVisible') : false;
  426. }, this)
  427. }
  428. return isEmptyList;
  429. },
  430. /**
  431. * Depending on currently viewed tab, call setSelectCount function
  432. *
  433. * @method updateSelectView
  434. */
  435. updateSelectView: function () {
  436. var isPaginate;
  437. if (this.get('parentView.isHostListHidden')) {
  438. if (this.get('parentView.isTaskListHidden')) {
  439. if (!this.get('parentView.isServiceListHidden')) {
  440. this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
  441. }
  442. }
  443. else {
  444. this.get('controller').setSelectCount(this.get("tasks"), this.get('categories'));
  445. }
  446. }
  447. else {
  448. //since lazy loading used for hosts, we need to get hosts info directly from controller, that always contains entire array of data
  449. this.get('controller').setSelectCount(this.get("controller.hosts"), this.get('categories'));
  450. isPaginate = true;
  451. }
  452. this.set('isPaginate', !!isPaginate);
  453. }.observes('tasks.@each.status', 'hosts.@each.status', 'parentView.isTaskListHidden', 'parentView.isHostListHidden', 'services.length', 'services.@each.status'),
  454. /**
  455. * control data uploading, depending on which display level is showed
  456. *
  457. * @param {string} levelName
  458. * @method switchLevel
  459. */
  460. switchLevel: function (levelName) {
  461. var dataSourceController = this.get('controller.dataSourceController');
  462. var args = [].slice.call(arguments);
  463. this.get('hostComponentLogs').clear();
  464. if (this.get("controller.isBackgroundOperations")) {
  465. var levelInfo = dataSourceController.get('levelInfo');
  466. levelInfo.set('taskId', this.get('openedTaskId'));
  467. levelInfo.set('requestId', this.get('controller.currentServiceId'));
  468. levelInfo.set('name', levelName);
  469. if (levelName === 'HOSTS_LIST') {
  470. this.set('isLevelLoaded', dataSourceController.requestMostRecent());
  471. this.set('hostCategory', this.get('categories').findProperty('value', 'all'));
  472. }
  473. else {
  474. if (levelName === 'TASK_DETAILS') {
  475. dataSourceController.requestMostRecent();
  476. this.set('isLevelLoaded', false);
  477. }
  478. else {
  479. if (levelName === 'REQUESTS_LIST') {
  480. this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
  481. this.get('controller.hosts').clear();
  482. dataSourceController.requestMostRecent();
  483. }
  484. else {
  485. this.set('taskCategory', this.get('categories').findProperty('value', 'all'));
  486. }
  487. }
  488. }
  489. }
  490. else {
  491. var customControllersSwitchLevelMap = this.get('customControllersSwitchLevelMap');
  492. Em.tryInvoke(this, customControllersSwitchLevelMap[dataSourceController.get('name')], args);
  493. }
  494. },
  495. levelDidChange: function() {
  496. var levelName = this.get('controller.dataSourceController.levelInfo.name'),
  497. self = this;
  498. if (levelName && this.get('isLevelLoaded')) {
  499. Em.run.next(this, function() {
  500. self.resizeHandler();
  501. });
  502. }
  503. }.observes('controller.dataSourceController.levelInfo.name', 'isLevelLoaded'),
  504. popupIsOpenDidChange: function() {
  505. if (!this.get('isOpen')) {
  506. this.get('hostComponentLogs').clear();
  507. }
  508. }.observes('parentView.isOpen'),
  509. /**
  510. * Switch-level custom method for <code>highAvailabilityProgressPopupController</code>
  511. *
  512. * @param {string} levelName
  513. * @private
  514. */
  515. _switchLevelForHAProgressPopupController: function (levelName) {
  516. var dataSourceController = this.get('controller.dataSourceController');
  517. if (levelName === 'TASK_DETAILS') {
  518. this.set('isLevelLoaded', false);
  519. dataSourceController.startTaskPolling(this.get('openedTask.request_id'), this.get('openedTask.id'));
  520. Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
  521. dataSourceController.addObserver('taskInfo.' + this.get('parentView.detailedProperties')[key], this, 'updateTaskInfo');
  522. }, this);
  523. }
  524. else {
  525. dataSourceController.stopTaskPolling();
  526. }
  527. },
  528. /**
  529. * @method updateTaskInfo
  530. */
  531. updateTaskInfo: function () {
  532. var dataSourceController = this.get('controller.dataSourceController');
  533. var openedTask = this.get('openedTask');
  534. if (openedTask && openedTask.get('id') == dataSourceController.get('taskInfo.id')) {
  535. this.set('isLevelLoaded', true);
  536. Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
  537. openedTask.set(key, dataSourceController.get('taskInfo.' + key));
  538. }, this);
  539. }
  540. },
  541. /**
  542. * Onclick handler for button <-Tasks
  543. *
  544. * @method backToTaskList
  545. */
  546. backToTaskList: function () {
  547. this.destroyClipBoard();
  548. this.set("openedTaskId", 0);
  549. this.set("parentView.isLogWrapHidden", true);
  550. this.set("parentView.isTaskListHidden", false);
  551. this.switchLevel("TASKS_LIST");
  552. },
  553. /**
  554. * Onclick handler for button <-Hosts
  555. *
  556. * @method backToHostList
  557. */
  558. backToHostList: function () {
  559. this.set("parentView.isHostListHidden", false);
  560. this.set("parentView.isTaskListHidden", true);
  561. this.get("controller").set("popupHeaderName", this.get("controller.serviceName"));
  562. this.get("controller").set("operationInfo", this.get('controller.servicesInfo').findProperty('name', this.get('controller.serviceName')));
  563. this.switchLevel("HOSTS_LIST");
  564. },
  565. /**
  566. * Onclick handler for button <-Services
  567. *
  568. * @method backToServiceList
  569. */
  570. backToServiceList: function () {
  571. this.get("controller").set("serviceName", "");
  572. this.set("parentView.isHostListHidden", true);
  573. this.set("parentView.isServiceListHidden", false);
  574. this.set("parentView.isTaskListHidden", true);
  575. this.set("parentView.isLogWrapHidden", true);
  576. this.set("hosts", null);
  577. this.get("controller").setBackgroundOperationHeader(false);
  578. this.switchLevel("REQUESTS_LIST");
  579. },
  580. /**
  581. * Onclick handler for Show more ..
  582. *
  583. * @method requestMoreOperations
  584. */
  585. requestMoreOperations: function () {
  586. var BGOController = App.router.get('backgroundOperationsController');
  587. var count = BGOController.get('operationsCount');
  588. BGOController.set('operationsCount', (count + 10));
  589. BGOController.requestMostRecent();
  590. },
  591. /**
  592. * @method setShowMoreAvailable
  593. */
  594. setShowMoreAvailable: function () {
  595. if (this.get('parentView.isOpen')) {
  596. this.set('isShowMore', App.router.get("backgroundOperationsController.isShowMoreAvailable"));
  597. }
  598. }.observes('parentView.isOpen', 'App.router.backgroundOperationsController.isShowMoreAvailable'),
  599. /**
  600. * Onclick handler for selected Service
  601. *
  602. * @param {{context: wrappedService}} event
  603. * @method gotoHosts
  604. */
  605. gotoHosts: function (event) {
  606. this.get("controller").set("serviceName", event.context.get("name"));
  607. this.get("controller").set("currentServiceId", event.context.get("id"));
  608. this.get("controller").set("currentHostName", null);
  609. this.get("controller").onHostUpdate();
  610. this.switchLevel("HOSTS_LIST");
  611. var servicesInfo = this.get("controller.hosts");
  612. this.set("controller.popupHeaderName", event.context.get("name"));
  613. this.set("controller.operationInfo", event.context);
  614. //apply lazy loading on cluster with more than 100 nodes
  615. this.set('hosts', servicesInfo.length > 100 ? servicesInfo.slice(0, 50) : servicesInfo);
  616. this.set("parentView.isServiceListHidden", true);
  617. this.set("parentView.isHostListHidden", false);
  618. this.set("parentView.isTaskListHidden", true);
  619. $(".modal").scrollTop(0);
  620. $(".modal-body").scrollTop(0);
  621. if (servicesInfo.length > 100) {
  622. Em.run.next(this, function () {
  623. this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
  624. });
  625. }
  626. // Determine if source request schedule is present
  627. this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
  628. this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
  629. this.refreshRequestScheduleInfo();
  630. },
  631. /**
  632. * Navigate to host details logs tab with preset filter.
  633. */
  634. navigateToHostLogs: function() {
  635. var relationType = this._determineRoleRelation(this.get('openedTask')),
  636. hostModel = App.Host.find().findProperty('hostName', this.get('openedTask.hostName')),
  637. queryParams = [],
  638. model;
  639. if (relationType.type === 'component') {
  640. model = App.StackServiceComponent.find().findProperty('componentName', relationType.value);
  641. queryParams.push('service_name=' + model.get('serviceName'));
  642. queryParams.push('component_name=' + relationType.value);
  643. }
  644. if (relationType.type === 'service') {
  645. queryParams.push('service_name=' + relationType.value);
  646. }
  647. App.router.transitionTo('main.hosts.hostDetails.logs', hostModel, { query: ''});
  648. if (this.get('parentView') && typeof this.get('parentView').onClose === 'function') this.get('parentView').onClose();
  649. },
  650. /**
  651. /**
  652. * Determines if opened task related to service or component.
  653. *
  654. * @return {boolean} <code>true</code> when relates to service or component
  655. */
  656. isLogsLinkVisible: function() {
  657. if (!this.get('openedTask') || !this.get('openedTask.id')) return false;
  658. return !!this._determineRoleRelation(this.get('openedTask'));
  659. }.property('openedTask'),
  660. /**
  661. * @param {wrappedTask} taskInfo
  662. * @return {boolean|TaskRelationObject}
  663. */
  664. _determineRoleRelation: function(taskInfo) {
  665. var foundComponentName,
  666. foundServiceName,
  667. componentNames = App.StackServiceComponent.find().mapProperty('componentName'),
  668. serviceNames = App.StackService.find().mapProperty('serviceName'),
  669. taskLog = this.get('currentHost.logTasks').findProperty('Tasks.id', Em.get(taskInfo, 'id')) || {},
  670. role = Em.getWithDefault(taskLog, 'Tasks.role', false),
  671. eqlFn = function(compare) {
  672. return function(item) {
  673. return item === compare;
  674. };
  675. };
  676. if (!role) {
  677. return false;
  678. }
  679. // component service check
  680. if (role.endsWith('_SERVICE_CHECK')) {
  681. role = role.replace('_SERVICE_CHECK', '');
  682. }
  683. foundComponentName = componentNames.filter(eqlFn(role))[0];
  684. foundServiceName = serviceNames.filter(eqlFn(role))[0];
  685. if (foundComponentName || foundServiceName) {
  686. return {
  687. type: foundComponentName ? 'component' : 'service',
  688. value: foundComponentName || foundServiceName
  689. }
  690. }
  691. return false;
  692. },
  693. /**
  694. * @type {boolean}
  695. */
  696. isRequestSchedule: function () {
  697. var id = this.get('sourceRequestScheduleId');
  698. return id != null && !isNaN(id) && id > -1;
  699. }.property('sourceRequestScheduleId'),
  700. /**
  701. * @method refreshRequestScheduleInfo
  702. */
  703. refreshRequestScheduleInfo: function () {
  704. var self = this;
  705. var id = this.get('sourceRequestScheduleId');
  706. batchUtils.getRequestSchedule(id, function (data) {
  707. var status = Em.get(data || {}, 'RequestSchedule.status');
  708. if (status) {
  709. switch (status) {
  710. case 'DISABLED':
  711. self.set('sourceRequestScheduleRunning', false);
  712. self.set('sourceRequestScheduleAborted', true);
  713. break;
  714. case 'COMPLETED':
  715. self.set('sourceRequestScheduleRunning', false);
  716. self.set('sourceRequestScheduleAborted', false);
  717. break;
  718. case 'SCHEDULED':
  719. self.set('sourceRequestScheduleRunning', true);
  720. self.set('sourceRequestScheduleAborted', false);
  721. break;
  722. }
  723. }
  724. else {
  725. self.set('sourceRequestScheduleRunning', false);
  726. self.set('sourceRequestScheduleAborted', false);
  727. }
  728. }, function () {
  729. self.set('sourceRequestScheduleRunning', false);
  730. self.set('sourceRequestScheduleAborted', false);
  731. });
  732. }.observes('sourceRequestScheduleId'),
  733. /**
  734. * Attempts to abort the current request schedule
  735. *
  736. * @param {{context: number}} event
  737. * @method doAbortRequestSchedule
  738. */
  739. doAbortRequestSchedule: function (event) {
  740. var self = this;
  741. var id = event.context;
  742. batchUtils.doAbortRequestSchedule(id, function () {
  743. self.refreshRequestScheduleInfo();
  744. });
  745. },
  746. /**
  747. * Onclick handler for selected Host
  748. *
  749. * @param {{context: wrappedHost}} event
  750. * @method gotoTasks
  751. */
  752. gotoTasks: function (event) {
  753. var tasksInfo = [];
  754. event.context.logTasks.forEach(function (_task) {
  755. tasksInfo.pushObject(this.get("controller").createTask(_task));
  756. }, this);
  757. if (tasksInfo.length) {
  758. this.get("controller").set("popupHeaderName", event.context.publicName);
  759. this.get("controller").set("currentHostName", event.context.publicName);
  760. }
  761. this.switchLevel("TASKS_LIST");
  762. this.set('currentHost.tasks', tasksInfo);
  763. this.set("parentView.isHostListHidden", true);
  764. this.set("parentView.isTaskListHidden", false);
  765. $(".modal").scrollTop(0);
  766. $(".modal-body").scrollTop(0);
  767. },
  768. /**
  769. * @method stopRebalanceHDFS
  770. * @returns {App.ModalPopup}
  771. */
  772. stopRebalanceHDFS: function () {
  773. var hostPopup = this;
  774. return App.showConfirmationPopup(function () {
  775. App.ajax.send({
  776. name: 'cancel.background.operation',
  777. sender: hostPopup,
  778. data: {
  779. requestId: hostPopup.get('controller.currentServiceId')
  780. }
  781. });
  782. hostPopup.backToServiceList();
  783. });
  784. },
  785. /**
  786. * Onclick handler for selected Task
  787. *
  788. * @method openTaskLogInDialog
  789. */
  790. openTaskLogInDialog: function () {
  791. var target = ".task-detail-log-info",
  792. activeHostLog = this.get('hostComponentLogs').findProperty('isActive', true),
  793. activeHostLogSelector = activeHostLog ? activeHostLog.get('tabClassNameSelector') + '.active' : false;
  794. if ($(".task-detail-log-clipboard").length) {
  795. this.destroyClipBoard();
  796. }
  797. if (activeHostLog && $(activeHostLogSelector).length) {
  798. target = activeHostLogSelector;
  799. }
  800. var newWindow = window.open();
  801. var newDocument = newWindow.document;
  802. newDocument.write($(target).html());
  803. newDocument.close();
  804. },
  805. /**
  806. * Onclick event for show task detail info
  807. *
  808. * @param {{context: wrappedTask}} event
  809. * @method toggleTaskLog
  810. */
  811. toggleTaskLog: function (event) {
  812. var taskInfo = event.context;
  813. this.set("parentView.isLogWrapHidden", false);
  814. if ($(".task-detail-log-clipboard").length) {
  815. this.destroyClipBoard();
  816. }
  817. this.set("parentView.isHostListHidden", true);
  818. this.set("parentView.isTaskListHidden", true);
  819. this.set('openedTaskId', taskInfo.id);
  820. this.switchLevel("TASK_DETAILS");
  821. $(".modal").scrollTop(0);
  822. $(".modal-body").scrollTop(0);
  823. },
  824. /**
  825. * Onclick event for copy to clipboard button
  826. *
  827. * @method textTrigger
  828. */
  829. textTrigger: function () {
  830. $(".task-detail-log-clipboard").length ? this.destroyClipBoard() : this.createClipBoard();
  831. },
  832. /**
  833. * Create Clip Board
  834. *
  835. * @method createClipBoard
  836. */
  837. createClipBoard: function () {
  838. var isLogComponentActive = this.get('hostComponentLogs').someProperty('isActive', true),
  839. logElement = isLogComponentActive ? $('.log-component-tab.active .log-tail-content'): $(".task-detail-log-maintext"),
  840. logElementRect = logElement[0].getBoundingClientRect(),
  841. clipBoardContent = this.get('clipBoardContent');
  842. $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
  843. $(".task-detail-log-clipboard")
  844. .html(isLogComponentActive ? this.get('clipBoardContent') : "stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
  845. .css('display', 'block')
  846. .width(logElementRect.width)
  847. .height(isLogComponentActive ? logElement[0].scrollHeight : logElementRect.height)
  848. .select();
  849. this.set('isClipBoardActive', true);
  850. },
  851. /**
  852. * Destroy Clip Board
  853. *
  854. * @method destroyClipBoard
  855. */
  856. destroyClipBoard: function () {
  857. var logElement = this.get('hostComponentLogs').someProperty('isActive', true) ? $('.log-component-tab.active .log-tail-content'): $(".task-detail-log-maintext");
  858. $(".task-detail-log-clipboard").remove();
  859. logElement.css("display", "block");
  860. this.set('isClipBoardActive', false);
  861. },
  862. isLogSearchInstalled: function() {
  863. return App.Service.find().someProperty('serviceName', 'LOGSEARCH');
  864. }.property(),
  865. /**
  866. * Host component logs associated with selected component on 'TASK_DETAILS' level.
  867. *
  868. * @property {object[]}
  869. */
  870. hostComponentLogs: function() {
  871. var relationType,
  872. componentName,
  873. hostName,
  874. logFile,
  875. self = this;
  876. if (this.get('openedTask.id')) {
  877. relationType = this._determineRoleRelation(this.get('openedTask'));
  878. if (relationType.type === 'component') {
  879. hostName = this.get('currentHost.name');
  880. componentName = relationType.value;
  881. return App.HostComponentLog.find()
  882. .filterProperty('hostComponent.host.hostName', hostName)
  883. .filterProperty('hostComponent.componentName', componentName)
  884. .reduce(function(acc, item, index) {
  885. var serviceName = item.get('hostComponent.service.serviceName'),
  886. logComponentName = item.get('name'),
  887. componentHostName = item.get('hostComponent.host.hostName');
  888. acc.pushObjects(item.get('logFileNames').map(function(logFileName, idx) {
  889. var tabClassName = logComponentName + '_' + index + '_' + idx;
  890. return Em.Object.create({
  891. hostName: componentHostName,
  892. logComponentName: logComponentName,
  893. fileName: logFileName,
  894. tabClassName: tabClassName,
  895. tabClassNameSelector: '.' + tabClassName,
  896. displayedFileName: fileUtils.fileNameFromPath(logFileName),
  897. url: self.get('logSearchUrlTemplate').format(hostName, logFileName, logComponentName),
  898. isActive: false
  899. });
  900. }));
  901. return acc;
  902. }, []);
  903. }
  904. }
  905. return [];
  906. }.property('openedTaskId', 'isLevelLoaded'),
  907. /**
  908. * Log Search UI link template used for 'Open In Log Search' links.
  909. *
  910. * @property {string}
  911. */
  912. logSearchUrlTemplate: function() {
  913. var quickLink = App.QuickLinks.find().findProperty('site', 'logsearch-site'),
  914. logSearchServerHost = App.HostComponent.find().findProperty('componentName', 'LOGSEARCH_SERVER').get('host.hostName');
  915. if (quickLink) {
  916. return quickLink.get('template').fmt('http', logSearchServerHost, quickLink.get('default_http_port')) + '?host_name={0}&file_name={1}&component_name={2}';
  917. }
  918. return '#';
  919. }.property('hostComponentLogsExists'),
  920. /**
  921. * Determines if there are component logs for selected component within 'TASK_DETAILS' level.
  922. *
  923. * @property {boolean}
  924. */
  925. hostComponentLogsExists: function() {
  926. return this.get('isLogSearchInstalled') && !!this.get('hostComponentLogs.length') && this.get('parentView.isOpen');
  927. }.property('hostComponentLogs.length', 'isLogSearchInstalled', 'parentView.isOpen'),
  928. /**
  929. * Minimum required content to embed in App.LogTailView. This property observes current active host component log.
  930. *
  931. * @property {object}
  932. */
  933. logTailViewContent: function() {
  934. if (!this.get('hostComponentLog')) {
  935. return null;
  936. }
  937. return Em.Object.create({
  938. hostName: this.get('currentHost.name'),
  939. logComponentName: this.get('hostComponentLog.name')
  940. });
  941. }.property('hostComponentLogs.@each.isActive'),
  942. logTailView: App.LogTailView.extend({
  943. isActiveDidChange: function() {
  944. var self = this;
  945. if (this.get('content.isActive') === false) return;
  946. setTimeout(function() {
  947. self.scrollToBottom();
  948. self.storeToClipBoard();
  949. }, 500);
  950. }.observes('content.isActive'),
  951. logRowsLengthDidChange: function() {
  952. if (!this.get('content.isActive') || this.get('state') === 'destroyed') return;
  953. this.storeToClipBoard();
  954. }.observes('logRows.length'),
  955. /**
  956. * Stores log content to use for clip board.
  957. */
  958. storeToClipBoard: function() {
  959. this.get('parentView').set('clipBoardContent', this.get('logRows').map(function(i) {
  960. return i.get('logtimeFormatted') + ' ' + i.get('level') + ' ' + i.get('logMessage');
  961. }).join('\n'));
  962. }
  963. }),
  964. setActiveLogTab: function(e) {
  965. var content = e.context;
  966. this.set('clipBoardContent', null);
  967. this.get('hostComponentLogs').without(content).setEach('isActive', false);
  968. if (this.get('isClipBoardActive')) {
  969. this.destroyClipBoard();
  970. }
  971. content.set('isActive', true);
  972. },
  973. setActiveTaskLogTab: function() {
  974. this.set('clipBoardContent', null);
  975. this.get('hostComponentLogs').setEach('isActive', false);
  976. if (this.get('isClipBoardActive')) {
  977. this.destroyClipBoard();
  978. }
  979. }
  980. });