【誰得】RubyでZabbix APIを引っ叩く

記事タイトルとURLをコピーする

おひさしぶりです。クラウドインテグレーション部 技術研究課の竹永です。
お盆時期の暴力的な飛行機代を目の当たりにして、実家に帰れない系エンジニアです。

実家にゃんこs(neko2 neko1)をモフりたいです…もふ…。

さて、技術研究課では社内向けになにかを作ったり、
いろんなサービスをフュージョンさせたり、
触ったことすら無いものを検証したりしています。

そんな中でRubyからZabbix APIを叩く機会があったので、
Ruby同梱のライブラリだけ使って叩いてみます。

前提条件

この記事はだいたい下記の環境で検証したものを元に記事を書いています。
それぞれのツールのインストール方法については割愛します。

  • OS X Mavericks
  • Zabbix 2.2.8
  • Ruby 2.1くらい

Zabbix APIってなに

そのままですが、ZabbixのAPIです。
HTTP経由でJSON-RPCを使って叩きます。

設定やホスト追加など、フロントエンドからできることはAPIでもできます。
やる気と根性で、フロントエンドを一回も使わずにすべての設定を行うなんてことも可能なはず。

ドキュメントはこちら

なぜライブラリを使わぬ

ドキュメントを見ながらAPIをペチペチと叩く際に、
ライブラリだと好きなようにAPIを叩けないことがあったので自分で書きました。

ライブラリの便利さを全く活かせないのが何とも言えない感じですが、
たまには自分で最初から叩いてみるのも良いものです。

(あくまでも個人の感想です)

叩く前に下準備

検証兼ブログ用に作ったコードはこちら。
1から作ったのに、なんか見たことがあるような無いようなコードです。

require "net/http"
require "json"

# Zabbix APIを叩きます
#
# @param [Hash] params Zabbixサーバーに投げるパラメーターをHashで指定します
# @return [Hash] Zabbixサーバーから返ってきたJSONをHashに変換して返します
def zabbix_api(params)
  uri = URI.parse("http://example.com/zabbix/api_jsonrpc.php")

  http = Net::HTTP.new(uri.host, uri.port)
  if uri.scheme == 'https'
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  end

  response = http.post(
    uri.request_uri,
    params.to_json,
    { "Content-Type" => "application/json-rpc" }
  )

  JSON.parse(response.body)
end

このコードをhoge.rbのようなファイル名で保存し、
irbpryなどのインタラクティブシェルでload "hoge.rb"みたいに呼び出すといい感じに使えます。

なお、プロキシを通すためのコードは含まれておらず、エラーハンドリングはサボっていて、
さらにパフォーマンスの検証は未実施など、業務ツールに使うにはあまりにもあんまりですので、
こっそりどこかに組み込んだりするのは絶対におやめください。

叩き方の例

ここから下のコードは全部pryの中で叩いてます。
コードに書いて試すときは、頭にpputsをつければ標準出力に中身がでてきます。

とりあえずはテスト

Zabbix APIにはログインしなくても叩けるメソッドがあります。
テストついでに叩いてみましょう。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "apiinfo.version"
)

上記コードを実行すると、下のような応答が返ってきます。

=> {"jsonrpc"=>"2.0", "result"=>"2.2.8", "id"=>"0"}

"result"の中身がAPIの応答です。エラーの時はキー名が"error"になります。
今回の中身はAPIバージョンですが、本体バージョンと同じような気がします。

APIを叩いても返り値が空っぽの場合、必須パラメーターが抜けているかもしれません。
jsonrpcidmethodはいつも必須です。

jsonrpcは省略できそうな気もしますが、全然できません。
省略したら2回怒られました。泣きそうです。

idはJSON-RPCのBatch requestをするときに使います。
Batchするとき以外は固定値でも何ら問題はありません。省略したい。

ログインしてみる

APIを叩けることを確認したので、ログインします。
paramsにHashを突っ込んであげれば立派なパラメーターです。

result = zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "user.login",
  params: { user: "admin", password: "hogera" }
)
=> {"jsonrpc"=>"2.0", "result"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "id"=>"0"}

resultに入っている値が認証トークンになります。
認証が必要なAPIの呼び出しに使うので、下記みたいな感じに取り出しておきましょう。

auth_token = result["result"]

ホストの一覧をとってくる

「これは絶対にあるだろうな」というAPIの中にホストの一覧を取得するものがあるので、こちらも取得してみます。

認証トークンをパラメーターにくっつけると色々見ることができます。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "host.get",
  params: { output: "extend" },
  auth: auth_token  # 認証トークン
)
=> {"jsonrpc"=>"2.0",
 "result"=>
  [{"maintenances"=>[],
    "hostid"=>"12345",
    "proxy_hostid"=>"0",
    "host"=>"Zabbix-Test",
(以下省略)

全ホストの全情報を取得するので、環境によっては取得に時間がかかります。

よくやらかします。気をつけます。

見たい列にしぼりこむ

paramsoutput: "extend"を指定すると全部の項目が返ってきますが、
下記のように出力する項目を配列で指定すると、そのとおりに返してくれます。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "host.get",
  params: { output: ["host", "name"] },  # 欲しい項目の名前
  auth: auth_token
)
=> {"jsonrpc"=>"2.0",
 "result"=>
  [{"hostid"=>"12345", "host"=>"Zabbix-Test", "name"=>"Zabbix鯖"},
   {"hostid"=>"12346", "host"=>"Test-Server", "name"=>"監視対象鯖"},
  (以下省略)

通信量が少なくなってエコですね。

フィルタリングする

全てのホストの一覧を毎回取得するのはやっぱり非効率なので、
フィルターを掛けて条件に一致したホストだけ取得します。

paramsfilter: { 項目名: 値 }を指定すると絞り込みが可能です。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "host.get",
  params: {
    output: ["host", "name"],
    filter: { name: "Zabbix鯖" }  # 今回はホストの名前で絞り込み
  },
  auth: auth_token
)
=> {"jsonrpc"=>"2.0", "result"=>[{"hostid"=>"12345", "host"=>"Zabbix-Test", "name"=>"Zabbix鯖"}], "id"=>"0"}

名前は実質プライマリーキーなので、1件だけ返ってきました。
フィルターに使う値には配列を指定することができるので、これまた便利です。

下記にサンプルを用意してみました。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "host.get",
  params: {
    output: ["host", "name"],
    filter: { name: ["Zabbix鯖", "監視対象鯖"] }  # 条件がいっぱい
  },
  auth: auth_token
)

ちなみに、項目の型がtextの場合はこの方法で絞り込みができず、
代わりにsearchパラメーターで絞り込みが可能です。

filterと同じような感じですが、searchはSQLのLIKE文風味です。
詳しくはドキュメントをごらんください。

関連する項目を一緒にとる

host.getのドキュメントに書いてあるselectから始まるパラメーターを使うと、
関連している項目を一緒に取得することができます。

たとえば、ホストが所属しているテンプレートの名前がほしい時は、
下記のような投げ方をすると取得することができます。

zabbix_api(
  jsonrpc: "2.0",
  id: "0",
  method: "host.get",
  params: {
    output: ["host", "name"],
    filter: { name: "Zabbix鯖" },
    selectParentTemplates: ["name"]  # 欲しい項目
  },
  auth: auth_token
)
=> {"jsonrpc"=>"2.0",
 "result"=>
  [{"hostid"=>"10084",
    "host"=>"Zabbix-Test",
    "name"=>"Zabbix鯖",
    "parentTemplates"=>[
      {"name"=>"Template Zabbix Server", "templateid"=>"23456"},
      {"name"=>"Linuxテンプレート", "templateid"=>"23457"}]}],
 "id"=>"0"}

"parentTemplates"に、ホストが所属しているテンプレートの一覧が格納されています。
自分で検索しなくてもいいので地味に便利です。

まとめ

後半からZabbix APIの使い方になってしまいましたが、いかがでしょうか。

絞り込んだり、件数カウントしたり、特定の日時以前・以降のイベントを取ってきたり、
外部キーでくっついているアイテムの情報を取ってきたりと、なかなかに柔軟です。
比較的プログラマーに優しい仕様となっています。

しかし、Zabbixのフロントエンドで見えるものがAPI 1発で全部見えるとは限りません。
たとえば、発生したイベントの継続時間や、
ホストが所属しているテンプレートの親などなどは一発で見えません。

そんなときは、APIを2発叩いたり、APIの返り値を元に値を計算すると欲しい値が出てきます。
出てこない時はソースコードを追いましょう。オープンです。読み放題です。

Zabbixが触れてプログラムが書ける方は、ぜひZabbix APIと戯れてみてください。
ダッシュボードづくりなどに役立つかもしれません。

余談

5, 6発/秒ほどでAPIを叩き続けても、サーバーは元気でした。
用法・用量を守って、正しくお使いください。