diff --git a/httpdocs/css/pie-chart.css b/httpdocs/css/pie-chart.css index 927b21805a..c8f788b42c 100644 --- a/httpdocs/css/pie-chart.css +++ b/httpdocs/css/pie-chart.css @@ -21,6 +21,12 @@ -moz-box-sizing: border-box; box-sizing: border-box; } + +.pie-chart-small { + height: 260px; + width: 280px; +} + .pie-chart .total{ font-size: 18px; font-weight: bold; diff --git a/httpdocs/js/pie-chart.js b/httpdocs/js/pie-chart.js index ed0b93a37e..f46356759b 100644 --- a/httpdocs/js/pie-chart.js +++ b/httpdocs/js/pie-chart.js @@ -326,6 +326,14 @@ function create_pie_chart(name, units) { var textOffset = 14; var tweenDuration = 250; var r = 116; //100; + + if($(name).hasClass("pie-chart-small")) { + w = 270; + h = 250; + r = w / 3 - 20; + ir = r / 2; + } + var lines, valueLabels, nameLabels; //D3 helper function to populate pie slice parameters from array data @@ -351,7 +359,7 @@ function create_pie_chart(name, units) { var vis = d3.select(name).append("svg:svg") .attr("width", w) .attr("height", h) - .attr("viewBox","0 0 500 325") + .attr("viewBox","0 0 " + w + " " + h) .attr("preserveAspectRatio","xMidYMid"); //GROUP FOR ARCS/PATHS diff --git a/include/AlertsManager.h b/include/AlertsManager.h index 3b4fdd1f1b..57c6c332be 100644 --- a/include/AlertsManager.h +++ b/include/AlertsManager.h @@ -166,6 +166,12 @@ class AlertsManager : protected StoreManager { int deleteAlerts(bool engaged, const int *rowid); int deleteAlerts(bool engaged, AlertEntity alert_entity, const char *alert_entity_value); + + /* + ========== raw API ====== + */ + int selectAlertsRaw(lua_State *vm, bool engaged, const char *selection, const char *clauses); + /* Following are the legacy methods that were formally global to the whole ntopng */ #ifdef NOTUSED /** diff --git a/scripts/lua/get_alerts_stats_data.lua b/scripts/lua/get_alerts_stats_data.lua new file mode 100644 index 0000000000..8438ef6bfb --- /dev/null +++ b/scripts/lua/get_alerts_stats_data.lua @@ -0,0 +1,106 @@ +-- +-- (C) 2013-16 - ntop.org +-- + +dirs = ntop.getDirs() +package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path + +require "lua_utils" +local json = require('dkjson') + +local stats_type = _GET["stats_type"] + +sendHTTPHeader('text/html; charset=iso-8859-1') + +interface.select(ifname) + + +local res = {} +local aggr = {} + +local selection +local aggregation +local labeller +if stats_type == "severity_pie" or stats_type == "type_pie" then + if stats_type == "severity_pie" then + selection = "alert_severity as label, count(*) as value" + aggregation = "group by label" + labeller = alertSeverityLabel + elseif stats_type == "type_pie" then + selection = "alert_type as label, count(*) as value" + aggregation = "group by label" + labeller = alertTypeLabel + end + for _, engaged in pairs({true, false}) do + local r = interface.selectAlertsRaw(engaged, selection, aggregation) + if r == nil then r = {} end + -- must aggregate again to sum counters between engaged and closed alerts + for k, v in ipairs(r) do + + if v["label"] ~= nil then + v["label"] = labeller(v["label"], true) + end + + if aggr[v["label"]] ~= nil then + aggr[v["label"]] = aggr[v["label"]] + v["value"] + else + aggr[v["label"]] = v["value"] + end + + end + end + +elseif stats_type == "duration_pie" then + + local binner = function(value) + value = tonumber(value) + if value == nil then return nil end + local bin + if value < 60 then bin = "[0,1)" + elseif value < 60 * 5 then bin = "[1,5)" + elseif value < 60 * 10 then bin = "[5,10)" + else bin = "10+" end + -- local log_base = 2 + -- local bin = math.floor(math.log(value) / math.log(log_base)) + -- bin = tostring(log_base^bin).." s <= d < "..tostring(log_base^(bin+1)).." s" + return bin.." min" + end + + -- engaged + -- the duration is the current instant minus the alert timestamp + selection = "(strftime('%s','now') - alert_tstamp) as label, count(*) as value" + aggregation = "group by label" + local r_engaged = interface.selectAlertsRaw(true, selection, aggregation) + if r_engaged == nil then r_engaged = {} end + + -- not engaged + -- the duration is the difference between the end and the start time + selection = "(alert_tstamp_end - alert_tstamp) as label, count(*) as value" + -- we don't take into account 'instant' alerts + aggregation = "where alert_tstamp_end is not null group by label" + local r_closed = interface.selectAlertsRaw(false, selection, aggregation) + if r_closed == nil then r_closed = {} end + + -- let's put things together + for _, r in pairs({r_engaged, r_closed}) do + for k, v in ipairs(r) do + if v["label"] ~= nil then + v["label"] = binner(v["label"]) + end + if aggr[v["label"]] ~= nil then + aggr[v["label"]] = aggr[v["label"]] + v["value"] + else + aggr[v["label"]] = v["value"] + end + end + end +elseif stats_type == "counts_pie" then + aggr["Engaged"] = interface.getNumAlerts(true) + aggr["Closed"] = interface.getNumAlerts(false) +end + +for k, v in pairs(aggr) do + res[#res + 1] = {label=k, value=tonumber(v)} +end + +print(json.encode(res, nil)) diff --git a/scripts/lua/modules/alert_utils.lua b/scripts/lua/modules/alert_utils.lua index 09d1544019..dccaf6fec3 100644 --- a/scripts/lua/modules/alert_utils.lua +++ b/scripts/lua/modules/alert_utils.lua @@ -671,6 +671,33 @@ end -- ################################# +function drawAlertStatsCharts() + print[[ + + + + + + + + +
SeverityTypeDurationCounts
+ + +]] +end + +-- ################################# + function drawAlertTables(num_alerts, num_engaged_alerts, url_params) local alert_items = {} diff --git a/scripts/lua/modules/lua_utils.lua b/scripts/lua/modules/lua_utils.lua index 08d40fc147..a5a5ba3f98 100644 --- a/scripts/lua/modules/lua_utils.lua +++ b/scripts/lua/modules/lua_utils.lua @@ -296,8 +296,15 @@ alert_entity_keys = { { "Flow", 4, "flow" } } -function alertSeverityLabel(v) - return(_handleArray(alert_level_keys, tonumber(v))) +function noHtml(s) + if s == nil then return nil end + return s:gsub("<.->(.-)","%1"):gsub("^%s*(.-)%s*$", "%1") +end + +function alertSeverityLabel(v, nohtml) + local res = _handleArray(alert_level_keys, tonumber(v)) + if res ~= nil and nohtml == true then res = noHtml(res) end + return res end function alertSeverity(v) @@ -308,8 +315,10 @@ function alertSeverity(v) return(_handleArray(severity_table, v)) end -function alertTypeLabel(v) - return(_handleArray(alert_type_keys, tonumber(v))) +function alertTypeLabel(v, nohtml) + local res = _handleArray(alert_type_keys, tonumber(v)) + if res ~= nil and nohtml == true then res = noHtml(res) end + return res end function alertType(v) @@ -320,8 +329,10 @@ function alertType(v) return(_handleArray(typetable, v)) end -function alertEntityLabel(v) - return(_handleArray(alert_entity_keys, tonumber(v))) +function alertEntityLabel(v, nothml) + local res = _handleArray(alert_entity_keys, tonumber(v)) + if res ~= nil and nohtml == true then res = noHtml(res) end + return res end function alertEntity(v) diff --git a/scripts/lua/show_alerts.lua b/scripts/lua/show_alerts.lua index f42914370f..5a0492b404 100644 --- a/scripts/lua/show_alerts.lua +++ b/scripts/lua/show_alerts.lua @@ -27,6 +27,7 @@ elseif num_alerts == 0 and num_engaged_alerts == 0 then print("
No recorded alerts so far for interface "..ifname.."
") else + -- drawAlertStatsCharts() drawAlertTables(num_alerts, num_engaged_alerts) end -- closes if ntop.getPrefs().are_alerts_enabled == false then diff --git a/src/AlertsManager.cpp b/src/AlertsManager.cpp index ea0b36b012..1b00e684ad 100644 --- a/src/AlertsManager.cpp +++ b/src/AlertsManager.cpp @@ -763,7 +763,7 @@ int AlertsManager::getAlerts(lua_State* vm, patricia_tree_t *allowed_hosts, out: m.unlock(__FILE__, __LINE__); - return rc; + return rc; } /* **************************************************** */ @@ -904,6 +904,43 @@ int AlertsManager::deleteAlerts(bool engaged, AlertEntity alert_entity, const ch return rc; } +/* ******************************************* */ + +int AlertsManager::selectAlertsRaw(lua_State *vm, bool engaged, const char *selection, const char *clauses) { + alertsRetriever ar; + char query[STORE_MANAGER_MAX_QUERY]; + char *zErrMsg = NULL; + int rc; + + snprintf(query, sizeof(query), + "SELECT %s FROM %s %s ", + selection ? selection : "*", + engaged ? ALERTS_MANAGER_ENGAGED_TABLE_NAME : ALERTS_MANAGER_TABLE_NAME, + clauses ? clauses : ""); + + // ntop->getTrace()->traceEvent(TRACE_NORMAL, "Going to execute: %s", query); + + m.lock(__FILE__, __LINE__); + + lua_newtable(vm); + + ar.vm = vm, ar.current_offset = 0; + rc = sqlite3_exec(db, query, getAlertsCallback, (void*)&ar, &zErrMsg); + + if( rc != SQLITE_OK ){ + rc = 1; + ntop->getTrace()->traceEvent(TRACE_ERROR, "SQL Error: %s\n%s", zErrMsg, query); + sqlite3_free(zErrMsg); + goto out; + } + + rc = 0; + out: + m.unlock(__FILE__, __LINE__); + + return rc; +} + /* ******************************************* */ #ifdef NOTUSED int AlertsManager::queueAlert(AlertLevel level, AlertStatus s, AlertType t, char *msg) { diff --git a/src/Lua.cpp b/src/Lua.cpp index 25221f41aa..d9fe06113c 100644 --- a/src/Lua.cpp +++ b/src/Lua.cpp @@ -4572,6 +4572,36 @@ static int ntop_interface_delete_alerts(lua_State* vm) { /* ****************************************** */ +static int ntop_interface_select_alerts_raw(lua_State* vm) { + NetworkInterface *iface = getCurrentInterface(vm); + AlertsManager *am; + bool engaged = false; + char *selection = NULL, *clauses = NULL; + + ntop->getTrace()->traceEvent(TRACE_DEBUG, "%s() called", __FUNCTION__); + + if(!iface || !(am = iface->getAlertsManager())) + return (CONST_LUA_ERROR); + + if(lua_type(vm, 1) == LUA_TBOOLEAN) + engaged = lua_toboolean(vm, 1); + + if(lua_type(vm, 2) == LUA_TSTRING) + if((selection = (char*)lua_tostring(vm, 2)) == NULL) + return(CONST_LUA_PARAM_ERROR); + + if(lua_type(vm, 3) == LUA_TSTRING) + if((clauses = (char*)lua_tostring(vm, 3)) == NULL) + return(CONST_LUA_PARAM_ERROR); + + if(am->selectAlertsRaw(vm, engaged, selection, clauses)) + return(CONST_LUA_ERROR); + + return (CONST_LUA_OK); +} + +/* ****************************************** */ + #if NTOPNG_PRO static int ntop_nagios_reload_config(lua_State* vm) { @@ -5063,6 +5093,7 @@ static const luaL_Reg ntop_interface_reg[] = { { "getAlerts" , ntop_interface_get_alerts }, { "getNumAlerts", ntop_interface_get_num_alerts }, { "deleteAlerts", ntop_interface_delete_alerts }, + { "selectAlertsRaw", ntop_interface_select_alerts_raw }, { "engageHostAlert", ntop_interface_engage_host_alert }, { "releaseHostAlert", ntop_interface_release_host_alert }, { "engageNetworkAlert", ntop_interface_engage_network_alert },