File: ts_common.lua

package info (click to toggle)
ntopng 5.2.1%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 121,832 kB
  • sloc: javascript: 143,431; cpp: 71,175; ansic: 11,108; sh: 4,687; makefile: 911; python: 587; sql: 512; pascal: 234; perl: 118; ruby: 52; exp: 4
file content (264 lines) | stat: -rw-r--r-- 5,869 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
--
-- (C) 2017 - ntop.org
--

local ts_common = {}

-- ##############################################

ts_common.metrics = {}
ts_common.metrics.counter = "counter"
ts_common.metrics.gauge = "gauge"
ts_common.metrics.derivative = "derivative"

ts_common.aggregation = {}
ts_common.aggregation.mean = "mean"
ts_common.aggregation.max = "max"
ts_common.aggregation.min = "min"
ts_common.aggregation.last = "last"

-- ##############################################

-- Find the percentile of a list of values
-- N - A list of values.  N must be sorted.
-- P - A float value from 0.0 to 1.0
local function percentile(N, P)
  local n = math.floor(math.floor(P * #N + 0.5))
  return(N[n-1])
end

function ts_common.ninetififthPercentile(serie)
  if #serie <= 1 then
    return serie[1]
  end

  local N = {}

  for i, val in ipairs(serie) do
    if val ~= val then
      -- Convert NaN to 0
      val = 0
    end

    N[i] = val
  end

  table.sort(N) -- <<== Sort first
  return(percentile(N, 0.95))
end

-- ##############################################

function ts_common.calculateMinMax(total_serie)
  local min_val, max_val
  local min_val_pt, max_val_pt

  for idx, val in pairs(total_serie) do
    -- Exclude NaN points
    if val == val then
      if (min_val_pt == nil) or (val < min_val) then
        min_val = val
        min_val_pt = idx - 1
      end
      if (max_val_pt == nil) or (val > max_val) then
        max_val = val
        max_val_pt = idx - 1
      end
    end
  end

  return {
    min_val = min_val,
    max_val = max_val,
    min_val_idx = min_val_pt,
    max_val_idx = max_val_pt,
  }
end

-- ##############################################

function ts_common.calculateStatistics(total_serie, step, tdiff, data_type)
  local total = 0
  local pt_sum = 0

  for idx, val in pairs(total_serie) do
    if val ~= val then
      -- Convert NaN to 0
      val = 0
    end

    -- integrate
    total = total + val * step
    pt_sum = pt_sum + val
  end

  local avg = pt_sum / #total_serie

  if data_type == ts_common.metrics.gauge then
    -- no total for gauge values!
    total = nil
  end

  return {
    total = total,
    average = avg,
    ["95th_percentile"] = ts_common.ninetififthPercentile(total_serie),
  }
end

-- ##############################################

-- NOTE: this corresponds to graph_utils interpolateSerie
-- This is approximate.
-- Aproximate means it doesn't adjust the step.
-- TODO: take as input and adjust the step as well.
function ts_common.upsampleSerie(serie, num_points)
  if num_points <= #serie then
    return serie
  end

  local res = {}
  local intervals = num_points / #serie;

  local lerp = function(v0, v1, t)
    return (1 - t) * v0 + t * v1
  end

  if #serie == 0 then
    return res
  end

  for i=1,num_points do
    local index = (i-1) / (intervals)
    local _, t = math.modf(index)
    local prev_i = math.floor(index)
    local next_i = math.min(math.ceil(index), #serie - 1)

    local v = lerp(serie[prev_i+1], serie[next_i+1], t)
    res[i] = v
  end

  return res
end

-- ##############################################

-- If a point value exceeds this value, it should be discarded as invalid
function ts_common.getMaxPointValue(schema, metric, tags)
  if tags.ifid ~= nil then
    if string.contains(metric, "bytes") then
      local ifspeed = getInterfaceSpeed(tonumber(tags.ifid))

      if ifspeed ~= nil then
        -- bit/s
        return ifspeed * 1000 * 1000
      end
    elseif string.contains(metric, "packets") then
      local ifspeed = getInterfaceSpeed(tonumber(tags.ifid))

      if ifspeed ~= nil then
        -- mbit/s
        local speed_mbps = ifspeed
        local max_pps_baseline = 14881 -- for 10 mbps

        return speed_mbps / 10 * max_pps_baseline
      end
    end
  end

  return math.huge
end

-- ##############################################

function ts_common.normalizeVal(v, max_val, options)
  -- it's undesirable to normalize and set a 0 when
  -- the number is greater than max value
  -- this was causing missing points in the charts
  -- with ZMQ interface which can actually exceed the nominal value

  if v ~= v then
    -- NaN value
    v = options.fill_value
  elseif v < options.min_value then
    v = options.min_value
  end

  return v
end

-- ##############################################

function ts_common.calculateSampledTimeStep(raw_step, tstart, tend, options)
  local estimed_num_points = math.ceil((tend - tstart) / raw_step)
  local time_step = raw_step

  if estimed_num_points > options.max_num_points then
    -- downsample
    local num_samples = math.ceil(estimed_num_points / options.max_num_points)
    time_step = num_samples * raw_step
  end

  return time_step
end

-- ##############################################

function ts_common.fillSeries(schema, tstart, tend, time_step, fill_value)
  local count = math.ceil((tend-tstart) / time_step)
  local res = {}

  for idx, metric in ipairs(schema._metrics) do
    local data = {}

    for i=1, count do
      data[i] = fill_value
      i = i + 1
    end

    res[idx] = {label=metric, data=data}
  end

  return res, count
end

-- ##############################################

function ts_common.serieWithTimestamp(serie, tstart, tstep)
  local data = {}
  local t = tstart

  for i, pt in ipairs(serie) do
    data[t] = pt
    t = t + tstep
  end

  return data
end

-- ##############################################

local last_error = nil
local last_error_msg = nil
ts_common.ERR_OPERATION_TOO_SLOW = "OPERATION_TOO_SLOW"

function ts_common.clearLastError()
  err = nil
end

function ts_common.setLastError(err, msg)
  last_error = err
  last_error_msg = msg
end

function ts_common.getLastError()
  return last_error
end

function ts_common.getLastErrorMessage()
  return last_error_msg
end

-- ##############################################

return ts_common