cactiでトラフィックとかリソースのグラフをカレンダー形式で表示させるプラグインを作成したので、簡単なプラグインの作り方をまとめました

# はじめに
使ってますか、cacti。いまいち人気ないような気がするんですよね。さみしい。
私はいまだにcactiから離れられませんので、プラグインを作ってみました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">トラフィックのグラフって、カレンダーで見る需要あんまりないのかなぁ。ニヤニヤできると思うのに。「毎月第2水曜日はWindowsUpdateの日！」とか。 <a href="https://twitter.com/hashtag/cacti?src=hash&amp;ref_src=twsrc%5Etfw">#cacti</a> <a href="https://t.co/oxrTP1ylD3">https://t.co/oxrTP1ylD3</a></p>&mdash; bashaway (@bashaway) <a href="https://twitter.com/bashaway/status/1262744269644677121?ref_src=twsrc%5Etfw">May 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


**https://github.com/bashaway/rrdcalendar**
[![Screenshot from Gyazo](https://gyazo.com/7e6bfa2da9f8e5cbd388a7adfa75498e/raw)](https://gyazo.com/7e6bfa2da9f8e5cbd388a7adfa75498e)

cactiでのプラグインの作り方を簡単にまとめましたので、なんか作りたい、という方の参考になれば幸いです。


# 対象機器および環境
* cacti (1.2.11)


# プラグインの作り方

## 公式マニュアル

**[公式マニュアル](https://github.com/Cacti/documentation#plugin-development)** は、ところどころ古いバージョンの記載もあるので、動作に必要な最低限の部分だけ抽出してみます。

## ファイル構成
**[公式マニュアル](https://github.com/Cacti/documentation/blob/develop/Plugin-Reference.md)**

### ツリー構造

以下のようなツリー構造が推奨されています。
プラグインを収容するディレクトリが、プラグイン名として認識されます。以下の例だと `myplugin` がプラグインの名称になります。

```
myplugin/
    docs/
        CHANGELOG
    images/
        myimage.png
        index.php
    lib/
        index.php
        function2s.php
    include/
        index.php
        settings.php
    INFO
    LICENSE
    README.md
    index.php
    setup.php
```

お作法もありますが、「動作させる」ためには以下のものが必須です。

|内容|ファイル名|必須？|
|:-:|:-:|:-:|
|docs/|ドキュメント類||
|images/|画像類||
|lib/|自作ライブラリ|ディレクトリの<BR>直下でもOK|
|include/|自作スクリプト群|ディレクトリの<BR>直下でもOK|
|INFO|プラグイン定義|必須|
|LICENSE|ライセンス||
|README.md|マニュアル||
|index.php|マッピング用php|必須|
|setup.php|インストール用php|必須|

### プラグイン定義
**[公式マニュアル](https://github.com/Cacti/documentation/blob/develop/Plugin-Reference.md#contents-of-files)**
INFOファイルのことです

プラグインの情報を持ち、以下の画面で表示させるときに利用されます。
[![Screenshot from Gyazo](https://gyazo.com/a473313cf6b7bea41ce563c20553160a/raw)](https://gyazo.com/a473313cf6b7bea41ce563c20553160a)
また、バージョン相違や依存プラグインの有無などで、インストールできなくさせることができます。（歯車に黄色のビックリマークはインストールできない）

以下のようなファイル形式です。

```:INFO
[info]
name = プラグイン名（ディレクトリ名と一致していなければいけません）
version = プラグインのバージョン（Versionに対応）
longname = プラグインの説明（PluginDescriptionに対応）
author = プラグイン作成者（Authorに対応）
email = メールアドレス
homepage = プラグインの公開元（PluginNameのリンク先に対応）
compat = 利用可能なcactiバージョンの最小値
requires = このプラグインが必要とするプラグイン名（Requiresに対応）
capabilities = （すいませんよくわかりません）
```

**プラグイン名＝ディレクトリ名＝INFOに記載されたname** である必要があります。
これが一致していないと、以下のように、歯車にビックリマークが付き、インストールできません。
これは、`cycle` というプラグインのディレクトリ名を `cycle_check` に変更したときの表示です。
[![Screenshot from Gyazo](https://gyazo.com/01ac4d26ca20392a8554e42e56689372/raw)](https://gyazo.com/01ac4d26ca20392a8554e42e56689372)



また、INFOファイルのrequiresに記載があり、そのプラグインがインストールされていないと、歯車にビックリマークが付き、インストールできません。
例えば、monitorというプラグインはtholdの1.2.1が必要で、INFOには以下のような記載があります。

```:monitor/INFO
[info]
（～省略～）
requires = thold:1.2.1
（～省略～）
```

tholdがインストールされていない場合、プラグイン一覧では以下のような表示になっています。
[![Screenshot from Gyazo](https://gyazo.com/21d01423813300ef34c0bcb6e6ecb239/raw)](https://gyazo.com/21d01423813300ef34c0bcb6e6ecb239)



### マッピング用php
**[公式マニュアル](https://github.com/Cacti/documentation/blob/develop/Plugin-Reference.md#standard-files)**
`index.php`のことです。
以下のファイルでOKだそうです。

```php:index.php
<?php
header("Location:../index.php");
?>
```

### インストール用php
**[公式マニュアル](https://github.com/Cacti/documentation/blob/develop/Plugin-Reference.md#contents-of-files)**
`setup.php`のことです。
以下の関数が必ず含まれている必要があります。

`plugin_プラグイン名_install()`
`plugin_プラグイン名_uninstall()`
`plugin_プラグイン名_check_config()`
`plugin_プラグイン名_upgrade()`
`plugin_プラグイン名_version()`

今回作成したプラグインで利用した関数も含めて、必要な関数を紹介します。

```php:setup.php
<?php

# インストールのアイコンがクリックされると、この関数が呼び出されます
# ここでインストール時に必要な設定を行います。
plugin_プラグイン名_install(){

  # cactiのweb画面にタブを表示させたい場合は、第二引数に"top_header_tabs"を持たせたフック関数を呼び出します。
  # 今回はタブの表示が不要なので、必要ありません。
  api_plugin_register_hook('プラグイン名', 'top_header_tabs', 'プラグイン名_show_tab', 'setup.php');

  # 今回作成したプラグインは、グラフの横のアイコンにプラグイン起動用のアイコンを配置しているため、以下の関数を呼び出します。
  api_plugin_register_hook('プラグイン名', 'graph_buttons',            'プラグイン名_graph_buttons',    "setup.php");
  api_plugin_register_hook('プラグイン名', 'graph_buttons_thumbnails', 'プラグイン名_graph_buttons',    "setup.php");

  # このプラグインで呼び出し可能なスクリプトを記載します。
  # これは Console -> Configuration -> Users -> 各ユーザ -> Permissionsタブの画面の下のほうにある
  # Plugin Permissions のON/OFFを設定可能にします。デフォルト設定で0もしくは1で設定します。
  # つまり、ユーザごとにこのプラグインを利用させるかどうか、を設定することができるようになります。
  api_plugin_register_realm('プラグイン名', '利用可能なスクリプト名', 'パーミッション設定画面での表示名', {0|1});

  # インストール時にテーブル作成などが必要な場合は、このあたりに記載します。
  plugin_プラグイン名_なんちゃら関数();
}


# プラグインのアンインストール関数がクリックされると、この関数が呼び出されます
# ここでアンインストール時に必要な設定を行います。
plugin_プラグイン名_uninstall(){
  # アンインストール時にテーブル削除などが必要な場合は、このあたりに記載します。
  plugin_プラグイン名_なんちゃら関数();
}

# プラグインのインストール後に有効化させることができるかどうかの処理をここにいれます。
# でも、インストールさせて有効化させない、という利用シーンが思いつかないので、
# インストール時点でそういうチェックをしてもいいんじゃないか、と思います。
plugin_プラグイン名_check_config(){
  # 有効化用のチェック処理を入れたいときはこのあたりに記載します。
  if(read_config_option('プラグイン名_settings') != '') {
    # 有効化してもいい場合は true を返します。
    return true;
  }
  # 有効化させたくない場合は false を返します。
  return false;
}

# マニュアルに記載がありません。。。（涙）
# たぶん、cactiのアップグレードする際にプラグイン用のデータ変更などを行うためなような気がします。
function plugin_プラグイン名_upgrade(){
}

# プラグインのインストールのためのバージョン確認のために利用されます。
# よくわからないので、このままの記載にしておきます。
plugin_プラグイン名_version(){
  global $config;
  $info = parse_ini_file($config['base_path'] . '/plugins/プラグイン名/INFO', true);
  return $info['info'];
}

# 今回のプラグインは、グラフ表示の際にグラフ横にボタンを配置し、
# そのアイコンががクリックされると、プラグインのスクリプトが呼び出されます（＝ここの処理が呼び出されます。）
function プラグイン名_graph_buttons($args) {
  global $config;
  $local_graph_id = $args[1]['local_graph_id'];
  if (api_user_realm_auth("register_realmで設定した利用可能なスクリプト名")) {
    # 閲覧しているユーザにこのプラグインの利用が許可されていれば、スクリプト起動用のアイコンを表示させ
    # それをクリックすれば、別の窓で表示用のスクリプトが起動される、ということです。
    echo "<a href='#' ";
    echo "onclick=\"window.open('".$config['url_path']."plugins/プラグイン名/スクリプト名.php?local_graph_id=".$local_graph_id."', 'popup_".$local_graph_id;
    echo "', 'toolbar=no,menubar=no,location=no,scrollbars=no,status=no,titlebar=no,width=800,height=1100,resizable=yes')\">";
    echo "<img src='".$config['url_path']."plugins/プラグイン名/アイコンファイル名.png' border='0' alt='rrdcalendar' title='calendar' style='padding: 3px;'>";
    echo "</a><br/>";
  }
}

?>
```

タブの表示とは、画面上部のこんな部分のことです。
[![Screenshot from Gyazo](https://gyazo.com/2a9491f6d4af1d7fe4505fd8812c865c/raw)](https://gyazo.com/2a9491f6d4af1d7fe4505fd8812c865c)

api_plugin_register_realmで呼び出す引数は、以下の画面で使われます。
この一番下にある、 Plugin Permissions という記載の部分です。
[![Screenshot from Gyazo](https://gyazo.com/5b85ceaf354d9944a725fe45715e6f47/raw)](https://gyazo.com/5b85ceaf354d9944a725fe45715e6f47)

グラフ横のボタンはこんな感じのものです。
[![Screenshot from Gyazo](https://gyazo.com/0dde95f669e3ebbf332405f627031f07/raw)](https://gyazo.com/0dde95f669e3ebbf332405f627031f07)


### スクリプト本体php
今回作成したスクリプトは、指定したグラフをカレンダー形式で表示させる、というものです。
cactiの本体側の関数で、`rrdtool_function_graph`というのがあり、個別のグラフのRRDtool呼び出しコマンドを引っ張ってきてくれます。

こんな画面で使われているやつです。
[![Screenshot from Gyazo](https://gyazo.com/bd5133edce41c8cfda5373ff49f62f38/raw)](https://gyazo.com/bd5133edce41c8cfda5373ff49f62f38)

このコマンドラインを横取りして、convertでいんぐりもんぐりして、カレンダー形式に結合しているだけです。
本体の機能におんぶにだっこ状態です。

プラグイン作成とは直接関係ないので、内容は **[コード参照](https://github.com/bashaway/rrdcalendar/blob/master/rrdcalendar_functions.php)** でお願いします。

## 公式プラグインの活用
https://github.com/Cacti/ には、公式プラグインがたくさんあるので、コピーしたものを自分用に作り替えて。。。みたいに習得するのが近道なのかもしれません。でも、公式プラグインとはいえ、マニュアルに記載されてない書き方も多々あるので、注意が必要です。過去の遺産を引きずっているように見える部分もあったりして、「ここ必要？」な箇所も多いです。自分で作っていても、なんでその処理が必要なのかよく理解できていない部分もあります。


# さいごに
グラフはカレンダーで見れるとうれしいと思うのですが、あまり需要はないのでしょうか。
他のトラフィック管理ツールでも見かけないです。
ニヤニヤできるのに。


# 出典
https://github.com/Cacti/documentation#plugin-development
https://github.com/bashaway/rrdcalendar