Navigation


source: freeDiameter/doc/dbg_interactive.py.sample @ 706:4ffbc9f1e922

Last change on this file since 706:4ffbc9f1e922 was 706:4ffbc9f1e922, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Large UNTESTED commit with the following changes:

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