@ -1,17 +1,24 @@
#!/usr/bin/python
import astconfigparser
###############################################################################
# TODO:
# (1) There is more work to do here, at least for the sip.conf items that
# aren't currently parsed. An issue will be created for that.
# (2) All of the scripts should probably be passed through pylint and have
# as many PEP8 issues fixed as possible
# (3) A public review is probably warranted at that point of the entire script
###############################################################################
# configuration parser for sip.conf
sip = astconfigparser . MultiOrderedConfigParser ( )
import optparse
import astdicts
import astconfigparser
# configuration writer for res_sip.conf
res_sip = astconfigparser . MultiOrderedConfigParser ( )
PREFIX = ' res_sip_ '
###############################################################################
### some utility functions
###############################################################################
def section_by_type ( section , type = ' endpoint ' ) :
def section_by_type ( section , res_sip , type ) :
""" Finds a section based upon the given type, adding it if not found. """
try :
return astconfigparser . find_dict (
@ -22,10 +29,11 @@ def section_by_type(section, type='endpoint'):
sect [ ' type ' ] = type
return sect
def set_value ( key = None , val = None , section = None , type = ' endpoint ' ) :
def set_value ( key = None , val = None , section = None , res_sip = None ,
nmapped = None , type = ' endpoint ' ) :
""" Sets the key to the value within the section in res_sip.conf """
def _set_value ( k , v , s ):
set_value ( key if key else k , v , s , type )
def _set_value ( k , v , s , r , n ):
set_value ( key if key else k , v , s , r , n , type )
# if no value or section return the set_value
# function with the enclosed key and type
@ -33,135 +41,151 @@ def set_value(key=None, val=None, section=None, type='endpoint'):
return _set_value
# otherwise try to set the value
section_by_type ( section , type ) [ key ] = val
section_by_type ( section , res_sip , type ) [ key ] = \
val [ 0 ] if isinstance ( val , list ) else val
def merge_value ( key = None , val = None , section = None ,
type = ' endpoint ' , section_to = None ) :
def merge_value ( key = None , val = None , section = None , res_sip = None ,
nmapped = None , type = ' endpoint ' , section_to = None ) :
""" Merge values from the given section with those from the default. """
def _merge_value ( k , v , s ):
merge_value ( key if key else k , v , s , type , section_to )
def _merge_value ( k , v , s , r , n ):
merge_value ( key if key else k , v , s , r , n , type , section_to )
# if no value or section return the merge_value
# function with the enclosed key and type
if not val and not section :
return _merge_value
# should return single section
sect = sip . section ( section )
# should return a single value section list
sect = sip . section ( section ) [ 0 ]
# for each merged value add it to res_sip.conf
for i in sect . get_merged ( key ) :
set_value ( key , i , section_to if section_to else section , type )
set_value ( key , i , section_to if section_to else section ,
res_sip , nmapped , type )
def is_in ( s , sub ) :
""" Returns true if ' sub ' is in ' s ' """
return s . find ( sub ) != - 1
def non_mapped ( nmapped ) :
def _non_mapped ( section , key , val ) :
""" Writes a non-mapped value from sip.conf to the non-mapped object. """
if section not in nmapped :
nmapped [ section ] = astconfigparser . Section ( )
if isinstance ( val , list ) :
for v in val :
# since coming from sip.conf we can assume
# single section lists
nmapped [ section ] [ 0 ] [ key ] = v
else :
nmapped [ section ] [ 0 ] [ key ] = val
return _non_mapped
###############################################################################
### mapping functions -
### define f(key, val, section) where key/val are the key/value pair to
### write to given section in res_sip.conf
###############################################################################
def set_dtmfmode ( key , val , section ) :
def set_dtmfmode ( key , val , section , res_sip , nmapped ):
""" Sets the dtmfmode value. If value matches allowable option in res_sip
then map it , otherwise set it to none .
"""
# available res_sip.conf values: frc4733, inband, info, none
if val != ' inband ' or val != ' info ' :
print " sip.conf: dtmfmode = %s did not fully map into " \
" res_sip.conf - setting to ' none ' " % val
nmapped ( section , key , val + " ; did not fully map - set to none " )
val = ' none '
set_value ( key , val , section )
set_value ( key , val , section , res_sip , nmapped )
def from_nat ( key , val , section ):
def from_nat ( key , val , section , res_sip , nmapped ):
""" Sets values from nat into the appropriate res_sip.conf options. """
# nat from sip.conf can be comma separated list of values:
# yes/no, [auto_]force_rport, [auto_]comedia
if is_in ( val , ' yes ' ) :
set_value ( ' rtp_symmetric ' , ' yes ' , section )
set_value ( ' rewrite_contact ' , ' yes ' , section )
set_value ( ' rtp_symmetric ' , ' yes ' , section , res_sip , nmapped )
set_value ( ' rewrite_contact ' , ' yes ' , section , res_sip , nmapped )
if is_in ( val , ' comedia ' ) :
set_value ( ' rtp_symmetric ' , ' yes ' , section )
set_value ( ' rtp_symmetric ' , ' yes ' , section , res_sip , nmapped )
if is_in ( val , ' force_rport ' ) :
set_value ( ' force_rport ' , ' yes ' , section )
set_value ( ' rewrite_contact ' , ' yes ' , section )
set_value ( ' force_rport ' , ' yes ' , section , res_sip , nmapped )
set_value ( ' rewrite_contact ' , ' yes ' , section , res_sip , nmapped )
def set_timers ( key , val , section ):
def set_timers ( key , val , section , res_sip , nmapped ):
""" Sets the timers in res_sip.conf from the session-timers option
found in sip . conf .
"""
# res_sip.conf values can be yes/no, required, always
if val == ' originate ' :
set_value ( ' timers ' , ' always ' , section )
set_value ( ' timers ' , ' always ' , section , res_sip , nmapped )
elif val == ' accept ' :
set_value ( ' timers ' , ' required ' , section )
set_value ( ' timers ' , ' required ' , section , res_sip , nmapped )
elif val == ' never ' :
set_value ( ' timers ' , ' no ' , section )
set_value ( ' timers ' , ' no ' , section , res_sip , nmapped )
else :
set_value ( ' timers ' , ' yes ' , section )
set_value ( ' timers ' , ' yes ' , section , res_sip , nmapped )
def set_direct_media ( key , val , section ):
def set_direct_media ( key , val , section , res_sip , nmapped ):
""" Maps values from the sip.conf comma separated direct_media option
into res_sip . conf direct_media options .
"""
if is_in ( val , ' yes ' ) :
set_value ( ' direct_media ' , ' yes ' , section )
set_value ( ' direct_media ' , ' yes ' , section , res_sip , nmapped )
if is_in ( val , ' update ' ) :
set_value ( ' direct_media_method ' , ' update ' , section )
set_value ( ' direct_media_method ' , ' update ' , section , res_sip , nmapped )
if is_in ( val , ' outgoing ' ) :
set_value ( ' directed_media_glare_mitigation ' , ' outgoing ' , section )
set_value ( ' directed_media_glare_mitigation ' , ' outgoing ' , section , res_sip , nmapped )
if is_in ( val , ' nonat ' ) :
set_value ( ' disable_directed_media_on_nat ' , ' yes ' , section )
set_value ( ' disable_directed_media_on_nat ' , ' yes ' , section , res_sip , nmapped )
if ( val == ' no ' ) :
set_value ( ' direct_media ' , ' no ' , section , res_sip , nmapped )
def from_sendrpid ( key , val , section ) :
def from_sendrpid ( key , val , section , res_sip , nmapped ):
""" Sets the send_rpid/pai values in res_sip.conf. """
if val == ' yes ' or val == ' rpid ' :
set_value ( ' send_rpid ' , ' yes ' , section )
set_value ( ' send_rpid ' , ' yes ' , section , res_sip , nmapped )
elif val == ' pai ' :
set_value ( ' send_pai ' , ' yes ' , section )
set_value ( ' send_pai ' , ' yes ' , section , res_sip , nmapped )
def set_media_encryption ( key , val , section ):
def set_media_encryption ( key , val , section , res_sip , nmapped ):
""" Sets the media_encryption value in res_sip.conf """
if val == ' yes ' :
set_value ( ' media_encryption ' , ' sdes ' , section )
set_value ( ' media_encryption ' , ' sdes ' , section , res_sip , nmapped )
def from_recordfeature ( key , val , section ):
def from_recordfeature ( key , val , section , res_sip , nmapped ):
""" If record on/off feature is set to automixmon then set
one_touch_recording , otherwise it can ' t be mapped.
"""
if val == ' automixmon ' :
set_value ( ' one_touch_recording ' , ' yes ' , section )
set_value ( ' one_touch_recording ' , ' yes ' , section , res_sip , nmapped )
else :
print " sip.conf: %s = %s could not be fully map " \
" one_touch_recording not set in res_sip.conf " % ( key , val )
nmapped ( section , key , val + " ; could not be fully mapped " )
def from_progressinband ( key , val , section ):
def from_progressinband ( key , val , section , res_sip , nmapped ):
""" Sets the inband_progress value in res_sip.conf """
# progressinband can = yes/no/never
if val == ' never ' :
val = ' no '
set_value ( ' inband_progress ' , val , section )
set_value ( ' inband_progress ' , val , section , res_sip , nmapped )
def from_host ( key , val , section ):
def from_host ( key , val , section , res_sip , nmapped ):
""" Sets contact info in an AOR section in in res_sip.conf using ' host '
data from sip . conf
"""
# all aors have the same name as the endpoint so makes
# it easy to endpoint's 'aors' value
set_value ( ' aors ' , section , section )
set_value ( ' aors ' , section , section , res_sip , nmapped )
if val != ' dynamic ' :
set_value ( ' contact ' , val , section , ' aor ' )
set_value ( ' contact ' , val , section , res_sip , nmapped , ' aor ' )
else :
set_value ( ' max_contacts ' , 1 , section , ' aor ' )
set_value ( ' max_contacts ' , 1 , section , res_sip , nmapped , ' aor ' )
def from_subscribemwi ( key , val , section ):
def from_subscribemwi ( key , val , section , res_sip , nmapped ):
""" Checks the subscribemwi value in sip.conf. If yes places the
mailbox value in mailboxes within the endpoint , otherwise puts
it in the aor .
"""
mailboxes = sip . get ( ' mailbox ' , section )
mailboxes = sip . get ( ' mailbox ' , section , res_sip )
type = ' endpoint ' if val == ' yes ' else ' aor '
set_value ( ' mailboxes ' , mailboxes , section , type )
set_value ( ' mailboxes ' , mailboxes , section , res_sip , nmapped , type )
###############################################################################
@ -170,51 +194,51 @@ def from_subscribemwi(key, val, section):
# connected_line_method
# known sip.conf peer keys that can be mapped to a res_sip.conf section/key
peer_map = {
peer_map = [
# sip.conf option mapping function res_sip.conf option(s)
###########################################################################
' context ' : set_value ,
' dtmfmode ' : set_dtmfmode ,
' disallow ' : merge_value ,
' allow ' : merge_value ,
' nat ' : from_nat , # rtp_symmetric, force_rport,
# rewrite_contact
' icesupport ' : set_value ( ' ice_support ' ) ,
' autoframing ' : set_value ( ' use_ptime ' ) ,
' outboundproxy ' : set_value ( ' outbound_proxy ' ) ,
' mohsuggest ' : set_value ,
' session-timers ' : set_timers , # timers
' session-minse ' : set_value ( ' timers_min_se ' ) ,
' session-expires ' : set_value ( ' timers_sess_expires ' ) ,
' externip ' : set_value ( ' external_media_address ' ) ,
' externhost ' : set_value ( ' external_media_address ' ) ,
[ ' context ' , set_value ] ,
[ ' dtmfmode ' , set_dtmfmode ] ,
[ ' disallow ' , merge_value ] ,
[ ' allow ' , merge_value ] ,
[ ' nat ' , from_nat ] , # rtp_symmetric, force_rport,
# rewrite_contact
[ ' icesupport ' , set_value ( ' ice_support ' ) ] ,
[ ' autoframing ' , set_value ( ' use_ptime ' ) ] ,
[ ' outboundproxy ' , set_value ( ' outbound_proxy ' ) ] ,
[ ' mohsuggest ' , set_value ] ,
[ ' session-timers ' , set_timers ] , # timers
[ ' session-minse ' , set_value ( ' timers_min_se ' ) ] ,
[ ' session-expires ' , set_value ( ' timers_sess_expires ' ) ] ,
[ ' externip ' , set_value ( ' external_media_address ' ) ] ,
[ ' externhost ' , set_value ( ' external_media_address ' ) ] ,
# identify_by ?
' direct_media ' : set_direct_media , # direct_media
# direct_media_method
# directed_media_glare_mitigation
# disable_directed_media_on_nat
' callerid ' : set_value , # callerid
' callingpres ' : set_value ( ' callerid_privacy ' ) ,
' cid_tag ' : set_value ( ' callerid_tag ' ) ,
' trustpid ' : set_value ( ' trust_id_inbound ' ) ,
' sendrpid ' : from_sendrpid , # send_pai, send_rpid
' send_diversion ' : set_value ,
' encrpytion ' : set_media_encryption ,
' use_avpf ' : set_value ,
' recordonfeature ' : from_recordfeature , # automixon
' recordofffeature ' : from_recordfeature , # automixon
' progressinband ' : from_progressinband , # in_band_progress
' callgroup ' : set_value ,
' pickupgroup ' : set_value ,
' namedcallgroup ' : set_value ,
' namedpickupgroup ' : set_value ,
' busylevel ' : set_value ( ' devicestate_busy_at ' ) ,
[ ' directmedia ' , set_direct_media ] , # direct_media
# direct_media_method
# directed_media_glare_mitigation
# disable_directed_media_on_nat
[ ' callerid ' , set_value ] , # callerid
[ ' callingpres ' , set_value ( ' callerid_privacy ' ) ] ,
[ ' cid_tag ' , set_value ( ' callerid_tag ' ) ] ,
[ ' trustpid ' , set_value ( ' trust_id_inbound ' ) ] ,
[ ' sendrpid ' , from_sendrpid ] , # send_pai, send_rpid
[ ' send_diversion ' , set_value ] ,
[ ' encrpytion ' , set_media_encryption ] ,
[ ' use_avpf ' , set_value ] ,
[ ' recordonfeature ' , from_recordfeature ] , # automixon
[ ' recordofffeature ' , from_recordfeature ] , # automixon
[ ' progressinband ' , from_progressinband ] , # in_band_progress
[ ' callgroup ' , set_value ] ,
[ ' pickupgroup ' , set_value ] ,
[ ' namedcallgroup ' , set_value ] ,
[ ' namedpickupgroup ' , set_value ] ,
[ ' busylevel ' , set_value ( ' devicestate_busy_at ' ) ] ,
############################ maps to an aor ###################################
' host ' : from_host , # contact, max_contacts
' subscribemwi ' : from_subscribemwi , # mailboxes
' qualifyfreq ' : set_value ( ' qualify_frequency ' , type = ' aor ' ) ,
[ ' host ' , from_host ] , # contact, max_contacts
[ ' subscribemwi ' , from_subscribemwi ] , # mailboxes
[ ' qualifyfreq ' , set_value ( ' qualify_frequency ' , type = ' aor ' ) ] ,
############################# maps to auth#####################################
# type = auth
@ -226,12 +250,12 @@ peer_map = {
# auth_type
######################### maps to acl/security ################################
' permit ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
' deny ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
' acl ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
' contactpermit ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
' contactdeny ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
' contactacl ' : merge_value ( type = ' security ' , section_to = ' acl ' ) ,
[ ' permit ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
[ ' deny ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
[ ' acl ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
[ ' contactpermit ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
[ ' contactdeny ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
[ ' contactacl ' , merge_value ( type = ' security ' , section_to = ' acl ' ) ] ,
########################### maps to transport #################################
# type = transport
@ -271,34 +295,98 @@ peer_map = {
# type = identify
# endpoint
# match
}
]
def map_peer ( s ection) :
for key, fun in peer_map . iteritems ( ) :
def map_peer ( s ip, s ection, res_sip , nmapped ) :
for i in peer_map :
try :
fun ( key , sip . get ( key , section ) , section )
# coming from sip.conf the values should mostly be a list with a
# single value. In the few cases that they are not a specialized
# function (see merge_value) is used to retrieve the values.
i [ 1 ] ( i [ 0 ] , sip . get ( section , i [ 0 ] ) [ 0 ] , section , res_sip , nmapped )
except LookupError :
pass
# print "%s not found for section %s - putting nothing in res_sip.conf" % (key, section)
pass # key not found in sip.conf
# since we are pulling from sip.conf this should always return
# a single peer value and never a list of peers
peer = sip . section ( section )
# loop through the peer and print out any that can't be mapped
# for key in peer.keys():
# if key not in peer_map:
# print "Peer: [{}] {} could not be mapped".format(section, key)
def find_non_mapped ( sections , nmapped ) :
for section , sect in sections . iteritems ( ) :
try :
# since we are pulling from sip.conf this should always
# be a single value list
sect = sect [ 0 ]
# loop through the section and store any values that were not mapped
for key in sect . keys ( True ) :
for i in peer_map :
if i [ 0 ] == key :
break ;
else :
nmapped ( section , key , sect [ key ] )
except LookupError :
pass
def convert ( ) :
def convert ( sip , filename , non_mappings ) :
res_sip = astconfigparser . MultiOrderedConfigParser ( )
non_mappings [ filename ] = astdicts . MultiOrderedDict ( )
nmapped = non_mapped ( non_mappings [ filename ] )
for section in sip . sections ( ) :
if section == ' authentication ' :
pass
elif section != ' general ' :
map_peer ( section )
else :
map_peer ( sip , section , res_sip , nmapped )
find_non_mapped ( sip . defaults ( ) , nmapped )
find_non_mapped ( sip . sections ( ) , nmapped )
for key , val in sip . includes ( ) . iteritems ( ) :
res_sip . add_include ( PREFIX + key , convert ( val , PREFIX + key , non_mappings ) [ 0 ] )
return res_sip , non_mappings
def write_res_sip ( filename , res_sip , non_mappings ) :
try :
with open ( filename , ' wt ' ) as fp :
fp . write ( ' ;-- \n ' )
fp . write ( ' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n ' )
fp . write ( ' Non mapped elements start \n ' )
fp . write ( ' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n \n ' )
astconfigparser . write_dicts ( fp , non_mappings [ filename ] )
fp . write ( ' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n ' )
fp . write ( ' Non mapped elements end \n ' )
fp . write ( ' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n ' )
fp . write ( ' --; \n \n ' )
# write out include file(s)
for key , val in res_sip . includes ( ) . iteritems ( ) :
write_res_sip ( key , val , non_mappings )
fp . write ( ' #include " %s " \n ' % key )
fp . write ( ' \n ' )
# write out mapped data elements
astconfigparser . write_dicts ( fp , res_sip . defaults ( ) )
astconfigparser . write_dicts ( fp , res_sip . sections ( ) )
except IOError :
print " Could not open file " , filename , " for writing "
###############################################################################
def cli_options ( ) :
global PREFIX
usage = " usage: % prog [options] [input-file [output-file]] \n \n " \
" input-file defaults to ' sip.conf ' \n " \
" output-file defaults to ' res_sip.conf ' "
parser = optparse . OptionParser ( usage = usage )
parser . add_option ( ' -p ' , ' --prefix ' , dest = ' prefix ' , default = PREFIX ,
help = ' output prefix for include files ' )
options , args = parser . parse_args ( )
PREFIX = options . prefix
sip_filename = args [ 0 ] if len ( args ) else ' sip.conf '
res_sip_filename = args [ 1 ] if len ( args ) == 2 else ' res_sip.conf '
return sip_filename , res_sip_filename
if __name__ == " __main__ " :
sip . read ( ' sip.conf ' )
convert ( )
res_sip . write ( ' res_sip.conf ' )
sip_filename , res_sip_filename = cli_options ( )
# configuration parser for sip.conf
sip = astconfigparser . MultiOrderedConfigParser ( )
sip . read ( sip_filename )
res_sip , non_mappings = convert ( sip , res_sip_filename , dict ( ) )
write_res_sip ( res_sip_filename , res_sip , non_mappings )