package org.openstack.atlas.util.snmp; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openstack.atlas.util.common.VerboseLogger; import org.openstack.atlas.util.snmp.exceptions.StingraySnmpGeneralException; import org.openstack.atlas.util.snmp.exceptions.StingraySnmpObjectNotFoundException; import org.openstack.atlas.util.snmp.exceptions.StingraySnmpRetryExceededException; import org.openstack.atlas.util.snmp.exceptions.StingraySnmpSetupException; import org.snmp4j.CommunityTarget; import org.snmp4j.PDU; import org.snmp4j.Snmp; import org.snmp4j.TransportMapping; import org.snmp4j.event.ResponseEvent; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.smi.*; import org.snmp4j.transport.DefaultUdpTransportMapping; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; public class StingraySnmpClient { public static long getTimeout() { return timeout; } public static void setTimeout(long aTimeout) { timeout = aTimeout; } private int nonRepeaters = 0; private int maxRepetitions = 1000; private String address; private String port = StingraySnmpConstants.PORT; private String community = StingraySnmpConstants.COMMUNITY; private long reportUdpCountEveryNMilliSeconds = 1000; private int maxRetrys = 13; private int version = SnmpConstants.version2c; private static final Random rnd = new Random(); private static final Pattern dotSplitter = Pattern.compile("\\."); private static final VerboseLogger vlog = new VerboseLogger(StingraySnmpClient.class); private static final Log LOG = LogFactory.getLog(StingraySnmpClient.class); private static int requestId; private static long timeout = 5000; static { requestId = Math.abs(rnd.nextInt()); } public static Random getRnd() { return rnd; } public StingraySnmpClient() { } public StingraySnmpClient(String address) { this(address, "1161"); } public StingraySnmpClient(String address, String port) { this(address, port, "public"); } public StingraySnmpClient(String address, String port, String community) { this.address = address; this.port = port; this.community = community; } @Override public String toString() { return "StringraySnmpClient{address=" + address + ", port=" + port + ", community=" + community + ", maxRetries=" + maxRetrys + ", maxRepetitions=" + maxRepetitions + ", nonRepeaters=" + nonRepeaters + ", curRequestId=" + getRequestId() + ", timeOut=" + timeout + "}"; } public synchronized static int incRequestId() { requestId = (requestId + 1) % Integer.MAX_VALUE; return requestId; } public Map<String, Long> getLongOidVals(String oid) throws StingraySnmpSetupException, StingraySnmpGeneralException { vlog.printf("In call getLongOidVals(%s)", oid); Map<String, Long> oidMap = new HashMap<String, Long>(); List<VariableBinding> bindings = getBulkOidBindingList(oid); for (VariableBinding vb : bindings) { String vsName = getVirtualServerNameFromOid(oid, vb.getOid().toString()); oidMap.put(vsName, new Long(vb.getVariable().toLong())); } return oidMap; } public Map<String, RawSnmpUsage> getSnmpUsage() throws StingraySnmpSetupException, StingraySnmpRetryExceededException, StingraySnmpGeneralException { vlog.printf("in call to getSnmpUsage()"); Map<String, RawSnmpUsage> rawSnmpMap = new HashMap<String, RawSnmpUsage>(); List<VariableBinding> bindings; // Fetch Current Connections bindings = getBulkOidBindingList(OIDConstants.VS_CURRENT_CONNECTIONS); for (VariableBinding vb : bindings) { String vsName = getVirtualServerNameFromOid(OIDConstants.VS_CURRENT_CONNECTIONS, vb.getOid().toString()); if (!rawSnmpMap.containsKey(vsName)) { RawSnmpUsage entry = new RawSnmpUsage(); entry.setVsName(vsName); rawSnmpMap.put(vsName, entry); } rawSnmpMap.get(vsName).setConcurrentConnections(vb.getVariable().toLong()); } // Fetch BytesIn In bindings = getBulkOidBindingList(OIDConstants.VS_BYTES_IN); for (VariableBinding vb : bindings) { String vsName = getVirtualServerNameFromOid(OIDConstants.VS_BYTES_IN, vb.getOid().toString()); if (!rawSnmpMap.containsKey(vsName)) { RawSnmpUsage entry = new RawSnmpUsage(); entry.setVsName(vsName); rawSnmpMap.put(vsName, entry); } rawSnmpMap.get(vsName).setBytesIn(vb.getVariable().toLong()); } // Fetch Bytes out bindings = getBulkOidBindingList(OIDConstants.VS_BYTES_OUT); for (VariableBinding vb : bindings) { String vsName = getVirtualServerNameFromOid(OIDConstants.VS_BYTES_OUT, vb.getOid().toString()); if (!rawSnmpMap.containsKey(vsName)) { RawSnmpUsage entry = new RawSnmpUsage(); entry.setVsName(vsName); rawSnmpMap.put(vsName, entry); } rawSnmpMap.get(vsName).setBytesOut(vb.getVariable().toLong()); } return rawSnmpMap; } public long getBytesIn(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getLongValueForVirtualServer(vsName, OIDConstants.VS_BYTES_IN, zeroOnNotFound, negativeOneOnNotFoundException); } public long getBytesOut(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getLongValueForVirtualServer(vsName, OIDConstants.VS_BYTES_OUT, zeroOnNotFound, negativeOneOnNotFoundException); } public long getConcurrentConnections(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getLongValueForVirtualServer(vsName, OIDConstants.VS_CURRENT_CONNECTIONS, zeroOnNotFound, negativeOneOnNotFoundException); } public int getMaxConnections(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_MAX_CONNECTIONS, zeroOnNotFound, negativeOneOnNotFoundException); } public int getConnectTimedOut(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_CONNECT_TIMED_OUT, zeroOnNotFound, negativeOneOnNotFoundException); } public int getDataTimedOut(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_DATA_TIMED_OUT, zeroOnNotFound, negativeOneOnNotFoundException); } public int getKeepAliveTimedOut(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_KEEPALIVE_TIMED_OUT, zeroOnNotFound, negativeOneOnNotFoundException); } public int getConnectionErrors(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_CONNECTION_ERRORS, zeroOnNotFound, negativeOneOnNotFoundException); } public int getConnectionFailures(String vsName, boolean zeroOnNotFound, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getIntegerValueForVirtualServer(vsName, OIDConstants.VS_CONNECTION_FAILURES, zeroOnNotFound, negativeOneOnNotFoundException); } public int getIntegerValueForVirtualServer(String vsName, String baseOid, boolean zeroOnNotFoundException, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getValueForVirtualServer(vsName, baseOid, zeroOnNotFoundException, negativeOneOnNotFoundException).toInt(); } public long getLongValueForVirtualServer(String vsName, String baseOid, boolean zeroOnNotFoundException, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { return getValueForVirtualServer(vsName, baseOid, zeroOnNotFoundException, negativeOneOnNotFoundException).toLong(); } public org.snmp4j.smi.Variable getValueForVirtualServer(String vsName, String baseOid, boolean zeroOnNotFoundException, boolean negativeOneOnNotFoundException) throws StingraySnmpSetupException, StingraySnmpObjectNotFoundException, StingraySnmpGeneralException { String searchOid = getOidFromVirtualServerName(baseOid, vsName); PDU req = new PDU(); req.add(new VariableBinding(new OID(searchOid))); req.setType(PDU.GET); req.setRequestID(new Integer32(incRequestId())); UdpAddress udpAddr = new UdpAddress(address + "/" + port); CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString(community)); target.setVersion(version); target.setAddress(udpAddr); target.setTimeout(timeout); TransportMapping transport; try { transport = new DefaultUdpTransportMapping(); } catch (IOException ex) { String msg = String.format("Error setting up connection to %s/%s for SNMP connections for oid value %s for vs %s", address, port, baseOid, vsName); LOG.error(msg, ex); throw new StingraySnmpSetupException(msg, ex); } Snmp snmp = new Snmp(transport); try { transport.listen(); } catch (IOException ex) { String msg = String.format("Error listening on udp port %s/%s for SNMP connections for oid value %s for vs %s", address, port, baseOid, vsName); LOG.error(msg, ex); closeConnection(snmp, null); throw new StingraySnmpSetupException(msg, ex); } VariableBinding vb = null; ResponseEvent respEvent = null; try { respEvent = snmp.get(req, target); } catch (IOException ex) { closeConnection(snmp, transport); String msg = String.format("Error getting OID value %s for vs %s at SNMP server %s/%s", baseOid, vsName, address, port); LOG.error(msg, ex); closeConnection(snmp, transport); throw new StingraySnmpGeneralException(msg, ex); } if (respEvent == null) { String msg = String.format("Error response for OID %s for vs %s was null on SNMP server %s/%s", baseOid, vsName, address, port); LOG.error(msg); closeConnection(snmp, transport); } PDU resp; resp = respEvent.getResponse(); if (resp == null) { String msg = String.format("Error responseEvent for OID %s for vs %s was null on SNMP server %s/%s", baseOid, vsName, address, port); LOG.error(msg); closeConnection(snmp, transport); throw new StingraySnmpGeneralException(msg); } int respSize = resp.size(); if (respSize < 1) { String msg = String.format("Error response binding size for for OID %s for vs %s was %d on SNMP server", baseOid, vsName, respSize, address, port); LOG.error(msg); closeConnection(snmp, transport); throw new StingraySnmpGeneralException(msg); } vb = resp.get(0); String vbOid = vb.getOid().toString(); closeConnection(snmp, transport); Class vbClass = vb.getVariable().getClass(); if (vbClass.equals(Null.class)) { if (zeroOnNotFoundException) { return new Counter64(0L); } if (negativeOneOnNotFoundException) { return new Counter64(-1L); } throw new StingraySnmpObjectNotFoundException(); } return vb.getVariable(); } public List<VariableBinding> getBulkOidBindingList(String oid) throws StingraySnmpSetupException, StingraySnmpGeneralException { vlog.printf("in call getBulkOidBindingList(%s) for %s", oid, getConnectionName()); List<VariableBinding> bindings = new ArrayList<VariableBinding>(); String startOID = oid; String currOID = startOID; int totalItems = 0; int matchedItems = 0; double currMaxReps = maxRepetitions; boolean finished = false; while (!finished) { vlog.printf("total items = %d matched items= %d from CONTINUING", totalItems, matchedItems, getConnectionName(oid)); PDU req = new PDU(); req.add(new VariableBinding(new OID(currOID))); req.setType(PDU.GETBULK); req.setNonRepeaters(nonRepeaters); req.setMaxRepetitions((int) currMaxReps); req.setRequestID(new Integer32(incRequestId())); UdpAddress udpAddr; try { udpAddr = new UdpAddress(address + "/" + port); } catch (Exception ex) { String msg = String.format("Invalid udpAddress specification %s/%s", address, port); LOG.error(msg, ex); throw new StingraySnmpSetupException(msg, ex); } CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString(community)); target.setVersion(version); target.setTimeout(timeout); target.setAddress(udpAddr); TransportMapping transport; try { transport = new DefaultUdpTransportMapping(); } catch (IOException ex) { String msg = String.format("Error setting up snmp connection to %s", getConnectionName()); LOG.error(msg, ex); throw new StingraySnmpSetupException(msg, ex); } Snmp snmp = new Snmp(transport); try { transport.listen(); } catch (IOException ex) { String msg = String.format("Error listening on local udp port for snmp connection %s/%s", address, port); LOG.error(msg, ex); closeConnection(snmp, transport); throw new StingraySnmpSetupException(msg, ex); } VariableBinding vb = null; ResponseEvent respEvent = null; try { respEvent = snmp.getBulk(req, target); } catch (IOException ex) { String msg = String.format("Error getting bulk request from snmp server %s", getConnectionName(oid)); LOG.error(msg, ex); closeConnection(snmp, transport); throw new StingraySnmpGeneralException(msg, ex); } PDU respPdu = respEvent.getResponse(); if (respPdu == null) { String msg = String.format("Error fetching bulk response reducing maxRepetitions from %d to %d for %s", (int) currMaxReps, (int) (currMaxReps * 0.75), getConnectionName(oid)); currMaxReps *= 0.75; LOG.warn(msg); if (currMaxReps <= 1.0) { String exMsg = String.format("Error maxRepetitions was shrunk to 1 to snmp server %s", getConnectionName(oid)); LOG.error(exMsg); closeConnection(snmp, transport); throw new StingraySnmpRetryExceededException(exMsg); } closeConnection(snmp, transport); continue; } int respSize = respPdu.size(); for (int i = 0; i < respSize; i++) { totalItems++; vb = respPdu.get(i); String vbOid = vb.getOid().toString(); if (!vbOid.startsWith(oid + ".")) { finished = true; continue; } matchedItems++; bindings.add(vb); currOID = vbOid; } closeConnection(snmp, transport); if (respSize < currMaxReps) { break; // This was the last set of entries. } } vlog.printf("total items = %d matched items= %d for %s FINISHED", totalItems, matchedItems, getConnectionName(oid)); return bindings; } private void closeConnection(Snmp snmp, TransportMapping transport) { try { snmp.close(); } catch (Exception ex) { LOG.warn(String.format("Warning unable to close snmp connection on %s", getConnectionName())); } try { transport.close(); } catch (Exception ex) { LOG.warn(String.format("Warning unable to close transport on %s", getConnectionName())); } } public static String getOidFromVirtualServerName(String baseOid, String vsName) { StringBuilder sb = new StringBuilder(); char[] vsChars = vsName.toCharArray(); sb.append(baseOid); sb.append(".").append(vsChars.length); for (int i = 0; i < vsChars.length; i++) { sb.append(".").append((int) vsChars[i]); } return sb.toString(); } public static String getVirtualServerNameFromOid(String baseOid, String oid) { StringBuilder sb = new StringBuilder(); String[] baseNums = dotSplitter.split(baseOid); String[] nums = dotSplitter.split(oid); for (int i = baseNums.length + 1; i < nums.length; i++) { sb.append((char) Integer.parseInt(nums[i])); } return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof StingraySnmpClient)) { return false; } StingraySnmpClient that = (StingraySnmpClient) o; if (maxRepetitions != that.maxRepetitions) { return false; } if (maxRetrys != that.maxRetrys) { return false; } if (nonRepeaters != that.nonRepeaters) { return false; } if (reportUdpCountEveryNMilliSeconds != that.reportUdpCountEveryNMilliSeconds) { return false; } if (version != that.version) { return false; } if (address != null ? !address.equals(that.address) : that.address != null) { return false; } if (community != null ? !community.equals(that.community) : that.community != null) { return false; } if (port != null ? !port.equals(that.port) : that.port != null) { return false; } return true; } @Override public int hashCode() { int result = nonRepeaters; result = 31 * result + maxRepetitions; result = 31 * result + (address != null ? address.hashCode() : 0); result = 31 * result + (port != null ? port.hashCode() : 0); result = 31 * result + (community != null ? community.hashCode() : 0); result = 31 * result + (int) (reportUdpCountEveryNMilliSeconds ^ (reportUdpCountEveryNMilliSeconds >>> 32)); result = 31 * result + maxRetrys; result = 31 * result + version; return result; } public String getAddress() { return address; } public String getPort() { return port; } public String getCommunity() { return community; } public void setAddress(String address) { this.address = address; } public void setPort(String port) { this.port = port; } public void setCommunity(String community) { this.community = community; } public static void nop() { } public static int getRequestId() { return requestId; } public int getMaxRetrys() { return maxRetrys; } public void setMaxRetrys(int maxRetrys) { this.maxRetrys = maxRetrys; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public synchronized static void setRequestId(int aRequestId) { requestId = aRequestId; } public long getReportUdpCountEveryNMilliSeconds() { return reportUdpCountEveryNMilliSeconds; } public void setReportUdpCountEveryNMilliSeconds(long reportUdpCountEveryNMilliSeconds) { this.reportUdpCountEveryNMilliSeconds = reportUdpCountEveryNMilliSeconds; } public int getNonRepeaters() { return nonRepeaters; } public void setNonRepeaters(int nonRepeaters) { this.nonRepeaters = nonRepeaters; } public int getMaxRepetitions() { return maxRepetitions; } public void setMaxRepetitions(int maxRepetitions) { this.maxRepetitions = maxRepetitions; } private String getConnectionName(String oid) { StringBuilder sb = new StringBuilder(); sb.append(address).append("/").append(port); if (oid != null) { sb.append(" for oid ").append(oid); } return sb.toString(); } private String getConnectionName() { return getConnectionName(null); } }