Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

fuzzy_stat.lua 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. local rspamd_util = require "rspamd_util"
  2. local lua_util = require "lua_util"
  3. local opts = {}
  4. local argparse = require "argparse"
  5. local parser = argparse()
  6. :name "rspamadm control fuzzystat"
  7. :description "Shows help for the specified configuration options"
  8. :help_description_margin(32)
  9. parser:flag "--no-ips"
  10. :description "No IPs stats"
  11. parser:flag "--no-keys"
  12. :description "No keys stats"
  13. parser:flag "--short"
  14. :description "Short output mode"
  15. parser:flag "-n --number"
  16. :description "Disable numbers humanization"
  17. parser:option "--sort"
  18. :description "Sort order"
  19. :convert {
  20. checked = "checked",
  21. matched = "matched",
  22. errors = "errors",
  23. name = "name"
  24. }
  25. local function add_data(target, src)
  26. for k, v in pairs(src) do
  27. if type(v) == 'number' then
  28. if target[k] then
  29. target[k] = target[k] + v
  30. else
  31. target[k] = v
  32. end
  33. elseif k == 'ips' then
  34. if not target['ips'] then
  35. target['ips'] = {}
  36. end
  37. -- Iterate over IPs
  38. for ip, st in pairs(v) do
  39. if not target['ips'][ip] then
  40. target['ips'][ip] = {}
  41. end
  42. add_data(target['ips'][ip], st)
  43. end
  44. elseif k == 'flags' then
  45. if not target['flags'] then
  46. target['flags'] = {}
  47. end
  48. -- Iterate over Flags
  49. for flag, st in pairs(v) do
  50. if not target['flags'][flag] then
  51. target['flags'][flag] = {}
  52. end
  53. add_data(target['flags'][flag], st)
  54. end
  55. elseif k == 'keypair' then
  56. if type(v.extensions) == 'table' then
  57. if type(v.extensions.name) == 'string' then
  58. target.name = v.extensions.name
  59. end
  60. end
  61. end
  62. end
  63. end
  64. local function print_num(num)
  65. if num then
  66. if opts['n'] or opts['number'] then
  67. return tostring(num)
  68. else
  69. return rspamd_util.humanize_number(num)
  70. end
  71. else
  72. return 'na'
  73. end
  74. end
  75. local function print_stat(st, tabs)
  76. if st['checked'] then
  77. if st.checked_per_hour then
  78. print(string.format('%sChecked: %s (%s per hour in average)', tabs,
  79. print_num(st['checked']), print_num(st['checked_per_hour'])))
  80. else
  81. print(string.format('%sChecked: %s', tabs, print_num(st['checked'])))
  82. end
  83. end
  84. if st['matched'] then
  85. if st.checked and st.checked > 0 and st.checked <= st.matched then
  86. local percentage = st.matched / st.checked * 100.0
  87. if st.matched_per_hour then
  88. print(string.format('%sMatched: %s - %s percent (%s per hour in average)', tabs,
  89. print_num(st['matched']), percentage, print_num(st['matched_per_hour'])))
  90. else
  91. print(string.format('%sMatched: %s - %s percent', tabs, print_num(st['matched']), percentage))
  92. end
  93. else
  94. if st.matched_per_hour then
  95. print(string.format('%sMatched: %s (%s per hour in average)', tabs,
  96. print_num(st['matched']), print_num(st['matched_per_hour'])))
  97. else
  98. print(string.format('%sMatched: %s', tabs, print_num(st['matched'])))
  99. end
  100. end
  101. end
  102. if st['errors'] then
  103. print(string.format('%sErrors: %s', tabs, print_num(st['errors'])))
  104. end
  105. if st['added'] then
  106. print(string.format('%sAdded: %s', tabs, print_num(st['added'])))
  107. end
  108. if st['deleted'] then
  109. print(string.format('%sDeleted: %s', tabs, print_num(st['deleted'])))
  110. end
  111. end
  112. -- Sort by checked
  113. local function sort_hash_table(tbl, sort_opts, key_key)
  114. local res = {}
  115. for k, v in pairs(tbl) do
  116. table.insert(res, { [key_key] = k, data = v })
  117. end
  118. local function sort_order(elt)
  119. local key = 'checked'
  120. local sort_res = 0
  121. if sort_opts['sort'] then
  122. if sort_opts['sort'] == 'matched' then
  123. key = 'matched'
  124. elseif sort_opts['sort'] == 'errors' then
  125. key = 'errors'
  126. elseif sort_opts['sort'] == 'name' then
  127. return elt[key_key]
  128. end
  129. end
  130. if elt.data[key] then
  131. sort_res = elt.data[key]
  132. end
  133. return sort_res
  134. end
  135. table.sort(res, function(a, b)
  136. return sort_order(a) > sort_order(b)
  137. end)
  138. return res
  139. end
  140. local function add_result(dst, src, k)
  141. if type(src) == 'table' then
  142. if type(dst) == 'number' then
  143. -- Convert dst to table
  144. dst = { dst }
  145. elseif type(dst) == 'nil' then
  146. dst = {}
  147. end
  148. for i, v in ipairs(src) do
  149. if dst[i] and k ~= 'fuzzy_stored' then
  150. dst[i] = dst[i] + v
  151. else
  152. dst[i] = v
  153. end
  154. end
  155. else
  156. if type(dst) == 'table' then
  157. if k ~= 'fuzzy_stored' then
  158. dst[1] = dst[1] + src
  159. else
  160. dst[1] = src
  161. end
  162. else
  163. if dst and k ~= 'fuzzy_stored' then
  164. dst = dst + src
  165. else
  166. dst = src
  167. end
  168. end
  169. end
  170. return dst
  171. end
  172. local function print_result(r)
  173. local function num_to_epoch(num)
  174. if num == 1 then
  175. return 'v0.6'
  176. elseif num == 2 then
  177. return 'v0.8'
  178. elseif num == 3 then
  179. return 'v0.9'
  180. elseif num == 4 then
  181. return 'v1.0+'
  182. elseif num == 5 then
  183. return 'v1.7+'
  184. end
  185. return '???'
  186. end
  187. if type(r) == 'table' then
  188. local res = {}
  189. for i, num in ipairs(r) do
  190. res[i] = string.format('(%s: %s)', num_to_epoch(i), print_num(num))
  191. end
  192. return table.concat(res, ', ')
  193. end
  194. return print_num(r)
  195. end
  196. return function(args, res)
  197. local res_ips = {}
  198. local res_databases = {}
  199. local wrk = res['workers']
  200. opts = parser:parse(args)
  201. if wrk then
  202. for _, pr in pairs(wrk) do
  203. -- processes cycle
  204. if pr['data'] then
  205. local id = pr['id']
  206. if id then
  207. local res_db = res_databases[id]
  208. if not res_db then
  209. res_db = {
  210. keys = {}
  211. }
  212. res_databases[id] = res_db
  213. end
  214. -- General stats
  215. for k, v in pairs(pr['data']) do
  216. if k ~= 'keys' and k ~= 'errors_ips' then
  217. res_db[k] = add_result(res_db[k], v, k)
  218. elseif k == 'errors_ips' then
  219. -- Errors ips
  220. if not res_db['errors_ips'] then
  221. res_db['errors_ips'] = {}
  222. end
  223. for ip, nerrors in pairs(v) do
  224. if not res_db['errors_ips'][ip] then
  225. res_db['errors_ips'][ip] = nerrors
  226. else
  227. res_db['errors_ips'][ip] = nerrors + res_db['errors_ips'][ip]
  228. end
  229. end
  230. end
  231. end
  232. if pr['data']['keys'] then
  233. local res_keys = res_db['keys']
  234. if not res_keys then
  235. res_keys = {}
  236. res_db['keys'] = res_keys
  237. end
  238. -- Go through keys in input
  239. for k, elts in pairs(pr['data']['keys']) do
  240. -- keys cycle
  241. if not res_keys[k] then
  242. res_keys[k] = {}
  243. end
  244. add_data(res_keys[k], elts)
  245. if elts['ips'] then
  246. for ip, v in pairs(elts['ips']) do
  247. if not res_ips[ip] then
  248. res_ips[ip] = {}
  249. end
  250. add_data(res_ips[ip], v)
  251. end
  252. end
  253. end
  254. end
  255. end
  256. end
  257. end
  258. end
  259. -- General stats
  260. for db, st in pairs(res_databases) do
  261. print(string.format('Statistics for storage %s', db))
  262. for k, v in pairs(st) do
  263. if k ~= 'keys' and k ~= 'errors_ips' then
  264. print(string.format('%s: %s', k, print_result(v)))
  265. end
  266. end
  267. print('')
  268. local res_keys = st['keys']
  269. if res_keys and not opts['no_keys'] and not opts['short'] then
  270. print('Keys statistics:')
  271. -- Convert into an array to allow sorting
  272. local sorted_keys = sort_hash_table(res_keys, opts, 'key')
  273. for _, key in ipairs(sorted_keys) do
  274. local key_stat = key.data
  275. if key_stat.name then
  276. print(string.format('Key id: %s, name: %s', key.key, key_stat.name))
  277. else
  278. print(string.format('Key id: %s', key.key))
  279. end
  280. print_stat(key_stat, '\t')
  281. if key_stat['ips'] and not opts['no_ips'] then
  282. print('')
  283. print('\tIPs stat:')
  284. local sorted_ips = sort_hash_table(key_stat['ips'], opts, 'ip')
  285. for _, v in ipairs(sorted_ips) do
  286. print(string.format('\t%s', v['ip']))
  287. print_stat(v['data'], '\t\t')
  288. print('')
  289. end
  290. end
  291. if key_stat.flags then
  292. print('')
  293. print('\tFlags stat:')
  294. for flag, v in pairs(key_stat.flags) do
  295. print(string.format('\t[%s]:', flag))
  296. -- Remove irrelevant fields
  297. v.checked = nil
  298. print_stat(v, '\t\t')
  299. print('')
  300. end
  301. end
  302. print('')
  303. end
  304. end
  305. if st['errors_ips'] and not opts['no_ips'] and not opts['short'] then
  306. print('')
  307. print('Errors IPs statistics:')
  308. local ip_stat = st['errors_ips']
  309. local ips = lua_util.keys(ip_stat)
  310. -- Reverse sort by number of errors
  311. table.sort(ips, function(a, b)
  312. return ip_stat[a] > ip_stat[b]
  313. end)
  314. for _, ip in ipairs(ips) do
  315. print(string.format('%s: %s', ip, print_result(ip_stat[ip])))
  316. end
  317. print('')
  318. end
  319. end
  320. if not opts['no_ips'] and not opts['short'] then
  321. print('')
  322. print('IPs statistics:')
  323. local sorted_ips = sort_hash_table(res_ips, opts, 'ip')
  324. for _, v in ipairs(sorted_ips) do
  325. print(string.format('%s', v['ip']))
  326. print_stat(v['data'], '\t')
  327. print('')
  328. end
  329. end
  330. end