Posts Tagged snmp

SNMP, Java and power switches

There is a  ‘power switch service’ at our datacenter.

Over many years we used the open source project Westhawk’s lightweight SNMP stack for this service. But over the years, the requests to our power switches were constantly growing. Depending on  that, we got more and more problems with Westhawks SNMP stack. It hungs from time to time not traceable, which leads to the conclusion that threads weren’t be finished clearly when exceptions were thrown. IPv6 gets more important and it is not officially supported by this library.  In fact it seems the project is dead. The last file release was 2009. So we decided to kick it.

Looking around for a better open source alternative I’ve found SNMP4J. The documentation promised what I was looking for. But after I tried to write some testing codes for our power switches, I’ve figured out, that it does not deliver what it promises. The supplied examples didn’t work as expected. E.g. the request of multiple ports of one switch needs very long or it doesn’t work at all for SNMPv1, simple SET and PUT commands didn’t work for SNMPv1 and other things.  I lost more than an entire day, – made my day, you know!

My last chance was to take a commercial SNMP API. One of my favorites I’ve found  is outstanding in terms of documentation, example code and last but not least the structure of the API! The Java SNMP API off ireasoning. All examples are running out-of-the-box and they are well documented. I was able to build a test code for our power switch environment in less than 30 minutes! Pure tech porn!

First of all: The ‘power switch service’, where the library does its’ job, has been running as expected since weeks.
The source code above demonstrates the easy handling of the API.

Beware! The code isn’t executable. The error handling has to be implemented!

/**
 * LGPL
 */
public class SnmpIreasoningApi {
	private static final int sessionRetries = 1;
	private static final int sessionTimeout = 2000;
	private InetAddress switchIP = null;
	private SnmpSession session;
	public enum SnmpStatus { ON, OFF };

	static {
		// init logging for the ireasoning-snmp-api
		//com.ireasoning.util.Logger.setUseLog4j(true);
		//com.ireasoning.util.Logger.setLevel(com.ireasoning.util.Logger.INFO);
	}

	/**
	 * Initializes snmp for v1 or v2c.
	 *
	 * @param version Should only be {@link SnmpConst#SNMPV1} or {@link SnmpConst#SNMPV2}.
	 * @param deviceIp
	 * @param community
	 * @throws SnmpException If the underlying snmp framework couldn't connect to 'deviceIp'.
	 * @throws IllegalArgumentException If 'version' isn't {@link SnmpConst#SNMPV1} or {@link SnmpConst#SNMPV2} or if the
	 *             connection params are invalid.
	 */
	public SnmpIreasoningApi(int version, String deviceIp, String community) {
		if(version == SnmpConst.SNMPV3)
			throw new IllegalArgumentException("Version 3 isn't allowed in this context!");
		switchIP = buildInetAddress(deviceIp);
		try {
			session = new SnmpSession(switchIP.getHostAddress(), SnmpConst.DEFAULT_SNMP_AGENT_PORT, community,
					community, version);
			session.setTimeout(sessionTimeout);
		} catch(IOException e) {
			// error handling: connection problems with ip
		} catch(IllegalArgumentException e) {
			// error handling: invalid connection params
		}
		session.setRetries(sessionRetries);
	}

	/**
	 * Initialize snmp for v3.
	 *
	 * @param deviceIp
	 * @param userName
	 * @param userPrivacyPassphrase
	 * @param userAuthenticationPassword
	 * @throws SnmpException If the connection parameters arn't valid or the connection to the agent couldn't
	 *             established.
	 * @throws IllegalArgumentException If 'deviceIp' isn't a valid ip address.
	 */
	public SnmpIreasoningApi(String deviceIp, String userName, String userPrivacyPassphrase, String userAuthenticationPassword) {
		switchIP = buildInetAddress(deviceIp);
		try {
			session = new SnmpSession(switchIP.getHostAddress(), SnmpConst.DEFAULT_SNMP_AGENT_PORT, null, null,
					SnmpConst.SNMPV3);
			session.setTimeout(sessionTimeout);
		} catch(IOException e) {
			// error handling: connection problems with ip
		} catch(IllegalArgumentException e) {
			// error handling: invalid connection params
		}
		session.setRetries(sessionRetries);
		session.setV3Params(
			userName,
			SnmpConst.MD5,
			new SnmpOctetString(userAuthenticationPassword).toString(),
			SnmpConst.DES,
			new SnmpOctetString(userPrivacyPassphrase).toString());
		session.getSnmpV3User().setSecurityLevel(UsmUser.AUTH_PRIV);
	}

	public SnmpStatus getPowerStatus(String fullOID, String verifyValue) {
		SnmpOID oid = new SnmpOID(fullOID);
		SnmpPdu retPdu = null;
		try {
			retPdu = session.snmpGetRequest(oid);
		} catch(IOException e) {
			// error handling: problems with oid
		}
		checkReponsePdu(retPdu, fullOID);
		String retVal = retPdu.getFirstVarBind().getValue().toString();
		return verifyValue.equals(retVal) ? SnmpStatus.ON : SnmpStatus.OFF;
	}

	public Map getBulkPowerStatus(String oID, int portMin, int portMax, String verifyValue) throws SnmpException {
		if(portMax == 0 || portMax 			throw new IllegalArgumentException("'portMax' isn't valid");

		// build the full oIDs
		Map oIDsPort = new HashMap(portMax - portMin + 1);
		for(int port = portMin; port 			String tmpOid = String.format("%s.%d", oID, port);
			oIDsPort.put(new SnmpOID(tmpOid), port);
		}

		// send the request
		SnmpPdu retPdu = null;
		try {
			// TODO using a real bulk vor v3
			retPdu = session.snmpGetRequest(oIDsPort.keySet().toArray(new SnmpOID[0]));
		} catch(IOException e) {
			// error handling: problems with oid
		}

		checkReponsePdu(retPdu, oID);

		// build the response
		Map result = new HashMap();
		for(SnmpVarBind vb : retPdu.getVarBinds()) {
			SnmpOID fullOID = vb.getName();
			String value = vb.getValue().toString();
			SnmpStatus status = (verifyValue.equals(value)) ? SnmpStatus.ON : SnmpStatus.OFF;
			int port = oIDsPort.get(fullOID);
			result.put(port, status);
		}
		return result;
	}

	public void action(String fullOID, String value) throws SnmpException {
		SnmpOID oid = new SnmpOID(fullOID);
		SnmpDataType val = new SnmpInt(value);
		SnmpVarBind vb = new SnmpVarBind(oid, val);

		// send the request
		SnmpPdu retPdu = null;
		try {
			retPdu = session.snmpSetRequest(vb);
		} catch(IOException e) {
			// throw new SnmpException(switchIP, fullOID, e);
		}

		checkReponsePdu(retPdu, fullOID);

		System.out.println(String.format("*** action (%s): %s successful set to %s",
			switchIP.getHostAddress(), oid, val.toString()));
	}

	/**
	 * Unconditionally clean up. It's very important to call this method at the end of each usage of this object.
	 */
	public void close() {
		try {
			session.close();
		} catch(Exception e) {
			System.out.println(String.format("Error while closing the session  for %s: %s", switchIP, e.getMessage()));
		}
	}

	private InetAddress buildInetAddress(String ipStr) throws IllegalArgumentException {
		try {
			return InetAddress.getByName(ipStr);
		} catch(UnknownHostException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * Analyzes the 'return pdu' with respect to errors.
	 *
	 * @param pdu The pdu to analyze.
	 * @param oid The OID, just for logging.
	 * @throws SnmpException If an error was found.
	 */
	private void checkReponsePdu(SnmpPdu pdu, String oid) {
		String dest = String.format("%s:%d", pdu.getDestinationAddress(), pdu.getDestinationPort());
		if(pdu.getErrorIndex() == 0) {
			System.out.println(String.format("check response (%s): successful", dest));
			return;
		}
		String errorMsg = String.format(
			"error-index: %d, error-status: %d, error-string: %s",
			pdu.getErrorIndex(),
			pdu.getErrorStatus(),
			pdu.getErrorString());
		// error handling for a failed action on 'dest' with oid
	}

}

, ,

Leave a comment