-
Notifications
You must be signed in to change notification settings - Fork 2
/
asteramiacct.py
319 lines (278 loc) · 15.2 KB
/
asteramiacct.py
1
#!/usr/bin/env python# -*- coding: utf-8 -*-# Todo check the init script i.e allow running of several instancesimport socketimport sysimport hashlibimport loggimport acctimport confiparsefrom pg import timestamp as timestamplogger = logg.Loggable(alog_name=__name__, )conf = confiparse.ConfigOpener()cel = conf.cel_config()if cel: cdr = conf.cdr_config()if cdr: logger.info(conf.ami_config()) am = conf.ami_config()asterisk_amihost = am['ami_host']asterisk_amiport = am['ami_port']asterisk_amiusername = am['ami_manager']asterisk_amisecret = am['ami_secret']def confid_gen(val): """Function which generates confid for h323_confid radius attribute""" m = hashlib.md5() m.update(str(val)) m = m.hexdigest() # Format to match the following standard # 16-byte number in hexadecimal notation with a space between each 4-byte integer # i.e 29FB117C 640E9815 F342ABAD 9DCB6A72 m = m[0:8] + ' ' + m[8:16] + ' ' + m[16:24] + ' ' + m[24:] return m.upper()def open_sock(asockip, asockport): """Function to open socket""" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sconnected = 1 try: s.connect((asockip, int(asockport))) except socket.error as msg: logger.critical( 'Something\'s wrong with connection to host %s:%d. Exception type is %s' % (asockip, asockport, msg)) s.close() sys.exit(1) if sconnected is not None: return s.makefile("rw", 8096)def stpincomingcall(adict): """"Function for incoming call""" if 'Cause' in adict.keys(): logger.info(" Running Accounting Stop with the following parameters ANI:%s DNI:%s " "Confid:%s Setup time:%s and direction :%s \t Cause code:%s " % (adict['Source'], adict['Destination'], confid_gen(adict['Channel']), adict['AnswerTime'], adict['AccountCode'], adict['Cause'])) acct.accountingStop(aani=adict['Source'], adni=adict['Destination'], aconnectime=timestamp(adict['StartTime']), acause=adict['Cause'], aconfid=confid_gen(adict['Channel']), adisconectime=timestamp(adict['EndTime']), agwid=adict['DestinationChannel'], aacountsessiontime=adict['BillableSeconds'], acalltype=adict['Channel'][0:3], acallorig='h323-call-origin=answer', asetuptime=timestamp(adict['StartTime'])) # In case nobody answer if adict['Disposition'] == 'NO ANSWER': adict['Cause'] = 19 logger.info("Running Accounting Stop with the following parameters ANI:%s DNI:%s " "Confid:%s Setup time:%s and direction :%s \t Cause code:%s \n " % (adict['Source'], adict['Destination'], confid_gen(adict['Channel']), adict['AnswerTime'], adict['AccountCode'], adict['Cause'])) acct.accountingStop(aani=adict['Source'], adni=adict['Destination'], aconnectime=timestamp(adict['StartTime']), acause=adict['Cause'], aconfid=confid_gen(adict['Channel']), asetuptime=timestamp(adict['AnswerTime']), adisconectime=timestamp(adict['EndTime']), agwid=adict['DestinationChannel'], aacountsessiontime=adict['BillableSeconds'], acalltype=adict['Channel'][0:3], acallorig='answer')def stpoutgoingcall(adict): """Function for Stop packet of outgouing call""" adict['AccountCode'] = 'outgoing' adict['Cause'] = 16 """Function which send accounting for a given outgoing call""" logger.info("Running Accounting Stop with the following parameters " "ANI:%s DNI:%s Confid:%s Setup time:%s and direction :%s \t Cause code:%s " % (adict['Source'], adict['Destination'], confid_gen(adict['Channel']), adict['AnswerTime'], adict['AccountCode'], adict['Cause'])) acct.accountingStop(aani=adict['Source'], adni=adict['Destination'], aconnectime=timestamp(adict['StartTime']), acause=adict['Cause'], aconfid=confid_gen(adict['Channel']), asetuptime=timestamp(adict['AnswerTime']), adisconectime=timestamp(adict['EndTime']), agwid=adict['DestinationChannel'], aacountsessiontime=adict['BillableSeconds'], acallorig='originate', acalltype=adict['Channel'][0:3])def stpoutgcall2(adict, disposion): """Function for Radius Accounting Stop packet of outgoing call leg""" if disposion == 'NO ANSWER': adict['AccountCode'] = 'outgoing' adict['Cause'] = 19 logger.info("Running Accounting Stop with the following parameters " " ANI:%s DNI:%s Confid:%s Call Dispassion:%s " " Setup time:%s and direction :%s " " Cause code:%s " % (adict['Source'], adict['Destination'], confid_gen(adict['Channel']), disposion, adict['AnswerTime'], adict['AccountCode'], adict['Cause'])) acct.accountingStop(aani=adict['Source'], adni=adict['Destination'], aconnectime=timestamp(adict['StartTime']), acause=adict['Cause'], aconfid=confid_gen(adict['Channel']), asetuptime=timestamp(adict['AnswerTime']), adisconectime=timestamp(adict['EndTime']), agwid=adict['DestinationChannel'], aacountsessiontime=adict['BillableSeconds'], acallorig='originate', acalltype=adict['Channel'][0:3]) elif disposion == 'ANSWERED': adict['AccountCode'] = 'outgoing' adict['Cause'] = 16 logger.info("Running Accounting Stop with the following parameters " "ANI:%s DNI:%s Confid:%s Call Dispassion:%s" "Setup time:%s and direction :%s " "\t Cause code:%s \n " % (adict['Source'], adict['Destination'], confid_gen(adict['Channel']), disposion, adict['AnswerTime'], adict['AccountCode'], adict['Cause'])) acct.accountingStop(aani=adict['Source'], adni=adict['Destination'], aconnectime=timestamp(adict['StartTime']), acause=adict['Cause'], aconfid=confid_gen(adict['Channel']), asetuptime=timestamp(adict['AnswerTime']), adisconectime=timestamp(adict['EndTime']), agwid=adict['DestinationChannel'], aacountsessiontime=adict['BillableSeconds'], acallorig='originate', acalltype=adict['Channel'][0:3])def conn_ami_manager(ami_username, ami_secret): """Asterisk ami connection""" return "Action: Login\r\nUsername: %s\r\nSecret: %s\r\nEvents: On\r\n\r\n" % (ami_username, ami_secret)def disconn_ami_manager(): """Asterisk ami manager disconnect """ return "Action: Logoff\r\n\r\n"# {'timestamp': '17:32:37.399 MSK Wed Apr 22 2015', 'Message': 'Already authenticated', 'Response': 'Success'}def ami_connection(ast_musername, ast_msecret, ast_mport, ast_ip='localhost', ): """Function to connect to astersik if no ip specified it make connection to a localhost """ avdict = {} connected = True # Prepare and open socket filesock = open_sock(ast_ip, ast_mport) ami_connect = False # Keep socket open to connect manager while connected: # try: # Connect to Asterisk AMI Manager if not ami_connect: filesock.write(conn_ami_manager(ast_musername, ast_msecret)) filesock.flush() ami_connect = True data = filesock.readline() data.strip(":") results = data.strip().split(": ") # Uncomment bellow to print output from Asterisk ami manager output # print(results) if len(results) == 1 and len(results[0]) > 0: avdict[results[0].strip(":")] = '' elif len(results) == 2: avdict[results[0]] = results[1] avdict['timestamp'] = timestamp() elif len(results[0]) == 0 and len(avdict) != 0: logger.debug(avdict) if avdict.get('Message') and avdict.get('Response'): if avdict['Message'] == 'Authentication accepted' and avdict['Response'] == 'Success': logger.info("AMI manager is connected starting to receive data ...\n") ami_connect = True # print(ami_connect) avdict.clear() elif avdict['Message'] == 'Authentication failed' and avdict['Response'] == 'Error': logger.error("Please check Asterisk ami manager credentials i.e Username and Secret ... \n") filesock.write(disconn_ami_manager()) filesock.flush() # connected = False avdict.clear() filesock.close() exit(1) elif avdict.get('Event') and avdict['Event'] == 'Shutdown' \ and avdict.get('Restart') and avdict['Restart'] == 'False': # Disconnect the manager filesock.write(disconn_ami_manager()) filesock.flush() avdict.clear() connected = False elif avdict.get('Event') and avdict.get('EventName') and avdict.get('Application'): # Start Events if avdict['Event'] == 'CEL' and avdict['EventName'] == 'ANSWER' and \ (avdict['Application'] == 'Answer' or avdict['Application'] == 'Dial'): # In case of incoming call switch to DND if avdict['CallerIDrdnis'] == '': # In case of incoming call switch to DND if avdict['CallerIDdnid'] == '': logger.info( "Running Accounting Start with the following parameters " "ANI:%s DNI:%s Confid:%s Setup time:%s" % (avdict['CallerIDnum'], avdict['Exten'], confid_gen(avdict['Channel']), avdict['timestamp'])) acct.accountingStart(aani=avdict['CallerIDnum'], adni=avdict['Exten'], aconnectime=avdict['timestamp'], aconfid=confid_gen(avdict['Channel']), asetuptime=avdict['timestamp'], acallorig='h323-call-origin=answer') else: logger.info("Running Accounting Start with the following parameters" " ANI:%s DNI:%s Confid:%s Setup time:%s" % (avdict['CallerIDnum'], avdict['CallerIDdnid'], confid_gen(avdict['Channel']), avdict['timestamp'])) acct.accountingStart(aani=avdict['CallerIDnum'], adni=avdict['CallerIDdnid'], aconnectime=avdict['timestamp'], aconfid=confid_gen(avdict['Channel']), asetuptime=avdict['timestamp'], acallorig='h323-call-origin=answer') else: logger.info("Running Accounting Start with the following " "parameters ANI:%s DNI:%s Confid:%s Setup time:%s" % (avdict['CallerIDnum'], avdict['CallerIDrdnis'], confid_gen(avdict['Channel']), avdict['timestamp'])) acct.accountingStart(aani=avdict['CallerIDnum'], adni=avdict['CallerIDrdnis'], aconnectime=avdict['timestamp'], aconfid=confid_gen(avdict['Channel']), asetuptime=avdict['timestamp']) avdict.clear() # Start and stop accounting events elif avdict.get('BillableSeconds'): if avdict['Event'] == 'Cdr': # print(avdict) if avdict['AccountCode'] == 'incoming': stpincomingcall(avdict) avdict.clear() elif avdict['Destination'] == '' or avdict['Destination'] == 's' and avdict.keys() in ['Address']: logger.debug('-------111--------') acct.accountingStop(aani=avdict['Source'], adni=avdict['Destination'], aconnectime=timestamp(avdict['StartTime']), acause=avdict['Cause'], aconfid=confid_gen(avdict['Channel']), asetuptime=timestamp(avdict['AnswerTime']), adisconectime=timestamp(avdict['EndTime']), agwid=avdict['Address'], aacountsessiontime=avdict['BillableSeconds'], acallorig='h323-call-origin=answer', acalltype=avdict['Channel'][0:3]) avdict.clear() elif avdict.keys() not in ['Cause'] and avdict['Disposition'] == 'ANSWERED': stpoutgcall2(avdict, disposion=avdict['Disposition']) avdict.clear() elif avdict['Disposition'] == 'NO ANSWER': stpoutgcall2(avdict, disposion=avdict['Disposition']) avdict.clear() else: logger.debug('-------333--------') avdict.clear() else: avdict.clear() avdict.clear() # except: # continue# Time format "10:11:09.000 UTC Tue Mar 31 2015"def main(): logger.info('Staring Accounting client for Asterisk ...') ami_connection(ast_ip=asterisk_amihost, ast_mport=int(asterisk_amiport), ast_musername=asterisk_amiusername, ast_msecret=asterisk_amisecret)if __name__ == "__main__": main()