Icingaという監視ツールがあります。 IcingaとはNagiosからForkした監視ツールで、Icinga 1はNagiosと互換性があるので、設定ファイルやプラグインなどNagiosで使っていたものがそのまま使えます。
Icinga 1とNagiosとの大きな違いの1つに、Icinga Web REST APIがあります。 これは、Icingaでの操作(監視の開始・停止)や条件に合った監視ホスト・サービスを取得するAPIが提供されています。 つまり、Icingaサーバ以外のサーバからIcingaの操作や情報の取得が可能になるものです。
操作を自動化したいと思っていたので、「これだ!」と思って調べていたのですが、あまり情報がなかったので数少ないドキュメントと数々の試行錯誤の結果得た知見を紹介します。
Icingaのインストール方法はQiitaに載せているのでこちらを参照してください。 Icinga Web REST APIを使うためには、Icinga のバージョンが1.5以上であることが必須です。
Nagios - Icinga 1をインストール - Qiita
事前準備
auth_enabled
をtrue
に変更します。
# /usr/share/icinga-web/app/modules/AppKit/config/auth.xml
<ae:parameter name="auth_key">
<ae:parameter name="auth_module">AppKit</ae:parameter>
<ae:parameter name="auth_provider">Auth.Provider.AuthKey</ae:parameter>
<ae:parameter name="auth_enable">true</ae:parameter>
<ae:parameter name="auth_authoritative">true</ae:parameter>
</ae:parameter>
キャッシュクリア
$ sudo rm -f /usr/share/icinga-web/app/config/*.php
APIキー発行
- Icinga-Webにログイン後、Admin -> Users をクリック
- 「Add new user」をクリックして、APIアクセス用のユーザを作成
- 「Auth via」で「auth_key」を選択
- 「Authkey for Api (optional)」 の更新ボタンをクリックしてAPIキーを発行する
- Rightタブ -> Credentialsタブ を選択し、
appkit.api.access
とicinga.user
にチェックを入れる - 「Create user」をクリック
テスト
以下のAPIが使えるかテストします。
CriticalかWarningの状態であるサービスすべてを取得するAPI
# XML
$ curl -s --globoff "http://icinga.example.com/icinga-web/web/api/service/filter[AND(HOST_CURRENT_STATE|=|0;OR(SERVICE_CURRENT_STATE|=|1;SERVICE_CURRENT_STATE|=|2))]/columns[SERVICE_NAME|HOST_NAME|SERVICE_CURRENT_STATE|HOST_NAME|HOST_CURRENT_STATE|HOSTGROUP_NAME]/order(SERVICE_CURRENT_STATE;DESC)/countColumn=SERVICE_ID/authkey=<APIキー>/xml"
<?xml version="1.0" encoding="UTF-8"?>
<results><result><column name="SERVICE_NAME">HTTPD</column><column name="HOST_NAME">host1</column><column name="SERVICE_CURRENT_STATE">1</column><column name="HOST_CURRENT_STATE">0</column><column name="HOST_IS_PENDING">0</column><column name="SERVICE_IS_PENDING">0</column>...</result><total>1</total></results>
# JSON (jqで整形)
curl -s --globoff "http://icinga.example.com/icinga-web/web/api/service/filter[AND(HOST_CURRENT_STATE|=|0;OR(SERVICE_CURRENT_STATE|=|1;SERVICE_CURRENT_STATE|=|2))]/columns[SERVICE_NAME|HOST_NAME|SERVICE_CURRENT_STATE|HOST_NAME|HOST_CURRENT_STATE|HOSTGROUP_NAME]/order(SERVICE_CURRENT_STATE;DESC)/countColumn=SERVICE_ID/authkey=<APIキー>/json" | jq .
{
"total": 1,
"success": "true",
"result": [
{
"SERVICE_IS_PENDING": "0",
"HOST_IS_PENDING": "0",
"HOST_CURRENT_STATE": "0",
"SERVICE_CURRENT_STATE": "1",
"HOST_NAME": "host1",
"SERVICE_NAME": "HTTPD"
},
...
]
}
通知がオフになっているサービスすべてを取得するAPI
curl -s --globoff "http://icinga.example.com/icinga-web/web/api/service/filter[OR(SERVICE_NOTIFICATIONS_ENABLED|=|0;HOST_NOTIFICATIONS_ENABLED|=|0)]/columns[HOST_NAME|SERVICE_NAME]/order(HOST_NAME;asc)/countColumn=SERVICE_ID/authkey=<APIキー>/json" | jq .
{
"total": 135,
"success": "true",
"result": [
{
"SERVICE_NAME": "CPU Usage System",
"HOST_NAME": "host1"
},
...
]
}
URLの構成
ひな形 : http://icinga.example.com/icinga-web/web/api/TARGET/COLUMNS/FILTER/ORDER/GROUPING/LIMIT/COUNTFIELD/auth=AUTH_KEY/OUTPUT_TYPE
パラメータ | 説明 | 例 |
---|---|---|
TARGET | service or host | service |
COLUMNS | 取得したいカラム名 | columns[COL1!COL2!COL3!...] |
FILTER | SQLで言うとwhere |
and(カラム!=!条件);or(カラム!=!条件;カラム=条件)・・・ で検索、!like! でLIKE検索 |
ORDER | SQLで言うとorder by |
order(カラム;ASC or DESC) |
GROUPING | SQLで言うとgroup by |
group(カラム) |
LIMIT | SQLで言うとlimit |
limit[START;END] |
COUNTFIELD | SQLで言うとcount(*) 、カウントしたいカラム名(idなど) |
countColumn=カラム名 |
AUTH_KEY | APIキー | 先ほど発行したキー |
OUTPUT | 出力フォーマット | json or xml |
FILTERについて
()
,and()
,or()
で1つのグループ- 演算子は
|=|
のようにする- 上記例では
!
を|
に置き換えてください
- 上記例では
;
がセパレータ
例
- A=1 =>
(A|=|1)
- A=1 and B=2 =>
and(A|=|1;B|=|2)
- A=1 or B=2 =>
or(A|=|1;B|=|2)
- A=1 and (B=2 or C=3) =>
and(A|=|1;or(B|=|2;C|=|3))
使用できるカラム名
/usr/share/icinga-web/app/modules/Cronks/lib/js/Icinga/Cronks/Tackle/Information/Head.js
に取得できるカラム一覧があるのでそちらをご参照ください。
ホスト
大体同じものやあまり使わなそうなものは説明を省略しています。
カラム名 | 説明 | 例 |
---|---|---|
HOST_ID | ホストID | - |
HOST_OBJECT_ID | ホストオブジェクトID | - |
HOST_INSTANCE_ID | ホストインスタンスID | - |
HOST_NAME | ホスト名 | host1 |
HOST_ALIAS | ホストエイリアス名 | - |
HOST_DISPLAY_NAME | ホストのディスプレイ表示名 | - |
HOST_ADDRESS | ホストのアドレス名(IPアドレス) | 192.168.192.5 |
HOST_ADDRESS6 | ホストのアドレス名(IPアドレスv6?) | - |
HOST_ACTIVE_CHECKS_ENABLED | ホストのアクティブチェックが有効になっているか | 0:無効, 1:有効 |
HOST_CONFIG_TYPE | ||
HOST_FLAP_DETECTION_ENABLED | ||
HOST_PROCESS_PERFORMANCE_DATA | ||
HOST_FRESHNESS_CHECKS_ENABLED | ||
HOST_FRESHNESS_THRESHOLD | ||
HOST_PASSIVE_CHECKS_ENABLED | ||
HOST_EVENT_HANDLER_ENABLED | ||
HOST_RETAIN_STATUS_INFORMATION | ||
HOST_RETAIN_NONSTATUS_INFORMATION | ||
HOST_NOTIFICATIONS_ENABLED | 通知が有効になっているか | 0:無効, 1:有効 |
HOST_OBSESS_OVER_HOST | ||
HOST_FAILURE_PREDICTION_ENABLED | ||
HOST_NOTES | ||
HOST_NOTES_URL | ||
HOST_ACTION_URL | ||
HOST_ICON_IMAGE | ||
HOST_ICON_IMAGE_ALT | ||
HOST_IS_ACTIVE | ||
HOST_OUTPUT | ||
HOST_LONG_OUTPUT | ||
HOST_PERFDATA | ||
HOST_CURRENT_STATE | ホストの状態 | 0:UP, 1:DOWN, 2:UNREACHABLE |
HOST_CURRENT_CHECK_ATTEMPT | ||
HOST_MAX_CHECK_ATTEMPTS | ||
HOST_LAST_CHECK | 最後に監視チェックした時刻 | 2015-06-10 18:46:43 |
HOST_LAST_STATE_CHANGE | 最後に状態が変わった時刻 | 2015-03-04 19:45:16 |
HOST_CHECK_TYPE | ||
HOST_LATENCY | ||
HOST_EXECUTION_TIME | ||
HOST_NEXT_CHECK | 次に監視チェックする時刻 | 2015-06-10 18:49:53 |
HOST_HAS_BEEN_CHECKED | ||
HOST_LAST_HARD_STATE_CHANGE | ||
HOST_LAST_NOTIFICATION | ||
HOST_PROCESS_PERFORMANCE_DATA | ||
HOST_STATE_TYPE | ||
HOST_IS_FLAPPING | ||
HOST_PROBLEM_HAS_BEEN_ACKNOWLEDGED | ||
HOST_SCHEDULED_DOWNTIME_DEPTH | ||
HOST_SHOULD_BE_SCHEDULED | ||
HOST_STATUS_UPDATE_TIME | ||
HOST_CHECK_SOURCE |
サービス
カラム名 | 説明 | 例 |
---|---|---|
SERVICE_ID | サービスID | |
SERVICE_INSTANCE_ID | サービスインスタンスID | |
SERVICE_CONFIG_TYPE | ||
SERVICE_IS_ACTIVE | ||
SERVICE_OBJECT_ID | サービスオブジェクトID | |
SERVICE_NAME | サービス名 | |
SERVICE_DISPLAY_NAME | サービスのディスプレイ表示名 | |
SERVICE_NOTIFICATIONS_ENABLED | 通知が有効になっているか | 0:無効, 1:有効 |
SERVICE_FLAP_DETECTION_ENABLED | ||
SERVICE_PASSIVE_CHECKS_ENABLED | ||
SERVICE_EVENT_HANDLER_ENABLED | ||
SERVICE_ACTIVE_CHECKS_ENABLED | ||
SERVICE_RETAIN_STATUS_INFORMATION | ||
SERVICE_RETAIN_NONSTATUS_INFORMATION | ||
SERVICE_OBSESS_OVER_SERVICE | ||
SERVICE_FAILURE_PREDICTION_ENABLED | ||
SERVICE_NOTES | ||
SERVICE_NOTES_URL | ||
SERVICE_ACTION_URL | ||
SERVICE_ICON_IMAGE | ||
SERVICE_ICON_IMAGE_ALT | ||
SERVICE_OUTPUT | ||
SERVICE_LONG_OUTPUT | ||
SERVICE_PERFDATA | ||
SERVICE_PROCESS_PERFORMANCE_DATA | ||
SERVICE_CURRENT_STATE | サービスの状態 | 0:OK, 1:WARNING, 2:CRITICAL, 3:UNKNOWN |
SERVICE_CURRENT_CHECK_ATTEMPT | ||
SERVICE_MAX_CHECK_ATTEMPTS | ||
SERVICE_LAST_CHECK | ||
SERVICE_LAST_STATE_CHANGE | ||
SERVICE_CHECK_TYPE | ||
SERVICE_LATENCY | ||
SERVICE_EXECUTION_TIME | ||
SERVICE_NEXT_CHECK | ||
SERVICE_HAS_BEEN_CHECKED | ||
SERVICE_LAST_HARD_STATE | ||
SERVICE_LAST_HARD_STATE_CHANGE | ||
SERVICE_LAST_NOTIFICATION | ||
SERVICE_STATE_TYPE | ||
SERVICE_IS_FLAPPING | ||
SERVICE_PROBLEM_HAS_BEEN_ACKNOWLEDGED | ||
SERVICE_SCHEDULED_DOWNTIME_DEPTH | ||
SERVICE_SHOULD_BE_SCHEDULED | ||
SERVICE_STATUS_UPDATE_TIME | ||
SERVICE_CHECK_SOURCE |
POSTメソッドでAPIコール
POSTリクエストでもGETリクエストと同じことができます。 POSTと聞いてデータを更新できるかと思ったら違って残念な気持ちになりました。
curl \
-d target=service \
-d 'filters_json={"type":"AND","field":[{"type":"atom","field":["HOST_CURRENT_STATE"],"method":["="],"value":[0]},{"type":"OR","field":[{"type":"atom","field":["SERVICE_CURRENT_STATE"],"method":["="],"value":[1]},{"type":"atom","field":["SERVICE_CURRENT_STATE"],"method":["="],"value" : [2] }]}]}' \
-d columns[0]=SERVICE_NAME \
-d columns[1]=HOST_NAME \
-d columns[2]=SERVICE_CURRENT_STATE \
-d columns[3]=HOST_NAME \
-d columns[4]=HOST_CURRENT_STATE \
-d columns[5]=HOSTGROUP_NAME \
-d 'order=SERVICE_CURRENT_STATE;DESC' \
-d countColumn=SERVICE_ID \
-d 'authkey=<APIキー>' \
http://icinga.example.com/icinga-web/web/api/xml
fileter_json
を整形するとこのようになっています。
{
"field": [
{
"field": [
"HOST_CURRENT_STATE"
],
"method": [
"="
],
"type": "atom",
"value": [
0
]
},
{
"field": [
{
"field": [
"SERVICE_CURRENT_STATE"
],
"method": [
"="
],
"type": "atom",
"value": [
1
]
},
{
"field": [
"SERVICE_CURRENT_STATE"
],
"method": [
"="
],
"type": "atom",
"value": [
2
]
}
],
"type": "OR"
}
],
"type": "AND"
}
APIからIcingaの外部コマンドを実行する
Nagiosに外部コマンドファイルが用意されているようにIcingaも同じ外部コマンドファイルが用意されています。
なんと、その外部コマンドをAPIで操作できるのです。
これで外部サーバから通知のオン/オフなどの操作ができるようになります。
URL構成
http://icinga.example.com/icinga-web/web/api/cmd/cmd=COMMAND_NAME/authkey=AUTH_KEY/target=TARGET/data=DATA
パラメータ | 説明 | 例 |
---|---|---|
COMMAND_NAME | コマンド名 | DISABLE_HOST_NOTIFICATIONS: 通知をオフにする, ENABLE_HOST_NOTIFICATIONS: 通知をオンにする |
AUTH_KEY | APIキー | 先ほど発行したキー |
TARGET | コマンド実行の対象 | JSON形式でURLエンコードする |
DATA | コマンド実行のデータ | JSON形式でURLエンコードする |
例
- 監視対象サーバ(host1)の通知をオフにする
# ホスト
$ curl http://icinga.example.com/icinga-web/web/api/cmd/cmd=DISABLE_HOST_NOTIFICATIONS/authkey=<APIキー>/target=%5B%7B%22instance%22%3A%22default%22%2C%22host%22%3A%22host1%22%7D%5D/data=%7B%22host%22%3A%22host1%22%7D
{"success":true}
# サービス
curl http://icinga.example.com/icinga-web/web/api/cmd/cmd=DISABLE_HOST_SVC_NOTIFICATIONS/authkey=<APIキー>/target=%5B%7B%22instance%22%3A%22default%22%2C%22host%22%3A%22host1%22%7D%5D/data=%7B%22host%22%3A%22host1%22%7D
{"success":true}
target
パラメータはURLエンコードされており、デコードするとこのようになります。
[{"instance":"default","host":"host1"}]
data
パラメータはこのようになっています。
{"host":"host1"}
# /var/log/icinga-web/debug.log
[Thu Jun 11 16:18:30 2015] [debug] Write session update: hf349klcer3llm5k4du413uar7 (AppKitDoctrineSessionStorage::sessionWrite(), line 143)
[Thu Jun 11 16:18:30 2015] [debug] Auth.Dispatch: Starting authenticate (username=user1)
[Thu Jun 11 16:18:30 2015] [info] Auth.Dispatch: Converting username to lowercase
[Thu Jun 11 16:18:30 2015] [debug] Auth.Dispatch: Userdata found in db (uid=2)
[Thu Jun 11 16:18:30 2015] [debug] Auth.Provider: Object (name=auth_key) initialized
[Thu Jun 11 16:18:30 2015] [debug] Auth.Dispatch: Authoritative provider found (provider=auth_key, authid=api test)
[Thu Jun 11 16:18:30 2015] [debug] Auth.Dispatch: Successful authentication (provder=auth_key)
[Thu Jun 11 16:18:30 2015] [info] User user1 (test, api) logged in! (ip=::1)
[Thu Jun 11 16:18:30 2015] [info] Array
(
[0] => Array
(
[instance] => default
[host] => host1
)
)
[Thu Jun 11 16:18:30 2015] [info] Array
(
[0] => Array
(
[instance] => default
[host] => host1
)
)
[Thu Jun 11 16:18:30 2015] [debug] Trying to send commands, targets: [{"instance":"default","host":"host1"}] , data: {"host":"host1"} (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:18:30 2015] [debug] Setting up console for instance default (Cronks_System_CommandSenderModel::getConsoleInstance(), line 79)
[Thu Jun 11 16:18:30 2015] [debug] Submitting command DISABLE_HOST_NOTIFICATIONS to {"instance":"default","host":"host1"} (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:18:30 2015] [debug] Sending icinga-command [1434007110] DISABLE_HOST_NOTIFICATIONS;host1 (Api_Commands_CommandDispatcherModel::submitCommand(), line 83)
[Thu Jun 11 16:18:30 2015] [debug] ["Finished submitting command"] (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:18:30 2015] [info] User api test (test, api) logged out! (ip=::1)
[Thu Jun 11 16:41:13 2015] [debug] Write session update: user2 (AppKitDoctrineSessionStorage::sessionWrite(), line 143)
[Thu Jun 11 16:41:13 2015] [debug] Auth.Dispatch: Starting authenticate (username=user1)
[Thu Jun 11 16:41:13 2015] [info] Auth.Dispatch: Converting username to lowercase
[Thu Jun 11 16:41:13 2015] [debug] Auth.Dispatch: Userdata found in db (uid=2)
[Thu Jun 11 16:41:13 2015] [debug] Auth.Provider: Object (name=auth_key) initialized
[Thu Jun 11 16:41:13 2015] [debug] Auth.Dispatch: Authoritative provider found (provider=auth_key, authid=api test)
[Thu Jun 11 16:41:13 2015] [debug] Auth.Dispatch: Successful authentication (provder=auth_key)
[Thu Jun 11 16:41:13 2015] [info] User user1 (test, api) logged in! (ip=::1)
[Thu Jun 11 16:41:13 2015] [info] Array
(
[0] => Array
(
[instance] => default
[host] => host1
)
)
[Thu Jun 11 16:41:13 2015] [info] Array
(
[0] => Array
(
[instance] => default
[host] => host1
)
)
[Thu Jun 11 16:41:13 2015] [debug] Trying to send commands, targets: [{"instance":"default","host":"host1"}] , data: {"host":"host1"} (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:41:13 2015] [debug] Setting up console for instance default (Cronks_System_CommandSenderModel::getConsoleInstance(), line 79)
[Thu Jun 11 16:41:13 2015] [debug] Submitting command DISABLE_HOST_SVC_NOTIFICATIONS to {"instance":"default","host":"host1"} (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:41:13 2015] [debug] Sending icinga-command [1434008473] DISABLE_HOST_SVC_NOTIFICATIONS;host1 (Api_Commands_CommandDispatcherModel::submitCommand(), line 83)
[Thu Jun 11 16:41:13 2015] [debug] ["Finished submitting command"] (Cronks_System_CommandSenderModel::dispatchCommands(), line 68)
[Thu Jun 11 16:41:13 2015] [info] User api test (test, api) logged out! (ip=::1)
ソース改変
連続でAPIを叩くとたまにエラーになる場合があるので、ソースを改変する。
# /usr/share/icinga-web/app/modules/Api/models/Commands/CommandDispatcherModel.class.php
77 AppKitLogger::debug("Sending icinga-command %s",$string);
78 $cmd = $this->getContext()->getModel($commandClass[0],$commandClass[1],
79 array(
80 //"command" => "printf",
81 "command" => "echo", // <-こちらに修正
82 "arguments" => array($string)
83 )
84 );
何をしているかというと、外部コマンドファイル(/var/spool/icinga/cmd/icinga.cmd
)にパイプで命令を渡すコマンドを修正しています。
printf
からecho
に変更することで、意図的に改行を挟むようにし、連続して実行してもエラーにならなくなりました。
# 修正前
printf '[1434600538] ENABLE_HOST_NOTIFICATIONS;host1' > /var/spool/icinga/cmd/icinga.cmd
# 修正後
echo '[1434600538] ENABLE_HOST_NOTIFICATIONS;host1' > /var/spool/icinga/cmd/icinga.cmd