| 日历 
			
				| 
	|  |  | 日 | 一 | 二 | 三 | 四 | 五 | 六 | 
|---|
 | 28 | 29 | 30 | 1 | 2 | 3 | 4 |  | 5 | 6 | 7 | 8 | 9 | 10 | 11 |  | 12 | 13 | 14 | 15 | 16 | 17 | 18 |  | 19 | 20 | 21 | 22 | 23 | 24 | 25 |  | 26 | 27 | 28 | 29 | 30 | 31 | 1 |  | 2 | 3 | 4 | 5 | 6 | 7 | 8 |  |  统计 
		随笔 - 11
		文章 - 0
		评论 - 28
		引用 - 0
	 导航常用链接留言簿(2)随笔分类随笔档案搜索最新评论
	阅读排行榜评论排行榜  | 
	
	
		一般主动告警系统的告警信息采集主要有5种方法: 
  1. 在告警服务器ping各种设备, 判断设备是否存活和掉包率  2. 接收设备发过来的系统日志(syslog), 并通过相应的规则库(正则表达式)匹配判断是否需要告警  3. 接收设备发过来的snmp Trap信息, 进行判断告警  4. 提取网管系统的告警信息  5. 通过snmp协议, 取回相应oid的值, 进行判断告警  什么是snmp: 
  Simple Network Management Protocol (SNMP)提供了一些"简单"的操作, 允许你更容易的监控和管理网络设备, 例如路由器,交换机,服务器,打印机等等. 通过snmp你可以监控很多信息, 例如端口流量, 路由器里面的温度, cpu使用率等等. 学习snmp其实并不是特别简单, 请通过别的资料学习更多的方面, 特别是mib,oid之类的概念.   推荐学习Essential SNMP, 2nd Edition这本书.  如何收集数据:  如果安装了NET-SNMP, 可以从http://net-snmp.sourceforge.net/ 获取NET-SNMP的RPM包以及源代码。下载 解压后  su - 
  cd ucd-snmp-4.2.3 
  ./configure --prefix=/usr  <-- 缺省是/usr/local 
  make clean all 
  make install snmpget <target> public system.sysDescr.0应该可以看到一个关于系统的简短描述,类似这样:
  system.sysDescr.0 = Sun SNMP Agent, Ultra-60 上述命令中的public可以理解为SNMP agent的口令,术语叫做"community string"。许多网络设备、操作系统都用"public"做为缺省"community string",潜在带来安全
 问题。应该修改这个缺省"community string"。
 上述命令还可以写成:  snmpget <target> public .1.3.6.1.2.1.1.1.0"system.sysDescr.0"只是".1.3.6.1.2.1.1.1.0"的另一种表述方式,最终还是要转
 换成数字形式的OID(对象标识符)。
 snmpget返回一个值, 类型可以是数值或者字符串等, 还有一个snmpwalk的操作, 大概就是返回一个数组的结果. 本系统使用java语言实现, 在网上下载了一个开源的snmp实现, 假设有以下工具类:
  public class Poller 
    { 
  public Poller( String host, String community, int version ) 
  throws IOException 
     { 
  //   
  } 
   
  public String get( String oid ) 
  throws IOException 
     { 
  //   
  return null; 
  } 
   
   
  public Map<String, String> walk( String base, int startIndex, 
  int indexCount ) 
     { 
  //   
  return null; 
  } 
   
  public void close() 
     { 
  } 
   
  public static void main( String[] args ) 
     { 
  Poller poller = new Poller(  ); // 该ip对应的设备是cisco-6509 
   
  // 1. cpu告警 
  String cpuStr = poller.get( "1.3.6.1.4.1.9.9.109.1.1.1.1.5.9" ); // cisco-6509的CPU使用率 
  long cpu = Long.parseLong( valueStr ); 
   
  if ( cpu > 85 ) 
     { 
  System.out.println( "告警! cisco-6509的CPU使用率超过85%" ) ; 
  } 
   
  // 2. 板卡告警 
  String statusStr = poller.get( "1.3.6.1.4.1.9.5.1.3.1.1.10.1" ); // cisco-6509的第一个板卡状态 
  long status = Long.parseLong( statusStr ); 
   
  if ( value != 2 && value != 1 ) // 1:未知 2:normal 3:minorFault 4:majorFault 
     { 
  System.out.println( "告警! cisco-6509的第一个板卡状态不正常" ) ; 
  } 
   
  // 3. 流量告警 
  String octetStr = poller.get( "ifHCInOctets.10" ); // cisco-6509的第10个接口的输入流量, 单位Byte 
  long value = Long.parseLong( octetStr ); 
  long time = System.currentTimeMillis()/1000; 
  long lastValue = getLastValue(  ); // 从数据库或文件取上次的流量值 
  long lastTime = getLastTime(  ); // 从数据库或文件取上次采集的时间 
   
  if ( (value-lastValue)/(time-lastTime)*8>800000000 ) // 一般流量单位是 bit/s, 所以要乘以8 
     { 
  System.out.println( "告警! cisco-6509的第10个接口的输入流量超过800M" ) ; 
  } 
   
   
  poller.close(); 
  } 
  }在上面的main函数, 我们已经基本可以实现snmp的告警功能了, 可是这样相当不灵活, 全部都是硬编码, 每添加一个新的snmp告警都要新加代码模块
  经过分析, 大部分的snmp采集告警都是这样的过程:
 1. 取得某设备的对象ID(oid)
 2. 通过snmp协议得到该oid相应的值, 赋值给value这个变量
 3. 取当前的时间(秒), 赋值给time这个变量
 4. 取上次采集的值和时间, 分别赋值给lastValue, lastValue
 5. 根据该oid返回值代表含义, 构造一个表达式, 这个表达式只能包括value, time, lastValue, lastTime这4个变量,
 有时不必全部用上, 而且该表达式应回一个布尔类型的值, 如果为真则需要告警
 6. 保存value, time为lastValue, lastTime, 用来在下次采集判断时使用
 
 这个时候就比较清楚了, 如果有一种动态语言或动态脚本在java环境里能运行就能够比较灵活的实现snmp告警了, 不需要硬编码所有的告警情况, 只需要在ui界面添加修改告警表达式就ok了
  经过在http://www.open-open.com或http://java-source.net上搜索, 发现BeanShell这个项目, 官方网站是http://www.beanshell.org/ 
 Beanshell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,具有对象脚本语言特性。BeanShell执行标准Java语句和表达式,另外包括一些脚本命令和语法。它将脚本化对象看作简单闭包方法(simple method closure)来支持,就如同在Perl和JavaScript中的一样。
 
 以下是用BeanShell改写的snmp告警模块:
 
  package com.kelefa.warnlet.job; 
  
  import java.io.IOException; 
  import java.util.Date; 
  
  import org.apache.log4j.Logger; 
  import org.hibernate.HibernateException; 
  import org.hibernate.classic.Session; 
  
  import bsh.EvalError; 
  import bsh.Interpreter; 
  
  import com.kelefa.common.util.HibernateUtil; 
  import com.kelefa.warnlet.dao.WarningDAO; 
  import com.kelefa.warnlet.interpreter.SimpleInterpreter; 
  import com.kelefa.warnlet.snmp.Poller; 
  import com.kelefa.warnlet.vo.Device; 
  import com.kelefa.warnlet.vo.SnmpObject; 
  import com.kelefa.warnlet.vo.Warning; 
  
  public class SnmpTask 
  implements Runnable 
    { 
  private final static Logger log = Logger.getLogger( SnmpTask.class ); 
  
  private SnmpObject snmpObject; 
  
  private WarningDAO warningDAO; 
  
  private static final String BSH = "bsh://"; 
  
  public SnmpTask( SnmpObject snmpObject, WarningDAO warningDAO ) 
     { 
  this.snmpObject = snmpObject; 
  this.warningDAO = warningDAO; 
  } 
  
  public void run() 
     { 
  log.debug( "----snmpObject.id=" + snmpObject.getId() ); 
  try 
     { 
  Session session = HibernateUtil.currentSession(); 
  HibernateUtil.beginTransaction(); 
  session.refresh( snmpObject ); 
  
  doSnmpTask(); 
  
  HibernateUtil.commitTransaction(); 
  } 
  catch ( Exception ex ) 
     { 
  HibernateUtil.rollbackTransaction(); 
  log.warn( ex.getMessage() ); 
  } 
  finally 
     { 
  HibernateUtil.closeSession(); 
  } 
  log.debug( "++++snmpObject.id=" + snmpObject.getId() ); 
  } 
  
   /** *//** 
  * 执行snmp任务, 包括: 
  * 1. 用snmp协议取相应oid的值, 如果网络异常或oid设置错误则直接结束 
  * 2. 如果返回的字符串不是数字则直接结束 
  * 3. 用BSH运算告警表达式, 表达式错误结束 
  * 4. 告警表达式返回真, 进行告警 
  * 5. 更新最后时间,值 
  * 
  */ 
  private void doSnmpTask() 
     { 
  Device device = snmpObject.getDevice(); 
  
  String valueStr; 
  try 
     { 
  valueStr = snmpget( device.getIp(), device.getCommunity(), device 
  .getSnmpVersion(), snmpObject.getOid() ); 
  } 
  catch ( IOException e ) 
     { // 1. 如果网络异常或oid设置错误则直接结束 
  log.warn( e.getMessage() ); 
  return; 
  } 
  
  if ( valueStr == null || valueStr.trim().length() == 0 ) 
  return; 
  
  Long value = null; 
  try 
     { 
  value = Long.valueOf( valueStr ); 
  } 
  catch ( NumberFormatException ex ) 
     {// 2. 如果返回的字符串不是数字则直接结束 
  log.warn( "NumberFormatException: " + ex.getMessage() + "\t" 
  + device.getCommunity() + "@" + device.getIp() + ": " 
  + snmpObject.getOid() ); 
  return; 
  } 
  
  Date now = new Date(); 
  Long time = new Long( (now.getTime() + 500) / 1000 ); 
  
  if ( snmpObject.getLastValue() > 0 && snmpObject.getLastTime() > 0 ) 
     { // 第一次不执行bsh脚本 
  Long lastValue = new Long( snmpObject.getLastValue() ); 
  Long lastTime = new Long( snmpObject.getLastTime() ); 
  
  boolean doWarn = false; 
  try 
     { // 3. 用BSH运算告警表达式 
  doWarn = evalExpr( value, time, lastValue, lastTime ); 
  } 
  catch ( EvalError ex ) 
     { 
  log.warn( ex.getMessage(), ex ); 
  updateSnmpObject( value, time ); 
  return; 
  } 
  
  if ( log.isDebugEnabled() ) 
     { 
  logResult( time, value, lastValue, lastTime, doWarn ); 
  } 
  
  if ( doWarn ) 
     { // 4. 告警表达式返回真, 进行告警 
  Warning warning = newWarning( now, time, value, lastValue, lastTime ); 
  
  try 
     { 
  warningDAO.insertWarning( warning ); 
  } 
  catch ( Exception ex ) 
     { 
  throw new HibernateException( ex.getMessage() ); 
  } 
  } 
  } 
  
  // 5. 更新最后时间,值 
  updateSnmpObject( value, time ); 
  } 
  
   /** *//** 
  * 更新监控对象的最后的执行时间(lastTime)以及最新值(lastValue) 
  * 
  * @param value 
  * @param time 
  */ 
  private void updateSnmpObject( Long value, Long time ) 
     { 
  snmpObject.setLastTime( time.longValue() ); 
  snmpObject.setLastValue( value.longValue() ); 
  } 
  
   /** *//** 
  * 执行动态bsh表达式, 并返回该表达式的结果值 
  * 
  * @param value 
  * @param time 
  * @param lastValue 
  * @param lastTime 
  * @return 
  * @throws EvalError 
  */ 
  private boolean evalExpr( Long value, Long time, Long lastValue, Long lastTime ) 
  throws EvalError 
     { 
  Interpreter bsh = new Interpreter(); 
  
  bsh.set( "value", value ); 
  bsh.set( "time", time ); 
  bsh.set( "lastValue", lastValue ); 
  bsh.set( "lastTime", lastTime ); 
  
  // 执行bsh脚本,返回true则需要告警 
  Boolean doWarn = (Boolean) bsh.eval( snmpObject.getWarnExpr() ); 
  
  return doWarn.booleanValue(); 
  } 
  
   /** *//** 
  * 通过snmpget或snmpwalk命令取snmpObject的oid对应的值, oid可能是单独的oid例如 1.3.6.1.4.5, 
  * 也可能是包括sum, count, max, min, avg等函数的表达式. 如果是单独的oid, 返回snmpget相应的值即可; 
  * 如果是复合函数, 用snmpwalk, 再进行运算, 返回最后结果值 
  * 
  * @param device 
  *          ip, community, version从这个对象取 
  * @return 
  * @throws IOException 
  */ 
  public static String snmpget( final String ip, final String community, 
  final int snmpversion, final String oid ) 
  throws IOException 
     { 
  String valueStr = null; 
  Poller poller = null; 
  try 
     { 
  poller = new Poller( ip, community, snmpversion, 100 ); 
  
  log.debug( "pollering " + oid ); 
  
  if ( oid.indexOf( '(' ) == -1 ) 
     {// 单独一个oid 
  valueStr = poller.get( oid ); 
  if ( log.isDebugEnabled() ) 
  log.debug( "snmpget(" + oid + ")=" + valueStr ); 
  } 
  else 
     {// 包括sum, count, max, min, avg等函数的表达式, 例如: 
  // sum(ippoolSize)*100/sum(ippoolUse) 
  SimpleInterpreter si = new SimpleInterpreter( poller, oid ); 
  Long result = si.interprete(); 
  if ( log.isDebugEnabled() ) 
  log.debug( oid + "=" + result ); 
  if ( result != null ) 
  valueStr = result.toString(); 
  } 
  } 
  finally 
     { 
  if ( poller != null ) 
  poller.close(); 
  } 
  
  return valueStr; 
  } 
  
  private Warning newWarning( Date now, Long time, Long value, Long lastValue, 
  Long lastTime ) 
     { 
  Warning warning = new Warning(); 
  warning.setDeviceID( snmpObject.getDeviceID() ); 
  warning.setWarnType( snmpObject.getWarnType() ); 
  warning.setWarnLevel( snmpObject.getWarnLevel() ); 
  warning.setPrimarykey( snmpObject.getOid() ); 
  
  String sms = snmpObject.getWarnSms(); 
  sms = getBshWarnMsg( sms, value, lastValue, time, lastTime ); 
  if ( sms == null || sms.trim().length() == 0 ) 
  warning.setWarnSms( snmpObject.getWarnType() ); 
  else 
  warning.setWarnSms( sms.trim() ); 
  
  String email = snmpObject.getWarnEmail(); 
  email = getBshWarnMsg( email, value, lastValue, time, lastTime ); 
  if ( email == null || email.trim().length() == 0 ) 
  warning.setWarnEmail( snmpObject.getWarnType() ); 
  else 
  warning.setWarnEmail( email.trim() ); 
  
  warning.setWarnTTS( snmpObject.getWarnTTS() ); 
  
  warning.setFirstTime( now ); 
  warning.setLastTime( now ); 
  warning.setSuggestion( snmpObject.getSuggestion() ); 
  return warning; 
  } 
  
  private void logResult( Long time, Long value, Long lastValue, Long lastTime, 
  boolean doWarn ) 
     { 
  StringBuffer buf = new StringBuffer(); 
  buf.append( "OID=" ).append( snmpObject.getOid() ); 
  buf.append( ",time=" ).append( time ); 
  buf.append( ",value=" ).append( value ); 
  buf.append( ",lastTime=" ).append( snmpObject.getLastTime() ); 
  buf.append( ",lastValue=" ).append( snmpObject.getLastValue() ); 
  buf.append( "\n\t" ).append( snmpObject.getWarnExpr() ).append( "=" ) 
  .append( doWarn ); 
  
  if ( snmpObject.getWarnExpr().indexOf( "(value-lastValue)/(time-lastTime)" ) > -1 ) 
     { 
  buf.append( "\n\t(value-lastValue)/(time-lastTime)=" ).append( 
  ((value - lastValue) / (time - lastTime)) ); 
  } 
  
  log.debug( buf.toString() ); 
  } 
  
   /** *//** 
  * 如果参数是以"bsh://"开头则通过BSH计算一个字符串表达式,返回最后结果; 否则直接返回。 
  * 表达式参数包括value,lastValue,time,lastTime,例如: 
  * bsh://"端口45流量大于800M:"+((value-lastValue)/(time-lastTime)*8/1000000)+"M" 
  * 
  * @param msgExpr 
  *          字符串表达式 
  * @return String 
  */ 
  private static String getBshWarnMsg( String msgExpr, Long value, 
  Long lastValue, Long time, Long lastTime ) 
     { 
  if ( msgExpr == null || !msgExpr.startsWith( BSH ) ) 
  return msgExpr; 
  
  msgExpr = msgExpr.substring( BSH.length() ); 
  try 
     { 
  Interpreter bsh = new Interpreter(); 
  
  bsh.set( "value", value ); 
  bsh.set( "time", time ); 
  bsh.set( "lastValue", lastValue ); 
  bsh.set( "lastTime", lastTime ); 
  
  // 执行bsh脚本,返回实际的告警信息 
  msgExpr = (String) bsh.eval( msgExpr ); 
  } 
  catch ( EvalError ex ) 
     { 
  log.warn( ex.getMessage() ); 
  } 
  
  return msgExpr; 
  } 
  }   
	    
    
评论:
 
	
			
		
			
				
					# 非常好,获益良多  回复  更多评论re: 网络设备主动告警系统之snmp告警的实现  
				Posted @ 2007-01-18 12:56
				 
 
				
					# 很好啊 非常好  回复  更多评论re: 网络设备主动告警系统之snmp告警的实现  
				Posted @ 2007-05-21 23:01
				 
 
				
					# 恩 获益匪浅  回复  更多评论re: 网络设备主动告警系统之snmp告警的实现[未登录]  
				Posted @ 2007-05-25 23:52
				 
 
				
					# 真的太好了。受益匪浅啊re: 网络设备主动告警系统之snmp告警的实现  
				Posted @ 2007-09-27 17:10
				 回复  更多评论
 
 
				
					# 狂顶,太好了,恩人啊!  回复  更多评论re: 网络设备主动告警系统之snmp告警的实现  
				Posted @ 2007-10-18 09:27
				 
 
				
					# 太好了re: 网络设备主动告警系统之snmp告警的实现  
				Posted @ 2007-11-07 15:32
				 网络设备所有的告警能实现吗?
 要是有能贴出来的话就更好了
 谢谢  回复  更多评论
 
 
				
					# 把下面这些代码也贴出来吧,否则不能运行啊re: 网络设备主动告警系统之snmp告警的实现   
				Posted @ 2008-10-13 13:10
				 import com.kelefa.common.util.HibernateUtil;
 import com.kelefa.warnlet.dao.WarningDAO;
 import com.kelefa.warnlet.interpreter.SimpleInterpreter;
 import com.kelefa.warnlet.snmp.Poller;
 import com.kelefa.warnlet.vo.Device;
 import com.kelefa.warnlet.vo.SnmpObject;
 import com.kelefa.warnlet.vo.Warning;
 
 
 回复  更多评论
 
 
   |