Navigation


source: freeDiameter/doc/dbg_interactive.py.sample

Last change on this file was 1228:e5010975da35, checked in by Thomas Klausner <tk@giga.or.at>, 9 years ago

Fix typo in comment.

File size: 23.2 KB
Line 
1# Example file for the dbg_interactive.fdx extension.
2#
3# This extension provides an interactive python interpreter console that allows
4# interacting with freeDiameter framework.
5#
6# The adaptation layer between Python and C is provided by SWIG (http://swig.org).
7# You may refer to SWIG documentation for more information on how the wrapper is generated and used.
8# The name of the module wrapping freeDiameter framework is: _fDpy
9#
10# Similar to all freeDiameter extensions, an optional filename can be specified in the
11# main freeDiameter.conf configuration file for the dbg_interactive.fdx extension.
12# If such file is provided, it will be passed to the python interpreter as a python script
13# to execute. Otherwise, the interpreter will be interactive.
14#
15# SWIG deals with structures as follow:
16# Given the structure:
17# struct foo { int a; }
18# The following functions are available to python (their C equivalent processing is given in [ ]):
19# s = new_foo()    [ s = calloc(1, sizeof(struct foo)) ]
20# foo_a_set(s, 2)  [ s->a = 2 ]
21# foo_a_get(s)     [ returns s->a value ]
22# delete_foo(s)    [ free(s)  ]
23#
24# In addition, thanks to the proxy (aka shadow) class, we can also do the more user-friendly:
25# s = foo()
26# s.a = 2
27# s.a
28# del s
29#
30
31# The remaining of this file gives some examples of how to use the python interpreter.
32# Note that at the moment not 100% of the framework is usable.
33# You may have to extend some classes or write some typemaps in the source code
34# of the extension to do what you want.
35
36
37############# Compilation-time constants (from freeDiameter-host.h) ############
38
39# Display current version
40print "%s %d.%d.%d" % (FD_PROJECT_NAME, FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, FD_PROJECT_VERSION_REV)
41
42
43############# Debug ############
44
45# Change the global debug level of the framework (cvar contains all global variables)
46cvar.fd_g_debug_lvl = 1
47
48
49# Turn on debug for a specific function (if framework compiled with DEBUG support)
50cvar.fd_debug_one_function = "gc_th_fct"
51
52
53# Print messages to freeDiameter's debug facility
54# Note: the python version does not support printf-like argument list. The formating should be done in python.
55#       See SWIG documentation about varargs functions for more information.
56fd_log(FD_LOG_NOTICE, "3 + 4 = %d" % (7))
57
58
59# Display some framework state information
60conf = fd_conf_dump();
61print conf;
62
63fd_peer_dump_list(0)
64fd_servers_dump(0)
65fd_ext_dump(0)
66
67
68############# Global variables ############
69
70# Display the local Diameter Identity:
71print "Local Diameter Identity:", cvar.fd_g_config.cnf_diamid
72
73# Display realm, using the low-level functions (skip proxy classe definitions):
74print "Realm:", _fDpy.fd_config_cnf_diamrlm_get(_fDpy.cvar.fd_g_config)
75
76
77
78############# Lists ############
79
80# Note: we use different names from the C API here, for usability.
81l1 = fd_list()   # Will be our sentinel
82l2 = fd_list()
83l3 = fd_list()
84l1.isempty()
85l1.insert_next(l2)   # l1 -> l2
86l1.isempty()
87l1.insert_prev(l3)   # l1 -> l2 -> l3 (circular list)
88l1.dump()
89l3.detach()          # l1 -> l2
90l4=fd_list()
91l5=fd_list()
92l3.insert_next(l4)   #   l3 -> l4
93l3.insert_next(l5)   #   l3 -> l5 -> l4
94l1.concat(l3)        # l1 -> l2 -> l5 -> l4
95
96elements = l1.enum_as()  # default: enumerates as fd_list. Warning: this a copy, changing the python list has no effect on the underlying fd_list.
97for li in elements:
98  li.dump()
99
100del elements
101del l2
102del l3
103del l4
104del l5
105l1.isempty() # The destructor has an implicit fd_list_unlink call
106del l1
107
108
109############# Hash ############
110
111hex(fd_os_hash("hello world"))  # It accepts binary data
112
113
114############# Dictionary ############
115
116##### Create a dedicated dictionary for our tests
117d = dictionary()
118d.dump()
119
120# New vendor
121v = dict_vendor_data()
122v.vendor_id = 123
123v.vendor_name = "My test vendor"
124my_vendor = d.new_obj(DICT_VENDOR, v)
125del v
126d.dump()
127d.vendors_list()
128
129# Compact invocation also possible:
130v2 = dict_vendor_data(124, "My test vendor 2")
131del v2
132
133# New application
134a = dict_application_data()
135a.application_id = 99
136a.application_name = "My test appl"
137my_appl = d.new_obj(DICT_APPLICATION, a, my_vendor)
138del a
139
140a2 = dict_application_data(99, "My test appl 2")
141del a2
142
143# New type (callbacks are not supported yet...)
144t = dict_type_data()
145t.type_base = AVP_TYPE_INTEGER32
146t.type_name = "My integer AVP"
147my_type_int = d.new_obj(DICT_TYPE, t, my_appl)
148t.type_base = AVP_TYPE_OCTETSTRING
149t.type_name = "My binary buffer AVP"
150my_type_os = d.new_obj(DICT_TYPE, t, my_appl)
151del t
152
153t2 = dict_type_data(AVP_TYPE_UNSIGNED32, "u32 type")
154del t2
155
156# Constants
157c = dict_enumval_data()
158c.enum_name = "AVP_VALUE_TROIS"
159c.enum_value.i32 = 3
160d.new_obj(DICT_ENUMVAL, c, my_type_int)
161
162c.enum_name = "A_BUFFER_CONSTANT"
163c.enum_value.os = "This is a very long AVP value that we prefer to represent as a constant"
164c.enum_value.os.dump()
165d.new_obj(DICT_ENUMVAL, c, my_type_os)
166del c
167
168c2 = dict_enumval_data("enum 23", 23)  # The constructor only accepts unsigned32, for other values, set them afterwards
169c3 = dict_enumval_data("enum other")
170c3.os = "other value"
171del c2
172del c3
173
174# AVP
175a = dict_avp_data()
176a.avp_code = 234
177a.avp_name = "my integer avp"
178a.avp_flag_mask = AVP_FLAG_MANDATORY
179a.avp_basetype = AVP_TYPE_INTEGER32
180my_avp_int = d.new_obj(DICT_AVP, a, my_type_int)
181
182a.avp_vendor = 123
183a.avp_name = "my OS avp"
184a.avp_flag_mask = AVP_FLAG_MANDATORY + AVP_FLAG_VENDOR
185a.avp_flag_val = AVP_FLAG_VENDOR
186a.avp_basetype = AVP_TYPE_OCTETSTRING
187my_avp_os = d.new_obj(DICT_AVP, a, my_type_os)
188del a
189
190a2 = dict_avp_data(235, "no vendor, not mandatory", AVP_TYPE_OCTETSTRING)
191a3 = dict_avp_data(236, "vendor 12, not mandatory", AVP_TYPE_OCTETSTRING, 12)
192a4 = dict_avp_data(237, "vendor 12, mandatory", AVP_TYPE_OCTETSTRING, 12, 1)
193a5 = dict_avp_data(238, "no vendor, mandatory", AVP_TYPE_OCTETSTRING, 0, 1)
194del a2
195del a3
196del a4
197del a5
198
199
200# Command
201c = dict_cmd_data()
202c.cmd_code = 345
203c.cmd_name = "My-Python-Request"
204c.cmd_flag_mask = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
205c.cmd_flag_val = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
206my_req = d.new_obj(DICT_COMMAND, c, my_appl)
207c.cmd_name = "My-Python-Answer"
208c.cmd_flag_val = CMD_FLAG_PROXIABLE
209my_ans = d.new_obj(DICT_COMMAND, c, my_appl)
210del c
211
212c2 = dict_cmd_data(346, "Second-Request", 1) # Default created with PROXIABLE flag.
213c3 = dict_cmd_data(346, "Second-Answer",  0)
214del c2
215del c3
216
217# Rule
218r = dict_rule_data()
219r.rule_avp = my_avp_int
220r.rule_position = RULE_REQUIRED
221r.rule_min = -1
222r.rule_max = -1
223d.new_obj(DICT_RULE, r, my_req)
224d.new_obj(DICT_RULE, r, my_ans)
225r.rule_avp = my_avp_os
226d.new_obj(DICT_RULE, r, my_req)
227d.new_obj(DICT_RULE, r, my_ans)
228del r
229
230r2 = dict_rule_data(my_avp_int, RULE_REQUIRED) # min & max are optional parameters, default to -1
231r3 = dict_rule_data(my_avp_int, RULE_REQUIRED, 2, 3) # min is 2, max is 3
232r4 = dict_rule_data(my_avp_int, RULE_FIXED_HEAD) # The r4.rule_order = 1 by default, change afterwards if needed.
233del r2
234del r3
235del r4
236
237d.dump()
238del d
239
240####### Now play with the "real" dictionary
241
242gdict = cvar.fd_g_config.cnf_dict
243
244appl = gdict.search ( DICT_APPLICATION, APPLICATION_BY_ID, 3 )
245appl.dump()
246avp = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host")
247avp.dump()
248errcmd = gdict.error_cmd()
249
250v = avp.getval()
251print v.avp_code
252del v
253
254t = avp.gettype()
255print t
256del t
257
258dict = avp.getdict()
259del dict
260
261
262############# Sessions ############
263
264# handler
265def my_cleanup(state,sid):
266    print "Cleaning up python state for session:", sid
267    print "Received state:", state
268    del state
269
270hdl = session_handler(my_cleanup)
271hdl.dump()
272del hdl
273
274# Session
275hdl = session_handler(my_cleanup)
276s1 = session()
277s1.getsid()
278s2 = session("this.is.a.full.session.id")
279r,s3,isnew = fd_sess_fromsid("this.is.a.full.session.id")  # use this call if "isnew" is really needed...
280s4 = session("host.id", "optional.part")
281s4.settimeout(30) # the python wrapper takes a number of seconds as parameter for simplicity
282s4.dump()
283
284# states
285mystate = [ 34, "blah", [ 32, 12 ] ]
286s1.store(hdl, mystate)
287del mystate
288gotstate = s1.retrieve(hdl)
289print gotstate
290del gotstate
291
292
293############# Routing ############
294
295rd = rt_data()
296
297rd.add("p1.testbed.aaa", "testbed.aaa")
298rd.add("p2.testbed.aaa", "testbed.aaa")
299rd.add("p3.testbed.aaa", "testbed.aaa")
300rd.add("p4.testbed.aaa", "testbed.aaa")
301
302rd.remove("p2.testbed.aaa")
303
304rd.error("p3.testbed.aaa", "relay.testbed.aaa", 3002)
305
306list = rd.extract(-1)
307for c in list.enum_as("struct rtd_candidate *"):
308  print "%s (%s): %s" % (c.diamid, c.realm, c.score)
309
310del rd
311
312
313# A rt_fwd callback has the following prototype:
314def my_rtfwd_cb(msg):
315    print "Forwarding the following message:"
316    msg.dump()
317    return [ 0, msg ]  # return None instead of msg to stop forwarding.
318
319fwdhdl = fd_rt_fwd_hdl( my_rtfwd_cb, RT_FWD_REQ )
320
321
322# A rt_out cb has the following prototype:
323def my_rtout_cb(msg, list):
324    print "Sending out the following message:"
325    msg.dump()
326    print "The possible candidates are:"
327    for c in list.enum_as("struct rtd_candidate *"):
328       print "%s (%s): %s" % (c.diamid, c.realm, c.score)
329    return 0   # returns an error code (standard errno values)
330
331outhdl = fd_rt_out_hdl( my_rtout_cb )  # a priority can be specified as 2nd parameter, default is 0.
332
333
334
335
336
337############# Messages, AVPs ############
338
339## AVP
340
341# Create empty
342blank_avp = avp()
343del blank_avp
344
345# Create from dictionary definitions
346oh = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host"))                      # Octet String
347vi = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Id"))                        # U32
348vsai = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id")) # Grouped
349
350# Set values
351val = avp_value()
352val.u32 = 123
353vi.setval(None)  # this cleans a previous value (usually not needed)
354vi.setval(val)
355val.os = "my.origin.host"
356oh.setval(val)
357vsai.add_child(vi) # call as add_child(vi, 1) to add the new AVP at the beginning, default is at the end
358
359# It is possible to initialize the AVP with a blank value as follow:
360blank_with_value = avp(None, AVPFL_SET_BLANK_VALUE)
361# it enables this without doing the setval call:
362blank_with_value.header().avp_value.u32 = 12
363
364
365## Messages
366
367# Create empt (as for avps, pass None or a dictionary object as 1st param, and flags as optional 2nd param)y
368a_msg = msg()
369a_msg.dump()
370del a_msg
371
372# It is also possible to pass MSGFL_* flags in second parameter (ALLOC_ETEID is default)
373msg_no_eid = msg(None, 0)
374msg_no_eid.dump()
375del msg_no_eid
376
377# Create from dictionary
378dwr_dict = cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" )
379dwr = msg(dwr_dict)
380dwr.dump()
381
382# Create msg from a binary buffer (then you should call parse_dict and parse_rules methods)
383dwr2 = msg("\x01\x00\x00\x14\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xf0\x00\x01")
384
385# Create answer from request (optional parameters: dictionary to use, and flags):
386dwr3 = msg(cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" ))
387dwa3 = dwr3.create_answer()
388dwr3cpy = dwa3.get_query()
389
390
391## Other functions with AVPs & messages
392
393# Add the AVPs in the message
394dwr.add_child(oh)
395oh.add_next(vsai)   # equivalent to add_child on the parent
396
397# Create a network byte buffer from the message
398dwr.bufferize()
399
400# Get first child AVP (fast)
401avp = dwr.first_child()
402
403# then:
404avp = avp.get_next() # when last AVP, returns None
405
406
407# Get all 1st level children (slower) -- warning, changes to the python list will not be reflected on the underlying message. read-only use.
408dwr.children()
409# example use:
410for a in dwr.children():
411  a.dump(0)  # 0 means: dump only this object, do not walk the tree
412
413
414# Search the first AVP of a given type
415oh_dict = cvar.fd_g_config.cnf_dict.search( DICT_AVP, AVP_BY_NAME, "Origin-Host")
416oh = dwr.search( oh_dict )
417
418# After adding AVPs, the length in the message header is outdated, refresh as follow:
419dwr.update_length()
420
421# Get dictionary model for a message or avp
422dwr.model()
423oh.model().dump()
424
425# Retrieve the header of messages & avp:
426dwr_hdr = dwr.header()
427dwr_hdr.msg_version
428dwr_hdr.msg_hbhid
429
430oh_hdr = oh.header()
431hex(oh_hdr.avp_flags)
432oh_hdr.avp_vendor
433oh_hdr.avp_value.os.as_str()
434
435
436# Get or set the routing data
437rd = rt_data()
438dwr.set_rtd(rd)
439rd = dwr.get_rtd()
440
441# Test if message is routable
442dwr.is_routable()
443
444# Which peer the message was received from (when received from network)
445dwr.source()
446
447# The session corresponding to this message (returns None when no Session-Id AVP is included)
448dwr.get_session()
449
450
451# Parse a buffer
452buf = "\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{"
453mydwr = msg(buf)
454# Resolve objects in the dictionary. Return value is None or a struct pei_error in case of problem.
455mydwr.parse_dict()  # if not using the fD global dict, pass it as parameter
456err = mydwr.parse_rules()
457err.pei_errcode
458
459
460# Grouped AVPs are browsed with same methods as messages:
461gavp = dwr.children()[1]
462gavp.first_child().dump()
463gavp.children()
464
465
466# Send a message:
467mydwr = msg(buf)
468mydwr.send()
469
470# Optionally, a callback can be registered when a request is sent, with an optional object.
471# This callback takes the answer message as parameter and should return None or a message. (cf. fd_msg_send)
472def send_callback(msg, obj):
473    print "Received answer:"
474    msg.dump()
475    print "Associated data:"
476    obj
477    return None
478
479mydwr = msg(buf)
480mydwr.send(send_callback, some_object)
481
482# Again optionally, a time limit can be specified in this case as follow:
483mydwr.send(send_callback, some_object, 10)
484# In that case, if no answer / error is received after 10 seconds (the value specified),
485# the callback is called with the request as parameter.
486# Testing for timeout case is done by using msg.is_request()
487def send_callback(msg, obj):
488    if (msg.is_request()):
489        print "Request timed out without answer:"
490    else:
491        print "Received answer:"
492    msg.dump()
493    print "Associated data:"
494    obj
495    return None
496
497
498# Set a result code in an answer message.
499mydwr = msg(buf)
500dwa = mydwr.create_answer()
501dwa.rescode_set()   # This adds the DIAMETER_SUCCESS result code
502dwa.rescode_set("DIAMETER_LIMITED_SUCCESS" )   # This adds a different result code
503dwa.rescode_set("DIAMETER_LIMITED_SUCCESS", "Something went not so well" )   # This adds a different result code + specified Error-Message
504dwa.rescode_set("DIAMETER_INVALID_AVP", None, faulty_avp )   # This adds a Failed-AVP
505dwa.rescode_set("DIAMETER_SUCCESS", None, None, 1 )   # This adds origin information (see fd_msg_rescode_set's type_id for more info)
506
507# Set the origin to local host
508mydwr.add_origin()  # adds Origin-Host & Origin-Realm
509mydwr.add_origin(1) # adds Origin-State-Id in addition.
510
511
512############# DISPATCH (aka. server application) ############
513
514# As for sessions, only one dispatch handler can be registered in this extension at the moment.
515# The callback for the handler has the following syntax:
516def dispatch_cb_model(inmsg, inavp, insession):
517   print "Callback trigged on message: "
518   inmsg.dump()
519   # inavp is None or the AVP that trigged the callback, depending on how it was registered.
520   if inavp:
521     print "From the following AVP:"
522     inavp.dump()
523   else:
524     print "No AVP"
525   # Session is provided only if a Session-Id is in the message
526   if insession:
527     print "The session is: ", insession.getsid()
528   else:
529     print "No session"
530   # Now, for the return value.
531   # This callback must return 3 elements:
532   # - an integer which is interpreted as an error code (errno.h)
533   # - a message or None, depending on the next item
534   # - an enum disp_action value, with the same meaning as in C (see libfreeDiameter.h)
535   del inmsg
536   return [ 0, None, DISP_ACT_CONT ]
537
538
539### Example use: rebuild the server-side of test_app.fdx in python
540
541# The following block defines the dictionary objects from the test_app.fdx application that we use on the remote peer
542gdict = cvar.fd_g_config.cnf_dict
543d_si = gdict.search ( DICT_AVP, AVP_BY_NAME, "Session-Id" )
544d_oh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host" )
545d_or  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Realm" )
546d_dh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Host" )
547d_dr  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Realm" )
548d_rc  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Result-Code" )
549d_vnd = gdict.new_obj(DICT_VENDOR,      dict_vendor_data(999999,        "app_test_py vendor") )
550d_app = gdict.new_obj(DICT_APPLICATION, dict_application_data(0xffffff, "app_test_py appli"), d_vnd)
551d_req = gdict.new_obj(DICT_COMMAND,     dict_cmd_data(0xfffffe, "Test_py-Request", 1), d_app)
552d_ans = gdict.new_obj(DICT_COMMAND,     dict_cmd_data(0xfffffe, "Test_py-Answer",  0), d_app)
553d_avp = gdict.new_obj(DICT_AVP,         dict_avp_data(0xffffff, "app_test_py avp", AVP_TYPE_INTEGER32, 999999 ))
554gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_req)
555gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_ans)
556gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_req)
557gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_ans)
558gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_req)
559gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_ans)
560gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_req)
561gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_ans)
562gdict.new_obj(DICT_RULE, dict_rule_data(d_dr, RULE_REQUIRED, 1, 1), d_req)
563gdict.new_obj(DICT_RULE, dict_rule_data(d_dh, RULE_OPTIONAL, 0, 1), d_req)
564gdict.new_obj(DICT_RULE, dict_rule_data(d_rc, RULE_REQUIRED, 1, 1), d_ans)
565
566# Now, create the Test_app server callback:
567def test_app_cb(inmsg, inavp, insession):
568   tval = inmsg.search(d_avp).header().avp_value.u32
569   print "Py ECHO Test message from '%s' with test value %x, replying..." % (inmsg.search(d_oh).header().avp_value.os.as_str(), tval)
570   answ = inmsg.create_answer()
571   answ.rescode_set()
572   answ.add_origin()
573   ta = avp(d_avp, AVPFL_SET_BLANK_VALUE)
574   ta.header().avp_value.u32 = tval
575   answ.add_child(ta)
576   return [ 0, answ, DISP_ACT_SEND ]
577
578# Register the callback for dispatch thread:
579hdl = 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)
580
581# Don't forget to register the application in the daemon for CER/CEA capabilities.
582fd_disp_app_support ( d_app, d_vnd, 1, 0 )
583
584
585###  For the fun, the client part of the test_app:
586
587def receive_answer(ans, testval):
588   try:
589     tval = ans.search(d_avp).header().avp_value.u32
590   except:
591     print "Error in receive_answer: no Test-AVP included"
592     tval = 0
593   try:
594     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())
595   except:
596     print "Error in receive_answer: Result-Code or Origin-Host are missing"
597   del ans
598   return None
599
600import random
601
602def send_query(destrealm="localdomain"):
603   qry = msg(d_req)
604   sess = session()
605   tv = random.randint(1, 1<<32)
606   # Session-Id
607   a = avp(d_si, AVPFL_SET_BLANK_VALUE)
608   a.header().avp_value.os = sess.getsid()
609   qry.add_child(a)
610   # Destination-Realm
611   a = avp(d_dr, AVPFL_SET_BLANK_VALUE)
612   a.header().avp_value.os = destrealm
613   qry.add_child(a)
614   # Origin-Host, Origin-Realm
615   qry.add_origin()
616   # Test-AVP
617   a = avp(d_avp, AVPFL_SET_BLANK_VALUE)
618   a.header().avp_value.u32 = tv
619   qry.add_child(a)
620   print "Py SEND %x to '%s'" % (tv, destrealm)
621   qry.send(receive_answer, tv)
622
623send_query()
624
625
626############# FIFO queues ############
627
628myqueue = fifo()
629
630# enqueue any object
631myqueue.post(3)
632myqueue.post("blah")
633myqueue.post( [ 3, 2 ] )
634
635# Simple get (blocks when the queue is empty)
636myqueue.get()
637
638# Try get: returns the next object, or None if the queue is empty
639myqueue.tryget()
640
641# timed get: like get, but returns None after x seconds
642myqueue.timedget(3)
643
644# Show the number of items in the queue
645myqueue.length()
646
647
648## Variants:
649# All the previous calls are suitable to queue Python objects.
650# In order to interact with objects queued / poped by C counterpart,
651# a second parameter must be passed to specify the object type,
652# as follow:
653ev = fd_event()
654ev.code = FDEV_DUMP_EXT
655cvar.fd_g_config.cnf_main_ev.post(ev, "struct fd_event *")
656
657# Similarly, for *get, we can specify the structure that was queued:
658myqueue.get("struct fd_event *")
659myqueue.tryget("struct fd_event *")
660myqueue.timedget(3, "struct fd_event *")
661
662del myqueue
663
664
665############# HOOKS ############
666
667def my_hook_cb(type, msg, peer, other, oldpmd):
668    print "callback type ", type, " called: ", msg, other, oldpmd
669    return "this is the new pmd"
670
671# Create a wrapped fd_hook_data_hdl:
672datahdl = fd_hook_data_hdl()
673
674# Register the hook callback:
675hdl = fd_hook_hdl(1 << HOOK_MESSAGE_SENT, my_hook_cb, datahdl)
676
677
678
679
680############# PEERS ############
681
682# Get the list of peers defined in the system
683# (we are supposed to readlock fd_g_peers_rw before accessing this list)
684cvar.fd_g_peers_rw.rdlock()
685peers = cvar.fd_g_peers.enum_as("struct peer_hdr *")
686cvar.fd_g_peers_rw.unlock()
687for p in peers:
688   print "Peer:", p.info.pi_diamid
689
690
691# Create a new peer
692np = peer_info()
693np.pi_diamid = "nas.localdomain"
694np.config.pic_flags.pro4 = PI_P4_TCP
695
696
697# Add this peer into the framework.
698np.add()
699
700# It is possible to specify a callback for when the connection completes or fails with this peer.
701# The prototype is as follow:
702def add_cb(peer):
703    if peer:
704        if peer.runtime.pir_state == STATE_OPEN:
705           print "Connection to peer '%s' completed" % (peer.pi_diamid)
706           # can find more information in peer.runtime.*
707        else:
708           print "Connection to peer '%s' failed (state:%d)" % (peer.pi_diamid, peer.runtime.pir_state)
709    else:
710        print "The peer has been destroyed before it completed the connection."
711
712# Then add the peer like this:
713np.add(add_cb)
714
715
716# Search a peer by its diameter id (returns a peer_hdr object if found) -- similar to fd_peer_getbyid
717p = peer_search("nas.domain.aaa")
718
719
720## Validation callback (see fd_peer_validate_register documentation)
721
722# cb2 prototype:
723def my_validate_cb2(pinfo):
724    print "Cb2 callback trigged for peer %s" % (pinfo.pi_diamid)
725    # Usually, this would be used only to check some TLS properties,
726    # which is not really possible yet through the python interpreter...
727    return 0   # return an error code if the peer is not validated
728
729# cb prototype:
730def my_validate_cb(pinfo):
731    print "Validate callback trigged for peer %s" % (pinfo.pi_diamid)
732    # If the peer is not allowed to connect:
733    #return -1
734    # If the peer is authorized:
735    #return 1
736    # In addition, if IPsec is allowed,
737    #pinfo.config.pic_flags.sec = PI_SEC_NONE
738    # If no decision has been made:
739    #return 0
740    # If the peer is temporarily authorized but a second callback must be called after TLS negociation:
741    return my_validate_cb2
742
743# Register the callback, it will be called on new incoming connections.
744peer_validate_register(my_validate_cb)
745
746
747
748############# ENDPOINTS ############
749
750ep = fd_endpoint("129.168.168.192")
751
752# with port:
753ep = fd_endpoint("129.168.168.192", 3868)
754
755# With different flags:
756ep = fd_endpoint("129.168.168.192", 3868, EP_FL_PRIMARY)
757
758# Add IP information for the peer
759np = peer_info()
760ep.add_merge(np.pi_endpoints)
761fd_ep_dump(0, np.pi_endpoints)
762
763
764
765############# POSIX functions wrappers ############
766
767# The interface also provides wrappers around base POSIX
768# synchronization functions:
769
770m = pthread_mutex_t()
771m.lock()
772m.unlock()
773
774c = pthread_cond_t()
775c.signal()
776c.broadcast()
777c.wait(m)
778c.timedwait(m, 5)  # it takes a relative time
779
780r = pthread_rwlock_t()
781r.rdlock()
782r.unlock()
783r.wrlock()
Note: See TracBrowser for help on using the repository browser.