Python
zabbix
python3

zabbixのマップをapiから作ってみる

Zabbixで監視を自動化したりしているのですが、マップも監視対象が追加された時点で自動的に作成できれば便利だなと思ったのでZabbix API経由からマップを作成する手順について簡単にですがまとめてみます。

環境

サーバ情報

項目 内容・バージョン
OS CentOS 7.4
Zabbix 3.0.13-2

開発情報

項目 内容・バージョン
Python 3.4.4

Zabbix Map APIドキュメント

https://www.zabbix.com/documentation/3.0/manual/api/reference/map

テンプレート

今回は、以下のテンプレートを元に処理を書いていきます。

#!/usr/bin/env python3
from collections import defaultdict
import requests
import json
import atexit

class zbxapi:
    """
    Zabbix APIを処理するクラス
    """
    def __init__(self):
        self.url = ''
        self.user = ''
        self.passwd = ''
        self.token = ''

    def login(self):
        """
        ログインメソッド
        ログインに成功したらTokenを取得する

        :rtype: str
        :return: Zabbix API Auth Token
        """
        params = multi_dimension_dict(1)
        params['user'] = self.user
        params['password'] = self.passwd

        # Tokenを取得
        r = self.post('user.login', params)
        self.token = json.loads(r)['result']

    def logout(self):
        """
        ログアウトメソッド

        :rtype: str
        :return: ログアウトの成功(True)・失敗(False)
        """
        params = {}
        r = self.post('user.logout', params)
        return json.loads(r)['result']

    def get(self, method, params):
        """
        Zabbixから情報を取得するメソッド

        :type method: str
        :param method: Zabbix APIメソッド

        :type params: dict
        :param params: 取得したい情報のパラメーター

        :rtype: json
        :return: レスポンスボディ
        """
        data = multi_dimension_dict(1)
        data['jsonrpc'] = '2.0'
        data['method'] = method
        data['params'] = params
        data['id'] = 1
        data['auth'] = self.token

        r = requests.get(self.url,
                         data=json.dumps(data),
                         headers=get_headers(),
                         verify=False)

        if (r.status_code == 200):
            return r.text

    def post(self, method, params):
        """
        Zabbixへ情報を送るメソッド

        :type method: str
        :param method: Zabbix APIメソッド

        :type params: dict
        :param params: 送信する情報のパラメーター

        :rtype: json
        :return: レスポンスボディ
        """
        data = multi_dimension_dict(1)
        data['jsonrpc'] = '2.0'
        data['method'] = method
        data['params'] = params
        data['id'] = 1
        if(self.token): data['auth'] = self.token

        r = requests.post(self.url,
                          data=json.dumps(data),
                          headers=get_headers(),
                          verify=False)

        if(r.status_code == 200):
            return(r.text)

def multi_dimension_dict(dimension, callable_obj=int):
    """
    pythonで多次元連想配列を使う関数
    参照元: http://materia.jp/blog/20121119.html
    """
    nodes = defaultdict(callable_obj)
    for i in range(dimension-1):
        p = nodes.copy()
        nodes = defaultdict(lambda : defaultdict(p.default_factory))
    return nodes

def get_headers():
    """
    ヘッダー情報

    :rtype: dict
    :return: Zabbix APIを実行するためのヘッダー
    """
    headers = {
        'Content-Type':'application/json-rpc'
    }
    return headers

if __name__ == "__main__":
    # Login
    zapi = zbxapi()
    zapi.url = 'http://IP or FQDN/zabbix/api_jsonrpc.php'
    zapi.user = 'admin'
    zapi.passwd = 'zabbix'
    zapi.login()
    atexit.register(zapi.logout)

    # ここから処理を書いていく

以下からは # ここから処理を書いていく 以降を書いていきます。

マップ自動生成をやってみた

マップ作成編

まずは、シンプルなマップを作ってみます。

    # ここから処理を書いていく
    # Create Map.
    params = {}
    params['name'] = "Map"
    params['width'] = 600
    params['height'] = 600
    r = zapi.post('map.create', params)
    print(json.dumps(json.loads(r), indent=2))
パラメーター 説明
name マップの名前
width マップ幅のサイズ
height マップ高さのサイズ

実行します。
以下はレスポンスボディです。

{
  "jsonrpc": "2.0",
  "result": {
    "sysmapids": [
      "12"
    ]
  },
  "id": 1
}

マップが作成されているか確認します。

マップ作成と同時にアイコンを追加

マップ作成と作成したマップにアイコンを追加する処理を一度にやってみようと思います。

    # ここから処理を書いていく
    # Element.
    element = {}
    element['elementid'] = 10105
    element['selementid'] = 1
    element['elementtype'] = 0
    element['iconid_off'] = 101

    # Create Map.
    params = {}
    params['name'] = "Map"
    params['width'] = 600
    params['height'] = 600
    params['selements'] = [element]
    r = zapi.post('map.create', params)
    print(json.dumps(json.loads(r), indent=2))
パラメーター 説明
elementid エレメント(アイコン)に紐づけるID(ホストやグループ)
selementid マップ内で判別するエレメントのID、これを元にエレメント同士を紐付けたりします
elementtype マップエレメントのタイプ、エレメントはhostなのかmapなのかなど
iconid_off アイコンの種類
selements エレメントを配列で渡す

注意点としては、アイコンに紐づけるホストなどがZabbixに登録されている必要があります。
elementid は省略できません。
ここでは example01 というホストを手動で登録してURLから hostid を取得しています。
決して自動で取得する処理を書くのがめんどくさかった訳では...
ただし elementtype4(イメージ) の場合は elementid が実在していないものでも登録出来ちゃいます。

実行するとMapにアイコンが出来ています。

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "sysmapids": [
      "13"
    ]
  }
}

アイコンの種類について

参考までに iconid_off の情報については以下に保存してあります。
imageidiconid_off に指定します。
https://gist.github.com/sky-joker/727f319e0f0601f2b08004de05ce0f17

ちなみに取得の仕方は以下のようにします。

    # ここから処理を書いていく
    params = {}
    params['output'] = ["imageid", "name"]
    r = zapi.get('image.get', params)
    print(json.dumps(json.loads(r), indent=2))

アイコンの座標を指定する

座標を指定しない場合は、全てのアイコンは画面左上に出来てしまうので座標を指定してみます。

    # ここから処理を書いていく
    # Element.
    element = {}
    element['elementid'] = 10105
    element['selementid'] = 1
    element['elementtype'] = 0
    element['iconid_off'] = 101
    element['x'] = 100
    element['y'] = 100
    (snip)
パラメーター 説明
x アイコンのx座標を指定します
y アイコンのy座標を指定します

実行するとアイコンが指定した座標に配置されています。

{
  "jsonrpc": "2.0",
  "result": {
    "sysmapids": [
      "14"
    ]
  },
  "id": 1
}

アイコンを紐づけてみる

マップにアイコンを2つ登録してお互いを紐づけてみます。

    # ここから処理を書いていく
    # Element1.
    element1 = {}
    element1['elementid'] = 10105
    element1['selementid'] = 1
    element1['elementtype'] = 0
    element1['iconid_off'] = 101
    element1['x'] = 100
    element1['y'] = 100

    # Element2.
    element2 = {}
    element2['elementid'] = 10106
    element2['selementid'] = 2
    element2['elementtype'] = 0
    element2['iconid_off'] = 101
    element2['x'] = 350
    element2['y'] = 100

    # Element Link.
    elementLink = {
        'selementid1': element1['selementid'],
        'selementid2': element2['selementid']
    }

    # Create Map.
    params = {}
    params['name'] = "Map"
    params['width'] = 600
    params['height'] = 600
    params['selements'] = [element1, element2]
    params['links'] = [elementLink]
    r = zapi.post('map.create', params)
    print(json.dumps(json.loads(r), indent=2))
パラメーター 説明
links 紐づけたいエレメントの selementid を渡します

実行するとアイコンが2つ出来て紐づけられています。

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "sysmapids": [
      "16"
    ]
  }
}

マップをアップデートする

既に作成したマップをアップデート(アイコンを追加したり)してみます。

    # ここから処理を書いていく
    # Element1.
    element1 = {}
    element1['elementid'] = 10105
    element1['selementid'] = 1
    element1['elementtype'] = 0
    element1['iconid_off'] = 101
    element1['x'] = 100
    element1['y'] = 100

    # Element2.
    element2 = {}
    element2['elementid'] = 10106
    element2['selementid'] = 2
    element2['elementtype'] = 0
    element2['iconid_off'] = 101
    element2['x'] = 350
    element2['y'] = 100

    # Element Link.
    elementLink = {
        'selementid1': element1['selementid'],
        'selementid2': element2['selementid']
    }

    # Update Map.
    params = {}
    params['sysmapid'] = 18
    params['selements'] = [element1, element2]
    params['links'] = [elementLink]
    r = zapi.post('map.update', params)
    print(json.dumps(json.loads(r), indent=2))
パラメーター 説明
sysmapid アップデート対象のsysmapid

作成時と違うのは、対象の sysmapid を指定します。
sysmapidはマップのURLとか作成時のレスポンスボディから確認できます。

独り言

今回は、シンプルな処理しかしなかったから多次元連想配列(multi_dimension_dict)は使わなかった。。。