shaoqin 发表于 2019-1-11 11:30:10

LNMP+NAMP++RRDTool+CACTI 实现设备监控

  讲监控之前首先要先介绍 SNMP,RRDTool, cacti;
  SNMP:简单网络管理协议,在全球要实现监控的内容很多,于是就有了一种全球的机制来实现对所需要的内容进行监控。这种机制类似于DNS的域系统一样。
  首先我们先来安装网络监控的监控端和被监控端:有下列软件
         net-snmp (Agent)
         net-snmp-utils (NMS:command)
   net-snmp是客户端安装的用来被监控使用。net-snmp-utils是用在服务端来获取被监控端的信息的。如果在同一台机器那么就要同时安装net-snmp和net-snmp-utils两个软件包。这来个软件包在我们的系统本身的yum源中就可以安装,安装之后就可以启支snmpd服务,注意snmpd服务监控在UDP的161端口,因为,tcp端口的监控需要三次握手,不能及时的对出现故障做出补救,当三次扬还没有完成服务就已经DOWN机了,这就大大的降低了我们的效率,故选择了UDP。
   如果我们在两台服务器上来实现监控则需分开安装net-snmp,net-snmp-utils,但是,对有服务端是周期性的获取被监控端的某些状态信息。但是如果在服务端的请求数据未到达之前而被监控端已经发生故障需要让监控端及时察觉到,那么应该主动连接服务监控端。那么就要启用被监控端的trap功能了,


[*]# netstat -unlp
[*]Active Internet connections (only servers)
[*]Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
[*]udp      0      0 0.0.0.0:47385               0.0.0.0:*                               3433/avahi-daemon   
[*]udp      0      0 0.0.0.0:161               0.0.0.0:*                               28164/snmpd

  实现监控要用到MIB库,当监控羰NMS要查看被监控端Agent的某些状态的时候要先查看Agent端的MIB库,其中记录的是功能码与名称的对应关系,只有MIB库中有的记录才会能被识别为某个设备,才会被监控端监控,否则,不能识别,不能被监控。
      被监控端要主动连接监控端的162来实现主动连接服务端。
  在安装完SNMP后默认的commnut是:public和private,好就是说,只要你有public那么你就可以来获取该主机上的信息。
    常用的命令有:
get:获取特定的一个设备的信息
getnext:获取特定的一类设备的信息
set:向对方发送控制指令
getresponse:是对于get信息的被监控端的响应信息
trap:是由trap端发起的。
SNMP的版本共有三种,有SNMP v1,SNMP v2,SNMP v3.现在最常用的是v2版本的。
  如下获取本机中host的信息:



[*]# snmpwalk -v 2c -c public localhost host
[*]HOST-RESOURCES-MIB::hrSystemUptime.0 = Timeticks: (11715047) 1 day, 8:32:30.47
[*]HOST-RESOURCES-MIB::hrSystemUptime.0 = No more variables left in this MIB View (It is past the end of the MIB tree)
[*]#

  -v 2c:是定义snmp的版本。
如果我们要获取本机上的一类的信息如下:


[*]# snmpwalk -v 2c -c public localhost
[*]SNMPv2-MIB::sysDescr.0 = STRING: Linux localhost.localdomain 2.6.18-308.el5 #1 SMP Fri Jan 27 17:21:15 EST 2012 i686
[*]SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
[*]DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (1640498) 4:33:24.98
[*]SNMPv2-MIB::sysContact.0 = STRING: Root(configure /etc/snmp/snmp.local.conf)
[*]SNMPv2-MIB::sysName.0 = STRING: localhost.localdomain
[*]SNMPv2-MIB::sysLocation.0 = STRING: Unknown (edit /etc/snmp/snmpd.conf)
[*]SNMPv2-MIB::sysORLastChange.0 = Timeticks: (10) 0:00:00.10
[*]SNMPv2-MIB::sysORID.1 = OID: SNMPv2-MIB::snmpMIB
[*]SNMPv2-MIB::sysORID.2 = OID: TCP-MIB::tcpMIB
[*]SNMPv2-MIB::sysORID.3 = OID: IP-MIB::ip
[*]SNMPv2-MIB::sysORID.4 = OID: UDP-MIB::udpMIB
[*]SNMPv2-MIB::sysORID.5 = OID: SNMP-VIEW-BASED-ACM-MIB::vacmBasicGroup
[*]SNMPv2-MIB::sysORID.6 = OID: SNMP-FRAMEWORK-MIB::snmpFrameworkMIBCompliance
[*]SNMPv2-MIB::sysORID.7 = OID: SNMP-MPD-MIB::snmpMPDCompliance
[*]SNMPv2-MIB::sysORID.8 = OID: SNMP-USER-BASED-SM-MIB::usmMIBCompliance
[*]SNMPv2-MIB::sysORDescr.1 = STRING: The MIB module for SNMPv2 entities
[*]SNMPv2-MIB::sysORDescr.2 = STRING: The MIB module for managing TCP implementations
[*]SNMPv2-MIB::sysORDescr.3 = STRING: The MIB module for managing IP and ICMP implementations
[*]SNMPv2-MIB::sysORDescr.4 = STRING: The MIB module for managing UDP implementations
[*]SNMPv2-MIB::sysORDescr.5 = STRING: View-based Access Control Model for SNMP.
[*]SNMPv2-MIB::sysORDescr.6 = STRING: The SNMP Management Architecture MIB.
[*]SNMPv2-MIB::sysORDescr.7 = STRING: The MIB for Message Processing and Dispatching.
[*]SNMPv2-MIB::sysORDescr.8 = STRING: The management information definitions for the SNMP User-based Security Model.
[*]SNMPv2-MIB::sysORUpTime.1 = Timeticks: (9) 0:00:00.09
[*]SNMPv2-MIB::sysORUpTime.2 = Timeticks: (9) 0:00:00.09
[*]SNMPv2-MIB::sysORUpTime.3 = Timeticks: (9) 0:00:00.09
[*]SNMPv2-MIB::sysORUpTime.4 = Timeticks: (9) 0:00:00.09
[*]SNMPv2-MIB::sysORUpTime.5 = Timeticks: (9) 0:00:00.09
[*]SNMPv2-MIB::sysORUpTime.6 = Timeticks: (10) 0:00:00.10
[*]SNMPv2-MIB::sysORUpTime.7 = Timeticks: (10) 0:00:00.10
[*]SNMPv2-MIB::sysORUpTime.8 = Timeticks: (10) 0:00:00.10
[*]HOST-RESOURCES-MIB::hrSystemUptime.0 = Timeticks: (11747562) 1 day, 8:37:55.62
[*]HOST-RESOURCES-MIB::hrSystemUptime.0 = No more variables left in this MIB View (It is past the end of the MIB tree)

  这磁就能获取到本机上的信息了。如果我们还想获取别的一些状太信息则要到编辑/etc/snmp/snmp.conf的配置文件,加入要监控的设备信息:


[*]# Make at leastsnmpwalk -v 1 localhost -c public system fast again.
[*]#       name         incl/excl   subtree         mask(optional)
[*]view    systemview    included   .1.3.6.1.2.1.1
[*]view    systemview    included   .1.3.6.1.2.1.25.1.1

  其中的:


[*]#       sec.namesource          community
[*]#com2sec notConfigUserdefault       public
[*]com2sec notConfigUser127.0.0.1       public
[*]com2sec notConfigUser172.16.0.0/16   public

  定义允许哪 一个网段的主机来获取设备信息,并且定义一个community,用来连接时的community.
  我们也可以来获取 一个物定设备的信息:在本地全局的信息中找到一个特定要查找的信息:如下



[*]# snmpget -v 2c -c public localhost SNMPv2-MIB::sysORID.1
[*]SNMPv2-MIB::sysORID.1 = OID: SNMPv2-MIB::snmpMIB

  这用我们在上面找到的设备信息是一样的。
  我们可以自己在SNMP的配置文件中定义一个我们需要监控的设备,如tcp的端口信息,流量,……, 当我们不知道某一个设备的MIB库代码的时候可以到/usr/share/snmp/mibs中查找相关对应的MIB代码,如下:



[*]# ls /usr/share/snmp/mibs/
[*]AGENTX-MIB.txt                     NOTIFICATION-LOG-MIB.txt
[*]DISMAN-EVENT-MIB.txt               RFC1155-SMI.txt
[*]DISMAN-SCHEDULE-MIB.txt            RFC1213-MIB.txt
[*]DISMAN-SCRIPT-MIB.txt                RFC-1215.txt
[*]EtherLike-MIB.txt                  RMON-MIB.txt
[*]HCNUM-TC.txt                         SCTP-MIB.txt
[*]HOST-RESOURCES-MIB.txt               SMUX-MIB.txt
[*]HOST-RESOURCES-TYPES.txt             SNMP-COMMUNITY-MIB.txt
[*]IANA-ADDRESS-FAMILY-NUMBERS-MIB.txtSNMP-FRAMEWORK-MIB.txt
[*]IANAifType-MIB.txt                   SNMP-MPD-MIB.txt
[*]IANA-LANGUAGE-MIB.txt                SNMP-NOTIFICATION-MIB.txt
[*]IANA-RTPROTO-MIB.txt               SNMP-PROXY-MIB.txt
[*]IF-INVERTED-STACK-MIB.txt            SNMP-TARGET-MIB.txt
[*]IF-MIB.txt                           SNMP-USER-BASED-SM-MIB.txt
[*].index                               SNMP-USM-AES-MIB.txt
[*]INET-ADDRESS-MIB.txt               SNMP-USM-DH-OBJECTS-MIB.txt
[*]IP-FORWARD-MIB.txt                   SNMPv2-CONF.txt
[*]IP-MIB.txt                           SNMPv2-MIB.txt
[*]IPV6-ICMP-MIB.txt                  SNMPv2-SMI.txt
[*]--More--

  打开这些文件,找到MIB代码就可以查找到,然后写到snmp.conf的文件中去就可以实现对该设备的监控,获得该设备的信息。
我们在上面定义了允许哪一个允许哪一个网络来获取信息,并定义community的密码,用来实现获取信息时的公社认证。这个密码是越复杂越好,如:我们上面定义了172.16.0.0/16的网段:如下:查看172.16.121上的所有定义的设备的信息。


[*]# snmpwalk -v 2c -c public 172.16.121.1 tcp
[*]TCP-MIB::tcp = No Such Object available on this agent at this OID
[*]# snmpwalk -v 2c -c public 172.16.121.1
[*]SNMPv2-MIB::sysDescr.0 = STRING: Linux localhost.localdomain 2.6.18-308.el5 #1 SMP Fri Jan 27 17:21:15 EST 2012 i686
[*]SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
[*]DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (17003) 0:02:50.03
[*]SNMPv2-MIB::sysContact.0 = STRING: Root(configure /etc/snmp/snmp.local.conf)
[*]SNMPv2-MIB::sysName.0 = STRING: localhost.localdomain
[*]SNMPv2-MIB::sysLocation.0 = STRING: Unknown (edit /etc/snmp/snmpd.conf)
[*]SNMPv2-MIB::sysORLastChange.0 = Timeticks: (17) 0:00:00.17
[*]SNMPv2-MIB::sysORID.1 = OID: SNMPv2-MIB::snmpMIB

  监控信息的获取完毕之后就是把所得到的信息通过绘图工具来进行绘图。不过我们这里要涉及到轮转数据库,安装软件如下:
      rrdtool-1.4.7-1.mage.i386.rpm
      rrdtool-devel-1.4.7-1.mage.i386.rpm
      rrdtool-perl-1.4.7-1.mage.i386.rpm
这是一个绘图工具,实现绘图。有如下的命令:
create:创建一个新的rrd文件,也就是轮转数据库,
update:向创建的轮状数据库中插入数据
graph: 对轮状数据库中数据进行绘图
info:查看文件rrd文件。
      rrdtool create filename [--start|-b start time][--step|-s step][--no-overwrite]
  DS:用于指定一个数据源
RRA:如何作聚合
数据源的数据类型有这样几种:
       GAUGE:保存没有作任何修改的初始值,也就是说是几就保存为几。
       COUNTER: 保存相对于上一次数据的初始值,必须一直处于递增的值,因为这种类型的值不能识别负数的情况。
       DERIVE:可以增,也可以减。
       ABSOLUTE:最原始的值的相对值。
--step:是每长时间进行一次数据写入。
如:如初始值 0
保存 2 3 6 9
    GAUGE: 2 3 6 9
    COMPUTE:2 1 3 3每一个都是在前面的结果的基础上减得的结果。2=2-0,1=3-2,3=6-3,3=9-6. 不能有负值。
   DERIVE:2,1,3,3 假如保存2,6,3,9,则对于COMPUTER则不能识别,对于DERIVE则是可以,保存为:2,4,-3,6
   ABSOLUTE:最原始的值的相对值。
DS段的格式如下:
   DS:ds-name:GAUGE|COMPUTE|DERIVE|ABSOLUTE:heartbeat:min:max
heartbeat:定义我们在多长时间内对于到来的数据我们仍有接受,超过主个时间段后就不再接受数据,而是归到下一个时间段。
   min:max :限定接收到的值的最大值和最小值。0:U 最小值0,最大值不限。
也就是说对于轮转数据库的时间槽中的每一个槽就是一个PDP,然后对PDP进行聚合,聚合后为PDF。对于一个PDP我们可以进行多个聚合。
RRA:AVERAGE|MIN|MAX|LAST:xff:steps:rows
   xff就是说,当PDP中UNKNOW的比例为多少时,我们把取合后的PDF也定义为UNKNOW,如0.5就是当有一半以上的pdp为unknow的时候,那么我们就定义取合后的PDF 也是UNKNOW,否则,根据机制可以推测出数据。
steps:定义多少个PDF进行聚合。
rows:是定义一共保存多少个这样的数据,比如我们要保存两天的数据,那么就要用3600*2/(每次PDP的数据写入时间*几个PDP进行聚合) 就得出在两小时内要记录多少次。
   下面来创建一个rrd文件来作演示:如下


[*]# rrdtool create test.rrd --step 5 DS:testds:GAUGE:8:0:U RRA:AVERAGE:0.5:1:17280 RRA:AVERAGE:0.5:10:3456 RRA:AVERAGE:0.5:100:1210

  我们前面说过一个数据源可以定义多个聚合函数。
  查看所创建的test.rrd文件:



[*]# rrdtool info test.rrd
[*]filename = "test.rrd"
[*]rrd_version = "0003"
[*]step = 5
[*]last_update = 1346805385
[*]header_size = 928
[*]ds.index = 0
[*]ds.type = "GAUGE"
[*]ds.minimal_heartbeat = 8
[*]ds.min = 0.0000000000e+00
[*]ds.max = NaN
[*]ds.last_ds = "U"
[*]ds.value = 0.0000000000e+00
[*]ds.unknown_sec = 0
[*]rra.cf = "AVERAGE"
[*]rra.rows = 17280
[*]rra.cur_row = 2018
[*]rra.pdp_per_row = 1
[*]rra.xff = 5.0000000000e-01
[*]rra.cdp_prep.value = NaN
[*]rra.cdp_prep.unknown_datapoints = 0
[*]rra.cf = "AVERAGE"
[*]rra.rows = 3456
[*]rra.cur_row = 1438
[*]rra.pdp_per_row = 10
[*]rra.xff = 5.0000000000e-01
[*]rra.cdp_prep.value = NaN
[*]rra.cdp_prep.unknown_datapoints = 7
[*]rra.cf = "AVERAGE"
[*]rra.rows = 1210
[*]rra.cur_row = 261
[*]rra.pdp_per_row = 100
[*]rra.xff = 5.0000000000e-01
[*]rra.cdp_prep.value = NaN
[*]rra.cdp_prep.unknown_datapoints = 77

  这就是我们定义的rrd文件,其中定义了如何的聚合函数,怎样聚合。
  下面介绍向轮状数据库中插入数据:
   插入数据的命令是:
      rrdtool uodate
rrdtool {update|updatev} filename [--template |-t ds-name]...] N:value [:value...]
filename 就是我们前面创建rrd文件的数据库,插入数据的时候我们要指定时间或当下的时间,用来在对应的时间点插入对应的数据。
另外我们在创建r轮转数据库的时候创建了多个数据源,那么我们在插入数据的时候要在对应的数据源中的DS,如 :
      rrdtool update test.rrd N:32:24   在当前时间点在对应的轮状数据库中插入32,24. N:表示当下时间。
   查看test.rrd 文件中的数据如下:


[*]# rrdtool fetch -r 10 test.rrd AVERAGE
[*]1346803495: nan
[*]1346803500: nan
[*]1346803505: nan
[*]1346803510: nan
[*]1346803515: nan
[*]1346803520: nan
[*]1346803525: nan
[*]1346803530: nan
[*]1346803535: nan
[*]1346803540: nan
[*]1346803545: nan
[*]1346803550: nan
[*]1346803555: nan
[*]1346803560: nan

  显示各个时间槽的状态。因为我们没有对数据库插入数据所以没有显示。
我们建一个脚本不断的向rrd文件中插入数据,那么就可以在这里查看到据:如,我们向轮转数据库中不断的插入随机数,则会显示:


[*]# rrdtool fetch -r 10 test.rrd AVERAGE   
[*]1345549485: 1.6518814226e+04
[*]1345549490: 1.2866882859e+04
[*]1345549495: 3.8330279200e+03
[*]1345549500: 1.8081723076e+04
[*]1345549505: 1.8319617188e+04
[*]1345549510: 5.6377045504e+03
[*]1345549515: 1.1787847095e+04
[*]1345549520: 1.5101760758e+04
[*]1345549525: 5.1187180280e+03
[*]1345549530: 5.2695425920e+03
[*]1345549535: 2.8047220127e+04
[*]1345549540: 4.7359389690e+03
[*]1345549545: 2.2955348688e+04
[*]1345549550: nan
[*]1345549555: nan

  这样就插入了数据。
  rrdtool :还是一强大的绘图工具。
  rrdtool graph|graphv filename
  [-s|--start time] [-e|--end time][-S|--step seconds]
  [-t|--title string][-v|--vertical-label string]
  [-w|--width pixels][-h|--height pixels][-j|--only-graph][-D|--full-size-mode]
  [-u|--upper-limit value][-l|--lower-limit value][-r|--rigid]
  [-A|--alt-autoscale ]自动缩放
  [-J|--alt-autoscale-min] 只自动缩放最小值
  [-M|--alt-autoscale-max] 只自动缩放最大值
  [-N|--no-gridfit]不显示网格线的。
  filename:是绘图后的文件名,
  -Axis
  [-x|--x-grid GTM:GST:MTM:MST:LTM:LSL:LPR:LFM]
  [-x|--x-grid none]
  e.g --x-grid MINUTE:10:HOUR:1:HOUR:4:0:%X
  灰色的网格线:
  GTM: 表示单位
  GST:表示多大的值 如上面的,单位分钟:MINUTE, 每10分钟。
  主网格线:
  MTM
  MST   和上面的灰色网格线相似。
  标签:
  LIM:以什么为单位
  LST:横柚多长时间显示一个标签。
  后面的是显示时间格式的。
  %X 就是它的时间格式。
  Y-Axis
  [-y|--y-grid grid step:label factor]
  [-y|--y-grid none]
  [-Y|--alt-y-grid]
  -c 指定线条颜色的。
  怎样来获取数据:
  DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]
  CDEF:
  VDEF:
  LINE :画图时线条的设置。
  LINE:value[#color][:]
  # rrdtool graph a.png --step 5 -s 1345568620DEF:vartest=/root/test.rrd:testds:AVERAGE LINE1:vartest#FF0000:"testline"
  481x155
  绘出的图。
  其中的testds是我们在创建轮状数据库时定义的数据源。
写如下脚本来向轮转数据库中插入数据:如下



[*]#!/bin/bash
[*]while true;do
[*]    rrdtool update test.rrd N:$RANDOM
[*]    sleep 5
[*]done

  然后再来查看轮转数据库就能查看到数据:如下


[*]# rrdtool fetch -r 10 test.rrd AVERAGE   
[*]1345549485: 1.6518814226e+04
[*]1345549490: 1.2866882859e+04
[*]1345549495: 3.8330279200e+03
[*]1345549500: 1.8081723076e+04
[*]1345549505: 1.8319617188e+04
[*]1345549510: 5.6377045504e+03
[*]1345549515: 1.1787847095e+04
[*]1345549520: 1.5101760758e+04
[*]1345549525: 5.1187180280e+03
[*]1345549530: 5.2695425920e+03
[*]1345549535: 2.8047220127e+04
[*]1345549540: 4.7359389690e+03
[*]1345549545: 2.2955348688e+04

  然后根据这些数据来绘图。就可以以更清晰的界面来显示所要监控的设备:
如下来建立图像。如下建立图像:


[*]# rrdtool graph a.png --step 5 -s 1346808380DEF:vartest=/root/test.rrd:testds:AVERAGE LINE1:vartest#FF0000:"testline"
[*]481x155
[*]#

  把生成的图片展示如下:
http://blog.运维网.com/attachment/201209/194823812.png
  就会查看我们所建立的监控图片。
   我们的图像建严重依赖时间,所以我们要确保时间为当下时间,不然在轮转数据库中会出现错误。
由于这种做图像对于繁忙的记录很不方便,所以便有个cacti。cacti是一群php的网页。他只是一个展示工具,分为图像模板,数据模板,和主机模板。他还是要依赖SNMP,RRDTool。由于我们cacti依赖于时间,所以在php.ini的文件中要定义时区。其实配置很简单,就是把cacti的压缩文件解压到对应的网页目录下,解压后如下:



[*]# ls
[*]about.php                graph_image.php             include            rra
[*]auth_changepassword.phpgraph.php                   index.php            rra.php
[*]auth_login.php         graph_settings.php          install            scripts
[*]cacti.sql                graphs_items.php            lib                  script_server.php
[*]cdef.php               graphs_new.php            LICENSE            script_server.pl
[*]cli                      graphs.php                  log                  settings.php
[*]cmd.php                  graph_templates_inputs.phplogout.php         templates_export.php
[*]color.php                graph_templates_items.php   plugins            templates_import.php
[*]data_input.php         graph_templates.php         plugins.php          tree.php
[*]data_queries.php         graph_view.php            poller_commands.phpuser_admin.php
[*]data_sources.php         graph_xport.php             poller_export.php    utilities.php
[*]data_templates.php       host.php                  poller.php
[*]docs                     host_templates.php          README
[*]gprint_presets.php       images                      resource

  cacti的运行要读取数据库的文件,所以要先建立数据库,建立用户cactiuser,并把上面的cacti.sql的myql语句导入到数据库中,因为cacti的运行要时刻访问log和rra文件,所以要把这些文件的属主属组改为cactiuser:cactiuser.完成之后就可以在nginx的配置文件中给cacti的配置文件添加一个虚拟主机来访问。
    cacti的访问默认会在访问路径的后面/cacti,如果我们想取消这种规模,要到cacti的解压后的include文件中的config.php中来定义访问cacti的相关设置:如下



[*]/* make sure these values refect your actual database/host/user/password */
[*]$database_type = "mysql";
[*]$database_default = "cactidb";
[*]$database_hostname = "localhost";
[*]$database_username = "cactiuser";
[*]$database_password = "cactiuser";
[*]$database_port = "3306";
[*]$database_ssl = false;
[*]
[*]/*
[*]   Edit this to point to the default URL of your Cacti install
[*]   ex: if your cacti install as at http://serverip/cacti/ this
[*]   would be set to /cacti/
[*]*/
[*]//$url_path = "/";
[*]$url_path = "/";

  在$url_path="/";就可以定义访问的路径是否要加cacti。
   第一次访问cacti要先初始化,然后会检查相关的配置,完成之后就可以访问我们在nginx的虚拟主机中定义的server_name,并要输入用户和密码,默认用户是admin,密码也是admin,第一次登录,服务会要求我们修改原密码,然后就可以进入cacti进行设置:如下:
http://blog.运维网.com/attachment/201209/203748989.jpg
  graphs:是显示图像,如下:
http://blog.运维网.com/attachment/201209/204047242.jpg
  
就可以看到我们的图像,这个图像是动态随时变换的。
    对图像模板,我们可以到cacti的官网下载相应的图像模板,数据模板可以节省我们的时间,并且那些模板更加完善。可以节省我们很多时间。具体的我也不多说了,多摸索下就可以搞定。



页: [1]
查看完整版本: LNMP+NAMP++RRDTool+CACTI 实现设备监控