/** * @fileoverview Javascript functions used in server status monitor page * @name Server Status Monitor * * @requires jQuery * @requires jQueryUI * @requires js/functions.js */ /* global isStorageSupported */ // js/config.js /* global codeMirrorEditor:writable */ // js/functions.js /* global firstDayOfCalendar, themeImagePath */ // templates/javascript/variables.twig /* global variableNames */ // templates/server/status/monitor/index.twig var runtime = {}; var serverTimeDiff; var serverOs; var isSuperUser; var serverDbIsLocal; var chartSize; var monitorSettings; function serverResponseError() { var btns = { [Messages.strReloadPage]: { text: Messages.strReloadPage, class: 'btn btn-primary', click: function () { window.location.reload(); } } }; $('#emptyDialog').dialog({ classes: { 'ui-dialog-titlebar-close': 'btn-close' }, title: Messages.strRefreshFailed }); $('#emptyDialog').html(Functions.getImage('s_attention') + Messages.strInvalidResponseExplanation); $('#emptyDialog').dialog({ classes: { 'ui-dialog-titlebar-close': 'btn-close' }, buttons: btns }); } /** * Destroys all monitor related resources */ function destroyGrid() { if (runtime.charts) { $.each(runtime.charts, function (key, value) { try { value.chart.destroy(); } catch (err) { // continue regardless of error } }); } try { runtime.refreshRequest.abort(); } catch (err) { // continue regardless of error } try { clearTimeout(runtime.refreshTimeout); } catch (err) { // continue regardless of error } $('#chartGrid').html(''); runtime.charts = null; runtime.chartAI = 0; monitorSettings = null; } AJAX.registerOnload('server/status/monitor.js', function () { var $jsDataForm = $('#js_data'); serverTimeDiff = new Date().getTime() - $jsDataForm.find('input[name=server_time]').val(); serverOs = $jsDataForm.find('input[name=server_os]').val(); isSuperUser = $jsDataForm.find('input[name=is_superuser]').val(); serverDbIsLocal = $jsDataForm.find('input[name=server_db_isLocal]').val(); }); /** * Unbind all event handlers before tearing down a page */ AJAX.registerTeardown('server/status/monitor.js', function () { $('#emptyDialog').remove(); $('a.popupLink').off('click'); $('body').off('click'); }); /** * Popup behaviour */ AJAX.registerOnload('server/status/monitor.js', function () { $('
').attr('id', 'emptyDialog').appendTo('#page_content'); $('a.popupLink').on('click', function () { var $link = $(this); $('div.' + $link.attr('href').substr(1)).show().offset({ top: $link.offset().top + $link.height() + 5, left: $link.offset().left }).addClass('openedPopup'); return false; }); $('body').on('click', function (event) { $('div.openedPopup').each(function () { var $cnt = $(this); var pos = $cnt.offset(); // Hide if the mouseclick is outside the popupcontent if (event.pageX > pos.left + $cnt.outerWidth() || event.pageY > pos.top + $cnt.outerHeight()) { $cnt.hide().removeClass('openedPopup'); } }); }); }); AJAX.registerTeardown('server/status/monitor.js', function () { $('a[href="#rearrangeCharts"], a[href="#endChartEditMode"]').off('click'); $('div.popupContent select[name="chartColumns"]').off('change'); $('div.popupContent select[name="gridChartRefresh"]').off('change'); $('a[href="#addNewChart"]').off('click'); $('a[href="#exportMonitorConfig"]').off('click'); $('a[href="#importMonitorConfig"]').off('click'); $('a[href="#clearMonitorConfig"]').off('click'); $('a[href="#pauseCharts"]').off('click'); $('a[href="#monitorInstructionsDialog"]').off('click'); $('input[name="chartType"]').off('click'); $('input[name="useDivisor"]').off('click'); $('input[name="useUnit"]').off('click'); $('select[name="varChartList"]').off('click'); $('a[href="#kibDivisor"]').off('click'); $('a[href="#mibDivisor"]').off('click'); $('a[href="#submitClearSeries"]').off('click'); $('a[href="#submitAddSeries"]').off('click'); // $("input#variableInput").destroy(); $('#chartPreset').off('click'); $('#chartStatusVar').off('click'); destroyGrid(); }); AJAX.registerOnload('server/status/monitor.js', function () { // Show tab links $('div.tabLinks').show(); $('#loadingMonitorIcon').remove(); // Codemirror is loaded on demand so we might need to initialize it if (!codeMirrorEditor) { var $elm = $('#sqlquery'); if ($elm.length > 0 && typeof CodeMirror !== 'undefined') { codeMirrorEditor = CodeMirror.fromTextArea($elm[0], { lineNumbers: true, matchBrackets: true, indentUnit: 4, mode: 'text/x-mysql', lineWrapping: true }); } } // Timepicker is loaded on demand so we need to initialize // datetime fields from the 'load log' dialog $('#logAnalyseDialog').find('.datetimefield').each(function () { Functions.addDatepicker($(this)); }); /** ** Monitor charting implementation ****/ /* Saves the previous ajax response for differential values */ var oldChartData = null; // Holds about to be created chart var newChart = null; var chartSpacing; // Whenever the monitor object (runtime.charts) or the settings object // (monitorSettings) changes in a way incompatible to the previous version, // increase this number. It will reset the users monitor and settings object // in their localStorage to the default configuration var monitorProtocolVersion = '1.0'; // Runtime parameter of the monitor, is being fully set in initGrid() runtime = { // Holds all visible charts in the grid charts: null, // Stores the timeout handler so it can be cleared refreshTimeout: null, // Stores the GET request to refresh the charts refreshRequest: null, // Chart auto increment chartAI: 0, // To play/pause the monitor redrawCharts: false, // Object that contains a list of nodes that need to be retrieved // from the server for chart updates dataList: [], // Current max points per chart (needed for auto calculation) gridMaxPoints: 20, // displayed time frame xmin: -1, xmax: -1 }; monitorSettings = null; var defaultMonitorSettings = { columns: 3, chartSize: { width: 295, height: 250 }, // Max points in each chart. Settings it to 'auto' sets // gridMaxPoints to (chartwidth - 40) / 12 gridMaxPoints: 'auto', /* Refresh rate of all grid charts in ms */ gridRefresh: 5000 }; // Allows drag and drop rearrange and print/edit icons on charts var editMode = false; /* List of preconfigured charts that the user may select */ var presetCharts = { // Query cache efficiency 'qce': { title: Messages.strQueryCacheEfficiency, series: [{ label: Messages.strQueryCacheEfficiency }], nodes: [{ dataPoints: [{ type: 'statusvar', name: 'Qcache_hits' }, { type: 'statusvar', name: 'Com_select' }], transformFn: 'qce' }], maxYLabel: 0 }, // Query cache usage 'qcu': { title: Messages.strQueryCacheUsage, series: [{ label: Messages.strQueryCacheUsed }], nodes: [{ dataPoints: [{ type: 'statusvar', name: 'Qcache_free_memory' }, { type: 'servervar', name: 'query_cache_size' }], transformFn: 'qcu' }], maxYLabel: 0 } }; // time span selection var selectionTimeDiff = []; var selectionStartX; var selectionStartY; var drawTimeSpan = false; /* Add OS specific system info charts to the preset chart list */ switch (serverOs) { case 'WINNT': $.extend(presetCharts, { 'cpu': { title: Messages.strSystemCPUUsage, series: [{ label: Messages.strAverageLoad }], nodes: [{ dataPoints: [{ type: 'cpu', name: 'loadavg' }] }], maxYLabel: 100 }, 'memory': { title: Messages.strSystemMemory, series: [{ dataType: 'memory', label: Messages.strUsedMemory, fill: true }, { label: Messages.strFreeMemory, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemFree' }], valueDivisor: 1024 }], maxYLabel: 0 }, 'swap': { title: Messages.strSystemSwap, series: [{ label: Messages.strUsedSwap, fill: true }, { label: Messages.strFreeSwap, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'SwapUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapFree' }], valueDivisor: 1024 }], maxYLabel: 0 } }); break; case 'Linux': $.extend(presetCharts, { 'cpu': { title: Messages.strSystemCPUUsage, series: [{ label: Messages.strAverageLoad }], nodes: [{ dataPoints: [{ type: 'cpu', name: 'irrelevant' }], transformFn: 'cpu-linux' }], maxYLabel: 0 }, 'memory': { title: Messages.strSystemMemory, series: [{ label: Messages.strBufferedMemory, fill: true }, { label: Messages.strUsedMemory, fill: true }, { label: Messages.strCachedMemory, fill: true }, { label: Messages.strFreeMemory, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'Buffers' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'Cached' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemFree' }], valueDivisor: 1024 }], maxYLabel: 0 }, 'swap': { title: Messages.strSystemSwap, series: [{ label: Messages.strCachedSwap, fill: true }, { label: Messages.strUsedSwap, fill: true }, { label: Messages.strFreeSwap, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'SwapCached' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapFree' }], valueDivisor: 1024 }], maxYLabel: 0 } }); break; case 'SunOS': $.extend(presetCharts, { 'cpu': { title: Messages.strSystemCPUUsage, series: [{ label: Messages.strAverageLoad }], nodes: [{ dataPoints: [{ type: 'cpu', name: 'loadavg' }] }], maxYLabel: 0 }, 'memory': { title: Messages.strSystemMemory, series: [{ label: Messages.strUsedMemory, fill: true }, { label: Messages.strFreeMemory, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemFree' }], valueDivisor: 1024 }], maxYLabel: 0 }, 'swap': { title: Messages.strSystemSwap, series: [{ label: Messages.strUsedSwap, fill: true }, { label: Messages.strFreeSwap, fill: true }], nodes: [{ dataPoints: [{ type: 'memory', name: 'SwapUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapFree' }], valueDivisor: 1024 }], maxYLabel: 0 } }); break; } // Default setting for the chart grid var defaultChartGrid = { 'c0': { title: Messages.strQuestions, series: [{ label: Messages.strQuestions }], nodes: [{ dataPoints: [{ type: 'statusvar', name: 'Questions' }], display: 'differential' }], maxYLabel: 0 }, 'c1': { title: Messages.strChartConnectionsTitle, series: [{ label: Messages.strConnections }, { label: Messages.strProcesses }], nodes: [{ dataPoints: [{ type: 'statusvar', name: 'Connections' }], display: 'differential' }, { dataPoints: [{ type: 'proc', name: 'processes' }] }], maxYLabel: 0 }, 'c2': { title: Messages.strTraffic, series: [{ label: Messages.strBytesSent }, { label: Messages.strBytesReceived }], nodes: [{ dataPoints: [{ type: 'statusvar', name: 'Bytes_sent' }], display: 'differential', valueDivisor: 1024 }, { dataPoints: [{ type: 'statusvar', name: 'Bytes_received' }], display: 'differential', valueDivisor: 1024 }], maxYLabel: 0 } }; // Server is localhost => We can add cpu/memory/swap to the default chart if (serverDbIsLocal && typeof presetCharts.cpu !== 'undefined') { defaultChartGrid.c3 = presetCharts.cpu; defaultChartGrid.c4 = presetCharts.memory; defaultChartGrid.c5 = presetCharts.swap; } $('a[href="#rearrangeCharts"], a[href="#endChartEditMode"]').on('click', function (event) { event.preventDefault(); editMode = !editMode; if ($(this).attr('href') === '#endChartEditMode') { editMode = false; } $('a[href="#endChartEditMode"]').toggle(editMode); if (editMode) { // Close the settings popup $('div.popupContent').hide().removeClass('openedPopup'); $('#chartGrid').sortableTable({ ignoreRect: { top: 8, left: chartSize.width - 63, width: 54, height: 24 } }); } else { $('#chartGrid').sortableTable('destroy'); } saveMonitor(); // Save settings return false; }); // global settings $('div.popupContent select[name="chartColumns"]').on('change', function () { monitorSettings.columns = parseInt(this.value, 10); calculateChartSize(); // Empty cells should keep their size so you can drop onto them $('#chartGrid').find('tr td').css('width', chartSize.width + 'px'); $('#chartGrid').find('.monitorChart').css({ width: chartSize.width + 'px', height: chartSize.height + 'px' }); /* Reorder all charts that it fills all column cells */ var numColumns; var $tr = $('#chartGrid').find('tr').first(); var tempManageCols = function () { if (numColumns > monitorSettings.columns) { if ($tr.next().length === 0) { $tr.after('' + Messages.strNoDataFound + '
'); dlgBtns[Messages.strClose].click = function () { $(this).dialog('close'); }; $('#emptyDialog').dialog('option', 'buttons', dlgBtns); return; } runtime.logDataCols = buildLogTable(logData, opts.removeVariables); /* Show some stats in the dialog */ $('#emptyDialog').dialog({ classes: { 'ui-dialog-titlebar-close': 'btn-close' }, title: Messages.strLoadingLogs }); $('#emptyDialog').html('' + Messages.strLogDataLoaded + '
'); $.each(logData.sum, function (key, value) { var newKey = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(); if (newKey === 'Total') { newKey = '' + newKey + ''; } $('#emptyDialog').append(newKey + ': ' + value + '