Support A2Billing :

provided by Star2Billing S.L.

Support A2Billing :
It is currently Sun Sep 24, 2017 5:50 pm
VoIP Billing solution


All times are UTC




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Cron Script to Track SIP Outbound Calls
PostPosted: Fri Nov 11, 2011 7:06 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
This script keeps track of calls on Asterisk SIP channels and drops them when account credit has expired. Usually accounts end up in negative balance because of script errors or by the way Asterisk and A2B handle concurrent calls on the same account. This script is meant to prevent negative balances or exceeding credit limit.

This script runs as a cron job that constantly checks on asterisk for active calls and the remaining account credit. It has been tested to work on SIP channels, but may work on any type of channel by removing the SIP restriction. Other types of channels were not tested.


Installation steps Asterisk > 1.6.x , A2B > 1.9.4, PHP 5.2+

Modify files in A2B

1a.
In /usr/local/src/Star2Billing194/common/lib/Class.A2Billing.php in the run_dial function.
Add these two lines at the top of the function before the dial
Code:
$agi ->set_variable("credit",$this->credit);
$agi ->set_variable("accountnumber",$agi->request['agi_accountcode']?$agi->request['agi_accountcode']:$this->accountcode);


1b.
In /usr/local/src/Star2Billing194/common/lib/Class.A2Billing.php in the call_2did function.
Add this line near the top of the function under the line "$selling_rate = $listdestination[0][9];" (ths is added to the channel in asterisk)

Code:
$agi ->set_variable("callrate",$selling_rate);


1c.
In /usr/local/src/Star2Billing194/common/lib/Class.A2Billing.php in the call_did function.
Add this line near the top of the function under the line "$res = 0;" (ths is added to the channel in asterisk)

Code:
$agi ->set_variable("callrate",$listdestination[0][9]);


1d.
In /usr/local/src/Star2Billing194/common/lib/Class.RateEngile.php in function rate_engine_performcall
Add this line
Code:
$agi ->set_variable("callrate",$this -> ratecard_obj[$k][12]);

near the top of the function under the block of code that looks like this:
$prefix = $this -> ratecard_obj[$k][$usetrunk+1];
$tech = $this -> ratecard_obj[$k][$usetrunk+2];
$ipaddress = $this -> ratecard_obj[$k][$usetrunk+3];
$removeprefix = $this -> ratecard_obj[$k][$usetrunk+4];
$timeout = $this -> ratecard_obj[$k]['timeout'];
$musiconhold = $this -> ratecard_obj[$k][39];
$failover_trunk = $this -> ratecard_obj[$k][40+$usetrunk_failover];
$addparameter = $this -> ratecard_obj[$k][42+$usetrunk_failover];
$cidgroupid = $this -> ratecard_obj[$k][44];
$inuse = $this -> ratecard_obj[$k][48+$usetrunk_failover];
$maxuse = $this -> ratecard_obj[$k][50+$usetrunk_failover];
$ifmaxuse = $this -> ratecard_obj[$k][52+$usetrunk_failover];


2. Copy the script below, change the name to .php
and set it as a cron job to run every minute (exactly one minute no more no less for proper operation). Also edit the cofiguration variable in the new .php file to point to the asterisk binary et al.

It can also be ran from the command line, eg;
php /usr/local/src/Star2Billing194/Cronjobs/a2billing_check_simult.php -v


Cron instruction
Code:
*/1 * * * * php /usr/local/src/Star2Billing194/Cronjobs/a2billing_check_simult.php


Code:
<?php
/*
  $Id: a2billing_check_simult.php,v 1.05 2013/03/10 00:00:00 vulcan Exp $
   Author: vulcan

  Designed for Asterisk2Billing

  Copyright (c) 2011  free to use, package, distribute, alter.

*/

   //*******************************
   //*******************************
   // C O N F I G U R A T I O N
   // V A R I A B L E S
   //*******************************
   //*******************************

      /* Requirements: PHP 5.x , Asterisk2Billing > 1.9.4 , Asterisk > 1.6.x  */
      /* this program checks active outbound SIP channels in asterisk and      */
      /* terminates them if cost of calls exceeds credit or credit limit.      */   
      /* Console command line :  ~]# php a2billing_check_simult.php [ -v ]   */

   define('ASTERISK_BINARY', '/usr/sbin/asterisk');

   //*******************************
   //*******************************
   // E N D
   // C O N F I G U R A T I O N
   // V A R I A B L E S
   //*******************************
   //*******************************

   if ( isset($argc) && $argc > 1) {
      array_shift($argv);

      $allow = array ('-verbose','-v','verbose','v');

      if (!in_array($argv[0],$allow)) {
         print "\nCommand Format: php <path to executable> -v\n";
      } else {
         $option = $argv[0];
      }
   }

   // shell command to get string of active channels - Origination/Destination
   $str = shell_exec(ASTERISK_BINARY . ' -Rx "core show channels concise"');

   $strArray = explode("\n",$str);

   foreach ($strArray as $key => $value) {

      if ( (strpos($value,'!') !== false) && (strpos($value,'(Outgoing Line)') !== false) ) {

         // Active channels to array            
         $channel = explode('!',trim($value));
         if (strpos($channel[0], 'SIP/') !== false ) {
            $outGoingChannels[] = $channel;
         }
      }
   }

   // build array of accounts
   if (isset( $outGoingChannels ) ) {

      foreach ( $outGoingChannels as $key => $value ) {

         $bridgedTo   = trim($value[12]);   // Originating channel name bridged to destination

         if (!empty($bridgedTo) && $bridgedTo != '(None)') {

            $chandest   = trim($value[0]);   // Active destination channel name
   
            // shell command to get channel variables string of the Originating channel after bridged with Destination
            $str = shell_exec(ASTERISK_BINARY . ' -Rx "dialplan show chanvar ' . $bridgedTo . '"'  );

                if (!empty($str)) {

                    $strArray = explode("\n",$str);

                    foreach ($strArray as $k => $v ) {
                        $index = strpos($v,'=');
                        if ($index !== false) {
                            // channel variables to array
                            $data[substr($v,0,$index)] = trim(substr($v,($index + 1)));
                        }
                    }
                }

                if (isset($data['accountnumber'])
                        && is_numeric($data['accountnumber'])
                        && isset($data['credit'])
                        && isset($data['callrate'])
                        && is_numeric($data['callrate'])  ) {

               $account   = $data['accountnumber']; //accountcode

               // Initialize using account number as key
               if (!isset($current[$account]['highestbalance']))  {
                  if (is_numeric($data['credit'])) {
                     $current[$account]['highestbalance'] = $data['credit'];
                  } else {
                     $current[$account]['highestbalance'] = 0;
                  }
                  $current[$account]['totallengthofcalls'] = 0;
                  $current[$account]['totalperminuterate'] = 0;            
                  $current[$account]['channeldata'] = array();
               }

               if ($current[$account]['highestbalance'] == 0) {
                  $current[$account]['highestbalance'] = $data['credit'];
               } elseif ($data['credit'] > $current[$account]['highestbalance'] )  {
                  $current[$account]['highestbalance'] = $data['credit'];
               }

               //Asterisk CDR
               $ttime = billsec($chandest);

               // store remaining data
               $current[$account]['totalperminuterate'] = $current[$account]['totalperminuterate'] + $data['callrate'];

               $data['lengthofcall']    = $ttime;
               $data['channel']       = $chandest;
               $data['marktime']      = time();
               array_push( $current[$account]['channeldata'], $data); // Destination channel names et al

            
            }
         }
      }

      if (isset( $current) ) {

         $tick = 0;

         while ($tick <= 59) {   

            // loop each account
            foreach ($current as $key => $accountdata) {

               // calculate current total cost of calls for this account
               $cost = 0;
               $totallengthofcall = 0;
               foreach ($accountdata['channeldata'] as $ikey => $ival ) {
                  $tick = time() - $ival['marktime'];

                  $cost = $cost + ((($ival['lengthofcall'] + $tick)/60) * $ival['callrate']);
                  $totallengthofcall = $totallengthofcall + $ival['lengthofcall'] + $tick;
               }

               $channelcount    = count($accountdata['channeldata']);
               $averagelength    = ($totallengthofcall/60) / $channelcount;

               //one second's worth of call
               $onesecworth = $accountdata['totalperminuterate']/60;
      
               if ( $cost >= ( $accountdata['highestbalance'] - ($onesecworth*5) ) ) {

                  //terminate outbound channels for this account
                  dropChannel   ($accountdata['channeldata'] , $key);
                  unset($current[$key]);
                  if (count($current) == 0) exit;

               } elseif (isset($option)) {

                  // print to console
                  stats($key);

               }

            }

            sleep(1);

         }
      }
   }

   /****
      Hangup the channel(s)
   */
   function dropChannel ($channel = array(), $account = null) {
      if (!empty($channel) ) {

         foreach ($channel as $k => $v) {
            $str = shell_exec(ASTERISK_BINARY . ' -Rx "dialplan show chanvar ' . $v['channel'] . '"'  );
            $strArray = explode("\n",$str);
      
            foreach ($strArray as $kk => $vv ) {
               $index = strpos($vv,'=');
               if ($index !== false) {
                  // channel variables to array
                  $data[substr($vv,0,$index)] = trim(substr($vv,($index + 1)));
               }
            }

            if (isset($data['BRIDGEPEER']) && !empty($data['BRIDGEPEER']) ){
               stats($account);
               shell_exec(ASTERISK_BINARY . ' -Rx "channel request hangup ' . trim($v['channel']) . '"'  );
               print_r('Hung up channel "' . $v['channel'] . '" for account: ' . $account . ' [ Not enough Credit ]' . "\n");
            }
         }
      }
   }

   /****
      Get the current CDR billing for call in seconds
   */
   function billsec($channel) {
      if (!empty($channel)) {
         $reply = shell_exec(ASTERISK_BINARY . ' -Rx "core show channel ' . $channel . '"'  );
         $channeldata = explode("\n",$reply);
         if (!empty($channeldata)) {
            foreach($channeldata as $key => $value) {

               $index = strpos($value,'billsec=');
               if ( $index !== false) {
                  $seconds = explode('=',$value);
                  break;
               }
            }
            if (isset($seconds[1]) && is_numeric(trim($seconds[1]))) {            
               return trim($seconds[1]);
            } else {
               return 0;
            }
   
         } else {
            return 0;
         }
      } else {
         return 0;
      }
   }      

   /****
      Print some statistics
   */
   function stats($account) {

      global $accountdata,$channelcount,$averagelength,$cost;

      print_r("\n");
      print_r("           Account Code: " . $account ."\n");
      print_r("       Available Credit: " . $accountdata['highestbalance'] ."\n");
      print_r("           Active Calls: " . $channelcount . "\n" );
      print_r("Average Length of Calls: " . $averagelength * 60 . " Seconds\n");
      print_r("        Rate Per Minute: " . $accountdata['totalperminuterate'] . "\n");
      print_r("  Current Cost of Calls: " . $cost . "\n\n");

   }
?>




Last edited by vulcan on Fri Mar 29, 2013 8:47 pm, edited 21 times in total.

Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Fri Nov 11, 2011 7:31 pm 
Offline

Joined: Sun Nov 07, 2010 10:00 pm
Posts: 253
Hi Vulcan,
As I started before in my my thread: viewtopic.php?f=34&t=9294 ...

But here, in your script, we're making some function out of asterisk box itself, why, where's the reason, is not better use own asterisk call-limit directive as it's running with the callcounter?? Because running an external script even, it' may have bugs and overload the cpu socket, or where's the reason, did you tried already the call-limit and the callcounter functions, and they fail with you??

Regards,


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Fri Nov 11, 2011 8:15 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
You made some good points. I have not yet tried what you suggested. but the thinking behind this is when [email protected] and Asterisk all fail, then this is the last line of defence.

Consider when [email protected] gives the wrong time-out value as in script errors, when asterisk fails to hangup the call for whatever reason or when concurrent calls for the same account is active, there is no mechanism to track this cost realtime. This script comes very close to that.

As for the load factor, that's a concern , but [email protected] already has 10 cron jobs running and this script does way less and does not access any database.

For the future, closer integration with asterisk is the only way to achieve this and minimize the load factor.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Fri Nov 11, 2011 8:43 pm 
Offline

Joined: Sun Nov 07, 2010 10:00 pm
Posts: 253
Quote:
Consider when [email protected] gives the wrong time-out value as in script errors, when asterisk fails to hangup the call for whatever reason or when concurrent calls for the same account is active, there is no mechanism to track this cost realtime. This script comes very close to that.


But if you turn callcounter directive to on, in sip.conf and in the db, as I add that row in cc_sip_buddies, asterisk will stat to run his own counter, so it's not depending on a2billing agi script any more... Then asterisk turn the counter, and according to the cal-limit fixed for each user, asterisk drop whatever incoming calls over that limite...

Sorry, this what I understand from that, if I'm mistaking, correct me!!

Also, here we may fix channels for users, so if we're using wholesale traffic, we need to set and limit channels, I'd use this directive for each peer to fix the channels, I guess is the best, or not??

Quote:
As for the load factor, that's a concern , but [email protected] already has 10 cron jobs running and this script does way less and does not access any database.


In my case I have more, cause I run a2billing crons, then anti scanning cron each half minute, from here: http://www.teamforrest.com/blog/171/ast ... und-block/ ... also the backups crons for the DB each 10 minutes... so lastly the serve will be only running crons or handle traffic...:)

The question, did you use already this what are you proposing, dose it worked for you?? I'd test what I did by running sipp script to check if really asterisk will do this, or how it's... if you want, send me a PM, we would run sipp reciprocally, to test and report the community here...

Regards,


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Fri Nov 11, 2011 8:58 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
Quote:
The question, did you use already this what are you proposing, dose it worked for you?


To the best of my knowledge it works.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Sat Nov 12, 2011 12:37 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
Update: Updated code and added command line statistic option.

Eg; php simult.php -v


Update: Fixed statistics .


Hopefully one day Digium will change the "show" command to provide the full channel values. If this is done, editing the asterisk code would be unnecessary.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Mon Nov 14, 2011 4:15 pm 
Offline

Joined: Sun Nov 07, 2010 10:00 pm
Posts: 253
Hi Vulcan,
I just re-test my previous proposed solution here: viewtopic.php?f=34&t=9294, by running sipp testing, and it seem to be working perfect, calls are being drop after limit excess. below the trace:
Quote:
[Nov 14 19:11:08] NOTICE[1549]: chan_sip.c:5829 update_call_counter: Call from peer '82724' rejected due to usage limit of 5
[Nov 14 19:11:08] NOTICE[1549]: chan_sip.c:22046 handle_request_invite: Failed to place call for device 82724, too many calls


Regards,


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Mon Nov 14, 2011 6:49 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
Thanks for the info. That addresses a different issue; though, those settings are limiting the number of concurrent calls a peer can make.

The script above addresses a different problem, and that is to cut off calls at the the credit limit if Asterisk does not do it. It is for those calls that might run into negative due to various errors. Currently A2B calculates the credit correctly, but not always the timeout and not always if enough credit is available for the call (bugs).

As long as A2B reports the correct credit, the script works because it gets the CDR billsec realtime from asterisk.

Script is currently being used live.

A one time run from the command line gives something like this:

Code:
           Account Code: xxxxxxxxxxxx
       Available Credit: 25.93748
           Active Calls: 1
Average Length of Calls: 4 Seconds
        Rate Per Minute: 0.0057
  Current Cost of Calls: 0.00038


           Account Code: xxxxxxxxxxx
       Available Credit: 3.92699
           Active Calls: 1
Average Length of Calls: 49 Seconds
        Rate Per Minute: 0.01
  Current Cost of Calls: 0.0081666666666667


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Mon Nov 14, 2011 7:05 pm 
Offline

Joined: Sun Nov 07, 2010 10:00 pm
Posts: 253
vulcan wrote:
Thanks for the info. That addresses a different issue; though, those settings are limiting the number of concurrent calls a peer can make.

The script above addresses a different problem, and that is to cut off calls at the the credit limit if Asterisk does not do it. It is for those calls that might run into negative due to various errors. Currently A2B calculates the credit correctly, but not always the timeout and not always if enough credit is available for the call (bugs).

As long as A2B reports the correct credit, the script works because it gets the CDR billsec realtime from asterisk.

Script is currently being used live.

A one time run from the command line gives something like this:

Code:
           Account Code: xxxxxxxxxxxx
       Available Credit: 25.93748
           Active Calls: 1
Average Length of Calls: 4 Seconds
        Rate Per Minute: 0.0057
  Current Cost of Calls: 0.00038


           Account Code: xxxxxxxxxxx
       Available Credit: 3.92699
           Active Calls: 1
Average Length of Calls: 49 Seconds
        Rate Per Minute: 0.01
  Current Cost of Calls: 0.0081666666666667



Hmmm, interesting, we're talking about different issues, in the same direction. We need first to limit the calls to avoid asterisk to accept more calls, this is generic, but your script appoint to calculate and run realtime billing to avoid going in negative... interesting, I'd go to setup this...

Thanks for sharing :)


Top
 Profile  
 
 Post subject: Re: Update: Cron Script to Track SIP Outbound Calls
PostPosted: Sat Dec 17, 2011 12:13 am 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
Newer version uploaded (small modification).


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Mon Dec 26, 2011 7:02 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
New version uploaded.

Added installation step 3c and changed 3a
Changed to rely on A2B account code after authentication instead of peer config setting.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Tue Dec 27, 2011 6:45 pm 
Offline

Joined: Thu Dec 03, 2009 11:18 am
Posts: 62
That is all great that both are working to be sure bad things does not happen.... My hat is off to you for the effort.
I think that if you are doing this to get around a bug in a2b why not fix the bug instead? Perhaps work with the developers to get it fixed and we all benefit from it. if this is not a real bug still it is big enough to justify some effort to get it changed before someone really gets hurt. This has a direct effect in all our pocket books.
Asterisk is already complex enough in itself that this recompiling and changing of asterisk can only come back to bite us all.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Tue Dec 27, 2011 11:50 pm 
Offline

Joined: Mon Jan 08, 2007 6:56 pm
Posts: 345
Quote:
...Perhaps work with the developers to get it fixed and we all benefit from it.


It is fixed in A2B with the inclusion below.

viewtopic.php?f=34&t=9308

Hopefully the developer gets around to taking a look at this.

The installation described is just a utility and not absolutely necessary if you patch the code. It was created as a stop gap until the real problem could be found (also safeguarding against unforseen problems in A2B , Asterisk or the SIP protocol).

The problem affected POSPAID and concurrent POSTPAID/PREPAID calls, in that, accounts ended up in negative balances , timeout value incorrect and credit limit verification. The timeout value and credit limit verification are fixed with patch , but the concurrent calls negative balance is inherent in the design. It will actually need a customised version of Asterisk or a running realtime utility to fix that. So compliling might be in the future to eliminate negative balances.

This utility only allows one minute over the credit limit in all cases (SIP) if call does not hangup for whatever reason.

Quote:
That is all great that both are working to be sure bad things does not happen.... My hat is off to you for the effort


Not both are tinkering with asterisk , just mine, ubunter's is standard in Asterisk and concerns max number of concurrent calls a peer can make and thats it. It is also a different issue entirely. Not a fix for the bugs.


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Mon Jan 02, 2012 11:21 am 
Offline

Joined: Sat Feb 19, 2011 6:19 am
Posts: 38
Hello

I followed th steps that you provided and also thanks for the php code.

My server is Asterisk 1.4.42,A2b1.9.4 and php 5.2.10

But i am not able to execute the code. At this point i subdivided your code to several pieces and tried them to find what is causing the problem.

What i have found is this:
On line 75 $str = shell_exec(ASTERISK_BINARY . ' -Rx "dialplan show chanvar ' . $bridgedTo . '"' );

"dial plan show chanvar" is not a vaild command in asterisk 1.4.42.


i coudnt find a way to get chanell variables
I will be glad if some one can help me to make this code working in asterisk 1.4


Regards
Rock


Top
 Profile  
 
 Post subject: Re: Cron Script to Track SIP Outbound Calls
PostPosted: Thu Jan 05, 2012 2:24 pm 
Offline

Joined: Sat Feb 19, 2011 6:19 am
Posts: 38
Hi nice to see that no response to my post.

I am answering my question myself

First of all no need to make modification in asterisk source code and recomiling
"core show channels concise " gives you the required information about the active channels with seperator as "!" and also provides FULL channel names without shortened.

now i am implementing your code to work with AMI, instead of shell execute.


At the end i will post the code

Regards
Rock


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Hosted Voice Broadcast


All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group