Skip to content
Snippets Groups Projects
Commit 2f84a81e authored by Robert Anderson's avatar Robert Anderson
Browse files

Topups summary support for smadmin tool

parent 4737edda
No related branches found
No related tags found
No related merge requests found
# SQL config database support # Topup support
# Copyright (C) 2007-2009, AllWorldIT # Copyright (C) 2007-2009, AllWorldIT
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
...@@ -26,6 +26,12 @@ use smradius::logging; ...@@ -26,6 +26,12 @@ use smradius::logging;
use smradius::dblayer; use smradius::dblayer;
use smradius::util; use smradius::util;
use smradius::attributes; use smradius::attributes;
use POSIX qw(ceil);
use DateTime;
use Date::Parse;
# Exporter stuff # Exporter stuff
require Exporter; require Exporter;
...@@ -40,9 +46,12 @@ our (@ISA,@EXPORT,@EXPORT_OK); ...@@ -40,9 +46,12 @@ our (@ISA,@EXPORT,@EXPORT_OK);
# Plugin info # Plugin info
our $pluginInfo = { our $pluginInfo = {
Name => "SQL Topups Database", Name => "SQL Topup Config",
Init => \&init, Init => \&init,
# Cleanup run by smadmin
Cleanup => \&cleanup,
# User database # User database
Config_get => \&getTopups Config_get => \&getTopups
}; };
...@@ -67,17 +76,17 @@ sub init ...@@ -67,17 +76,17 @@ sub init
# Default configs... # Default configs...
$config->{'get_topups_query'} = ' $config->{'get_topups_query'} = '
SELECT SELECT
@TP@topups.Type, @TP@topups.Type,
@TP@topups.ValidFrom, @TP@topups.ValidFrom,
@TP@topups.ValidTo, @TP@topups.ValidTo,
@TP@topups.Value @TP@topups.Value
FROM FROM
@TP@topups, @TP@topups,
@TP@users @TP@users
WHERE WHERE
@TP@topups.UserID = @TP@users.ID @TP@topups.UserID = @TP@users.ID
AND AND
@TP@users.Username = ? @TP@users.Username = ?
'; ';
...@@ -140,5 +149,337 @@ sub getTopups ...@@ -140,5 +149,337 @@ sub getTopups
} }
# Topup summary function
sub cleanup
{
my ($server) = @_;
my $sth;
# TODO - be more dynamic, we may not be using SQL users
# Get all usernames
$sth = DBSelect('SELECT ID, Username FROM @TP@users');
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select from users: ".
smradius::dblayer::Error());
return;
}
# Create hash of usernames
my %users;
while (my $user = $sth->fetchrow_hashref()) {
$users{$user->{'id'}} = $user->{'username'};
}
# Finished for now
DBFreeRes($sth);
# The datetime now
my $now = DateTime->now;
# Make datetime for a month ago
my $thisMonth = DateTime->new( year => $now->year, month => $now->month, day => 1 );
# Get begin date of last month
my ($prevYear,$prevMonth);
if ($now->month == 1) {
$prevYear = $now->year - 1;
$prevMonth = 12;
} else {
$prevYear = $now->year;
$prevMonth = $now->month - 1;
}
my $lastMonth = DateTime->new( year => $prevYear, month => $prevMonth, day => 1 );
# Get begin date of next month
my ($folYear,$folMonth);
if ($now->month == 12) {
$folYear = $now->year + 1;
$folMonth = 1;
} else {
$folYear = $now->year;
$folMonth = $now->month + 1;
}
my $nextMonth = DateTime->new( year => $folYear, month => $folMonth, day => 1 );
my $unix_nextMonth = $nextMonth->epoch();
# Start of multiple queries
DBBegin();
# Loop through users and check each topup, setting it to default if needed and updating summary
foreach my $userID (keys %users) {
my $userName = $users{$userID};
# TODO - in future we must be more dynamic, we may not be using SQL accunting
# Get current usage
$sth = DBSelect('
SELECT
Username,
SUM(AcctSessionTime) as AcctSessionTime,
SUM(AcctInputOctets) as AcctInputOctets,
SUM(AcctInputGigawords) as AcctInputGigawords,
SUM(AcctOutputOctets) as AcctOutputOctets,
SUM(AcctOutputGigawords) as AcctOutputGigawords
FROM
@TP@accounting
WHERE
EventTimestamp > ?
AND Username = ?
GROUP BY
Username
',
$lastMonth,$userName
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select accounting records: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
# Pull data
my $row = $sth->fetchrow_hashref();
# Total up input
my $totalData = 0;
if (defined($row->{'acctinputoctets'}) && $row->{'acctinputoctets'} > 0) {
$totalData += $row->{'acctinputoctets'} / 1024 / 1024;
}
if (defined($row->{'acctinputgigawords'}) && $row->{'acctinputgigawords'} > 0) {
$totalData += $row->{'acctinputgigawords'} * 4096;
}
# Add up output
if (defined($row->{'acctoutputoctets'}) && $row->{'acctoutputoctets'} > 0) {
$totalData += $row->{'acctoutputoctets'} / 1024 / 1024;
}
if (defined($row->{'acctoutputgigawords'}) && $row->{'acctoutputgigawords'} > 0) {
$totalData += $row->{'acctoutputgigawords'} * 4096;
}
# Add up uptime
my $totalTime = 0;
if (defined($row->{'acctsessiontime'}) && $row->{'acctsessiontime'} > 0) {
$totalTime = $row->{'acctsessiontime'} / 60;
}
# Rounding up
my %res;
$res{'TotalTrafficUsage'} = ceil($totalData);
$res{'TotalUptimeUsage'} = ceil($totalTime);
# Finished for now
DBFreeRes($sth);
# TODO?
# FIXME - support for groups and realm config?
# Get users caps
$sth = DBSelect('
SELECT
@TP@user_attributes.Name, @TP@user_attributes.Value
FROM
@TP@user_attributes, @TP@users
WHERE
@TP@users.Username = ?
',
$userName
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select usage caps: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
# Add cap limits to capRecord hash
my %capRecord;
while ($row = $sth->fetchrow_hashref()) {
if ($row->{'name'} eq 'SMRadius-Capping-Traffic-Limit') {
$capRecord{'TrafficLimit'} = $row->{'value'};
}
if ($row->{'name'} eq 'SMRadius-Capping-Uptime-Limit') {
$capRecord{'UptimeLimit'} = $row->{'value'};
}
}
# Finished for now
DBFreeRes($sth);
# Set excess traffic / uptime used
my $excessUptime = 0;
my $excessTraffic = 0;
if (defined($capRecord{'TrafficLimit'})) {
$excessTraffic = $res{'TotalTrafficUsage'} - $capRecord{'TrafficLimit'};
}
if (defined($capRecord{'UptimeLimit'})) {
$excessUptime = $res{'TotalUptimeUsage'} - $capRecord{'UptimeLimit'};
}
# Get users topups not depleted
$sth = DBSelect('
SELECT
@TP@topups.ID, @TP@topups.Value, @TP@topups.Type, @TP@topups.ValidTo
FROM
@TP@topups, @TP@users
WHERE
@TP@topups.Depleted = 0
AND @TP@topups.UserID = @TP@users.ID
AND @TP@users.Username = ?
AND @TP@topups.ValidFrom <= ?
AND @TP@topups.ValidTo >= ?
ORDER BY
@TP@topups.Timestamp
',
$userName,$lastMonth,$thisMonth
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select topups: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
# Loop with the topups and push them into arrays
my @trafficTopups = ();
my @uptimeTopups = ();
while (my $row = $sth->fetchrow_hashref()) {
# Convert string to unix time
my $unix_validto = str2time($row->{'validto'});
# If this is a traffic topup ...
if ($row->{'type'} == 1) {
push(@trafficTopups, {
ID => $row->{'id'},
Value => $row->{'value'},
ValidTo => $unix_validto
});
# Or a uptime topup...
} elsif ($row->{'type'} == 2) {
push(@uptimeTopups, {
ID => $row->{'id'},
Value => $row->{'value'},
ValidTo => $unix_validto
});
}
}
#my %topups = ( Traffic => \@trafficTopups, Uptime => \@uptimeTopups );
# Finished for now
DBFreeRes($sth);
# List of topups to be set depleted
my @depletedTopups = ();
# Working with traffic
if (defined($excessTraffic) && $excessTraffic gt 0) {
foreach my $topup (@trafficTopups) {
# Topup depleted
if ($topup->{'Value'} < $excessTraffic) {
# Add topup ID to depleted list
push(@depletedTopups, $topup->{'ID'});
# Excess traffic remaining
$excessTraffic -= $topup->{'Value'};
# Topup still alive
} else {
my $trafficRemaining = $topup->{'Value'} - $excessTraffic;
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
# Set users depleted topups
$sth = DBDo('
INSERT INTO
@TP@topups_summary (TopupID,PeriodKey,Balance)
VALUES
(?,?,?)
',
$topup->{'ID'},$thisMonth,$trafficRemaining
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topup summary: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
}
# We dont want to continue if a non depleted topup found
last;
}
}
}
# Working with uptime
if (defined($excessUptime) && $excessUptime gt 0) {
foreach my $topup (@uptimeTopups) {
# Topup depleted
if ($topup->{'Value'} < $excessUptime) {
# Add topup ID to depleted list
push(@depletedTopups, $topup->{'ID'});
# Excess uptime remaining
$excessUptime -= $topup->{'Value'};
# Topup still alive
} else {
my $uptimeRemaining = $topup->{'Value'} - $excessUptime;
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
# Set users depleted topups
$sth = DBDo('
INSERT INTO
@TP@topups_summary (TopupID,PeriodKey,Balance)
VALUES
(?,?,?)
',
$topup->{'ID'},$thisMonth,$uptimeRemaining
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topups: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
}
# We dont want to continue if a non depleted topup found
last;
}
}
}
# Loop through topups that are depleted
my $topupID;
foreach $topupID (@depletedTopups) {
# Set users depleted topups
$sth = DBSelect('
UPDATE
@TP@topups
SET
Depleted = 1
WHERE
ID = ?
',
$topupID
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topups: ".
smradius::dblayer::Error());
goto FAIL_ROLLBACK;
}
}
}
# Finished
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Topup summaries have been updated");
DBCommit();
return;
FAIL_ROLLBACK:
DBRollback();
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Database has been rolled back, no records updated");
return;
}
1; 1;
# vim: ts=4 # vim: ts=4
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment