Date created: Tuesday, December 31, 2013 9:56:19 AM. Last modified: Saturday, April 1, 2017 8:32:11 AM

95th Percentile Calculation

The following PHP script calculates the 95th percentile from a typical RRD file using two DS' (in this example, 'traffic_in' and 'traffic_out') and then generates a graph using rrdtools and parses the 95th percentile value to the graph as a COMMENT and HRULE. It's a classic ISP joint in & out 95th calculation;

95th_one.php.txt

<?php 

$rrd="/var/lib/cacti/rra/router1_traffic_in_2078.rrd";
$start="1383264000";
$end="1385855700";
$rrdtmpfile="/tmp/rrd_values";


    // Blank out a file and dump this months values from rrd file
    exec("echo \"\" > $rrdtmpfile");
    exec("rrdtool fetch -s $start -e $end $rrd MAX > $rrdtmpfile");
    exec("sed -i 1,+1d $rrdtmpfile");
    // remove the titles from the rrd output
    // Blank out a new file for sorting into max values
    exec("echo \"\" > $rrdtmpfile"."_max");
    // Open file for writing and sort it
    $handle_in = fopen("$rrdtmpfile", "r");
    $handle_out = fopen("$rrdtmpfile"."_max", "w");
    $lastin = 0.0;
    $innow = 0;
    $lastout = 0.0;
    $outnow = 0;
    $average = 0;
    $samples = 0;

    while(!feof($handle_in)) {
      $line = fgets($handle_in);
      $values = explode(" ", $line);
      if($values[1]=="nan") {
        $innow = $lastin;
      } else {
        $innow = sprintf("%f", $values[1]);
        $lastin = $innow;
      }
      if($values[2]=="nan") {
        $outnow = $lastout;
      } else {
        $outnow = sprintf("%f", $values[2]);
        $lastout = $outnow;
      }
      if($innow >= $outnow) {
        fwrite($handle_out, $innow."\n");
        $average=$average+$innow;
      } else {
        fwrite($handle_out, $outnow."\n");
        $average=$average+$outnow;
      }
      $samples=$samples+1;
    }
    fclose($handle_in);
    fclose($handle_out);

    //Calculate an average
    $average=$average/$samples;

    // Now we have filled ./rrd_values_max with the MAX value from each time period,
    // sort them in desending order
    exec("sort -g -r $rrdtmpfile"."_max > $rrdtmpfile"."_max_sorted");
    // Remove any blank lines (typically one on the end)
    exec("sed '/^$/d' $rrdtmpfile"."_max_sorted > $rrdtmpfile"."_max_sorted_trim");
    // Get the highest/max value from the first line of the file
    $max = shell_exec("head -n 1 $rrdtmpfile"."_max_sorted_trim");
    // Ge the lowest/minimum value from the last line of the file
    $min = shell_exec("tail -n 1 $rrdtmpfile"."_max_sorted_trim");
    // Count the number of lines of rrd values
    $linecount = shell_exec("grep -c ^ $rrdtmpfile"."_max_sorted_trim");
    // Get 5 percent of the line count value, round it down with floor()
    $fivepc = floor(($linecount/100)*5);
    // Now read the value from the line, 5% down from the top of the line count of the file
    $ninetyfifth = shell_exec("awk -v n1=$fivepc 'NR==n1' $rrdtmpfile"."_max_sorted_trim");
    $ninetyfifth = trim($ninetyfifth);
    // Round it up
    //$ninetyfifth = ceil($ninetyfifth);
    // Convert to bps,kbps,mbps
    $ninetyfifthbps = ceil(($ninetyfifth*8));
    $ninetyfifthkbps = round(($ninetyfifthbps/1000), 2);
    $ninetyfifthmbps = ($ninetyfifthbps/1000)/1000;

    $rrdcmd = "/usr/bin/rrdtool graph /tmp/php-graph.png ".
                "--imgformat=PNG ".
                "--start=$start ".
                "--end=$end ".
                "--rigid ".
                "--base=1000 ".
                "--height=120 ".
                "--width=500 ".
                "--alt-autoscale-max ".
                "--lower-limit=0 ".
                "COMMENT:\"From ".date("Y-m-d H", $start)."\:".date(i, $start)."\:".date(s, $start).
                " to ".date("Y-m-d H", $end)."\:".date(i, $end)."\:".date(s, $end)."\c\" ".
                "COMMENT:\" \c\" ".
                "--vertical-label='bits per second' ".
                "--slope-mode ".
                "--font TITLE:10: ".
                "--font AXIS:8: ".
                "--font LEGEND:7: ".
                "--font UNIT:8: ".
                "DEF:in=\"$rrd\":traffic_in:MAX ".
                "DEF:out=\"$rrd\":traffic_out:MAX ".
                "VDEF:inbytesavg=in,AVERAGE ".
                "VDEF:outbytesavg=out,AVERAGE ".
                "CDEF:inbytesmod=in,UN,inbytesavg,in,IF ".
                "CDEF:outbytesmod=out,UN,outbytesavg,out,IF ".
                "CDEF:speedin=in,8,* ".
                "CDEF:speedout=out,8,* ".
                "VDEF:intotal=inbytesmod,TOTAL ".
                "VDEF:outtotal=outbytesmod,TOTAL ".
                "AREA:speedin#00CF00FF:\"Inbound\" ".
                "GPRINT:speedin:LAST:\"Current\:%8.2lf %s\" ".
                "GPRINT:speedin:AVERAGE:\"Average\:%8.2lf %s\" ".
                "GPRINT:speedin:MAX:\"Maximum\:%8.2lf %s\"  ".
                "GPRINT:intotal:\"Total In\:%8.2lf %s\" ".
                "COMMENT:\" \c\" ".
                "LINE1:speedout#002A97FF:\"Outbound\" ".
                "GPRINT:speedout:LAST:\"Current\:%8.2lf %s\" ".
                "GPRINT:speedout:AVERAGE:\"Average\:%8.2lf %s\" ".
                "GPRINT:speedout:MAX:\"Maximum\:%8.2lf %s\" ".
                "GPRINT:outtotal:\"Total Out\:%8.2lf %s \c\" ".
                "COMMENT:\" \c\" ".
                "HRULE:$ninetyfifthbps#FF0000FF:\"95th Percentile\" ".
                "COMMENT:\"($ninetyfifthmbps mbit in+out)\c\" ".
                "PRINT:intotal:\"%8.2lf%SBs In\" ".
                "PRINT:outtotal:\"%8.2lf%SBs Out\" ".
                "PRINT:speedin:AVERAGE:\"%8.2lf%sbps In\" ".
                "PRINT:speedout:AVERAGE:\"%8.2lf%sbps Out\" ".
                "PRINT:speedin:MAX:\"%8.2lf%sbps In\" ".
                "PRINT:speedout:MAX:\"%8.2lf%sbps Out\" ";
    exec ($rrdcmd, $cmdout);


    // Clean up - Commment out to see each stage of the calculation
    //exec("rm $rrdtmpfile");
    //exec("rm $rrdtmpfile"."_max");
    //exec("rm $rrdtmpfile"."_max_sorted");
    //exec("rm $rrdtmpfile"."_max_sorted_trim");

?>