|
@@ -19,6 +19,7 @@
|
|
|
var App = require('app');
|
|
|
var batchUtils = require('utils/batch_scheduled_requests');
|
|
|
var date = require('utils/date/date');
|
|
|
+var fileUtils = require('utils/file_utils');
|
|
|
|
|
|
/**
|
|
|
* @typedef {object} TaskRelationObject
|
|
@@ -86,6 +87,13 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
*/
|
|
|
sourceRequestScheduleCommand: null,
|
|
|
|
|
|
+ /**
|
|
|
+ * @type {string}
|
|
|
+ */
|
|
|
+ clipBoardContent: null,
|
|
|
+
|
|
|
+ isClipBoardActive: false,
|
|
|
+
|
|
|
/**
|
|
|
* Alias for <code>controller.hosts</code>
|
|
|
*
|
|
@@ -237,14 +245,95 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
|
|
|
didInsertElement: function () {
|
|
|
this.updateHostInfo();
|
|
|
+ this.subscribeResize();
|
|
|
},
|
|
|
|
|
|
willDestroyElement: function () {
|
|
|
if (this.get('controller.dataSourceController.name') == 'highAvailabilityProgressPopupController') {
|
|
|
this.set('controller.dataSourceController.isTaskPolling', false);
|
|
|
}
|
|
|
+ this.unsubscribeResize();
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Subscribe for window <code>resize</code> event.
|
|
|
+ *
|
|
|
+ * @method subscribeResize
|
|
|
+ */
|
|
|
+ subscribeResize: function() {
|
|
|
+ var self = this;
|
|
|
+ $(window).on('resize', this.resizeHandler.bind(this));
|
|
|
+ Em.run.next(this, function() {
|
|
|
+ self.resizeHandler();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Remove event listener for window <code>resize</code> event.
|
|
|
+ */
|
|
|
+ unsubscribeResize: function() {
|
|
|
+ $(window).off('resize', this.resizeHandler.bind(this));
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method handles window resize and fit modal body content according to visible items for each <code>level</code>.
|
|
|
+ *
|
|
|
+ * @method resizeHandler
|
|
|
+ */
|
|
|
+ resizeHandler: function() {
|
|
|
+ if (this.get('state') === 'destroyed' || !this.get('parentView.isOpen')) return;
|
|
|
+ var headerHeight = 48,
|
|
|
+ modalFooterHeight = 60,
|
|
|
+ taskTopWrapHeight = 40,
|
|
|
+ modalTopOffset = $('.modal').offset().top,
|
|
|
+ contentPaddingBottom = 40,
|
|
|
+ hostsPageBarHeight = 45,
|
|
|
+ tabbedContentNavHeight = 68,
|
|
|
+ logComponentFileNameHeight = 30,
|
|
|
+ levelName = this.get('currentLevelName'),
|
|
|
+ boLevelHeightMap = {
|
|
|
+ 'REQUESTS_LIST': {
|
|
|
+ height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
|
|
|
+ target: '#service-info'
|
|
|
+ },
|
|
|
+ 'HOSTS_LIST': {
|
|
|
+ height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom - hostsPageBarHeight,
|
|
|
+ target: '#host-info'
|
|
|
+ },
|
|
|
+ 'TASKS_LIST': {
|
|
|
+ height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
|
|
|
+ target: '#host-log'
|
|
|
+ },
|
|
|
+ 'TASK_DETAILS': {
|
|
|
+ height: window.innerHeight - 2*modalTopOffset - headerHeight - taskTopWrapHeight - modalFooterHeight - contentPaddingBottom,
|
|
|
+ target: ['.task-detail-log-info', '.log-tail-content.pre-styled']
|
|
|
+ }
|
|
|
+ },
|
|
|
+ currentLevelHeight,
|
|
|
+ resizeTarget;
|
|
|
+
|
|
|
+ if (levelName && levelName in boLevelHeightMap) {
|
|
|
+ resizeTarget = boLevelHeightMap[levelName].target;
|
|
|
+ currentLevelHeight = boLevelHeightMap[levelName].height;
|
|
|
+ if (levelName === 'TASK_DETAILS' && $('.task-detail-info').hasClass('task-detail-info-tabbed')) {
|
|
|
+ currentLevelHeight -= tabbedContentNavHeight;
|
|
|
+ }
|
|
|
+ if (!Em.isArray(resizeTarget)) {
|
|
|
+ resizeTarget = [resizeTarget];
|
|
|
+ }
|
|
|
+ resizeTarget.forEach(function(target) {
|
|
|
+ if (target === '.log-tail-content.pre-styled') {
|
|
|
+ currentLevelHeight -= logComponentFileNameHeight;
|
|
|
+ }
|
|
|
+ $(target).css('maxHeight', currentLevelHeight + 'px');
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ currentLevelName: function() {
|
|
|
+ return this.get('controller.dataSourceController.levelInfo.name');
|
|
|
+ }.property('controller.dataSourceController.levelInfo.name'),
|
|
|
+
|
|
|
/**
|
|
|
* Preset values on init
|
|
|
*
|
|
@@ -277,6 +366,7 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
});
|
|
|
this.get("controller").setBackgroundOperationHeader(false);
|
|
|
this.setOnStart();
|
|
|
+ this.rerender();
|
|
|
}
|
|
|
}.observes('parentView.isOpen'),
|
|
|
|
|
@@ -421,6 +511,7 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
switchLevel: function (levelName) {
|
|
|
var dataSourceController = this.get('controller.dataSourceController');
|
|
|
var args = [].slice.call(arguments);
|
|
|
+ this.get('hostComponentLogs').clear();
|
|
|
if (this.get("controller.isBackgroundOperations")) {
|
|
|
var levelInfo = dataSourceController.get('levelInfo');
|
|
|
levelInfo.set('taskId', this.get('openedTaskId'));
|
|
@@ -453,6 +544,24 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ levelDidChange: function() {
|
|
|
+ var levelName = this.get('controller.dataSourceController.levelInfo.name'),
|
|
|
+ self = this;
|
|
|
+
|
|
|
+ if (levelName && this.get('isLevelLoaded')) {
|
|
|
+ Em.run.next(this, function() {
|
|
|
+ self.resizeHandler();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }.observes('controller.dataSourceController.levelInfo.name', 'isLevelLoaded'),
|
|
|
+
|
|
|
+
|
|
|
+ popupIsOpenDidChange: function() {
|
|
|
+ if (!this.get('isOpen')) {
|
|
|
+ this.get('hostComponentLogs').clear();
|
|
|
+ }
|
|
|
+ }.observes('parentView.isOpen'),
|
|
|
+
|
|
|
/**
|
|
|
* Switch-level custom method for <code>highAvailabilityProgressPopupController</code>
|
|
|
*
|
|
@@ -589,7 +698,7 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
*/
|
|
|
navigateToHostLogs: function() {
|
|
|
var relationType = this._determineRoleRelation(this.get('openedTask')),
|
|
|
- hostModel = App.Host.find().findProperty('id', this.get('currentHost.name')),
|
|
|
+ hostModel = App.Host.find().findProperty('hostName', this.get('openedTask.hostName')),
|
|
|
queryParams = [],
|
|
|
model;
|
|
|
|
|
@@ -601,7 +710,7 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
if (relationType.type === 'service') {
|
|
|
queryParams.push('service_name=' + relationType.value);
|
|
|
}
|
|
|
- App.router.transitionTo('main.hosts.hostDetails.logs', hostModel, { query: '?' + queryParams.join('&') });
|
|
|
+ App.router.transitionTo('main.hosts.hostDetails.logs', hostModel, { query: ''});
|
|
|
if (this.get('parentView') && typeof this.get('parentView').onClose === 'function') this.get('parentView').onClose();
|
|
|
},
|
|
|
|
|
@@ -754,12 +863,19 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
* @method openTaskLogInDialog
|
|
|
*/
|
|
|
openTaskLogInDialog: function () {
|
|
|
+ var target = ".task-detail-log-info",
|
|
|
+ activeHostLog = this.get('hostComponentLogs').findProperty('isActive', true),
|
|
|
+ activeHostLogSelector = activeHostLog ? activeHostLog.get('tabClassNameSelector') + '.active' : false;
|
|
|
+
|
|
|
if ($(".task-detail-log-clipboard").length) {
|
|
|
this.destroyClipBoard();
|
|
|
}
|
|
|
+ if (activeHostLog && $(activeHostLogSelector).length) {
|
|
|
+ target = activeHostLogSelector;
|
|
|
+ }
|
|
|
var newWindow = window.open();
|
|
|
var newDocument = newWindow.document;
|
|
|
- newDocument.write($(".task-detail-log-info").html());
|
|
|
+ newDocument.write($(target).html());
|
|
|
newDocument.close();
|
|
|
},
|
|
|
|
|
@@ -798,17 +914,19 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
* @method createClipBoard
|
|
|
*/
|
|
|
createClipBoard: function () {
|
|
|
- var logElement = $(".task-detail-log-maintext"),
|
|
|
- logElementRect = logElement[0].getBoundingClientRect();
|
|
|
+ var isLogComponentActive = this.get('hostComponentLogs').someProperty('isActive', true),
|
|
|
+ logElement = isLogComponentActive ? $('.log-component-tab.active .log-tail-content'): $(".task-detail-log-maintext"),
|
|
|
+ logElementRect = logElement[0].getBoundingClientRect(),
|
|
|
+ clipBoardContent = this.get('clipBoardContent');
|
|
|
$(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
|
|
|
$(".task-detail-log-clipboard")
|
|
|
- .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
|
|
|
+ .html(isLogComponentActive ? this.get('clipBoardContent') : "stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
|
|
|
.css('display', 'block')
|
|
|
.width(logElementRect.width)
|
|
|
- .height(logElementRect.height)
|
|
|
+ .height(isLogComponentActive ? logElement[0].scrollHeight : logElementRect.height)
|
|
|
.select();
|
|
|
|
|
|
- logElement.css("display", "none");
|
|
|
+ this.set('isClipBoardActive', true);
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -817,8 +935,142 @@ App.HostProgressPopupBodyView = App.TableView.extend({
|
|
|
* @method destroyClipBoard
|
|
|
*/
|
|
|
destroyClipBoard: function () {
|
|
|
+ var logElement = this.get('hostComponentLogs').someProperty('isActive', true) ? $('.log-component-tab.active .log-tail-content'): $(".task-detail-log-maintext");
|
|
|
+
|
|
|
$(".task-detail-log-clipboard").remove();
|
|
|
- $(".task-detail-log-maintext").css("display", "block");
|
|
|
- }
|
|
|
+ logElement.css("display", "block");
|
|
|
+ this.set('isClipBoardActive', false);
|
|
|
+ },
|
|
|
+
|
|
|
+ isLogSearchInstalled: function() {
|
|
|
+ return App.Service.find().someProperty('serviceName', 'LOGSEARCH');
|
|
|
+ }.property(),
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Host component logs associated with selected component on 'TASK_DETAILS' level.
|
|
|
+ *
|
|
|
+ * @property {object[]}
|
|
|
+ */
|
|
|
+ hostComponentLogs: function() {
|
|
|
+ var relationType,
|
|
|
+ componentName,
|
|
|
+ hostName,
|
|
|
+ logFile,
|
|
|
+ self = this;
|
|
|
+
|
|
|
+ if (this.get('openedTask.id')) {
|
|
|
+ relationType = this._determineRoleRelation(this.get('openedTask'));
|
|
|
+ if (relationType.type === 'component') {
|
|
|
+ hostName = this.get('currentHost.name');
|
|
|
+ componentName = relationType.value;
|
|
|
+ return App.HostComponentLog.find()
|
|
|
+ .filterProperty('hostComponent.host.hostName', hostName)
|
|
|
+ .filterProperty('hostComponent.componentName', componentName)
|
|
|
+ .reduce(function(acc, item, index) {
|
|
|
+ var serviceName = item.get('hostComponent.service.serviceName'),
|
|
|
+ logComponentName = item.get('name'),
|
|
|
+ componentHostName = item.get('hostComponent.host.hostName');
|
|
|
+ acc.pushObjects(item.get('logFileNames').map(function(logFileName, idx) {
|
|
|
+ var tabClassName = logComponentName + '_' + index + '_' + idx;
|
|
|
+ return Em.Object.create({
|
|
|
+ hostName: componentHostName,
|
|
|
+ logComponentName: logComponentName,
|
|
|
+ fileName: logFileName,
|
|
|
+ tabClassName: tabClassName,
|
|
|
+ tabClassNameSelector: '.' + tabClassName,
|
|
|
+ displayedFileName: fileUtils.fileNameFromPath(logFileName),
|
|
|
+ url: self.get('logSearchUrlTemplate').format(hostName, logFileName, logComponentName),
|
|
|
+ isActive: false
|
|
|
+ });
|
|
|
+ }));
|
|
|
+ return acc;
|
|
|
+ }, []);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ }.property('openedTaskId', 'isLevelLoaded'),
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Log Search UI link template used for 'Open In Log Search' links.
|
|
|
+ *
|
|
|
+ * @property {string}
|
|
|
+ */
|
|
|
+ logSearchUrlTemplate: function() {
|
|
|
+ var quickLink = App.QuickLinks.find().findProperty('site', 'logsearch-site'),
|
|
|
+ logSearchServerHost = App.HostComponent.find().findProperty('componentName', 'LOGSEARCH_SERVER').get('host.hostName');
|
|
|
+
|
|
|
+ if (quickLink) {
|
|
|
+ return quickLink.get('template').fmt('http', logSearchServerHost, quickLink.get('default_http_port')) + '?host_name={0}&file_name={1}&component_name={2}';
|
|
|
+ }
|
|
|
+ return '#';
|
|
|
+ }.property('hostComponentLogsExists'),
|
|
|
|
|
|
+ /**
|
|
|
+ * Determines if there are component logs for selected component within 'TASK_DETAILS' level.
|
|
|
+ *
|
|
|
+ * @property {boolean}
|
|
|
+ */
|
|
|
+ hostComponentLogsExists: function() {
|
|
|
+ return this.get('isLogSearchInstalled') && !!this.get('hostComponentLogs.length') && this.get('parentView.isOpen');
|
|
|
+ }.property('hostComponentLogs.length', 'isLogSearchInstalled', 'parentView.isOpen'),
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Minimum required content to embed in App.LogTailView. This property observes current active host component log.
|
|
|
+ *
|
|
|
+ * @property {object}
|
|
|
+ */
|
|
|
+ logTailViewContent: function() {
|
|
|
+ if (!this.get('hostComponentLog')) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return Em.Object.create({
|
|
|
+ hostName: this.get('currentHost.name'),
|
|
|
+ logComponentName: this.get('hostComponentLog.name')
|
|
|
+ });
|
|
|
+ }.property('hostComponentLogs.@each.isActive'),
|
|
|
+
|
|
|
+ logTailView: App.LogTailView.extend({
|
|
|
+
|
|
|
+ isActiveDidChange: function() {
|
|
|
+ var self = this;
|
|
|
+ if (this.get('content.isActive') === false) return;
|
|
|
+ setTimeout(function() {
|
|
|
+ self.scrollToBottom();
|
|
|
+ self.storeToClipBoard();
|
|
|
+ }, 500);
|
|
|
+ }.observes('content.isActive'),
|
|
|
+
|
|
|
+ logRowsLengthDidChange: function() {
|
|
|
+ if (!this.get('content.isActive') || this.get('state') === 'destroyed') return;
|
|
|
+ this.storeToClipBoard();
|
|
|
+ }.observes('logRows.length'),
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Stores log content to use for clip board.
|
|
|
+ */
|
|
|
+ storeToClipBoard: function() {
|
|
|
+ this.get('parentView').set('clipBoardContent', this.get('logRows').map(function(i) {
|
|
|
+ return i.get('logtimeFormatted') + ' ' + i.get('level') + ' ' + i.get('logMessage');
|
|
|
+ }).join('\n'));
|
|
|
+ }
|
|
|
+ }),
|
|
|
+
|
|
|
+ setActiveLogTab: function(e) {
|
|
|
+ var content = e.context;
|
|
|
+ this.set('clipBoardContent', null);
|
|
|
+ this.get('hostComponentLogs').without(content).setEach('isActive', false);
|
|
|
+ if (this.get('isClipBoardActive')) {
|
|
|
+ this.destroyClipBoard();
|
|
|
+ }
|
|
|
+ content.set('isActive', true);
|
|
|
+ },
|
|
|
+
|
|
|
+ setActiveTaskLogTab: function() {
|
|
|
+ this.set('clipBoardContent', null);
|
|
|
+ this.get('hostComponentLogs').setEach('isActive', false);
|
|
|
+ if (this.get('isClipBoardActive')) {
|
|
|
+ this.destroyClipBoard();
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|