File: dbg_interactive.py.sample

package info (click to toggle)
freediameter 1.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 7,120 kB
  • sloc: ansic: 74,390; yacc: 3,340; lex: 1,937; cpp: 1,579; xml: 795; php: 486; makefile: 463; python: 443; sql: 337; sh: 242; perl: 105
file content (783 lines) | stat: -rw-r--r-- 23,777 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
# 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()