Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What is going on with this article?
@KoriCori

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の疎通確認方法を見ていきましたが、今回確認できたことで、個人的にとても勉強になりました。

9
Help us understand the problem. What is going on with this article?
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
KoriCori
不動産業界からIT業界へ転向。 インフラエンジニアとして勉強中。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
9
Help us understand the problem. What is going on with this article?