#!/usr/bin/ruby OUTDIR = "/var/www/html/rrd" RRD = "#{OUTDIR}/wis.rrd" THR_FILE = "#{OUTDIR}/wis-thr.txt" START = "-604800" # 7日 END_T = "now" UPPER_LIMIT = 500.0 MARGIN = 1.1 $VERBOSE=true if STDERR.tty? # ---------------------------- # 閾値読み込み # ---------------------------- t_wnm, t_top, t_brw = File.read(THR_FILE).split.map(&:to_f) # ---------------------------- # RRD fetch # ---------------------------- raw = `rrdtool fetch #{RRD} AVERAGE --start #{START} --end #{END_T}` data = [] raw.each_line do |line| next unless line =~ /^\d+:/ ts, wnm, top, brw = line.split next if wnm == "nan" data << [ts.to_i, wnm.to_f, top.to_f, brw.to_f] end # ---------------------------- # kaku kenti script ga izyou ti to suru atai # ---------------------------- def is_syserr val val > 500.0 end # ---------------------------- # 単発スパイク検出 # ---------------------------- def detect_spikes(series, threshold) spikes = [] (1...(series.size - 1)).each do |i| prev = series[i - 1] curr = series[i] nxt = series[i + 1] if is_syserr(curr) warn "syserr #{curr} excluded from spike-detecting" next end if curr > threshold && prev <= threshold && nxt <= threshold spikes << curr end end spikes end wnm_series = data.map { |r| r[1] } top_series = data.map { |r| r[2] } brw_series = data.map { |r| r[3] } wnm_spikes = detect_spikes(wnm_series, t_wnm) top_spikes = detect_spikes(top_series, t_top) brw_spikes = detect_spikes(brw_series, t_brw) # ---------------------------- # 新しい閾値計算 # ---------------------------- def calc_new_threshold(spikes, current) return current if spikes.empty? max = spikes.max new_val = max * MARGIN # 上限適用 new_val = UPPER_LIMIT if new_val > UPPER_LIMIT new_val end new_wnm = calc_new_threshold(wnm_spikes, t_wnm) new_top = calc_new_threshold(top_spikes, t_top) new_brw = calc_new_threshold(brw_spikes, t_brw) updated = (new_wnm != t_wnm) || (new_top != t_top) || (new_brw != t_brw) fmsg='' # ---------------------------- # 書き込み # ---------------------------- begin if updated then File.write( THR_FILE, format("%.1f %.1f %.1f\n", new_wnm, new_top, new_brw) ) end rescue Errno::EACCES => e fmsg=e.to_s end # ---------------------------- # ログ出力 # ---------------------------- subject = if !fmsg.empty? "[Error]WIS Monitor" elsif updated "[Adjusted]WIS Monitor" else "[NoChange]WIS Monitor" end open("|/usr/sbin/sendmail -t","wt"){|mail| mail.puts "Subject: #{subject}\r" mail.puts "To: news\r" mail.puts "\r" if updated mail.puts "Thresholds were updated." else mail.puts "No threshold update was required." mail.puts "No single-point spikes exceeding the current thresholds were found in the past 7 days." end mail.puts "" mail.puts "---- Threshold Update ----" mail.puts "WNM: #{t_wnm} -> #{new_wnm} (spikes=#{wnm_spikes.size})" mail.puts "TOP: #{t_top} -> #{new_top} (spikes=#{top_spikes.size})" mail.puts "BRW: #{t_brw} -> #{new_brw} (spikes=#{brw_spikes.size})" mail.puts "WNM spikes: count=#{wnm_spikes.size} max=#{wnm_spikes.max || '-'}" mail.puts "TOP spikes: count=#{top_spikes.size} max=#{top_spikes.max || '-'}" mail.puts "BRW spikes: count=#{brw_spikes.size} max=#{brw_spikes.max || '-'}" mail.puts "Upper limit applied: #{UPPER_LIMIT}" unless fmsg.empty? mail.puts "" mail.puts fmsg end }