/* * Copyright (c) 2015, Paessler AG <support@paessler.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @Author JR Andreassen */ package com.paessler.prtg.jmx.sensors; import java.util.ArrayList; import java.util.List; import com.google.gson.JsonObject; import com.paessler.prtg.jmx.channels.Channel; import com.paessler.prtg.jmx.channels.FloatChannel; import com.paessler.prtg.jmx.channels.LongChannel; import com.paessler.prtg.jmx.definitions.SensorConstants; import com.paessler.prtg.jmx.definitions.PingSensorDefinition; import com.paessler.prtg.jmx.responses.DataResponse; // ---------------------- import org.icmp4j.IcmpPingUtil; import org.icmp4j.IcmpPingRequest; import org.icmp4j.IcmpPingResponse; public class PingSensor extends RemoteSensor<String> { //---------------------------------------------------------------------- public PingSensor(){ setDefinition(new PingSensorDefinition()); setKind(PingSensorDefinition.KIND); setSensorName("PingSensor"); } //---------------------------------------------------------------------- public PingSensor(PingSensor tocopy){ super(tocopy); } //---------------------------------------------------------------------- @Override public Sensor copy(){ return new PingSensor(this); } //---------------------------------------------------------------------- protected int pingCount = 5; public int getPingCount() { return pingCount; } public void setPingCount(int pingCount) { this.pingCount = pingCount; } //---------------------------------------------------------------------- protected List<IcmpPingResponse> doPings(){ List<IcmpPingResponse> retVal = null; // try { final IcmpPingRequest request = IcmpPingUtil.createIcmpPingRequest (); request.setHost (getHost()); request.setTimeout(getTimeout()); retVal = new ArrayList<IcmpPingResponse>(getPingCount()); for (int count = 1; count <= getPingCount(); count ++) { // delegate retVal.add(IcmpPingUtil.executePingRequest (request)); Thread.yield(); } // } // catch (final Throwable t){ // log // if(get) // t.printStackTrace (); // } return retVal; } //---------------------------------------------------------------------- //---------------------------------------------------------------------- public class PingStats { public long min = Long.MAX_VALUE; public float avg = 0.0f; public long max = Long.MIN_VALUE; public float mDev = 0.0f; public long lossPercent = 0; private int lossCnt = 0; private double sumSq = 0.0; private int cnt = 0; protected void update(IcmpPingResponse val){ if(val.getSuccessFlag()){ int tmp =val.getRtt(); min = (min < tmp ? min : tmp); avg += tmp; max = (max > tmp ? max : tmp); sumSq += tmp*tmp; } else {++lossCnt;} ++cnt; } //----------------------------------------- protected void calcFinal(){ // http://serverfault.com/questions/333116/what-does-mdev-mean-in-ping8 double tmp = avg / (cnt+lossCnt); mDev = (float) Math.sqrt( (sumSq / (cnt+lossCnt)) - (tmp * tmp)); sumSq += 0.0f; avg = -1; if((cnt - lossCnt) > 0){ avg /= cnt - lossCnt; } lossPercent = 100*(lossCnt/cnt); } } //---------------------------------------------------------------------- //---------------------------------------------------------------------- protected PingStats calcStats(List<IcmpPingResponse> pingResponses){ PingStats retVal = new PingStats(); for(IcmpPingResponse curr : pingResponses){ retVal.update(curr); } retVal.calcFinal(); return retVal; } //---------------------------------------------------------------------- protected DataResponse calcStats(DataResponse response, List<IcmpPingResponse> pingResponses){ PingStats stats = calcStats(pingResponses); int errcnt = 0; LongChannel lchannel = new LongChannel("Ping Time Min", Channel.UNIT_STR_TRESPONSE, stats.min); response.addChannel(lchannel); if(stats.max < stats.min) {lchannel.setWarning(1); errcnt++;} FloatChannel fchannel = new FloatChannel("Ping Time Avg", Channel.UNIT_STR_TRESPONSE, stats.avg); // if(stats.avg < 0) {lchannel.setWarning(1); errcnt++;} response.addChannel(fchannel); lchannel = new LongChannel("Ping Time Max", Channel.UNIT_STR_TRESPONSE, stats.max); if(stats.max < stats.min) {lchannel.setWarning(1); errcnt++;} response.addChannel(lchannel); fchannel = new FloatChannel("Ping Time MDEV", Channel.UNIT_STR_TRESPONSE, stats.mDev); response.addChannel(fchannel); lchannel = new LongChannel("Packet Loss", Channel.UNIT_STR_PERCENT, stats.lossPercent); response.addChannel(lchannel); if(stats.lossCnt > 0){ String errmsg = stats.lossPercent+" % Packet Loss"; lchannel.setWarning(1); response.setMessage(errmsg); if(stats.lossPercent > 99 ) {response = getErrorResponse("Error", -1, errmsg);} } return response; } // {'sensorid': xxx, 'message': 'OK', 'channel': [ // {'kind': 'TimeResponse', 'name': 'Ping Time Min', 'value': 5.101, 'mode': 'float'}, // {'kind': 'TimeResponse', 'name': 'Ping Time Avg', 'value': 5.139, 'mode': 'float'}, // {'kind': 'TimeResponse', 'name': 'Ping Time Max', 'value': 5.193, 'mode': 'float'}, // {'kind': 'TimeResponse', 'name': 'Ping Time MDEV', 'value': 0.037, 'mode': 'float'}, // {'kind': 'Percent', 'name': 'Packet Loss', 'value': 0, 'mode': 'integer'} //---------------------------------------------------------------------- /* * (non-Javadoc) * @see com.paessler.prtg.jmx.sensors.Sensor#go() */ @Override public DataResponse go() { DataResponse response = new DataResponse(getSensorid(), "Ping"); // TODO Auto-generated method stub // handle exceptions try { List<IcmpPingResponse> pingResponses = doPings(); response = calcStats(response, pingResponses); } catch (Exception e) { response = getErrorResponse("Exception", -1, e.getLocalizedMessage()); } return response; } //---------------------------------------------------------------------- @Override public void loadFromJson(JsonObject json) throws Exception { // Delegate super.loadFromJson(json); // Local parameters setPingCount(getJsonElementInt(json, SensorConstants.COUNT, getPingCount())); } //---------------------------------------------------------------------- }