view doc/dbg_interactive.py.sample @ 1327:82b386714795

Set callback data also when only setting expire callback (and not answer callback as well). It is used when calling the expire callback, so not setting it makes no sense.
author Thomas Klausner <tk@giga.or.at>
date Mon, 27 Nov 2017 15:21:20 +0100
parents e5010975da35
children
line wrap: on
line source

# Example file for the dbg_interactive.fdx extension.
#
# This extension provides an interactive python interpreter console that allows
# interacting with freeDiameter framework.
#
# The adaptation layer between Python and C is provided by SWIG (http://swig.org). 
# You may refer to SWIG documentation for more information on how the wrapper is generated and used.
# The name of the module wrapping freeDiameter framework is: _fDpy
#
# Similar to all freeDiameter extensions, an optional filename can be specified in the
# main freeDiameter.conf configuration file for the dbg_interactive.fdx extension.
# If such file is provided, it will be passed to the python interpreter as a python script
# to execute. Otherwise, the interpreter will be interactive.
#
# SWIG deals with structures as follow:
# Given the structure:
# struct foo { int a; }
# The following functions are available to python (their C equivalent processing is given in [ ]):
# s = new_foo()	   [ s = calloc(1, sizeof(struct foo)) ]
# foo_a_set(s, 2)  [ s->a = 2 ]
# foo_a_get(s)     [ returns s->a value ]
# delete_foo(s)    [ free(s)  ]
#
# In addition, thanks to the proxy (aka shadow) class, we can also do the more user-friendly:
# s = foo()
# s.a = 2
# s.a
# del s
#

# The remaining of this file gives some examples of how to use the python interpreter.
# Note that at the moment not 100% of the framework is usable. 
# You may have to extend some classes or write some typemaps in the source code 
# of the extension to do what you want.


############# Compilation-time constants (from freeDiameter-host.h) ############

# Display current version
print "%s %d.%d.%d" % (FD_PROJECT_NAME, FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, FD_PROJECT_VERSION_REV)


############# Debug ############

# Change the global debug level of the framework (cvar contains all global variables)
cvar.fd_g_debug_lvl = 1


# Turn on debug for a specific function (if framework compiled with DEBUG support)
cvar.fd_debug_one_function = "gc_th_fct"


# Print messages to freeDiameter's debug facility
# Note: the python version does not support printf-like argument list. The formating should be done in python.
#       See SWIG documentation about varargs functions for more information.
fd_log(FD_LOG_NOTICE, "3 + 4 = %d" % (7))


# Display some framework state information
conf = fd_conf_dump();
print conf;

fd_peer_dump_list(0)
fd_servers_dump(0)
fd_ext_dump(0)


############# Global variables ############

# Display the local Diameter Identity:
print "Local Diameter Identity:", cvar.fd_g_config.cnf_diamid

# Display realm, using the low-level functions (skip proxy classe definitions):
print "Realm:", _fDpy.fd_config_cnf_diamrlm_get(_fDpy.cvar.fd_g_config)



############# Lists ############

# Note: we use different names from the C API here, for usability.
l1 = fd_list()   # Will be our sentinel
l2 = fd_list()
l3 = fd_list()
l1.isempty()
l1.insert_next(l2)   # l1 -> l2
l1.isempty()
l1.insert_prev(l3)   # l1 -> l2 -> l3 (circular list)
l1.dump()
l3.detach()          # l1 -> l2
l4=fd_list()
l5=fd_list()
l3.insert_next(l4)   #   l3 -> l4
l3.insert_next(l5)   #   l3 -> l5 -> l4
l1.concat(l3)        # l1 -> l2 -> l5 -> l4

elements = l1.enum_as()  # default: enumerates as fd_list. Warning: this a copy, changing the python list has no effect on the underlying fd_list.
for li in elements:
  li.dump()

del elements
del l2
del l3
del l4
del l5
l1.isempty() # The destructor has an implicit fd_list_unlink call
del l1


############# Hash ############

hex(fd_os_hash("hello world"))	# It accepts binary data


############# Dictionary ############

##### Create a dedicated dictionary for our tests
d = dictionary()
d.dump()

# New vendor
v = dict_vendor_data()
v.vendor_id = 123
v.vendor_name = "My test vendor"
my_vendor = d.new_obj(DICT_VENDOR, v)
del v
d.dump()
d.vendors_list()

# Compact invocation also possible:
v2 = dict_vendor_data(124, "My test vendor 2")
del v2

# New application
a = dict_application_data()
a.application_id = 99
a.application_name = "My test appl"
my_appl = d.new_obj(DICT_APPLICATION, a, my_vendor)
del a

a2 = dict_application_data(99, "My test appl 2")
del a2

# New type (callbacks are not supported yet...)
t = dict_type_data()
t.type_base = AVP_TYPE_INTEGER32
t.type_name = "My integer AVP"
my_type_int = d.new_obj(DICT_TYPE, t, my_appl)
t.type_base = AVP_TYPE_OCTETSTRING
t.type_name = "My binary buffer AVP"
my_type_os = d.new_obj(DICT_TYPE, t, my_appl)
del t

t2 = dict_type_data(AVP_TYPE_UNSIGNED32, "u32 type")
del t2

# Constants
c = dict_enumval_data()
c.enum_name = "AVP_VALUE_TROIS"
c.enum_value.i32 = 3
d.new_obj(DICT_ENUMVAL, c, my_type_int)

c.enum_name = "A_BUFFER_CONSTANT"
c.enum_value.os = "This is a very long AVP value that we prefer to represent as a constant"
c.enum_value.os.dump()
d.new_obj(DICT_ENUMVAL, c, my_type_os)
del c

c2 = dict_enumval_data("enum 23", 23)  # The constructor only accepts unsigned32, for other values, set them afterwards
c3 = dict_enumval_data("enum other")
c3.os = "other value"
del c2
del c3

# AVP
a = dict_avp_data()
a.avp_code = 234
a.avp_name = "my integer avp"
a.avp_flag_mask = AVP_FLAG_MANDATORY
a.avp_basetype = AVP_TYPE_INTEGER32
my_avp_int = d.new_obj(DICT_AVP, a, my_type_int)

a.avp_vendor = 123
a.avp_name = "my OS avp"
a.avp_flag_mask = AVP_FLAG_MANDATORY + AVP_FLAG_VENDOR
a.avp_flag_val = AVP_FLAG_VENDOR
a.avp_basetype = AVP_TYPE_OCTETSTRING
my_avp_os = d.new_obj(DICT_AVP, a, my_type_os)
del a

a2 = dict_avp_data(235, "no vendor, not mandatory", AVP_TYPE_OCTETSTRING)
a3 = dict_avp_data(236, "vendor 12, not mandatory", AVP_TYPE_OCTETSTRING, 12)
a4 = dict_avp_data(237, "vendor 12, mandatory", AVP_TYPE_OCTETSTRING, 12, 1)
a5 = dict_avp_data(238, "no vendor, mandatory", AVP_TYPE_OCTETSTRING, 0, 1)
del a2
del a3
del a4
del a5


# Command
c = dict_cmd_data()
c.cmd_code = 345
c.cmd_name = "My-Python-Request"
c.cmd_flag_mask = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
c.cmd_flag_val = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
my_req = d.new_obj(DICT_COMMAND, c, my_appl)
c.cmd_name = "My-Python-Answer"
c.cmd_flag_val = CMD_FLAG_PROXIABLE
my_ans = d.new_obj(DICT_COMMAND, c, my_appl)
del c

c2 = dict_cmd_data(346, "Second-Request", 1) # Default created with PROXIABLE flag.
c3 = dict_cmd_data(346, "Second-Answer",  0) 
del c2
del c3

# Rule
r = dict_rule_data()
r.rule_avp = my_avp_int
r.rule_position = RULE_REQUIRED
r.rule_min = -1
r.rule_max = -1
d.new_obj(DICT_RULE, r, my_req)
d.new_obj(DICT_RULE, r, my_ans)
r.rule_avp = my_avp_os
d.new_obj(DICT_RULE, r, my_req)
d.new_obj(DICT_RULE, r, my_ans)
del r

r2 = dict_rule_data(my_avp_int, RULE_REQUIRED) # min & max are optional parameters, default to -1
r3 = dict_rule_data(my_avp_int, RULE_REQUIRED, 2, 3) # min is 2, max is 3
r4 = dict_rule_data(my_avp_int, RULE_FIXED_HEAD) # The r4.rule_order = 1 by default, change afterwards if needed.
del r2
del r3
del r4

d.dump()
del d

####### Now play with the "real" dictionary

gdict = cvar.fd_g_config.cnf_dict

appl = gdict.search ( DICT_APPLICATION, APPLICATION_BY_ID, 3 )
appl.dump()
avp = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host")
avp.dump()
errcmd = gdict.error_cmd()

v = avp.getval()
print v.avp_code
del v

t = avp.gettype()
print t
del t

dict = avp.getdict()
del dict


############# Sessions ############

# handler
def my_cleanup(state,sid):
    print "Cleaning up python state for session:", sid
    print "Received state:", state
    del state

hdl = session_handler(my_cleanup)
hdl.dump()
del hdl

# Session
hdl = session_handler(my_cleanup)
s1 = session()
s1.getsid()
s2 = session("this.is.a.full.session.id")
r,s3,isnew = fd_sess_fromsid("this.is.a.full.session.id")  # use this call if "isnew" is really needed...
s4 = session("host.id", "optional.part")
s4.settimeout(30) # the python wrapper takes a number of seconds as parameter for simplicity
s4.dump()

# states
mystate = [ 34, "blah", [ 32, 12 ] ]
s1.store(hdl, mystate)
del mystate
gotstate = s1.retrieve(hdl)
print gotstate
del gotstate


############# Routing ############

rd = rt_data()

rd.add("p1.testbed.aaa", "testbed.aaa")
rd.add("p2.testbed.aaa", "testbed.aaa")
rd.add("p3.testbed.aaa", "testbed.aaa")
rd.add("p4.testbed.aaa", "testbed.aaa")

rd.remove("p2.testbed.aaa")

rd.error("p3.testbed.aaa", "relay.testbed.aaa", 3002)

list = rd.extract(-1)
for c in list.enum_as("struct rtd_candidate *"):
  print "%s (%s): %s" % (c.diamid, c.realm, c.score)

del rd


# A rt_fwd callback has the following prototype:
def my_rtfwd_cb(msg):
    print "Forwarding the following message:"
    msg.dump()
    return [ 0, msg ]  # return None instead of msg to stop forwarding.

fwdhdl = fd_rt_fwd_hdl( my_rtfwd_cb, RT_FWD_REQ )


# A rt_out cb has the following prototype:
def my_rtout_cb(msg, list):
    print "Sending out the following message:"
    msg.dump()
    print "The possible candidates are:"
    for c in list.enum_as("struct rtd_candidate *"):
       print "%s (%s): %s" % (c.diamid, c.realm, c.score)
    return 0   # returns an error code (standard errno values)

outhdl = fd_rt_out_hdl( my_rtout_cb )  # a priority can be specified as 2nd parameter, default is 0.





############# Messages, AVPs ############

## AVP

# Create empty
blank_avp = avp()
del blank_avp

# Create from dictionary definitions
oh = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host"))	                # Octet String
vi = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Id"))                        # U32
vsai = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id")) # Grouped

# Set values
val = avp_value()
val.u32 = 123
vi.setval(None)  # this cleans a previous value (usually not needed)
vi.setval(val)
val.os = "my.origin.host"
oh.setval(val)
vsai.add_child(vi) # call as add_child(vi, 1) to add the new AVP at the beginning, default is at the end

# It is possible to initialize the AVP with a blank value as follow:
blank_with_value = avp(None, AVPFL_SET_BLANK_VALUE)
# it enables this without doing the setval call:
blank_with_value.header().avp_value.u32 = 12


## Messages

# Create empt (as for avps, pass None or a dictionary object as 1st param, and flags as optional 2nd param)y
a_msg = msg()
a_msg.dump()
del a_msg

# It is also possible to pass MSGFL_* flags in second parameter (ALLOC_ETEID is default)
msg_no_eid = msg(None, 0)
msg_no_eid.dump()
del msg_no_eid

# Create from dictionary
dwr_dict = cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" )
dwr = msg(dwr_dict)
dwr.dump()

# Create msg from a binary buffer (then you should call parse_dict and parse_rules methods)
dwr2 = msg("\x01\x00\x00\x14\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xf0\x00\x01")

# Create answer from request (optional parameters: dictionary to use, and flags):
dwr3 = msg(cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" ))
dwa3 = dwr3.create_answer()
dwr3cpy = dwa3.get_query()


## Other functions with AVPs & messages

# Add the AVPs in the message
dwr.add_child(oh)
oh.add_next(vsai)   # equivalent to add_child on the parent

# Create a network byte buffer from the message
dwr.bufferize()

# Get first child AVP (fast)
avp = dwr.first_child()

# then:
avp = avp.get_next() # when last AVP, returns None


# Get all 1st level children (slower) -- warning, changes to the python list will not be reflected on the underlying message. read-only use.
dwr.children()
# example use:
for a in dwr.children():
  a.dump(0)  # 0 means: dump only this object, do not walk the tree


# Search the first AVP of a given type
oh_dict = cvar.fd_g_config.cnf_dict.search( DICT_AVP, AVP_BY_NAME, "Origin-Host")
oh = dwr.search( oh_dict )

# After adding AVPs, the length in the message header is outdated, refresh as follow:
dwr.update_length()

# Get dictionary model for a message or avp
dwr.model()
oh.model().dump()

# Retrieve the header of messages & avp:
dwr_hdr = dwr.header()
dwr_hdr.msg_version
dwr_hdr.msg_hbhid

oh_hdr = oh.header()
hex(oh_hdr.avp_flags)
oh_hdr.avp_vendor
oh_hdr.avp_value.os.as_str() 


# Get or set the routing data
rd = rt_data()
dwr.set_rtd(rd)
rd = dwr.get_rtd()

# Test if message is routable
dwr.is_routable()

# Which peer the message was received from (when received from network)
dwr.source()

# The session corresponding to this message (returns None when no Session-Id AVP is included)
dwr.get_session()


# Parse a buffer
buf = "\x01\x00\x00@\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00N\x10\x00\x00\x00\x00\x01\x08@\x00\x00\x16my.origin.host\x00\x00\x00\x00\x01\x04@\x00\x00\x14\x00\x00\x01\n@\x00\x00\x0c\x00\x00\x00{"
mydwr = msg(buf)
# Resolve objects in the dictionary. Return value is None or a struct pei_error in case of problem.
mydwr.parse_dict()  # if not using the fD global dict, pass it as parameter
err = mydwr.parse_rules()
err.pei_errcode


# Grouped AVPs are browsed with same methods as messages:
gavp = dwr.children()[1]
gavp.first_child().dump()
gavp.children()


# Send a message:
mydwr = msg(buf)
mydwr.send()

# Optionally, a callback can be registered when a request is sent, with an optional object.
# This callback takes the answer message as parameter and should return None or a message. (cf. fd_msg_send)
def send_callback(msg, obj):
    print "Received answer:"
    msg.dump()
    print "Associated data:"
    obj
    return None

mydwr = msg(buf)
mydwr.send(send_callback, some_object)

# Again optionally, a time limit can be specified in this case as follow:
mydwr.send(send_callback, some_object, 10)
# In that case, if no answer / error is received after 10 seconds (the value specified), 
# the callback is called with the request as parameter.
# Testing for timeout case is done by using msg.is_request()
def send_callback(msg, obj):
    if (msg.is_request()):
        print "Request timed out without answer:"
    else:
        print "Received answer:"
    msg.dump()
    print "Associated data:"
    obj
    return None


# Set a result code in an answer message.
mydwr = msg(buf)
dwa = mydwr.create_answer()
dwa.rescode_set()   # This adds the DIAMETER_SUCCESS result code
dwa.rescode_set("DIAMETER_LIMITED_SUCCESS" )   # This adds a different result code
dwa.rescode_set("DIAMETER_LIMITED_SUCCESS", "Something went not so well" )   # This adds a different result code + specified Error-Message
dwa.rescode_set("DIAMETER_INVALID_AVP", None, faulty_avp )   # This adds a Failed-AVP
dwa.rescode_set("DIAMETER_SUCCESS", None, None, 1 )   # This adds origin information (see fd_msg_rescode_set's type_id for more info)

# Set the origin to local host
mydwr.add_origin()  # adds Origin-Host & Origin-Realm
mydwr.add_origin(1) # adds Origin-State-Id in addition.


############# DISPATCH (aka. server application) ############

# As for sessions, only one dispatch handler can be registered in this extension at the moment.
# The callback for the handler has the following syntax:
def dispatch_cb_model(inmsg, inavp, insession):
   print "Callback trigged on message: "
   inmsg.dump()
   # inavp is None or the AVP that trigged the callback, depending on how it was registered.
   if inavp:
     print "From the following AVP:"
     inavp.dump()
   else:
     print "No AVP"
   # Session is provided only if a Session-Id is in the message
   if insession:
     print "The session is: ", insession.getsid()
   else:
     print "No session"
   # Now, for the return value.
   # This callback must return 3 elements:
   # - an integer which is interpreted as an error code (errno.h)
   # - a message or None, depending on the next item
   # - an enum disp_action value, with the same meaning as in C (see libfreeDiameter.h)
   del inmsg
   return [ 0, None, DISP_ACT_CONT ]


### Example use: rebuild the server-side of test_app.fdx in python

# The following block defines the dictionary objects from the test_app.fdx application that we use on the remote peer
gdict = cvar.fd_g_config.cnf_dict
d_si = gdict.search ( DICT_AVP, AVP_BY_NAME, "Session-Id" )
d_oh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host" )
d_or  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Realm" )
d_dh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Host" )
d_dr  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Realm" )
d_rc  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Result-Code" )
d_vnd = gdict.new_obj(DICT_VENDOR, 	dict_vendor_data(999999, 	"app_test_py vendor") )
d_app = gdict.new_obj(DICT_APPLICATION, dict_application_data(0xffffff, "app_test_py appli"), d_vnd)
d_req = gdict.new_obj(DICT_COMMAND, 	dict_cmd_data(0xfffffe, "Test_py-Request", 1), d_app)
d_ans = gdict.new_obj(DICT_COMMAND, 	dict_cmd_data(0xfffffe, "Test_py-Answer",  0), d_app)
d_avp = gdict.new_obj(DICT_AVP, 	dict_avp_data(0xffffff, "app_test_py avp", AVP_TYPE_INTEGER32, 999999 ))
gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_ans)
gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_ans)
gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_ans)
gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_ans)
gdict.new_obj(DICT_RULE, dict_rule_data(d_dr, RULE_REQUIRED, 1, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_dh, RULE_OPTIONAL, 0, 1), d_req)
gdict.new_obj(DICT_RULE, dict_rule_data(d_rc, RULE_REQUIRED, 1, 1), d_ans)

# Now, create the Test_app server callback:
def test_app_cb(inmsg, inavp, insession):
   tval = inmsg.search(d_avp).header().avp_value.u32
   print "Py ECHO Test message from '%s' with test value %x, replying..." % (inmsg.search(d_oh).header().avp_value.os.as_str(), tval)
   answ = inmsg.create_answer()
   answ.rescode_set()
   answ.add_origin()
   ta = avp(d_avp, AVPFL_SET_BLANK_VALUE)
   ta.header().avp_value.u32 = tval
   answ.add_child(ta)
   return [ 0, answ, DISP_ACT_SEND ]

# Register the callback for dispatch thread:
hdl = disp_hdl(test_app_cb, DISP_HOW_CC, disp_when(d_app, d_req))  # disp_when() takes 0 to 4 arguments as follow: (app=NULL, cmd=NULL, avp=NULL, val=NULL)

# Don't forget to register the application in the daemon for CER/CEA capabilities.
fd_disp_app_support ( d_app, d_vnd, 1, 0 )


###  For the fun, the client part of the test_app:

def receive_answer(ans, testval):
   try:
     tval = ans.search(d_avp).header().avp_value.u32
   except:
     print "Error in receive_answer: no Test-AVP included"
     tval = 0
   try:
     print "Py RECV %x (expected: %x) Status: %d From: '%s'" % (tval, testval, ans.search(d_rc).header().avp_value.u32, ans.search(d_oh).header().avp_value.os.as_str())
   except:
     print "Error in receive_answer: Result-Code or Origin-Host are missing"
   del ans
   return None

import random

def send_query(destrealm="localdomain"):
   qry = msg(d_req)
   sess = session()
   tv = random.randint(1, 1<<32)
   # Session-Id
   a = avp(d_si, AVPFL_SET_BLANK_VALUE)
   a.header().avp_value.os = sess.getsid()
   qry.add_child(a)
   # Destination-Realm
   a = avp(d_dr, AVPFL_SET_BLANK_VALUE)
   a.header().avp_value.os = destrealm
   qry.add_child(a)
   # Origin-Host, Origin-Realm
   qry.add_origin()
   # Test-AVP
   a = avp(d_avp, AVPFL_SET_BLANK_VALUE)
   a.header().avp_value.u32 = tv
   qry.add_child(a)
   print "Py SEND %x to '%s'" % (tv, destrealm)
   qry.send(receive_answer, tv)

send_query()


############# FIFO queues ############

myqueue = fifo()

# enqueue any object
myqueue.post(3)
myqueue.post("blah")
myqueue.post( [ 3, 2 ] )

# Simple get (blocks when the queue is empty)
myqueue.get()

# Try get: returns the next object, or None if the queue is empty
myqueue.tryget()

# timed get: like get, but returns None after x seconds
myqueue.timedget(3)

# Show the number of items in the queue
myqueue.length()


## Variants:
# All the previous calls are suitable to queue Python objects.
# In order to interact with objects queued / poped by C counterpart,
# a second parameter must be passed to specify the object type,
# as follow:
ev = fd_event()
ev.code = FDEV_DUMP_EXT
cvar.fd_g_config.cnf_main_ev.post(ev, "struct fd_event *")

# Similarly, for *get, we can specify the structure that was queued:
myqueue.get("struct fd_event *")
myqueue.tryget("struct fd_event *")
myqueue.timedget(3, "struct fd_event *")

del myqueue


############# HOOKS ############

def my_hook_cb(type, msg, peer, other, oldpmd):
    print "callback type ", type, " called: ", msg, other, oldpmd
    return "this is the new pmd"

# Create a wrapped fd_hook_data_hdl:
datahdl = fd_hook_data_hdl()

# Register the hook callback:
hdl = fd_hook_hdl(1 << HOOK_MESSAGE_SENT, my_hook_cb, datahdl)




############# PEERS ############

# Get the list of peers defined in the system 
# (we are supposed to readlock fd_g_peers_rw before accessing this list)
cvar.fd_g_peers_rw.rdlock()
peers = cvar.fd_g_peers.enum_as("struct peer_hdr *")
cvar.fd_g_peers_rw.unlock()
for p in peers:
   print "Peer:", p.info.pi_diamid


# Create a new peer
np = peer_info()
np.pi_diamid = "nas.localdomain"
np.config.pic_flags.pro4 = PI_P4_TCP


# Add this peer into the framework.
np.add()

# It is possible to specify a callback for when the connection completes or fails with this peer.
# The prototype is as follow:
def add_cb(peer):
    if peer:
        if peer.runtime.pir_state == STATE_OPEN:
	   print "Connection to peer '%s' completed" % (peer.pi_diamid)
	   # can find more information in peer.runtime.*
	else:
	   print "Connection to peer '%s' failed (state:%d)" % (peer.pi_diamid, peer.runtime.pir_state)
    else:
        print "The peer has been destroyed before it completed the connection."

# Then add the peer like this:
np.add(add_cb)


# Search a peer by its diameter id (returns a peer_hdr object if found) -- similar to fd_peer_getbyid
p = peer_search("nas.domain.aaa")


## Validation callback (see fd_peer_validate_register documentation)

# cb2 prototype:
def my_validate_cb2(pinfo):
    print "Cb2 callback trigged for peer %s" % (pinfo.pi_diamid)
    # Usually, this would be used only to check some TLS properties, 
    # which is not really possible yet through the python interpreter...
    return 0   # return an error code if the peer is not validated

# cb prototype:
def my_validate_cb(pinfo):
    print "Validate callback trigged for peer %s" % (pinfo.pi_diamid)
    # If the peer is not allowed to connect:
    #return -1
    # If the peer is authorized:
    #return 1
    # In addition, if IPsec is allowed,
    #pinfo.config.pic_flags.sec = PI_SEC_NONE
    # If no decision has been made:
    #return 0
    # If the peer is temporarily authorized but a second callback must be called after TLS negociation:
    return my_validate_cb2

# Register the callback, it will be called on new incoming connections.
peer_validate_register(my_validate_cb)



############# ENDPOINTS ############

ep = fd_endpoint("129.168.168.192")

# with port:
ep = fd_endpoint("129.168.168.192", 3868)

# With different flags:
ep = fd_endpoint("129.168.168.192", 3868, EP_FL_PRIMARY)

# Add IP information for the peer
np = peer_info()
ep.add_merge(np.pi_endpoints)
fd_ep_dump(0, np.pi_endpoints)



############# POSIX functions wrappers ############

# The interface also provides wrappers around base POSIX 
# synchronization functions:

m = pthread_mutex_t()
m.lock()
m.unlock()

c = pthread_cond_t()
c.signal()
c.broadcast()
c.wait(m)
c.timedwait(m, 5)  # it takes a relative time

r = pthread_rwlock_t()
r.rdlock()
r.unlock()
r.wrlock()
"Welcome to our mercurial repository"