Added aggregate config option to Billing 95th percentile calculations (#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
This commit is contained in:
Llarian 2019-05-27 19:44:02 -07:00 committed by Tony Murray
parent 38a638358d
commit 9c837bee87
7 changed files with 101 additions and 39 deletions

View File

@ -39,8 +39,11 @@ foreach (dbFetchRows('SELECT * FROM `bills` ORDER BY `bill_id`') as $bill) {
$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'];

View File

@ -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
@ -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

View File

@ -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()
@ -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)) {
@ -123,41 +119,55 @@ function getLastMeasurement($bill_id)
return ($return);
}//end getLastMeasurement()
function get95thin($bill_id, $datefrom, $dateto)
function get95thagg($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 (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 get95thagg()
function get95thIn($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 (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()
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 = [];
@ -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;
@ -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()

View File

@ -12,6 +12,7 @@
*/
use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Config;
if (LegacyAuth::user()->hasGlobalAdmin()) {
require 'includes/html/javascript-interfacepicker.inc.php';
@ -73,6 +74,11 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
<?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');

View File

@ -84,6 +84,7 @@ if ($_POST['action'] == 'update_bill') {
'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'],

View File

@ -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>

View File

@ -64,7 +64,7 @@ if ($_POST['addbill'] == 'yes') {
'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,