host_progress_popup_body_view.js 35 KB

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