3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Sensuで取得したSNMP対応機器の転送量をGrafana経由でモニタリング

3
Posted at

GWですね。こんな気持の良い日はなにか新しい物をウォッチングしたくなります。

ということで、題名通りSensuとGrafanaを使ってSNMPで取得したスイッチのデータを可視化してみました。

書いてある内容は 「Sensuでスイッチの通信量を取得」 とほぼ同じですが、

  • ポート単位でデータを取得している
  • キャッシュファイルを作ってSNMPデータの取得回数を抑えてる
  • 無線LANの転送量も取得している(Ruckus限定?)
  • 機器ややポートの名称も取得している
  • Grafanaの表示例つき(おまけ)

あたりを追加しています。

うまく動けば下記のようにポートの数が多いスイッチでもトップ10だけを描画させるということも出来るようになります。

l3_snmp.png

Grafanaへ追加するメトリックスは network.hdf5p35*.snmp.tx.*.sortByMaxima().highestMax(10).aliasSub((.)\.(.)\.(.)\.(.)\.(.), \2.\5) な感じで、

  • network : 接頭語(ハードコーティングされているので調整してください)
  • hdf5p35* : 機器名(番号などがついていれば*で全取得)
  • snmp : データ取得元
  • tx : Transmitter(受信であればrx、どちらもであれば*を指定してください)
  • * : ポート(ポート名が設定されていれば名称、されていなければ番号、全部であれば*を指定してください)
  • sortByMaxima : データ量が大きい順にソート
  • highestMax : トップ10だけを描画
  • aliasSub : 正規表現を使って名称を指定

としています。

ちなみに、 sortByMaxima().highestMax(10)sumSeries() にすれば元記事のように合計の値を描画させることも出来ます。

プラグインのコードは以下のとおりで、

snmp_metrics.rb
#!/usr/bin/env ruby
require "sensu-plugin/metric/cli"
require "snmp"
require "json"

class SNMPTrafficMetrics < Sensu::Plugin::Metric::CLI::Graphite
  option :hosts,
    short: "-h HOSTS",
    long: "--hosts HOSTS",
    default: "localhost,127.0.0.1"

  option :community,
    short: "-c COMMUNITY",
    long: "--community COMMUNITY",
    default: "public"

  option :sleep,
    long: "--sleep SLEEP",
    proc: proc {|a| a.to_f},
    default: 1

  option :cache,
    long: "--cache",
    default: "/tmp/snmp_cache.json"

  def oids type
    lists = {
      "wlan_rx" => "1.3.6.1.4.1.25053.1.2.2.1.1.1.1.1.14",   # for Ruckus ZoneDirector
      "wlan_tx" => "1.3.6.1.4.1.25053.1.2.2.1.1.1.1.1.16",   # for Ruckus ZoneDirector
      "lan_rx"  => "1.3.6.1.2.1.2.2.1.10",
      "lan_tx"  => "1.3.6.1.2.1.2.2.1.16",
      "name"    => "1.3.6.1.2.1.1.5.0",
      "port"    => "1.3.6.1.2.1.2.2.1.2"
    }
    lists[type]
  end

  def get_portname(manager)
    name = []
    begin
      manager.walk [oids("port")] { |vb|name << vb[0].value}
    end
    name
  end

  def get_name(manager)
    response = manager.get [oids("name")]
    name = nil
    begin
      response.each_varbind { |vb| name = vb.value }
    end
    name
  end

  def get_traffic(manager, type)
    ret = {"rx" => [], "tx" => []}
    traffic = ["rx", "tx"]
    traffic.each do |x|
      i = 1
      loop do
        oid = oids("#{type}_#{x}")
        response = manager.get ["#{oid}.#{i}"]
        begin
          response.each_varbind { |vb| ret[x][i - 1] = vb.value.to_i }
        rescue NoMethodError => e
          ret[x] = nil if i == 1
          break
        end
        i += 1
      end
    end

    ret = nil if ret["rx"] == nil or ret["tx"] == nil
    ret
  end

  def run

    json_file_path = config[:cache]
    begin
      cache = open(json_file_path) { |io| JSON.load io }
    rescue
      cache = {}
    end

    timestamp = Time.now.to_i
    threads = []

    config[:hosts].split(/,\s?/).each do |host|
      manager = SNMP::Manager.new host: host, community: config[:community]

      portname =  get_portname manager

      name = get_name manager
      threads << Thread.new do
        type = "wlan"
        traffic = get_traffic(manager, type) # wlanを試して
        if traffic == nil                    # wlanがなかったら
          type = "lan"                       # 有線ポート
          traffic = get_traffic(manager, type)
        end

        now = Time.now
        traffic_type = ["rx", "tx"]
        traffic_type.each do |x|
          traffic[x].each_index do |i|
            if cache[name]!= nil && cache[name][type] != nil
              traffic_value = traffic[x][i] - (cache[name][type]["traffic"][x][i].to_f || 0)
              time = now - Time.at(cache[name][type]["time"])
              output "network.#{name.downcase}.snmp.#{x}.#{portname[i]}", (traffic_value / time).round(2), timestamp
            else
              cache[name] = {} if cache[name] == nil
              cache[name][type] = {}
              break
            end
          end
          break if cache[name][type] == {}
        end
        cache[name][type] = {"time" => now.to_i, "traffic" => {"rx" => traffic["rx"], "tx" => traffic["tx"]}}
      end
    end

    threads.each { |thread| thread.join }

    open(json_file_path, "w") { |io| JSON.dump(cache, io) }

    ok
  end
end

設定は下記の感じで、監視したい機器のIPを指定(""で囲めば「, 」で区切ることも出来ます)すればOK。

snmp_metrics.json
{
  "checks": {
    "snmp_metrics": {
      "type": "metric",
      "handlers": ["graphite"],
      "command": "ruby /etc/sensu/plugins/snmp_metrics.rb --host \"127.0.0.1, localhost\"",
      "interval": 15,
      "subscribers": [ "sensu" ]
    }
  }
}

L3スイッチで見てみると、どのセグメントからどのセグメントへの流れが多いのか分かるようになるのでなかなかよろしい感じですね。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?