Help us understand the problem. What is going on with this article?

CactiのDownは何をもって検知しているのか調べてみた

More than 5 years have passed since last update.

経緯

snmpでロードアベレージなどの値が取得できない状態なのに、CactiではDownを検知しない、という状態が何回かありました。
前々から気になっていたのですが、せっかくなので今回調べてみました。

私の環境では、snmpでの疎通確認しか使用していない為、正しくない表現もあると思います。
その場合、ご指摘頂けましたら幸いです。

確認した環境

Cacti公式サイトからv0.8.8bをダウンロードして、ソースコードを調べてみます。

※ なお、以下にCactiのソースコードを記載していきますが、ライセンスはGNU General Public Licenseとなっています。

詳細

1. monitorプラグインから調べ始める

CactiでホストがDownすると、「Attention・・・」と音声を流してくれたりするプラグインです。
よく夜勤で眠くなると、眠気覚ましに流すあれですw

monitorプラグインのv1.3-1を確認。

monitor.phpを見ていきましたが、$host_downがtrue / false で音を出す、出さないの内容でしたので、コードは割愛。

となると、Cacti本体でやはりDownやUpの判断をしているようなので、本体のコードを確認していきます。

2. host情報に何か手がかりはあるか?

Cacti本体だと、それなりの情報になるので、まずはホストに関する情報があるであろう、host.phpから確認しました。

downで検索をかけると、monitorプラグインでも出てきた$host_downと、その近くに$ping = new Net_Pingという情報が!

host.php
if (($host["availability_method"] == AVAIL_PING) ||
    ($host["availability_method"] == AVAIL_SNMP_AND_PING) ||
    ($host["availability_method"] == AVAIL_SNMP_OR_PING)) {
    /* create new ping socket for host pinging */
        $ping = new Net_Ping;

        $ping->host = $host;
        $ping->port = $host["ping_port"];

        /* perform the appropriate ping check of the host */
        if ($ping->ping($host["availability_method"], $host["ping_method"],
            $host["ping_timeout"], $host["ping_retries"])) {
            $host_down = false;
            $color     = "#000000";
    }else{
            $host_down = true;
            $color     = "#ff0000";
    }

3. Pingに手がかりがあるのか?

コマンドを発行する場所だと思われるlibディレクトリにping.phpがある。

functionの後にping_icmp/ping_snmp/ping_udp/ping_tcpと実行する箇所が!
恐らくこの部分が確認処理の箇所だと思うので、以下該当項目別で実際にソースコードを確認していきます。

  • ping_icmpでは、"min/avg/max"の結果が取れれば疎通可、取れなければTime out、それ以外のホストまで辿り着けなければ"Destination address not specified"と処理しているようです。
ping.php
    function ping_icmp() {
(中略)
            if (strtolower(PHP_OS) != "winnt") {
                $position = strpos($result, "min/avg/max");

                if ($position > 0) {
                    $output  = trim(str_replace(" ms", "", substr($result, $position)));
                    $pieces  = explode("=", $output);
                    $results = explode("/", $pieces[1]);

                    $this->ping_status = $results[1];
                    $this->ping_response = "ICMP Ping Success (" . $results[1] . " ms)";

                    return true;
                }else{
                    $this->status = "down";
                    $this->ping_response = "ICMP ping Timed out";

                    return false;
                }
            }else{
                $position = strpos($result, "Minimum");

                if ($position > 0) {
                    $output  = trim(substr($result, $position));
                    $pieces  = explode(",", $output);
                    $results = explode("=", $pieces[2]);

                    $this->ping_status = trim(str_replace("ms", "", $results[1]));
                    $this->ping_response = "ICMP Ping Success (" . $this->ping_status . " ms)";

                    return true;
                }else{
                    $this->status = "down";
                    $this->ping_response = "ICMP ping Timed out";

                    return false;
                }
            }
        }else{
            $this->ping_status   = "down";
            $this->ping_response = "Destination address not specified";

            return false;
  • ping_snmpでは、sysuptimeのOIDを用いて確認。
    OIDで取得したsnmpの起動時刻が、get_timeで出した現在時刻との差分が+-10%以上であればレスポンスあり、それ以外の場合にはDownと判断しているようです。
ping.php
    function ping_snmp() {
        /* initialize variables */
        $this->snmp_status   = "down";
        $this->snmp_response = "Host did not respond to SNMP";
        $output              = "";

        /* get start time */
        $this->start_time();

        /* by default, we look at sysUptime */
        if ($this->avail_method == AVAIL_SNMP_GET_NEXT) {
            if (version_compare("5", phpversion(), "<")) {
                $oid = ".1.3";
            }else{
                $oid = ".1.3.6.1.2.1.1.3.0";
            }
        }else if ($this->avail_method == AVAIL_SNMP_GET_SYSDESC) {
            $oid = ".1.3.6.1.2.1.1.1.0";
        }else {
            $oid = ".1.3.6.1.2.1.1.3.0";
        }
(中略)
        /* determine total time +- ~10% */
        $this->time = $this->get_time($this->precision);

        /* check result for uptime */
        if (strlen($output)) {
            /* calculte total time */
            $this->snmp_status   = $this->time*1000;
            $this->snmp_response = "Host responded to SNMP";

            return true;
        }else{
            $this->snmp_status   = "down";
            $this->snmp_response = "Host did not respond to SNMP";

            return false;
        }
    } /* ping_snmp */
  • ping_udpでは、ソケット接続で通信を確認。
    まずソケット通信ができているか否か。その上で、通信がtime outしないかどうかで判断しているようです。
ping.php
    function ping_udp() {
(中略)
                /* get start time */
                $this->start_time();

                /* write to the socket */
                socket_write($this->socket, $this->request, $this->request_len);

                /* get the socket response */
                switch(socket_select($r = array($this->socket), $w = NULL, $f = NULL, $to_sec, $to_usec)) {
                case 2:
                    /* connection refused */
                    $error = "refused";
                    break;
                case 1:
                    /* get the end time */
                    $this->time = $this->get_time($this->precision);

                    /* get packet response */
                    $code = @socket_recv($this->socket, $this->reply, 256, 0);

                    /* get the error, if applicable */
                    $err = socket_last_error($this->socket);

                    /* set the return message */
                    $this->ping_status = $this->time * 1000;
                    $this->ping_response = "UDP Ping Success (" . $this->time*1000 . " ms)";

                    $this->close_socket();
                    return true;
                case 0:
                    /* timeout */
                    $error = "timeout";
                    break;
                }

                $retry_count++;
            }
        } else {
            $this->ping_response = "Destination address not specified";
            $this->ping_status   = "down";

            return false;
        }
    } /* end ping_udp */
  • ping_tcpでもソケット接続で通信を確認。
    まずソケット通信ができているか否か。その上で、通信がtime outしていないかどうかで判断しているようです。
ping.php
    function ping_tcp() {
(中略)
                /* set start time */
                $this->start_time();

                /* allow immediate return */
                socket_set_nonblock($this->socket);
                @socket_connect($this->socket, $host_ip, $this->port);
                socket_set_block($this->socket);

                switch(socket_select($r = array($this->socket), $w = array($this->socket), $f = array($this->socket), $to_sec, $to_usec)){
                case 2:
                    /* connection refused */
                    $this->time = $this->get_time($this->precision);

                    if (($this->time*1000) <= $this->timeout) {
                        $this->ping_response = "TCP Ping connection refused (" . $this->time*1000 . " ms)";
                        $this->ping_status   = $this->time*1000;
                    }

                    $this->close_socket();

                    return true; /* "connection refused" says: host is alive (else ping would time out) */
                case 1:
                    /* connected, so calculate the total time and return */
                    $this->time = $this->get_time($this->precision);

                    if (($this->time*1000) <= $this->timeout) {
                        $this->ping_response = "TCP Ping Success (" . $this->time*1000 . " ms)";
                        $this->ping_status   = $this->time*1000;
                    }

                    $this->close_socket();

                    return true;
                case 0:
                    /* timeout */
                    $this->ping_response = "TCP ping timed out";
                    $this->ping_status   = "down";

                    $this->close_socket();

                    return false;
                }
            }
        } else {
            $this->ping_response = "Destination address not specified";
            $this->ping_status   = "down";

            return false;
        }
    } /* end ping_tcp */

以上、簡単ですが、Cactiの疎通確認方法を見ていきましたが、今回確認できたことで、個人的にとても勉強になりました。

KoriCori
不動産業界からIT業界へ転向。 インフラエンジニアとして勉強中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした