Posted at

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