Skip to content

Commit

Permalink
Added aggregate config option to Billing 95th percentile calculations (
Browse files Browse the repository at this point in the history
…#10202)

* Added configuration options to aggregate input and output bits before making 95th percentile billing calculations

* Changed aggregate to per-bill instead of global.  Added config options for making aggregate the default selected option.  Refactored out mres() calls in touched files.  Changed to Config::get where appropriate.

* Fixed documentation typo

* Fixed scope of aggregate default config option to be under billing
  • Loading branch information
llarian0 authored and murrant committed May 28, 2019
1 parent 38a6383 commit 9c837be
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 38 deletions.
5 changes: 4 additions & 1 deletion billing-calculate.php
Expand Up @@ -39,8 +39,11 @@

$date_updated = str_replace('-', '', str_replace(':', '', str_replace(' ', '', $check['updated'])));

// Send the current dir_95th to the getRates function so it knows to aggregate or return the max in/out value and highest direction
$dir_95th = $bill['dir_95th'];

if ($period > 0 && $dateto > $date_updated) {
$rate_data = getRates($bill['bill_id'], $datefrom, $dateto);
$rate_data = getRates($bill['bill_id'], $datefrom, $dateto, $dir_95th);
$rate_95th = $rate_data['rate_95th'];
$dir_95th = $rate_data['dir_95th'];
$total_data = $rate_data['total_data'];
Expand Down
20 changes: 19 additions & 1 deletion doc/Extensions/Billing-Module.md
Expand Up @@ -23,7 +23,7 @@ Edit `/etc/cron.d/librenms` and add the following:

Create billing graphs as required.

## Options
## Data Retention

Billing data is stored in the MySQL database, and you may wish to purge the detailed
stats for old data (per-month totals will always be kept). To enable this, add the
Expand All @@ -36,3 +36,21 @@ $config['billing_data_purge'] = 12; // Number of months to retain
Data for the last complete billing cycle will always be retained - only data older than
this by the configured number of months will be removed. This task is performed in the
daily cleanup tasks.

## 95th Percentile Calculation

For 95th Percentile billing, the default behavior is to use the highest of the input
or output 95th Percentile calculation.

To instead use the combined total of inout + output to derive the 95th percentile,
This can be changed on a per bill basis by setting 95th Calculation to "Aggregate".

To change the default option to Aggregate,
add the following the `config.php`:

```php
$config['billing']['95th_default_agg'] = 1; // Set aggregate 95th as default
```

This configuration setting is cosmetic and only changes the default selected option
when adding a new bill
85 changes: 50 additions & 35 deletions includes/billing.php
@@ -1,19 +1,17 @@
<?php

use LibreNMS\Config;


function format_bytes_billing($value)
{
global $config;

return format_number($value, $config['billing']['base']).'B';
return format_number($value, Config::get('billing.base')).'B';
}//end format_bytes_billing()


function format_bytes_billing_short($value)
{
global $config;

return format_number($value, $config['billing']['base'], 2, 3);
return format_number($value, Config::get('billing.base'), 2, 3);
}//end format_bytes_billing_short()


Expand Down Expand Up @@ -75,10 +73,8 @@ function getPredictedUsage($bill_day, $cur_used)

function getValue($host, $port, $id, $inout)
{
global $config;

$oid = 'IF-MIB::ifHC'.$inout.'Octets.'.$id;
$device = dbFetchRow("SELECT * from `devices` WHERE `hostname` = '".mres($host)."' LIMIT 1");
$device = dbFetchRow("SELECT * from `devices` WHERE `hostname` = ? LIMIT 1", array($host));
$value = snmp_get($device, $oid, '-Oqv');

if (!is_numeric($value)) {
Expand Down Expand Up @@ -123,41 +119,55 @@ function getLastMeasurement($bill_id)
return ($return);
}//end getLastMeasurement()

function get95thagg($bill_id, $datefrom, $dateto)
{
$mq_sql = "SELECT count(delta) FROM bill_data WHERE bill_id = ?";
$mq_sql .= " AND timestamp > ? AND timestamp <= ?";
$measurements = dbFetchCell($mq_sql, array($bill_id, $datefrom, $dateto));
$measurement_95th = (round(($measurements / 100 * 95)) - 1);

$q_95_sql = "SELECT (delta / period * 8) AS rate FROM bill_data WHERE bill_id = ?";
$q_95_sql .= " AND timestamp > ? AND timestamp <= ? ORDER BY rate ASC";
$a_95th = dbFetchColumn($q_95_sql, array($bill_id, $datefrom, $dateto));
$m_95th = $a_95th[$measurement_95th];

return (round($m_95th, 2));
}//end get95thagg()


function get95thin($bill_id, $datefrom, $dateto)
function get95thIn($bill_id, $datefrom, $dateto)
{
$mq_sql = "SELECT count(delta) FROM bill_data WHERE bill_id = '".mres($bill_id)."'";
$mq_sql .= " AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."'";
$measurements = dbFetchCell($mq_sql);
$mq_sql = "SELECT count(delta) FROM bill_data WHERE bill_id = ?";
$mq_sql .= " AND timestamp > ? AND timestamp <= ?";
$measurements = dbFetchCell($mq_sql, array($bill_id, $datefrom, $dateto));
$measurement_95th = (round(($measurements / 100 * 95)) - 1);

$q_95_sql = "SELECT (in_delta / period * 8) AS rate FROM bill_data WHERE bill_id = '".mres($bill_id)."'";
$q_95_sql .= " AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."' ORDER BY rate ASC";
$a_95th = dbFetchColumn($q_95_sql);
$q_95_sql = "SELECT (in_delta / period * 8) AS rate FROM bill_data WHERE bill_id = ?";
$q_95_sql .= " AND timestamp > ? AND timestamp <= ? ORDER BY rate ASC";
$a_95th = dbFetchColumn($q_95_sql, array($bill_id, $datefrom, $dateto));
$m_95th = $a_95th[$measurement_95th];

return (round($m_95th, 2));
}//end get95thin()
}//end get95thIn()


function get95thout($bill_id, $datefrom, $dateto)
{
$mq_sql = "SELECT count(delta) FROM bill_data WHERE bill_id = '".mres($bill_id)."'";
$mq_sql .= " AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."'";
$measurements = dbFetchCell($mq_sql);
$mq_sql = "SELECT count(delta) FROM bill_data WHERE bill_id = ?";
$mq_sql .= " AND timestamp > ? AND timestamp <= ?";
$measurements = dbFetchCell($mq_sql, array($bill_id, $datefrom, $dateto));
$measurement_95th = (round(($measurements / 100 * 95)) - 1);

$q_95_sql = "SELECT (out_delta / period * 8) AS rate FROM bill_data WHERE bill_id = '".mres($bill_id)."'";
$q_95_sql .= " AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."' ORDER BY rate ASC";

$a_95th = dbFetchColumn($q_95_sql);
$m_95th = $a_95th[$measurement_95th];
$q_95_sql = "SELECT (out_delta / period * 8) AS rate FROM bill_data WHERE bill_id = ?";
$q_95_sql .= " AND timestamp > ? AND timestamp <= ? ORDER BY rate ASC";
$a_95th = dbFetchColumn($q_95_sql, array($bill_id, $datefrom, $dateto));
$m_95th = $a_95th[$measurement_95th];

return (round($m_95th, 2));
}//end get95thout()


function getRates($bill_id, $datefrom, $dateto)
function getRates($bill_id, $datefrom, $dateto, $dir_95th)
{
$data = [];

Expand All @@ -168,14 +178,19 @@ function getRates($bill_id, $datefrom, $dateto)
$ptot = $sum_data['period'];

$data['rate_95th_in'] = get95thIn($bill_id, $datefrom, $dateto);
$data['rate_95th_out'] = get95thOut($bill_id, $datefrom, $dateto);
$data['rate_95th_out'] = get95thout($bill_id, $datefrom, $dateto);

if ($data['rate_95th_out'] > $data['rate_95th_in']) {
$data['rate_95th'] = $data['rate_95th_out'];
$data['dir_95th'] = 'out';
if ($dir_95th == 'agg') {
$data['rate_95th'] = get95thagg($bill_id, $datefrom, $dateto);
$data['dir_95th'] = 'agg';
} else {
$data['rate_95th'] = $data['rate_95th_in'];
$data['dir_95th'] = 'in';
if ($data['rate_95th_out'] > $data['rate_95th_in']) {
$data['rate_95th'] = $data['rate_95th_out'];
$data['dir_95th'] = 'out';
} else {
$data['rate_95th'] = $data['rate_95th_in'];
$data['dir_95th'] = 'in';
}
}

$data['total_data'] = $mtot;
Expand All @@ -192,21 +207,21 @@ function getRates($bill_id, $datefrom, $dateto)

function getTotal($bill_id, $datefrom, $dateto)
{
$mtot = dbFetchCell("SELECT SUM(delta) FROM bill_data WHERE bill_id = '".mres($bill_id)."' AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."'");
$mtot = dbFetchCell("SELECT SUM(delta) FROM bill_data WHERE bill_id = ? AND timestamp > ? AND timestamp <= ?", array($bill_id, $datefrom, $dateto));
return ($mtot);
}//end getTotal()


function getSum($bill_id, $datefrom, $dateto)
{
$sum = dbFetchRow("SELECT SUM(period) as period, SUM(delta) as total, SUM(in_delta) as inbound, SUM(out_delta) as outbound FROM bill_data WHERE bill_id = '".mres($bill_id)."' AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."'");
$sum = dbFetchRow("SELECT SUM(period) as period, SUM(delta) as total, SUM(in_delta) as inbound, SUM(out_delta) as outbound FROM bill_data WHERE bill_id = ? AND timestamp > ? AND timestamp <= ?", array($bill_id, $datefrom, $dateto));
return ($sum);
}//end getSum()


function getPeriod($bill_id, $datefrom, $dateto)
{
$ptot = dbFetchCell("SELECT SUM(period) FROM bill_data WHERE bill_id = '".mres($bill_id)."' AND timestamp > '".mres($datefrom)."' AND timestamp <= '".mres($dateto)."'");
$ptot = dbFetchCell("SELECT SUM(period) FROM bill_data WHERE bill_id = ? AND timestamp > ? AND timestamp <= ?", array($bill_id, $datefrom, $dateto));
return ($ptot);
}//end getPeriod()

Expand Down
6 changes: 6 additions & 0 deletions includes/html/modal/new_bill.inc.php
Expand Up @@ -12,6 +12,7 @@
*/

use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Config;

if (LegacyAuth::user()->hasGlobalAdmin()) {
require 'includes/html/javascript-interfacepicker.inc.php';
Expand Down Expand Up @@ -73,6 +74,11 @@

<?php

if (Config::get('billing.95th_default_agg') == 1) {
$bill_data['dir_95th'] = 'agg';
} else {
$bill_data['dir_95th'] = 'in';
}
$bill_data['bill_type'] = 'cdr';
$quota = array('select_gb' => ' selected');
$cdr = array('select_mbps' => ' selected');
Expand Down
1 change: 1 addition & 0 deletions includes/html/pages/bill/actions.inc.php
Expand Up @@ -84,6 +84,7 @@
'bill_quota' => (string)$bill_quota,
'bill_cdr' => (string)$bill_cdr,
'bill_type' => $_POST['bill_type'],
'dir_95th' => $_POST['dir_95th'],
'bill_custid' => $_POST['bill_custid'],
'bill_ref' => $_POST['bill_ref'],
'bill_notes' => $_POST['bill_notes'],
Expand Down
19 changes: 19 additions & 0 deletions includes/html/pages/bill/addoreditbill.inc.php
Expand Up @@ -39,6 +39,25 @@
<option <?php echo $cdr['select_gbps'] ?> value="Gbps">Gigabits per second (Gbps)</option>
</select>
</div>
<label class="col-sm-4 control-label" for="dir_95th">95th Calculation</label>
<div class="col-sm-8">
<label class="radio-inline">
<input type="radio" name="dir_95th" id="dir_95th_inout" value="in"
<?php
if ($bill_data['dir_95th'] == 'in' || $bill_data['dir_95th'] == 'out') {
echo "checked";
}
?> /> Max In/Out
</label>
<label class="radio-inline">
<input type="radio" name="dir_95th" id="dir_95th_agg" value="agg"
<?php
if ($bill_data['dir_95th'] == 'agg') {
echo "checked";
}
?> /> Aggregate
</label>
</div>
</div>
<div id="quotaDiv">
<label class="col-sm-4 control-label" for="bill_quota">Quota</label>
Expand Down
2 changes: 1 addition & 1 deletion includes/html/pages/bills.inc.php
Expand Up @@ -64,7 +64,7 @@
'rate_95th_in' => 0,
'rate_95th_out' => 0,
'rate_95th' => 0,
'dir_95th' => 'in',
'dir_95th' => $_POST['dir_95th'],
'total_data' => 0,
'total_data_in' => 0,
'total_data_out' => 0,
Expand Down

0 comments on commit 9c837be

Please sign in to comment.