blob: b9181e20bd0e8dd4a71e04ca0bd73e2e3266b5e2 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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
* @property {string} type relation type 'service', 'component'
* @property {string} [value] optional value of relation e.g. name of component or service
*/
/**
* Option for "filter by state" dropdown
* @typedef {object} progressPopupCategoryObject
* @property {string} value "all|pending|in progress|failed|completed|aborted|timedout"
* @property {number} count number of items with <code>state</code> equal to <code>this.value</code>
* @property {string} labelPath key in the messages.js
* @property {string} label localized label
*/
var categoryObject = Em.Object.extend({
value: '',
count: 0,
labelPath: '',
label: function () {
return Em.I18n.t(this.get('labelPath')).format(this.get('count'));
}.property('count', 'labelPath')
});
/**
* @class HostProgressPopupBodyView
* @type {Em.View}
*/
App.HostProgressPopupBodyView = App.TableView.extend({
templateName: require('templates/common/host_progress_popup'),
/**
* @type {boolean}
*/
showTextArea: false,
/**
* @type {boolean}
*/
isServiceEmptyList: true,
/**
* @type {boolean}
*/
isTasksEmptyList: true,
/**
* @type {number}
*/
sourceRequestScheduleId: -1,
/**
* @type {boolean}
*/
sourceRequestScheduleRunning: false,
/**
* @type {boolean}
*/
sourceRequestScheduleAborted: false,
/**
* @type {?string}
*/
sourceRequestScheduleCommand: null,
/**
* @type {string}
*/
clipBoardContent: null,
isClipBoardActive: false,
/**
* Determines that host information become loaded and mapped with <code>App.hostsMapper</code>.
* This property play in only when Log Search service installed.
*
* @type {boolean}
*/
hostInfoLoaded: true,
/**
* Alias for <code>controller.hosts</code>
*
* @type {wrappedHost[]}
*/
hosts: function () {
return this.get('controller.hosts')
}.property('controller.hosts.[]'),
/**
* Alias for <code>controller.servicesInfo</code>
*
* @type {wrappedService[]}
*/
services: function () {
return this.get('controller.servicesInfo');
}.property('controller.servicesInfo.[]'),
/**
* @type {number}
*/
openedTaskId: 0,
/**
* Return task detail info of opened task
*
* @type {wrappedTask}
*/
openedTask: function () {
if (!(this.get('openedTaskId') && this.get('tasks'))) {
return Em.Object.create();
}
return this.get('tasks').findProperty('id', this.get('openedTaskId'));
}.property('tasks', 'tasks.@each.stderr', 'tasks.@each.stdout', 'openedTaskId'),
/**
* @type {object}
*/
filterMap: {
pending: ["pending", "queued"],
in_progress: ["in_progress", "upgrading"],
failed: ["failed"],
completed: ["completed", "success"],
aborted: ["aborted"],
timedout: ["timedout"]
},
/**
* Determines if "Show More ..."-link should be shown
* @type {boolean}
*/
isShowMore: true,
/**
* @type {boolean}
*/
pagination: true,
/**
* @type {boolean}
*/
isPaginate: false,
/**
* Select box, display names and values
*
* @type {progressPopupCategoryObject[]}
*/
categories: [
categoryObject.create({value: 'all', labelPath: 'hostPopup.status.category.all'}),
categoryObject.create({value: 'pending', labelPath: 'hostPopup.status.category.pending'}),
categoryObject.create({value: 'in_progress', labelPath: 'hostPopup.status.category.inProgress'}),
categoryObject.create({value: 'failed', labelPath: 'hostPopup.status.category.failed'}),
categoryObject.create({value: 'completed', labelPath: 'hostPopup.status.category.success'}),
categoryObject.create({value: 'aborted', labelPath: 'hostPopup.status.category.aborted'}),
categoryObject.create({value: 'timedout', labelPath: 'hostPopup.status.category.timedout'})
],
/**
* Selected option is bound to this values
* @type {?progressPopupCategoryObject}
*/
serviceCategory: null,
/**
* @type {?progressPopupCategoryObject}
*/
hostCategory: null,
/**
* @type {?progressPopupCategoryObject}
*/
taskCategory: null,
/**
* flag to indicate whether level data has already been loaded
* applied only to HOSTS_LIST and TASK_DETAILS levels, whereas async query used to obtain data
*
* @type {boolean}
*/
isLevelLoaded: true,
/**
* <code>switchLevel</code> for some <code>controller.dataSourceController</code> should be customized
* So, this map contains information about customize-methods
* Format: key - <code>dataSourceController.name</code>, value - method name in this view
* Method is called with same arguments as <code>switchLevel</code> is
*
* @type {object}
*/
customControllersSwitchLevelMap: {
highAvailabilityProgressPopupController: '_switchLevelForHAProgressPopupController'
},
/**
* @type {boolean}
*/
isHostEmptyList: Em.computed.empty('pageContent'),
/**
* @type {wrappedHost}
*/
currentHost: function () {
return this.get('hosts') && this.get('hosts').findProperty('name', this.get('controller.currentHostName'));
}.property('controller.currentHostName'),
/**
* Tasks for current shown host (<code>currentHost</code>)
*
* @type {wrappedTask[]}
*/
tasks: function () {
var currentHost = this.get('currentHost');
return currentHost ? currentHost.get('tasks') : [];
}.property('currentHost.tasks', 'currentHost.tasks.@each.status'),
/**
* Message about aborting operation
* Custom for Rolling Restart
*
* @type {string}
*/
requestScheduleAbortLabel: function () {
return 'ROLLING-RESTART' == this.get('sourceRequestScheduleCommand') ?
Em.I18n.t("hostPopup.bgop.abort.rollingRestart"):
Em.I18n.t("common.abort");
}.property('sourceRequestScheduleCommand'),
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
*
* @method setOnStart
*/
setOnStart: function () {
this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
if (this.get("controller.isBackgroundOperations")) {
this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
this.updateHostInfo();
}
else {
this.set("parentView.isHostListHidden", false);
this.set("parentView.isServiceListHidden", true);
}
},
/**
* force popup to show list of operations
*
* @method resetState
*/
resetState: function () {
if (this.get('parentView.isOpen')) {
this.get('parentView').setProperties({
isLogWrapHidden: true,
isTaskListHidden: true,
isHostListHidden: true,
isServiceListHidden: false
});
this.get("controller").setBackgroundOperationHeader(false);
this.get('controller.hosts').clear();
this.setOnStart();
this.rerender();
}
}.observes('parentView.isOpen'),
/**
* When popup is opened, and data after polling has changed, update this data in component
*
* @method updateHostInfo
*/
updateHostInfo: function () {
if (!this.get('parentView.isOpen')) {
return;
}
this.set('parentView.isLoaded', false);
this.get("controller").set("inputData", this.get("controller.dataSourceController.services"));
this.get("controller").onServiceUpdate(this.get('parentView.isServiceListHidden'));
this.get("controller").onHostUpdate();
this.set('parentView.isLoaded', true);
this.set("hosts", this.get("controller.hosts"));
this.set("services", this.get("controller.servicesInfo"));
this.set('isLevelLoaded', true);
}.observes("controller.dataSourceController.serviceTimestamp"),
/**
* Depending on service filter, set which services should be shown
*
* @method visibleServices
*/
visibleServices: function () {
if (this.get("services")) {
this.set("isServiceEmptyList", true);
if (this.get('serviceCategory.value')) {
var filter = this.get('serviceCategory.value');
var services = this.get('services');
this.set("isServiceEmptyList", this.setVisibility(filter, services));
}
}
}.observes('serviceCategory', 'services', 'services.@each.status'),
/**
* Depending on hosts filter, set which hosts should be shown
*
* @method filter
*/
filter: function () {
var filter = this.get('hostCategory.value');
var hosts = this.get('hosts') || [];
var filterMap = this.get('filterMap');
if (!filter || !hosts.length) {
return;
}
if (filter === 'all') {
this.set('filteredContent', hosts);
}
else {
this.set('filteredContent', hosts.filter(function (item) {
return filterMap[filter].contains(item.status);
}));
}
}.observes('hosts.length', 'hostCategory.value'),
/**
* Reset startIndex property back to 1 when filter type has been changed.
*
* @method resetIndex
*/
resetIndex: function () {
if (this.get('hostCategory.value')) {
this.set('startIndex', 1);
}
}.observes('hostCategory.value'),
/**
* Depending on tasks filter, set which tasks should be shown
*
* @method visibleTasks
*/
visibleTasks: function () {
this.set("isTasksEmptyList", true);
if (this.get('taskCategory.value') && this.get('tasks')) {
var filter = this.get('taskCategory.value');
var tasks = this.get('tasks');
this.set("isTasksEmptyList", this.setVisibility(filter, tasks));
}
}.observes('taskCategory', 'tasks', 'tasks.@each.status'),
/**
* Depending on selected filter type, set object visibility value
*
* @param filter
* @param obj
* @return {bool} isEmptyList
* @method setVisibility
*/
setVisibility: function (filter, obj) {
var isEmptyList = true;
var filterMap = this.get('filterMap');
if (filter == "all") {
obj.setEach("isVisible", true);
isEmptyList = !obj.length;
}
else {
obj.forEach(function (item) {
item.set('isVisible', filterMap[filter].contains(item.status));
isEmptyList = (isEmptyList) ? !item.get('isVisible') : false;
}, this)
}
return isEmptyList;
},
/**
* Depending on currently viewed tab, call setSelectCount function
*
* @method updateSelectView
*/
updateSelectView: function () {
var isPaginate;
if (this.get('parentView.isHostListHidden')) {
if (this.get('parentView.isTaskListHidden')) {
if (!this.get('parentView.isServiceListHidden')) {
this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
}
}
else {
this.get('controller').setSelectCount(this.get("tasks"), this.get('categories'));
}
}
else {
//since lazy loading used for hosts, we need to get hosts info directly from controller, that always contains entire array of data
this.get('controller').setSelectCount(this.get("controller.hosts"), this.get('categories'));
isPaginate = true;
}
this.set('isPaginate', !!isPaginate);
}.observes('tasks.@each.status', 'hosts.@each.status', 'parentView.isTaskListHidden', 'parentView.isHostListHidden', 'services.length', 'services.@each.status'),
/**
* control data uploading, depending on which display level is showed
*
* @param {string} levelName
* @method switchLevel
*/
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'));
levelInfo.set('requestId', this.get('controller.currentServiceId'));
levelInfo.set('name', levelName);
if (levelName === 'HOSTS_LIST') {
this.set('isLevelLoaded', dataSourceController.requestMostRecent());
this.set('hostCategory', this.get('categories').findProperty('value', 'all'));
}
else {
if (levelName === 'TASK_DETAILS') {
dataSourceController.requestMostRecent();
this.set('isLevelLoaded', false);
}
else {
if (levelName === 'REQUESTS_LIST') {
this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
this.get('controller.hosts').clear();
dataSourceController.requestMostRecent();
}
else {
this.set('taskCategory', this.get('categories').findProperty('value', 'all'));
}
}
}
}
else {
var customControllersSwitchLevelMap = this.get('customControllersSwitchLevelMap');
Em.tryInvoke(this, customControllersSwitchLevelMap[dataSourceController.get('name')], args);
}
},
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>
*
* @param {string} levelName
* @private
*/
_switchLevelForHAProgressPopupController: function (levelName) {
var dataSourceController = this.get('controller.dataSourceController');
if (levelName === 'TASK_DETAILS') {
this.set('isLevelLoaded', false);
dataSourceController.startTaskPolling(this.get('openedTask.request_id'), this.get('openedTask.id'));
Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
dataSourceController.addObserver('taskInfo.' + this.get('parentView.detailedProperties')[key], this, 'updateTaskInfo');
}, this);
}
else {
dataSourceController.stopTaskPolling();
}
},
/**
* @method updateTaskInfo
*/
updateTaskInfo: function () {
var dataSourceController = this.get('controller.dataSourceController');
var openedTask = this.get('openedTask');
if (openedTask && openedTask.get('id') == dataSourceController.get('taskInfo.id')) {
this.set('isLevelLoaded', true);
Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
openedTask.set(key, dataSourceController.get('taskInfo.' + key));
}, this);
}
},
/**
* Onclick handler for button <-Tasks
*
* @method backToTaskList
*/
backToTaskList: function () {
this.destroyClipBoard();
this.set("openedTaskId", 0);
this.set("parentView.isLogWrapHidden", true);
this.set("parentView.isTaskListHidden", false);
this.switchLevel("TASKS_LIST");
},
/**
* Onclick handler for button <-Hosts
*
* @method backToHostList
*/
backToHostList: function () {
this.set("parentView.isHostListHidden", false);
this.set("parentView.isTaskListHidden", true);
this.get("controller").set("popupHeaderName", this.get("controller.serviceName"));
this.get("controller").set("operationInfo", this.get('controller.servicesInfo').findProperty('name', this.get('controller.serviceName')));
this.switchLevel("HOSTS_LIST");
},
/**
* Onclick handler for button <-Services
*
* @method backToServiceList
*/
backToServiceList: function () {
this.get("controller").set("serviceName", "");
this.set("parentView.isHostListHidden", true);
this.set("parentView.isServiceListHidden", false);
this.set("parentView.isTaskListHidden", true);
this.set("parentView.isLogWrapHidden", true);
this.set("hosts", null);
this.get("controller").setBackgroundOperationHeader(false);
this.switchLevel("REQUESTS_LIST");
},
/**
* Onclick handler for Show more ..
*
* @method requestMoreOperations
*/
requestMoreOperations: function () {
var BGOController = App.router.get('backgroundOperationsController');
var count = BGOController.get('operationsCount');
BGOController.set('operationsCount', (count + 10));
BGOController.requestMostRecent();
},
/**
* @method setShowMoreAvailable
*/
setShowMoreAvailable: function () {
if (this.get('parentView.isOpen')) {
this.set('isShowMore', App.router.get("backgroundOperationsController.isShowMoreAvailable"));
}
}.observes('parentView.isOpen', 'App.router.backgroundOperationsController.isShowMoreAvailable'),
/**
* Onclick handler for selected Service
*
* @param {{context: wrappedService}} event
* @method gotoHosts
*/
gotoHosts: function (event) {
this.get("controller").set("serviceName", event.context.get("name"));
this.get("controller").set("currentServiceId", event.context.get("id"));
this.get("controller").set("currentHostName", null);
this.get("controller").onHostUpdate();
this.switchLevel("HOSTS_LIST");
var servicesInfo = this.get("controller.hosts");
this.set("controller.popupHeaderName", event.context.get("name"));
this.set("controller.operationInfo", event.context);
//apply lazy loading on cluster with more than 100 nodes
this.set('hosts', servicesInfo.length > 100 ? servicesInfo.slice(0, 50) : servicesInfo);
this.set("parentView.isServiceListHidden", true);
this.set("parentView.isHostListHidden", false);
this.set("parentView.isTaskListHidden", true);
$(".modal").scrollTop(0);
$(".modal-body").scrollTop(0);
if (servicesInfo.length > 100) {
Em.run.next(this, function () {
this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
});
}
// Determine if source request schedule is present
this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
this.refreshRequestScheduleInfo();
},
/**
* Navigate to host details logs tab with preset filter.
*/
navigateToHostLogs: function() {
var relationType = this._determineRoleRelation(this.get('openedTask')),
hostModel = App.Host.find().findProperty('hostName', this.get('openedTask.hostName')),
queryParams = [],
model;
if (relationType.type === 'component') {
model = App.StackServiceComponent.find().findProperty('componentName', relationType.value);
queryParams.push('service_name=' + model.get('serviceName'));
queryParams.push('component_name=' + relationType.value);
}
if (relationType.type === 'service') {
queryParams.push('service_name=' + relationType.value);
}
App.router.transitionTo('main.hosts.hostDetails.logs', hostModel, { query: ''});
if (this.get('parentView') && typeof this.get('parentView').onClose === 'function') this.get('parentView').onClose();
},
/**
/**
* Determines if opened task related to service or component.
*
* @return {boolean} <code>true</code> when relates to service or component
*/
isLogsLinkVisible: function() {
if (!this.get('openedTask') || !this.get('openedTask.id')) return false;
return !!this._determineRoleRelation(this.get('openedTask'));
}.property('openedTask'),
/**
* @param {wrappedTask} taskInfo
* @return {boolean|TaskRelationObject}
*/
_determineRoleRelation: function(taskInfo) {
var foundComponentName,
foundServiceName,
componentNames = App.StackServiceComponent.find().mapProperty('componentName'),
serviceNames = App.StackService.find().mapProperty('serviceName'),
taskLog = this.get('currentHost.logTasks').findProperty('Tasks.id', Em.get(taskInfo, 'id')) || {},
role = Em.getWithDefault(taskLog, 'Tasks.role', false),
eqlFn = function(compare) {
return function(item) {
return item === compare;
};
};
if (!role) {
return false;
}
// component service check
if (role.endsWith('_SERVICE_CHECK')) {
role = role.replace('_SERVICE_CHECK', '');
}
foundComponentName = componentNames.filter(eqlFn(role))[0];
foundServiceName = serviceNames.filter(eqlFn(role))[0];
if (foundComponentName || foundServiceName) {
return {
type: foundComponentName ? 'component' : 'service',
value: foundComponentName || foundServiceName
}
}
return false;
},
/**
* @type {boolean}
*/
isRequestSchedule: function () {
var id = this.get('sourceRequestScheduleId');
return id != null && !isNaN(id) && id > -1;
}.property('sourceRequestScheduleId'),
/**
* @method refreshRequestScheduleInfo
*/
refreshRequestScheduleInfo: function () {
var self = this;
var id = this.get('sourceRequestScheduleId');
batchUtils.getRequestSchedule(id, function (data) {
var status = Em.get(data || {}, 'RequestSchedule.status');
if (status) {
switch (status) {
case 'DISABLED':
self.set('sourceRequestScheduleRunning', false);
self.set('sourceRequestScheduleAborted', true);
break;
case 'COMPLETED':
self.set('sourceRequestScheduleRunning', false);
self.set('sourceRequestScheduleAborted', false);
break;
case 'SCHEDULED':
self.set('sourceRequestScheduleRunning', true);
self.set('sourceRequestScheduleAborted', false);
break;
}
}
else {
self.set('sourceRequestScheduleRunning', false);
self.set('sourceRequestScheduleAborted', false);
}
}, function () {
self.set('sourceRequestScheduleRunning', false);
self.set('sourceRequestScheduleAborted', false);
});
}.observes('sourceRequestScheduleId'),
/**
* Attempts to abort the current request schedule
*
* @param {{context: number}} event
* @method doAbortRequestSchedule
*/
doAbortRequestSchedule: function (event) {
var self = this;
var id = event.context;
batchUtils.doAbortRequestSchedule(id, function () {
self.refreshRequestScheduleInfo();
});
},
/**
* Onclick handler for selected Host
*
* @param {{context: wrappedHost}} event
* @method gotoTasks
*/
gotoTasks: function (event) {
var tasksInfo = [];
event.context.logTasks.forEach(function (_task) {
tasksInfo.pushObject(this.get("controller").createTask(_task));
}, this);
if (tasksInfo.length) {
this.get("controller").set("popupHeaderName", event.context.publicName);
this.get("controller").set("currentHostName", event.context.publicName);
}
this.switchLevel("TASKS_LIST");
this.set('currentHost.tasks', tasksInfo);
this.set("parentView.isHostListHidden", true);
this.set("parentView.isTaskListHidden", false);
this.preloadHostModel(Em.getWithDefault(event.context || {}, 'name', false));
$(".modal").scrollTop(0);
$(".modal-body").scrollTop(0);
},
/**
* Will add <code>App.Host</code> model with relevant host info by specified host name only when it missed
* and Log Search service installed.
* This update needed to get actual status of logs for components located on this host and get appropriate model
* for navigation to hosts/:host_id/logs route.
*
* @method preloadHostModel
* @param {string} hostName host name to get info
*/
preloadHostModel: function(hostName) {
var self = this,
fields;
if (!hostName) {
self.set('hostInfoLoaded', true);
return;
}
if (this.get('isLogSearchInstalled') && App.get('supports.logSearch') && !App.Host.find().someProperty('hostName', hostName)) {
this.set('hostInfoLoaded', false);
fields = ['stack_versions/repository_versions/RepositoryVersions/repository_version',
'host_components/HostRoles/host_name'];
App.router.get('updateController').updateLogging(hostName, fields, function(data) {
App.hostsMapper.map({ items: [data] });
}).always(function() {
self.set('hostInfoLoaded', true);
});
} else {
self.set('hostInfoLoaded', true);
}
},
/**
* @method stopRebalanceHDFS
* @returns {App.ModalPopup}
*/
stopRebalanceHDFS: function () {
var hostPopup = this;
return App.showConfirmationPopup(function () {
App.ajax.send({
name: 'cancel.background.operation',
sender: hostPopup,
data: {
requestId: hostPopup.get('controller.currentServiceId')
}
});
hostPopup.backToServiceList();
});
},
/**
* Onclick handler for selected Task
*
* @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($(target).html());
newDocument.close();
},
/**
* Onclick event for show task detail info
*
* @param {{context: wrappedTask}} event
* @method toggleTaskLog
*/
toggleTaskLog: function (event) {
var taskInfo = event.context;
this.set("parentView.isLogWrapHidden", false);
if ($(".task-detail-log-clipboard").length) {
this.destroyClipBoard();
}
this.set("parentView.isHostListHidden", true);
this.set("parentView.isTaskListHidden", true);
this.set('openedTaskId', taskInfo.id);
this.switchLevel("TASK_DETAILS");
$(".modal").scrollTop(0);
$(".modal-body").scrollTop(0);
},
/**
* Onclick event for copy to clipboard button
*
* @method textTrigger
*/
textTrigger: function () {
$(".task-detail-log-clipboard").length ? this.destroyClipBoard() : this.createClipBoard();
},
/**
* Create Clip Board
*
* @method createClipBoard
*/
createClipBoard: function () {
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(isLogComponentActive ? this.get('clipBoardContent') : "stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
.css('display', 'block')
.width(logElementRect.width)
.height(isLogComponentActive ? logElement[0].scrollHeight : logElementRect.height)
.select();
this.set('isClipBoardActive', true);
},
/**
* Destroy Clip Board
*
* @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();
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,
linkTailTpl = '?host_name={0}&file_name={1}&component_name={2}',
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),
linkTail: linkTailTpl.format(hostName, logFileName, logComponentName),
isActive: false
});
}));
return acc;
}, []);
}
}
return [];
}.property('openedTaskId', 'isLevelLoaded'),
/**
* 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();
}
}
});