LibreNMS 監控主機Port 連線數

使用LibreNMS 自定功能,監控主機上特定Port 的連線數量

因為需要知道目前主機的特定Port連線數,所以做了這個功能,總共要設定三個地方。(異常處理見最後)

Poller Module

  1. 檔案名稱:  /opt/librenms/includes/polling/server-port-common-poller.inc.php
  2. 功能:  呼叫一次SNMP後,對取回內容處理,統計關心的Port連線數及特定狀態連線數。
  3. 程式內容:
<?php

  use LibreNMS\RRD\RrdDefinition;
 
  #本程式使用的SNMP MIB及OID
  $mibname="TCP-MIB";
  $oid="tcpConnectionState.ipv4";

  $esport80=0;
  $esport443=0;
  $esport1433=0;
  $esport3389=0;
  $esport8080=0;
  $esport8443=0;
  $establishedtotal=0;
  $totalportcount=0;
  $timeWaitCount=0;
  $closeWaitCount=0;
  $synSentCount=0;

  #組合要用到的regular expression  
  $exprhead = '/ipv4\\.(("[^"]+")|(\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}))\\.' ; 
  $exprend  = '\.ipv4/' ;
  $expr80   = $exprhead . '80'   . $exprend ;        
  $expr443  = $exprhead . '443'  . $exprend ;        
  $expr1433 = $exprhead . '1433' . $exprend ;        
  $expr3389 = $exprhead . '3389' . $exprend ;        
  $expr8080 = $exprhead . '8080' . $exprend ;        
  $expr8443 = $exprhead . '8443' . $exprend ;        

  #利用snmp取得主機的SNMP值
  $data = snmpwalk_cache_multi_oid($device, $oid, $data, $mibname);

  #不要多次呼叫SNMP,所以取得資料後以迴圈統計
  foreach ($data as $key => $value)
  {
    if ($value["tcpConnectionState"] == "established")
    {
      /*	
      #Debug 用來檢查迴圈值用
      echo ("### key=(" . $key . ") value=(" . implode(", ",$value) . ")\n" );
      */
      
      if ( preg_match($expr80, $key) == 1)
      {
        $esport80++;
      } elseif( preg_match($expr443, $key) == 1)
      {
        $esport443++;
      } elseif( preg_match($expr1433, $key) == 1)
      {
        $esport1433++;
      } elseif( preg_match($expr3389, $key) == 1)
      {
        $esport3389++;
      } elseif( preg_match($expr8080, $key) == 1)
      {
        $esport8080++;
      } elseif( preg_match($expr8443, $key) == 1)
      {
        $esport8443++;
      }
      $totalportcount++;
    } elseif ($value["tcpConnectionState"] == "timeWait")
    {
      $timeWaitCount++;
      $totalportcount++;
    } elseif($value["tcpConnectionState"] == "closeWait")
    {
      $closeWaitCount++;
      $totalportcount++;   
    }elseif ($value["tcpConnectionState"] == "synSent")
    {
      $synSentCount++;
      $totalportcount++;
    }
  }

  if (is_numeric($esport80) && is_numeric($esport443) && is_numeric($esport1433) && is_numeric($esport3389) && is_numeric($esport8080) && is_numeric($esport8443) && is_numeric($establishedtotal) && is_numeric($totalportcount) && is_numeric($timeWaitCount) && is_numeric($closeWaitCount) && is_numeric($synSentCount)) {
    $rrd_def = RrdDefinition::make()
      ->addDataset('esport80', 'GAUGE', 0)
      ->addDataset('esport443', 'GAUGE', 0)
      ->addDataset('esport1433', 'GAUGE', 0)
      ->addDataset('esport3389', 'GAUGE', 0)
      ->addDataset('esport8080', 'GAUGE', 0)
      ->addDataset('esport8443', 'GAUGE', 0)
      ->addDataset('establishedtotal', 'GAUGE', 0)
      ->addDataset('totalportcount', 'GAUGE', 0)
      ->addDataset('timeWaitCount', 'GAUGE', 0)
      ->addDataset('closeWaitCount', 'GAUGE', 0)
      ->addDataset('synSentCount', 'GAUGE', 0);

    $fields = [
      'esport80'         => $esport80,
      'esport443'        => $esport443,
      'esport1433'       => $esport1433,
      'esport3389'       => $esport3389,
      'esport8080'       => $esport8080,
      'esport8443'       => $esport8443,
      'establishedtotal' => $establishedtotal,
      'totalportcount'   => $totalportcount,
      'timeWaitCount'    => $timeWaitCount,
      'closeWaitCount'   => $closeWaitCount,
      'synSentCount'     => $synSentCount,   
    ];

    /*
    搭配上面迴圈內的Debug,可以用來檢查程式跑出來結果是不是符合預期
    #Debug use: ./poller.php -h <serverip> -m server-port-common-poller -d
    echo ("######################################################\n");
    echo ("exprhead = " . $exprhead  . "\n" );
    echo ("exprend  = " . $exprend   . "\n" );
    echo ("expr80   = " . $expr80    . "\n" );
    echo ("expr443  = " . $expr443   . "\n" );
    echo ("expr1433 = " . $expr1433  . "\n" );
    echo ("expr3389 = " . $expr3389  . "\n" );
    echo ("expr8080 = " . $expr8080  . "\n" );
    echo ("expr8443 = " . $expr8443  . "\n" );
    echo ("\n");
    foreach ($fields as $key => $value)
    {
       echo("key=" . $key . "  ,  value=" . $value . "\n");
    }
    echo ("######################################################\n");
    */

        $tags = compact('rrd_def');
        
        #Poller Module將處理資料存入「server_port_common_graph」        
        data_update($device, 'server_port_common_graph', $tags, $fields);

        $os->enableGraph('server_port_common_graph');
    }

    unset($data, $rrd_def, $fields, $oid, $mibname);
//end if

 

Graph

  1. 檔案名稱:  /opt/librenms/includes/html/graphs/device/server_port_common_graph.inc.php
    很重要:這裡檔名跟Poller Module不同,檔名不能用「-」號,只能用底線「_」,不然LibreNMS會認不出來
    圖片就不會顯示!
    圖片就不會顯示!
    圖片就不會顯示!
  2. 功能:  門將Poller取得的值顯示在畫面上。
  3. 程式內容:
<?php

$scale_min = '0';

require 'includes/html/graphs/common.inc.php';

#Poller Module將處理資料存入「server_port_common_graph」
#rrd從「server_port_common_graph」取值,並繪圖

$rrd_filename = Rrd::name($device['hostname'], 'server_port_common_graph');

$rrd_options .= " DEF:esport80=$rrd_filename:esport80:AVERAGE";
$rrd_options .= " DEF:esport443=$rrd_filename:esport443:AVERAGE";
$rrd_options .= " DEF:esport1433=$rrd_filename:esport1433:AVERAGE";
$rrd_options .= " DEF:esport3389=$rrd_filename:esport3389:AVERAGE";
$rrd_options .= " DEF:esport8080=$rrd_filename:esport8080:AVERAGE";
$rrd_options .= " DEF:esport8443=$rrd_filename:esport8443:AVERAGE";
$rrd_options .= " DEF:establishedtotal=$rrd_filename:establishedtotal:AVERAGE";
$rrd_options .= " DEF:timeWaitCount=$rrd_filename:timeWaitCount:AVERAGE";
$rrd_options .= " DEF:closeWaitCount=$rrd_filename:closeWaitCount:AVERAGE";
$rrd_options .= " DEF:synSentCount=$rrd_filename:synSentCount:AVERAGE";
$rrd_options .= " DEF:totalportcount=$rrd_filename:totalportcount:AVERAGE";

$rrd_options .= " COMMENT:'Sessions            Current    Average    Maximum\\n'";

# %6.0 表示數字格式為整數6位; Line1表示寬一個畫素的線,要粗一點,也有Line1.25的用法
# 詳細可以找rrd 文件參考
$rrd_options .= ' LINE1:esport80#FF0000:Port80        ';
$rrd_options .= " GPRINT:esport80:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:esport80:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport80:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:esport443#FF00FF:Port443       ';
$rrd_options .= " GPRINT:esport443:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:esport443:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport443:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:esport1433#0000FF:Port1433      ';
$rrd_options .= ' GPRINT:esport1433:LAST:%6.0lf';
$rrd_options .= " GPRINT:esport1433:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport1433:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:esport3389#FFD660:Port3389      ';
$rrd_options .= ' GPRINT:esport3389:LAST:%6.0lf';
$rrd_options .= " GPRINT:esport3389:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport3389:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:esport8080#6EA100:Port8080      ';
$rrd_options .= " GPRINT:esport8080:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:esport8080:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport8080:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:esport8443#6DC8FE:Port8443      ';
$rrd_options .= " GPRINT:esport8443:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:esport8443:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:esport8443:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:establishedtotal#9FA4EE:Established   ';
$rrd_options .= " GPRINT:establishedtotal:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:establishedtotal:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:establishedtotal:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:timeWaitCount#8D00BA:Time_Wait     ';
$rrd_options .= " GPRINT:timeWaitCount:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:timeWaitCount:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:timeWaitCount:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:closeWaitCount#CCBB00:Close_Wait    ';
$rrd_options .= " GPRINT:closeWaitCount:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:closeWaitCount:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:closeWaitCount:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1:synSentCount#008A6D:Syn_Sent      ';
$rrd_options .= " GPRINT:synSentCount:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:synSentCount:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:synSentCount:MAX:' %6.0lf\\\\n'";

$rrd_options .= ' LINE1.25:totalportcount#750F7D:TotalPort     ';
$rrd_options .= " GPRINT:totalportcount:LAST:'%6.0lf'";
$rrd_options .= " GPRINT:totalportcount:AVERAGE:' %6.0lf'";
$rrd_options .= " GPRINT:totalportcount:MAX:' %6.0lf\\\\n'";

 

config.php

  1. 檔案名稱:  /opt/librenms/config.php
  2. 功能:  將Poller Module及Graph掛載到LibreNMS上。
  3. 程式內容:
#在config.php內增加以下設定

#底下true表示預設套用到各設備上。若不要預設套用則設為false,然後到要啟用的主機→設定(齒輪)→Modules→Poller Modules 啟用
$config['poller_modules']['server-port-common-poller'] = true;

#設定掛載RRD圖,掛在主機→Graph→Netstats 底下的Server Common Port Connection 區段下
$config['graph_types']['device']['server_port_common_graph'] = ['section' => 'netstats', 'order' => 0, 'descr' => 'Server Common Port Connection'];
圖片掛載在 Netstats 類別
圖片掛載在 Server Common Port Connection 區段下

 

補充說明

  1. 設定若有異常或程式有錯,則在指定的位置下(本例為Netstats)不會有任何圖出現。
  2. 可以在/opt/librenms 用底下的指令測試,若有錯誤會直接看到。
  3. 打開程式裡 Debug區段可以直接看到變數內容是不是跟預期的相同。
  4. 此設定內容,「不會」出現在Global Setting→Poller→Poller Modules 底下。
  5. 此設定內容,「會」出現在單台主機的→Edit(齒輪)→Modules→Poller Modules底下,並可On、Off。
  6. 若一切正常,可以直接在單台主機的→Graph→Netstats底下看到。
  7. 若RRD繪圖有問題,可以選到該圖片後,點選「Show RRD Command」,RRD會有訊息可以參考。
./poller.php -h <serverip> -m server-port-common-poller -d

 

<異常處理>

圖形出現Error … No DS Called xxxxx 時

在設定過程中如果設定錯誤,可能導致rrd檔內容錯誤。好在每個圖形是單獨一個rrd檔,只要刪除讓它重新產生就好,不會影響其它項目累積的資訊。

  1. 移至"/data/rrd" 目錄下,或自己定義的rrd所在位置。
  2. 如果針對單一主機,可以直接刪除主機名稱目錄下的 特定rrd檔案。如:./my_server_ip/server_port_common_graph.rrd
  3. 如果想一次刪除全部的特定的rrd檔案(因為設定錯誤後,可能就全部大量產生),可用以下指令:
#檢視符合的檔案清單(建議看下有沒有意外的檔案)
find ./ -name server_port_common_graph.rrd

#檢視相關檔案詳細資訊(需要檢視檔案日期、大小時)(空白都不能少)
find ./ -name server_port_common_graph.rrd -exec ls -al {} \;

#執行批次刪除(請審慎小心)(空白都不能少)
find ./ -name server_port_common_graph.rrd -exec rm {} \;