1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: Verify the application specifics of the Megaco application
24%%----------------------------------------------------------------------
25
26-module(megaco_mib_SUITE).
27
28-export([
29	 suite/0, all/0, groups/0,
30         init_per_suite/1, end_per_suite/1,
31         init_per_group/2, end_per_group/2,
32         init_per_testcase/2, end_per_testcase/2,
33
34         plain/1,
35         connect/1,
36         traffic/1,
37
38         mg/3,
39         mgc/3,
40
41         handle_connect/3,
42         handle_disconnect/4,
43         handle_syntax_error/4,
44         handle_message_error/4,
45         handle_trans_request/4,
46         handle_trans_long_request/4,
47         handle_trans_reply/5,
48         handle_trans_ack/5
49        ]).
50
51-include_lib("megaco/include/megaco.hrl").
52-include_lib("megaco/include/megaco_message_v1.hrl").
53-include("megaco_test_lib.hrl").
54
55-define(TEST_VERBOSITY, info). % silence | info | debug
56-define(MGC_VERBOSITY,  debug).
57-define(MG_VERBOSITY,   debug).
58
59-define(LOAD_COUNTER_START, 100).
60-define(A4444, ["11111111", "00000000", "00000000"]).
61
62-record(mgc, {parent  = undefined,
63	      tcp_sup = undefined,
64	      udp_sup = undefined,
65	      mid     = undefined,
66	      mg      = []}).
67-record(mg, {parent       = undefined,
68	     mid          = undefined,
69	     conn_handle  = undefined,
70	     state        = initiated,
71	     load_counter = 0}).
72
73
74%%======================================================================
75%% Common Test interface functions
76%%======================================================================
77
78suite() ->
79    [{ct_hooks, [ts_install_cth]}].
80
81all() ->
82    [
83     plain,
84     connect,
85     traffic
86    ].
87
88groups() ->
89    [].
90
91
92
93%%
94%% -----
95%%
96
97init_per_suite(suite) ->
98    [];
99init_per_suite(doc) ->
100    [];
101init_per_suite(Config0) when is_list(Config0) ->
102
103    ?ANNOUNCE_SUITE_INIT(),
104
105    p("init_per_suite -> entry with"
106      "~n      Config: ~p"
107      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),
108
109    case ?LIB:init_per_suite(Config0) of
110        {skip, _} = SKIP ->
111            SKIP;
112
113        Config1 when is_list(Config1) ->
114
115            %% We need a (local) monitor on this node also
116            megaco_test_sys_monitor:start(),
117
118            p("init_per_suite -> end when"
119              "~n      Config: ~p"
120              "~n      Nodes:  ~p", [Config1, erlang:nodes()]),
121
122            Config1
123    end.
124
125end_per_suite(suite) -> [];
126end_per_suite(doc) -> [];
127end_per_suite(Config0) when is_list(Config0) ->
128
129    p("end_per_suite -> entry with"
130      "~n      Config: ~p"
131      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),
132
133    megaco_test_sys_monitor:stop(),
134    Config1 = ?LIB:end_per_suite(Config0),
135
136    p("end_per_suite -> end when"
137      "~n      Nodes:  ~p", [erlang:nodes()]),
138
139    Config1.
140
141
142%%
143%% -----
144%%
145
146init_per_group(_GroupName, Config) ->
147    Config.
148
149end_per_group(_GroupName, Config) ->
150    Config.
151
152
153
154%%
155%% -----
156%%
157
158init_per_testcase(Case, Config) ->
159    process_flag(trap_exit, true),
160
161    progress("init_per_testcase -> ~w", [Case]),
162
163    megaco_test_global_sys_monitor:reset_events(),
164
165    case Case of
166	traffic ->
167	    Conf0 = lists:keydelete(tc_timeout, 1, Config),
168	    Conf  = [{tc_timeout, timer:minutes(5)}|Conf0],
169	    megaco_test_lib:init_per_testcase(Case, Conf);
170	_ ->
171	    megaco_test_lib:init_per_testcase(Case, Config)
172    end.
173
174end_per_testcase(Case, Config) ->
175    process_flag(trap_exit, false),
176
177    progress("end_per_testcase -> ~w", [Case]),
178
179    p("system events during test: "
180      "~n   ~p", [megaco_test_global_sys_monitor:events()]),
181
182    megaco_test_lib:end_per_testcase(Case, Config).
183
184
185
186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187
188plain(suite) ->
189    [];
190plain(doc) ->
191    ["Test case for the basic statistics counter handling. "];
192plain(Config) when is_list(Config) ->
193    io:format("create test table 1~n", []),
194    Tab1 = megaco_test_cnt1,
195    megaco_stats:init(Tab1),
196
197    io:format("~ncreate test table 2~n", []),
198    Tab2 = megaco_test_cnt2,
199    megaco_stats:init(Tab2, [kalle, hobbe]),
200
201    io:format("~ntable 1 increments~n", []),
202    H1 = #megaco_conn_handle{local_mid  = {deviceName, "a"},
203			     remote_mid = {deviceName, "b"}},
204    H2 = #megaco_conn_handle{local_mid  = {deviceName, "a"},
205			     remote_mid = {deviceName, "c"}},
206    1 = megaco_stats:inc(Tab1, H1, sune),
207    2 = megaco_stats:inc(Tab1, H2, sune, 2),
208    3 = megaco_stats:inc(Tab1, H1, gurka, 3),
209    4 = megaco_stats:inc(Tab1, H2, sune, 2),
210    4 = megaco_stats:inc(Tab1, H1, gurka),
211
212    io:format("~ntable 2 increments~n", []),
213    H3 = #megaco_conn_handle{local_mid  = {deviceName, "e"},
214			     remote_mid = {deviceName, "c"}},
215    H4 = #megaco_conn_handle{local_mid  = {deviceName, "e"},
216			     remote_mid = {deviceName, "d"}},
217    1 = megaco_stats:inc(Tab2, H3, tomat),
218    4 = megaco_stats:inc(Tab2, H3, tomat, 3),
219    5 = megaco_stats:inc(Tab2, H4, paprika, 5),
220
221    io:format("~ntable 2 global increments~n", []),
222    1 = megaco_stats:inc(Tab2, kalle),
223    1 = megaco_stats:inc(Tab2, hobbe),
224    2 = megaco_stats:inc(Tab2, hobbe),
225    2 = megaco_stats:inc(Tab2, kalle),
226    3 = megaco_stats:inc(Tab2, kalle),
227    4 = megaco_stats:inc(Tab2, hobbe, 2),
228
229    io:format("~ntable 1 stats~n", []),
230    {ok, Stats1} = megaco_stats:get_stats(Tab1),
231    io:format("Stats1 = ~p~n", [Stats1]),
232    {value, {H1, H1Stats}} = lists:keysearch(H1, 1, Stats1),
233    Stats1_2 = lists:keydelete(H1, 1, Stats1),
234    {value, {H2, H2Stats}} = lists:keysearch(H2, 1, Stats1_2),
235    Stats1_3 = lists:keydelete(H2, 1, Stats1_2),
236    [] = Stats1_3,
237    io:format("H1Stats = ~p~n", [H1Stats]),
238    io:format("H2Stats = ~p~n", [H2Stats]),
239
240    {value, {sune, 1}}  = lists:keysearch(sune, 1, H1Stats),
241    H1Stats_2 = lists:keydelete(sune, 1, H1Stats),
242    {value, {gurka, 4}} = lists:keysearch(gurka, 1, H1Stats_2),
243    H1Stats_3 = lists:keydelete(gurka, 1, H1Stats_2),
244    [] = H1Stats_3,
245
246    {value, {sune, 4}} = lists:keysearch(sune, 1, H2Stats),
247    H2Stats_2 = lists:keydelete(sune, 1, H2Stats),
248    [] = H2Stats_2,
249
250
251    %% --
252    io:format("~ntable 2 stats~n", []),
253    {ok, Stats2} = megaco_stats:get_stats(Tab2),
254    io:format("Stats2 = ~p~n", [Stats2]),
255    {ok, 3} = megaco_stats:get_stats(Tab2, kalle),
256    {ok, 4} = megaco_stats:get_stats(Tab2, hobbe),
257
258
259    %% --
260    io:format("~ntable 1 reset stats for ~p~n", [H1]),
261    megaco_stats:reset_stats(Tab1, H1),
262    {ok, Stats1_4} = megaco_stats:get_stats(Tab1),
263    io:format("Stats1_4 = ~p~n", [Stats1_4]),
264    {ok, 0} = megaco_stats:get_stats(Tab1, H1, sune),
265    {ok, 0} = megaco_stats:get_stats(Tab1, H1, gurka),
266
267
268    %% --
269    io:format("~ntable 2 reset stats for kalle and ~p~n", [H4]),
270    megaco_stats:reset_stats(Tab2, kalle),
271    megaco_stats:reset_stats(Tab2, H4),
272    {ok, Stats2_2} = megaco_stats:get_stats(Tab2),
273    io:format("Stats2_2 = ~p~n", [Stats2_2]),
274    {ok, 0} = megaco_stats:get_stats(Tab2, kalle),
275    {ok, 4} = megaco_stats:get_stats(Tab2, hobbe),
276    {ok, 4} = megaco_stats:get_stats(Tab2, H3, tomat),
277    {ok, 0} = megaco_stats:get_stats(Tab2, H4, paprika),
278    {ok, Stats4_4} = megaco_stats:get_stats(Tab2, H4),
279    io:format("Stats4_4 = ~p~n", [Stats4_4]),
280
281    %% --
282    io:format("~ntable 2 stats for nonexisting counters~n", []),
283    {error, _} = megaco_stats:get_stats(Tab2, kalla),
284    {error, _} = megaco_stats:get_stats(Tab2, H3, paprika),
285    ok.
286
287
288%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289
290connect(suite) ->
291    [];
292connect(doc) ->
293    [];
294connect(Config) when is_list(Config) ->
295    Pre = fun() ->
296                  progress("start nodes"),
297                  MgcNode = make_node_name(mgc),
298                  Mg1Node = make_node_name(mg1),
299                  Mg2Node = make_node_name(mg2),
300                  d("connect -> Nodes: "
301                    "~n   MgcNode: ~p"
302                    "~n   Mg1Node: ~p"
303                    "~n   Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]),
304                  Nodes = [MgcNode, Mg1Node, Mg2Node],
305                  ok = ?START_NODES(Nodes, true),
306                  Nodes
307          end,
308    Case = fun do_connect/1,
309    Post = fun(Nodes) ->
310                   progress("stop nodes"),
311                   d("stop nodes"),
312                   ?STOP_NODES(lists:reverse(Nodes))
313           end,
314    try_tc(connect, Pre, Case, Post).
315
316do_connect([MgcNode, Mg1Node, Mg2Node]) ->
317    %% Start the MGC and MGs
318
319    ET = [{text, tcp}, {text, udp}, {binary, tcp}, {binary, udp}],
320    progress("start MGC (on ~p)", [MgcNode]),
321    {ok, Mgc} =
322	start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY),
323
324    progress("start MG1 (on ~p) using tcp", [Mg1Node]),
325    {ok, Mg1} =
326	start_mg(Mg1Node,  {deviceName, "mg1"}, text,   tcp, ?MG_VERBOSITY),
327
328    progress("start MG2 (on ~p) using udp", [Mg2Node]),
329    {ok, Mg2} =
330	start_mg(Mg2Node,  {deviceName, "mg2"}, binary, udp, ?MG_VERBOSITY),
331
332    %% Collect the initial statistics (should be zero if anything)
333    progress("collect initial MG1 stats"),
334    {ok, Mg1Stats0} = get_stats(Mg1, 1),
335    d("connect  -> stats for Mg1: ~n~p", [Mg1Stats0]),
336    progress("collect initial MG2 stats"),
337    {ok, Mg2Stats0} = get_stats(Mg2, 1),
338    d("connect  -> stats for Mg2: ~n~p", [Mg2Stats0]),
339    progress("collect initial MGC stats"),
340    {ok, MgcStats0} = get_stats(Mgc, 1),
341    d("connect  -> stats for Mgc: ~n~p", [MgcStats0]),
342
343    %% Ask Mg1 to do a service change
344    progress("perform MG1 service change"),
345    {ok, Res1} = service_change(Mg1),
346    d("connect -> (Mg1) service change result: ~p", [Res1]),
347
348    %% Collect the statistics
349    progress("collect MG1 statistics (after service change)"),
350    {ok, Mg1Stats1} = get_stats(Mg1, 1),
351    d("connect  -> stats for Mg1: ~n~p", [Mg1Stats1]),
352    progress("collect MGC statistics (after MG1 service change)"),
353    {ok, MgcStats1} = get_stats(Mgc, 1),
354    d("connect  -> stats (1) for Mgc: ~n~p", [MgcStats1]),
355    {ok, MgcStats2} = get_stats(Mgc, 2),
356    d("connect  -> stats (2) for Mgc: ~n~p", [MgcStats2]),
357
358    %% Ask Mg2 to do a service change
359    progress("perform MG2 service change"),
360    {ok, Res2} = service_change(Mg2),
361    d("connect -> (Mg2) service change result: ~p", [Res2]),
362
363    %% Collect the statistics
364    progress("collect MG2 statistics (after service change)"),
365    {ok, Mg2Stats1} = get_stats(Mg2, 1),
366    d("connect  -> stats for Mg1: ~n~p", [Mg2Stats1]),
367    progress("collect MGC statistics (after MG2 service change)"),
368    {ok, MgcStats3} = get_stats(Mgc, 1),
369    d("connect  -> stats (1) for Mgc: ~n~p", [MgcStats3]),
370    {ok, MgcStats4} = get_stats(Mgc, 2),
371    d("connect  -> stats (2) for Mgc: ~n~p", [MgcStats4]),
372
373    %% Tell Mg1 to stop
374    progress("stop MG1"),
375    stop(Mg1),
376
377    %% Collect the statistics
378    progress("collect MGC statistics (after MG1 stop)"),
379    {ok, MgcStats5} = get_stats(Mgc, 1),
380    d("connect  -> stats (1) for Mgc: ~n~p", [MgcStats5]),
381    {ok, MgcStats6} = get_stats(Mgc, 2),
382    d("connect  -> stats (2) for Mgc: ~n~p", [MgcStats6]),
383
384    %% Tell Mg2 to stop
385    progress("stop MG2"),
386    stop(Mg2),
387
388    %% Collect the statistics
389    progress("collect MGC statistics (after MG2 stop)"),
390    {ok, MgcStats7} = get_stats(Mgc, 1),
391    d("connect  -> stats (1) for Mgc: ~n~p", [MgcStats7]),
392    {ok, MgcStats8} = get_stats(Mgc, 2),
393    d("connect  -> stats (2) for Mgc: ~n~p", [MgcStats8]),
394
395    %% Tell Mgc to stop
396    progress("stop MGC"),
397    stop(Mgc),
398
399    i("connect -> done", []),
400    ok.
401
402
403
404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405
406traffic(suite) ->
407    [];
408traffic(doc) ->
409    [];
410traffic(Config) when is_list(Config) ->
411    Pre = fun() ->
412                  progress("start nodes"),
413                  MgcNode = make_node_name(mgc),
414                  Mg1Node = make_node_name(mg1),
415                  Mg2Node = make_node_name(mg2),
416                  Mg3Node = make_node_name(mg3),
417                  Mg4Node = make_node_name(mg4),
418                  Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node],
419                  d("traffic -> Nodes: "
420                    "~n   MgcNode: ~p"
421                    "~n   Mg1Node: ~p"
422                    "~n   Mg2Node: ~p"
423                    "~n   Mg3Node: ~p"
424                    "~n   Mg4Node: ~p",
425                    [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]),
426                  ok = ?START_NODES(Nodes, true),
427                  Nodes
428          end,
429    Case = fun do_traffic/1,
430    Post = fun(Nodes) ->
431                   progress("stop nodes"),
432                   d("stop nodes"),
433                   ?STOP_NODES(lists:reverse(Nodes))
434           end,
435    try_tc(traffic, Pre, Case, Post).
436
437do_traffic([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]) ->
438    %% Start the MGC and MGs
439    i("start the MGC"),
440    progress("start MGC (on ~p)", [MgcNode]),
441    ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}],
442    {ok, Mgc} =
443	start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY),
444
445    i("traffic -> start and connect the MGs"),
446    progress("start and connect MGs"),
447    MgConf0 = [{Mg1Node, "mg1", text,   tcp},
448	       {Mg2Node, "mg2", text,   udp},
449	       {Mg3Node, "mg3", binary, tcp},
450	       {Mg4Node, "mg4", binary, udp}],
451    MgConf = traffic_connect_mg(MgConf0, []),
452
453    %% Collect and check the MGs statistics
454    i("traffic -> collect and check the MGs stats"),
455    progress("collect and verify MGs (initial) stats"),
456    traffic_verify_mg_stats(MgConf, 1, 1),
457
458    %% Collect and check the MGC statistics
459    i("traffic -> collect and check the MGC (initial) stats"),
460    progress("collect and verify MGC stats"),
461    {ok, MgcStats1} = get_stats(Mgc, 1),
462    d("traffic  -> stats (1) for Mgc: ~n~p~n", [MgcStats1]),
463    traffic_verify_mgc_stats(Mgc, 1, 1),
464
465
466    ?SLEEP(1000),
467
468
469    %% And apply some load
470    i("traffic -> apply traffic load (1)"),
471    progress("apply some load (1)"),
472    ok = traffic_apply_load(MgConf),
473
474    %% Await completion of load part and the collect traffic
475    i("traffic -> await load (1) competion"),
476    progress("await load (1) completion"),
477    ok = traffic_await_load_complete(MgConf),
478
479
480    ?SLEEP(1000),
481
482
483    i("traffic -> collect and check the MGs statistics"),
484    progress("collect and verify MGs (after load 1) stats"),
485    traffic_verify_mg_stats(MgConf,
486			    1 + ?LOAD_COUNTER_START,
487			    1 + ?LOAD_COUNTER_START),
488
489    i("traffic -> collect and check the MGC statistics"),
490    progress("collect and verify MGC (after load 1) stats"),
491    {ok, MgcStats3} = get_stats(Mgc, 1),
492    d("traffic  -> stats (1) for Mgc: ~n~p~n", [MgcStats3]),
493    traffic_verify_mgc_stats(Mgc,
494			     1 + ?LOAD_COUNTER_START,
495			     1 + ?LOAD_COUNTER_START),
496
497
498    ?SLEEP(1000),
499
500
501    %% Reset counters
502    i("traffic -> reset the MGs statistics"),
503    progress("reset MGs stats"),
504    traffic_reset_mg_stats(MgConf),
505    i("traffic -> collect and check the MGs statistics"),
506    progress("collect and verify MGs (after reset) stats"),
507    traffic_verify_mg_stats(MgConf, 0, 0),
508
509    i("traffic -> reset the MGC statistics"),
510    progress("reset MGC stats"),
511    traffic_reset_mgc_stats(Mgc),
512    i("traffic -> collect and check the MGC statistics"),
513    progress("collect and verify MGC (after reset) stats"),
514    traffic_verify_mgc_stats(Mgc, 0, 0),
515
516
517    ?SLEEP(1000),
518
519
520    %% And apply some load
521    i("traffic -> apply traffic load (2)"),
522    progress("apply some load (2)"),
523    ok = traffic_apply_load(MgConf),
524
525    %% Await completion of load part and the collect traffic
526    i("traffic -> await load (2) competion"),
527    progress("await load (2) completion"),
528    ok = traffic_await_load_complete(MgConf),
529
530
531    ?SLEEP(1000),
532
533
534    i("traffic -> collect and check the MGs statistics"),
535    progress("collect and verify MGs (after load 2) stats"),
536    traffic_verify_mg_stats(MgConf,
537			    ?LOAD_COUNTER_START,
538			    ?LOAD_COUNTER_START),
539
540    i("traffic -> collect and check the MGC statistics"),
541    progress("collect and verify MGC (after load 2) stats"),
542    traffic_verify_mgc_stats(Mgc,
543			     ?LOAD_COUNTER_START,
544			     ?LOAD_COUNTER_START),
545
546
547    ?SLEEP(1000),
548
549
550    %% Tell MGs to stop
551    i("traffic -> stop the MGs"),
552    progress("stop MGs"),
553    traffic_stop_mg(MgConf),
554
555
556    ?SLEEP(1000),
557
558
559    %% Collect the statistics
560    i("traffic -> collect the MGC statistics"),
561    progress("collect and verify MGC (after MGs stop) stats"),
562    {ok, MgcStats7} = get_stats(Mgc, 1),
563    d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats7]),
564    {ok, MgcStats8} = get_stats(Mgc, 2),
565    d("traffic -> stats (2) for Mgc: ~n~p~n", [MgcStats8]),
566
567    %% Tell Mgc to stop
568    i("traffic -> stop the MGC"),
569    progress("stop MGC"),
570    stop(Mgc),
571
572    i("traffic -> done", []),
573    ok.
574
575
576traffic_verify_mgc_stats(Pid, Out, In)
577  when is_pid(Pid) andalso is_integer(Out) andalso is_integer(In) ->
578    d("traffic_verify_mgc_stats -> entry with"
579      "~n   Out:   ~p"
580      "~n   In:    ~p", [Out, In]),
581    {ok, Stats} = get_stats(Pid, 2),
582    d("traffic_verify_mgc_stats -> stats (2) for Mgc: ~n~p~n", [Stats]),
583    traffic_verify_mgc_stats(Stats, Out, In);
584
585traffic_verify_mgc_stats(Stats, Out, In) when is_list(Stats) ->
586    d("traffic_verify_mgc_stats -> checking stats"),
587    Gen   = traffic_verify_get_stats(gen, Stats),
588    Trans = traffic_verify_get_stats(trans, Stats),
589    traffic_verify_mgc_stats_gen(Gen),
590    traffic_verify_mgc_stats_trans(Trans, Out, In).
591
592traffic_verify_mgc_stats_gen([]) ->
593    d("traffic_verify_mgc_stats_gen -> done"),
594    ok;
595traffic_verify_mgc_stats_gen([{medGwyGatewayNumErrors, 0}|Stats]) ->
596    traffic_verify_mgc_stats_gen(Stats);
597traffic_verify_mgc_stats_gen([{medGwyGatewayNumErrors, Val}|_]) ->
598    exit({global_error_counter, Val, mgc});
599traffic_verify_mgc_stats_gen([{Handle, Counters}|Stats]) ->
600    N = {mgc, Handle, Handle},
601    traffic_verify_counter(N, medGwyGatewayNumErrors, Counters, 0),
602    traffic_verify_mgc_stats_gen(Stats).
603
604
605traffic_verify_mgc_stats_trans([], _Out, _In) ->
606    ok;
607traffic_verify_mgc_stats_trans([{Mod, Stats}|MgcStats], Out, In) ->
608    d("traffic_verify_mgc_stats_trans -> entry with"
609      "~n   Mod:   ~p"
610      "~n   Stats: ~p", [Mod, Stats]),
611    traffic_verify_mgc_stats_trans(Mod, Stats, Out, In),
612    traffic_verify_mgc_stats_trans(MgcStats, Out, In).
613
614traffic_verify_mgc_stats_trans(_Mod, [], _Out, _In) ->
615    ok;
616traffic_verify_mgc_stats_trans(Mod, [{Handle,Counters}|Stats], Out, In) ->
617    N = {mgc, Mod, Handle},
618    traffic_verify_counter(N, medGwyGatewayNumErrors, Counters, 0),
619    traffic_verify_counter(N, medGwyGatewayNumOutMessages, Counters, Out),
620    traffic_verify_counter(N, medGwyGatewayNumInMessages, Counters, In),
621    traffic_verify_mgc_stats_trans(Mod, Stats, Out, In).
622
623
624traffic_verify_mg_stats(MgConf, Out, In)
625  when is_integer(Out) andalso is_integer(In) ->
626    d("traffic_verify_mg_stats -> entry with"
627      "~n   Out:   ~p"
628      "~n   In:    ~p", [Out, In]),
629    Stats = traffic_get_mg_stats(MgConf, []),
630    d("traffic_verify_mg_stats -> stats for MGs: ~n~p", [Stats]),
631    traffic_verify_mg_stats1(Stats, Out, In).
632
633traffic_verify_mg_stats1([], _, _) ->
634    ok;
635traffic_verify_mg_stats1([{Name, Stats}|MgStats], Out, In) ->
636    d("traffic_verify_mg_stats1 -> entry with"
637      "~n   Name:  ~s"
638      "~n   Stats: ~p", [Name, Stats]),
639    Gen   = traffic_verify_get_stats(gen, Stats),
640    Trans = traffic_verify_get_stats(trans, Stats),
641    traffic_verify_mg_stats_gen(Name, Gen),
642    traffic_verify_mg_stats_trans(Name, Trans, Out, In),
643    traffic_verify_mg_stats1(MgStats, Out, In).
644
645traffic_verify_mg_stats_gen(Mg, []) ->
646    d("traffic_verify_mg_stats_gen -> ~s checked out OK",[Mg]),
647    ok;
648traffic_verify_mg_stats_gen(Mg, [{medGwyGatewayNumErrors, 0}|Stats]) ->
649    traffic_verify_mg_stats_gen(Mg, Stats);
650traffic_verify_mg_stats_gen(Mg, [{medGwyGatewayNumErrors, Val}|_]) ->
651    exit({global_error_counter, Val, Mg});
652traffic_verify_mg_stats_gen(Mg, [{_Handle, Counters}|Stats]) ->
653    traffic_verify_counter(Mg, medGwyGatewayNumErrors, Counters, 0),
654    traffic_verify_mg_stats_gen(Mg, Stats).
655
656traffic_verify_mg_stats_trans(Mg, Counters, Out, In) ->
657    traffic_verify_counter(Mg, medGwyGatewayNumErrors,      Counters, 0),
658    traffic_verify_counter(Mg, medGwyGatewayNumOutMessages, Counters, Out),
659    traffic_verify_counter(Mg, medGwyGatewayNumInMessages,  Counters, In).
660
661
662traffic_verify_get_stats(S, Stats) ->
663    case lists:keysearch(S, 1, Stats) of
664	{value, {S, Val}} ->
665	    Val;
666	false ->
667	    exit({not_found, S, Stats})
668    end.
669
670traffic_verify_counter(Name, Counter, Counters, Expected) ->
671    case lists:keysearch(Counter, 1, Counters) of
672	{value, {Counter, Expected}} ->
673            i("counter ~w verified for ~p", [Counter, Name]),
674	    ok;
675	{value, {Counter, Val}} ->
676            i("counter ~w *not* verified for ~p: "
677              "~n   Expected: ~w"
678              "~n   Actual:   ~w", [Counter, Name, Expected, Val]),
679	    exit({illegal_counter_value, Counter, Val, Expected, Name});
680	false ->
681            i("counter ~w *not* found for ~p", [Counter, Name]),
682	    exit({not_found, Counter, Counters, Name, Expected})
683    end.
684
685
686traffic_connect_mg([], Acc) ->
687    lists:reverse(Acc);
688traffic_connect_mg([{Node, Name, Coding, Trans}|Mg], Acc) ->
689    Pid = traffic_connect_mg(Node, Name, Coding, Trans),
690    traffic_connect_mg(Mg, [{Name, Pid}|Acc]).
691
692traffic_connect_mg(Node, Name, Coding, Trans) ->
693    progress("start (and connect) ~s (on ~p) using ~p", [Name, Node, Trans]),
694    Mid = {deviceName, Name},
695    {ok, Pid} = start_mg(Node, Mid, Coding, Trans, ?MG_VERBOSITY),
696
697    %% Ask the MGs to do a service change
698    progress("perform ~s service change", [Name]),
699    {ok, Res} = service_change(Pid),
700    d("traffic_connect_mg -> (~s) service change result: ~p", [Name, Res]),
701    Pid.
702
703
704traffic_stop_mg(MGs) ->
705    [stop(Pid) || {_Name, Pid} <- MGs].
706
707
708traffic_get_mg_stats([], Acc) ->
709    lists:reverse(Acc);
710traffic_get_mg_stats([{Name, Pid}|Mgs], Acc) ->
711    {ok, Stats} = get_stats(Pid, 1),
712    d("traffic_get_mg_stats -> stats for ~s: "
713      "~n   ~p"
714      "~n", [Name, Stats]),
715    traffic_get_mg_stats(Mgs, [{Name, Stats}|Acc]).
716
717
718traffic_apply_load([]) ->
719    ok;
720traffic_apply_load([{_,MG}|MGs]) ->
721    MG ! {apply_load, self(), ?LOAD_COUNTER_START},
722    receive
723	{apply_load_ack, MG} ->
724	    traffic_apply_load(MGs);
725	{'EXIT', MG, Reason} ->
726	    exit({mg_exit, MG, Reason})
727    after 10000 ->
728	    exit({apply_load_ack_timeout, MG})
729    end.
730
731
732traffic_reset_mg_stats([]) ->
733    ok;
734traffic_reset_mg_stats([{Name, Pid}|MGs]) ->
735    d("traffic_reset_mg_stats -> resetting ~s", [Name]),
736    traffic_reset_stats(Pid),
737    traffic_reset_mg_stats(MGs).
738
739traffic_reset_mgc_stats(Mgc) ->
740    d("traffic_reset_mgc_stats -> resetting ~p", [Mgc]),
741    traffic_reset_stats(Mgc).
742
743traffic_reset_stats(Pid) ->
744    Pid ! {reset_stats, self()},
745    receive
746	{reset_stats_ack, Pid} ->
747	    ok;
748	{'EXIT', Pid, Reason} ->
749	    exit({client_exit, Pid, Reason})
750    after 10000 ->
751	    exit({reset_stats_ack_timeout, Pid})
752    end.
753
754
755traffic_await_load_complete([]) ->
756    ok;
757traffic_await_load_complete(MGs0) ->
758    receive
759	{load_complete, Pid} ->
760	    d("received load_complete from ~p", [Pid]),
761	    MGs1 = lists:keydelete(Pid, 2, MGs0),
762	    traffic_await_load_complete(lists:delete(Pid, MGs1));
763	{'EXIT', Pid, Reason} ->
764	    i("exit signal from ~p: ~p", [Pid, Reason]),
765	    case lists:keymember(Pid, 2, MGs0) of
766		true ->
767		    exit({mg_exit, Pid, Reason});
768		false ->
769		    MGs1 = lists:keydelete(Pid, 2, MGs0),
770		    traffic_await_load_complete(lists:delete(Pid, MGs1))
771	    end
772    end.
773
774
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776
777make_node_name(Name) ->
778    case string:tokens(atom_to_list(node()), [$@]) of
779	[_,Host] ->
780	    list_to_atom(lists:concat([atom_to_list(Name) ++ "@" ++ Host]));
781	_ ->
782	    exit("Test node must be started with '-sname'")
783     end.
784
785
786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787
788start_mgc(Node, Mid, ET, Verbosity) ->
789    d("start mgc[~p]: ~p", [Node, Mid]),
790    RI = {receive_info, mk_recv_info(ET)},
791    Config = [{local_mid, Mid}, RI],
792    Pid = spawn_link(Node, ?MODULE, mgc, [self(), Verbosity, Config]),
793    await_started(Pid).
794
795mk_recv_info(ET) ->
796    mk_recv_info(ET, []).
797
798mk_recv_info([], Acc) ->
799    Acc;
800mk_recv_info([{text,tcp}|ET], Acc) ->
801    RI = [{encoding_module, megaco_pretty_text_encoder},
802	  {encoding_config, []},
803	  {transport_module, megaco_tcp},
804	  {port, 2944}],
805    mk_recv_info(ET, [RI|Acc]);
806mk_recv_info([{text,udp}|ET], Acc) ->
807    RI = [{encoding_module, megaco_pretty_text_encoder},
808	  {encoding_config, []},
809	  {transport_module, megaco_udp},
810	  {port, 2944}],
811    mk_recv_info(ET, [RI|Acc]);
812mk_recv_info([{binary,tcp}|ET], Acc) ->
813    RI = [{encoding_module, megaco_ber_encoder},
814	  {encoding_config, []},
815	  {transport_module, megaco_tcp},
816	  {port, 2945}],
817    mk_recv_info(ET, [RI|Acc]);
818mk_recv_info([{binary,udp}|ET], Acc) ->
819    RI = [{encoding_module, megaco_ber_encoder},
820	  {encoding_config, []},
821	  {transport_module, megaco_udp},
822	  {port, 2945}],
823    mk_recv_info(ET, [RI|Acc]);
824mk_recv_info([ET|_], _) ->
825    throw({error, {invaalid_encoding_transport, ET}}).
826
827mgc(Parent, Verbosity, Config) ->
828    process_flag(trap_exit, true),
829    put(verbosity, Verbosity),
830    put(sname,   "MGC"),
831    i("mgc -> starting"),
832    {Mid, TcpSup, UdpSup} = mgc_init(Config),
833    notify_started(Parent),
834    S = #mgc{parent = Parent,
835	     tcp_sup = TcpSup, udp_sup = UdpSup, mid = Mid},
836    i("mgc -> started"),
837    mgc_loop(S).
838
839mgc_init(Config) ->
840    d("mgc_init -> entry"),
841    Mid = get_conf(local_mid, Config),
842    RI  = get_conf(receive_info, Config),
843    i("mgc_init -> start megaco"),
844    application:start(megaco),
845    d("mgc_init -> start megaco user"),
846    megaco:start_user(Mid, []),
847    d("mgc_init -> update user info (user_mod)"),
848    megaco:update_user_info(Mid, user_mod,  ?MODULE),
849    d("mgc_init -> update user info (user_args)"),
850    megaco:update_user_info(Mid, user_args, [self()]),
851    d("mgc_init -> get user info (receive_handle)"),
852    RH = megaco:user_info(Mid,receive_handle),
853    d("mgc_init -> parse receive info"),
854    ListenTo = mgc_parse_receive_info(RI, RH),
855    i("mgc_init -> start transport(s) with:"
856      "~n      ListenTo: ~p", [ListenTo]),
857    {Tcp, Udp} = mgc_start_transports(ListenTo),
858    i("mgc_init -> transport(s) started:"
859      "~n      Tcp: ~p"
860      "~n      Udp: ~p", [Tcp, Udp]),
861    {Mid, Tcp, Udp}.
862
863
864mgc_loop(S) ->
865    d("mgc_loop -> await request"),
866    receive
867	{stop, Parent} when S#mgc.parent == Parent ->
868	    i("mgc_loop -> stopping", []),
869  	    Mid = S#mgc.mid,
870	    (catch mgc_close_conns(Mid)),
871	    megaco:stop_user(Mid),
872	    application:stop(megaco),
873	    i("mgc_loop -> stopped", []),
874	    Parent ! {stopped, self()},
875	    exit(normal);
876
877
878
879	%% Reset stats
880	{reset_stats, Parent} when S#mgc.parent == Parent ->
881	    i("mgc_loop -> got request to reset stats counters"),
882	    mgc_reset_stats(S#mgc.mid),
883	    Parent ! {reset_stats_ack, self()},
884	    mgc_loop(S);
885
886
887	%% Give me statistics
888	{statistics, 1, Parent} when S#mgc.parent == Parent ->
889	    i("mgc_loop -> got request for statistics 1"),
890	    {ok, Gen} = megaco:get_stats(),
891	    GetTrans =
892		fun(CH) ->
893			Reason = {statistics, CH},
894			Pid = megaco:conn_info(CH, control_pid),
895			SendMod = megaco:conn_info(CH, send_mod),
896			SendHandle = megaco:conn_info(CH, send_handle),
897			{ok, Stats} =
898			    case SendMod of
899				megaco_tcp -> megaco_tcp:get_stats(SendHandle);
900				megaco_udp -> megaco_udp:get_stats(SendHandle);
901				SendMod    -> exit(Pid, Reason)
902			    end,
903			{SendHandle, Stats}
904		end,
905	    Mid = S#mgc.mid,
906	    Trans =
907		lists:map(GetTrans, megaco:user_info(Mid, connections)),
908	    Parent ! {statistics, 1, [{gen, Gen}, {trans, Trans}], self()},
909	    mgc_loop(S);
910
911
912	{statistics, 2, Parent} when S#mgc.parent == Parent ->
913	    i("mgc_loop -> got request for statistics 2"),
914	    {ok, Gen} = megaco:get_stats(),
915	    #mgc{tcp_sup = TcpSup, udp_sup = UdpSup} = S,
916	    TcpStats = get_trans_stats(TcpSup, megaco_tcp),
917	    UdpStats = get_trans_stats(UdpSup, megaco_udp),
918	    Parent ! {statistics, 2, [{gen, Gen}, {trans, [TcpStats, UdpStats]}], self()},
919	    mgc_loop(S);
920
921
922	%% Megaco callback messages
923	{request, Request, From} ->
924	    d("mgc_loop -> received megaco request: ~n~p~n   From: ~p",
925	      [Request, From]),
926	    Reply = mgc_handle_request(Request),
927	    d("mgc_loop -> send request reply: ~n~p", [Reply]),
928	    From ! {reply, Reply, self()},
929	    mgc_loop(S);
930
931
932	{'EXIT', Pid, Reason} ->
933	    error_msg("MGC received unexpected exit signal from ~p:~n~p",
934		      [Pid, Reason]),
935	    mgc_loop(S);
936
937
938	Invalid ->
939	    i("mgc_loop -> received invalid request: ~p", [Invalid]),
940	    mgc_loop(S)
941    end.
942
943
944mgc_reset_stats(Mid) ->
945    megaco:reset_stats(),
946    mgc_reset_trans_stats(megaco:user_info(Mid, connections), []).
947
948mgc_reset_trans_stats([], _Reset) ->
949    ok;
950mgc_reset_trans_stats([CH|CHs], Reset) ->
951    SendMod = megaco:conn_info(CH, send_mod),
952    case lists:member(SendMod, Reset) of
953	true ->
954	    mgc_reset_trans_stats(CHs, Reset);
955	false ->
956	    SendMod:reset_stats(),
957	    mgc_reset_trans_stats(CHs, [SendMod|Reset])
958    end.
959
960
961mgc_close_conns(Mid) ->
962    Reason = {self(), ignore},
963    Disco  = fun(CH) ->
964		     (catch mgc_close_conn(CH, Reason))
965	     end,
966    lists:map(Disco, megaco:user_info(Mid, connections)).
967
968mgc_close_conn(CH, Reason) ->
969    d("close connection to ~p", [CH#megaco_conn_handle.remote_mid]),
970    Pid        = megaco:conn_info(CH, control_pid),
971    SendMod    = megaco:conn_info(CH, send_mod),
972    SendHandle = megaco:conn_info(CH, send_handle),
973    megaco:disconnect(CH, Reason),
974    case SendMod of
975	megaco_tcp -> megaco_tcp:close(SendHandle);
976	megaco_udp -> megaco_udp:close(SendHandle);
977	SendMod    -> exit(Pid, Reason)
978    end.
979
980get_trans_stats(P, SendMod) when is_pid(P) ->
981    case (catch SendMod:get_stats()) of
982	{ok, Stats} ->
983	    {SendMod, Stats};
984	Else ->
985	    {SendMod, Else}
986    end;
987get_trans_stats(_P, SendMod) ->
988    {SendMod, undefined}.
989
990mgc_parse_receive_info([], _RH) ->
991    throw({error, no_receive_info});
992mgc_parse_receive_info(RI, RH) ->
993    mgc_parse_receive_info(RI, RH, []).
994
995mgc_parse_receive_info([], _RH, ListenTo) ->
996    ListenTo;
997mgc_parse_receive_info([RI|RIs], RH, ListenTo) ->
998    d("mgc_parse_receive_info -> parse receive info"),
999    RH1 = mgc_parse_receive_info1(RI, RH),
1000    case (catch mgc_parse_receive_info1(RI, RH)) of
1001	{error, Reason} ->
1002	    i("failed parsing receive info: ~p~n~p", [RI, Reason]),
1003	    exit({failed_parsing_recv_info, RI, Reason});
1004	RH1 ->
1005	    mgc_parse_receive_info(RIs, RH, [RH1|ListenTo])
1006    end.
1007
1008mgc_parse_receive_info1(RI, RH) ->
1009    d("mgc_parse_receive_info1 -> get encoding module"),
1010    EM = get_encoding_module(RI),
1011    d("mgc_parse_receive_info1 -> get encoding config"),
1012    EC = get_encoding_config(RI, EM),
1013    d("mgc_parse_receive_info1 -> get transport module"),
1014    TM = get_transport_module(RI),
1015    d("mgc_parse_receive_info1 -> get transport port"),
1016    TP = get_transport_port(RI),
1017    RH1 = RH#megaco_receive_handle{send_mod        = TM,
1018				   encoding_mod    = EM,
1019				   encoding_config = EC},
1020    {TP, RH1}.
1021
1022
1023mgc_start_transports([]) ->
1024    throw({error, no_transport});
1025mgc_start_transports(ListenTo) ->
1026    mgc_start_transports(ListenTo, undefined, undefined).
1027
1028
1029mgc_start_transports([], TcpSup, UdpSup) ->
1030    {TcpSup, UdpSup};
1031mgc_start_transports([{Port, RH}|ListenTo], TcpSup, UdpSup)
1032  when RH#megaco_receive_handle.send_mod == megaco_tcp ->
1033    TcpSup1 = mgc_start_tcp(RH, Port, TcpSup),
1034    mgc_start_transports(ListenTo, TcpSup1, UdpSup);
1035mgc_start_transports([{Port, RH}|ListenTo], TcpSup, UdpSup)
1036  when RH#megaco_receive_handle.send_mod == megaco_udp ->
1037    UdpSup1 = mgc_start_udp(RH, Port, UdpSup),
1038    mgc_start_transports(ListenTo, TcpSup, UdpSup1);
1039mgc_start_transports([{_Port, RH}|_ListenTo], _TcpSup, _UdpSup) ->
1040    throw({error, {bad_send_mod, RH#megaco_receive_handle.send_mod}}).
1041
1042
1043mgc_start_tcp(RH, Port, undefined) ->
1044    i("start tcp transport"),
1045    case megaco_tcp:start_transport() of
1046	{ok, Sup} ->
1047	    mgc_start_tcp(RH, Port, Sup);
1048	Else ->
1049	    throw({error, {failed_starting_tcp_transport, Else}})
1050    end;
1051mgc_start_tcp(RH, Port, Sup) when is_pid(Sup) ->
1052    i("tcp listen on ~p", [Port]),
1053    Opts = [{port,           Port},
1054	    {receive_handle, RH},
1055	    {tcp_options,    [{nodelay, true}]}],
1056    mgc_tcp_create_listen(Sup, Opts, 3).
1057
1058mgc_tcp_create_listen(Sup, Opts, N) ->
1059    mgc_tcp_create_listen(Sup, Opts, N, 1, undefined).
1060
1061mgc_tcp_create_listen(_Sup, _Opts, N, N, InitialReason) ->
1062    d("failed creating mgc tcp listen socket after ~p tries: ~p",
1063      [N, InitialReason]),
1064    throw({error, {failed_starting_tcp_listen, InitialReason}});
1065mgc_tcp_create_listen(Sup, Opts, MaxN, N, _InitialReason)
1066  when is_integer(N) andalso is_integer(MaxN) andalso (MaxN > N) ->
1067    d("try create mgc tcp listen socket [~w]", [N]),
1068    case megaco_tcp:listen(Sup, Opts) of
1069	ok ->
1070	    Sup;
1071	{error, {could_not_start_listener, {gen_tcp_listen, eaddrinuse} = Reason}} ->
1072	    ?SLEEP(N * 200),
1073	    mgc_tcp_create_listen(Sup, Opts, MaxN, N + 1, Reason);
1074	{error, Reason} ->
1075	    throw({error, {failed_starting_tcp_listen, Reason}});
1076	Else ->
1077	    throw({error, {failed_starting_tcp_listen, Else}})
1078    end.
1079
1080
1081mgc_start_udp(RH, Port, undefined) ->
1082    i("start udp transport"),
1083    case megaco_udp:start_transport() of
1084	{ok, Sup} ->
1085	    mgc_start_udp(RH, Port, Sup);
1086	Else ->
1087	    throw({error, {failed_starting_udp_transport, Else}})
1088    end;
1089mgc_start_udp(RH, Port, Sup) ->
1090    i("open udp ~p", [Port]),
1091    Opts = [{port, Port}, {receive_handle, RH}],
1092    case megaco_udp:open(Sup, Opts) of
1093	{ok, _SendHandle, _ControlPid} ->
1094	    Sup;
1095	Else ->
1096	    exit({error, {failed_starting_udp_listen, Else}})
1097    end.
1098
1099
1100
1101%% -----------------------
1102%% Handle megaco callbacks
1103%%
1104
1105mgc_handle_request({handle_connect, _CH, _PV}) ->
1106    ok;
1107mgc_handle_request({handle_disconnect, CH, _PV, R}) ->
1108    megaco:cancel(CH, R), % Cancel the outstanding messages
1109    ok;
1110mgc_handle_request({handle_syntax_error, _RH, _PV, _ED}) ->
1111    reply;
1112mgc_handle_request({handle_message_error, _CH, _PV, _ED}) ->
1113    no_reply;
1114mgc_handle_request({handle_trans_request, CH, PV, ARs}) ->
1115    ED =  #'ErrorDescriptor'{errorCode = ?megaco_not_implemented,
1116                             errorText = "Only single service change on null context handled"},
1117    case ARs of
1118        [AR] ->
1119            ContextId = AR#'ActionRequest'.contextId,
1120            case AR#'ActionRequest'.commandRequests of
1121                [CR] when ContextId == ?megaco_null_context_id ->
1122                    case CR#'CommandRequest'.command of
1123                        {serviceChangeReq, Req} ->
1124                            Rep    = mgc_service_change(CH, PV, Req),
1125			    CmdRep = [{serviceChangeReply, Rep}],
1126                            {discard_ack,
1127                             [#'ActionReply'{contextId    = ContextId,
1128                                             commandReply = CmdRep}]};
1129                        _ ->
1130                            {discard_ack, ED}
1131                    end;
1132                _ ->
1133                    {discard_ack, ED}
1134            end;
1135        _ ->
1136            {discard_ack, ED}
1137    end;
1138mgc_handle_request({handle_trans_long_request, _CH, _PV, _RD}) ->
1139    ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented,
1140                            errorText = "Long transaction requests not handled"},
1141    {discard_ack,  ED};
1142mgc_handle_request({handle_trans_reply, _CH, _PV, _AR, _RD}) ->
1143    ok;
1144mgc_handle_request({handle_trans_ack, _CH, _PV, _AS, _AD}) ->
1145    ok.
1146
1147mgc_service_change(CH, _PV, SCR) ->
1148    SCP = SCR#'ServiceChangeRequest'.serviceChangeParms,
1149    #'ServiceChangeParm'{serviceChangeAddress = Address,
1150                         serviceChangeProfile = Profile,
1151                         serviceChangeReason  = [_Reason]} = SCP,
1152    TermId = SCR#'ServiceChangeRequest'.terminationID,
1153    if
1154        TermId == [?megaco_root_termination_id] ->
1155            MyMid = CH#megaco_conn_handle.local_mid,
1156            Res = {serviceChangeResParms,
1157                   #'ServiceChangeResParm'{serviceChangeMgcId   = MyMid,
1158                                           serviceChangeAddress = Address,
1159                                           serviceChangeProfile = Profile}},
1160            #'ServiceChangeReply'{terminationID       = TermId,
1161                                  serviceChangeResult = Res};
1162        true ->
1163            Res = {errorDescriptor,
1164                   #'ErrorDescriptor'{errorCode = ?megaco_not_implemented,
1165                                      errorText = "Only handled for root"}},
1166
1167             #'ServiceChangeReply'{terminationID       = TermId,
1168                                   serviceChangeResult = Res}
1169    end.
1170
1171
1172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1173
1174start_mg(Node, Mid, Encoding, Transport, Verbosity) ->
1175    d("start mg[~p]: ~p", [Node, Mid]),
1176    RI1 =
1177	case Encoding of
1178	    text ->
1179		[{encoding_module, megaco_pretty_text_encoder},
1180		 {encoding_config, []},
1181		 {port,2944}];
1182	    binary ->
1183		[{encoding_module, megaco_ber_encoder},
1184		 {encoding_config, []},
1185		 {port,2945}]
1186	end,
1187    RI2 =
1188	case Transport of
1189	    tcp ->
1190		[{transport_module, megaco_tcp}];
1191	    udp ->
1192		[{transport_module, megaco_udp}]
1193	end,
1194    RI = {receive_info, RI1 ++ RI2},
1195    Config = [{local_mid, Mid}, RI],
1196    Pid = spawn_link(Node, ?MODULE, mg, [self(), Verbosity, Config]),
1197    await_started(Pid).
1198
1199
1200mg(Parent, Verbosity, Config) ->
1201    process_flag(trap_exit, true),
1202    put(verbosity, Verbosity),
1203    put(sname,   "MG"),
1204    i("mg -> starting"),
1205    {Mid, ConnHandle} = mg_init(Config),
1206    notify_started(Parent),
1207    S = #mg{parent = Parent, mid = Mid, conn_handle = ConnHandle},
1208    i("mg -> started"),
1209    mg_loop(S).
1210
1211mg_init(Config) ->
1212    d("mg_init -> entry"),
1213    Mid = get_conf(local_mid, Config),
1214    RI  = get_conf(receive_info, Config),
1215    i("mg_init -> start megaco"),
1216    application:start(megaco),
1217    d("mg_init -> start megaco user"),
1218    megaco:start_user(Mid, []),
1219    d("mg_init -> update user info (user_mod)"),
1220    megaco:update_user_info(Mid, user_mod,  ?MODULE),
1221    d("mg_init -> update user info (user_args)"),
1222    megaco:update_user_info(Mid, user_args, [self()]),
1223    d("mg_init -> get user info (receive_handle)"),
1224    RH = megaco:user_info(Mid,receive_handle),
1225    d("mg_init -> parse receive info"),
1226    {MgcPort,RH1} = mg_parse_receive_info(RI, RH),
1227    i("mg_init -> start transport(s)"),
1228    ConnHandle = mg_start_transport(MgcPort, RH1),
1229    {Mid, ConnHandle}.
1230
1231mg_loop(#mg{state = State} = S) ->
1232    d("mg_loop(~p) -> await request", [State]),
1233    receive
1234	{stop, Parent} when S#mg.parent == Parent ->
1235	    i("mg_loop(~p) -> stopping", [State]),
1236	    mg_close_conn(S#mg.conn_handle),
1237	    megaco:stop_user(S#mg.mid),
1238	    application:stop(megaco),
1239	    i("mg_loop(~p) -> stopped", [State]),
1240	    Parent ! {stopped, self()},
1241	    exit(normal);
1242
1243
1244	{reset_stats, Parent} when S#mg.parent == Parent ->
1245	    i("mg_loop(~p) -> got request to reset stats counters", [State]),
1246	    %% mg_reset_stats(S#mgc.conn_handle),
1247	    mg_reset_stats(S#mg.conn_handle),
1248	    Parent ! {reset_stats_ack, self()},
1249	    mg_loop(S);
1250
1251
1252
1253	%% Give me statistics
1254	{statistics, 1, Parent} when S#mg.parent == Parent ->
1255	    i("mg_loop(~p) -> got request for statistics 1", [State]),
1256	    {ok, Gen}   = megaco:get_stats(),
1257	    CH          = S#mg.conn_handle,
1258	    Reason      = {statistics, CH},
1259	    Pid         = megaco:conn_info(CH, control_pid),
1260	    SendMod     = megaco:conn_info(CH, send_mod),
1261	    SendHandle  = megaco:conn_info(CH, send_handle),
1262	    {ok, Trans} =
1263		case SendMod of
1264		    megaco_tcp -> megaco_tcp:get_stats(SendHandle);
1265		    megaco_udp -> megaco_udp:get_stats(SendHandle);
1266		    SendMod    -> exit(Pid, Reason)
1267		end,
1268	    Parent ! {statistics, 1, [{gen, Gen}, {trans, Trans}], self()},
1269	    mg_loop(S);
1270
1271
1272	%% Do a service change
1273	{service_change, Parent} when S#mg.parent == Parent,
1274				      State == initiated ->
1275	    i("mg_loop(~p) -> received request to perform service change when:"
1276              "~n      Conn Handle: ~p"
1277              "~n      Conn Data:   ~p",
1278	      [State,
1279               S#mg.conn_handle,
1280               megaco:conn_info(S#mg.conn_handle, conn_data)]),
1281	    Res = mg_service_change(S#mg.conn_handle),
1282	    d("mg_loop(~p) -> service change request send result: ~p",
1283              [State, Res]),
1284	    mg_loop(S#mg{state = connecting});
1285
1286
1287	%% Apply some load
1288	{apply_load, Parent, Times} when S#mg.parent == Parent ->
1289	    i("mg_loop(~p) -> received apply_load request", [State]),
1290	    apply_load_timer(),
1291	    Parent ! {apply_load_ack, self()},
1292	    mg_loop(S#mg{load_counter = Times - 1});
1293
1294
1295	apply_load_timeout ->
1296	    d("mg_loop(~p) -> received apply_load timeout [~p]",
1297	      [State, S#mg.load_counter]),
1298	    mg_apply_load(S),
1299	    mg_loop(S);
1300
1301
1302	%% Megaco callback messages
1303	{request, Request, From} ->
1304	    d("mg_loop(~p) -> received megaco request: ~n~p~n   From: ~p",
1305	      [State, Request, From]),
1306	    {Reply, NewS} = mg_handle_request(Request, S),
1307	    d("mg_loop(~p) -> send request reply: ~n~p",
1308	      [NewS#mg.state, Reply]),
1309	    From ! {reply, Reply, self()},
1310	    mg_loop(NewS);
1311
1312
1313	{'EXIT', Pid, Reason} ->
1314	    error_msg("MG ~p received unexpected exit signal from ~p:~n~p",
1315		      [S#mg.mid, Pid, Reason]),
1316	    mg_loop(S);
1317
1318
1319	Invalid ->
1320	    i("mg_loop(~p) -> received invalid request: ~p", [State, Invalid]),
1321	    mg_loop(S)
1322
1323    end.
1324
1325
1326mg_reset_stats(CH) ->
1327    megaco:reset_stats(),
1328    case (catch megaco:conn_info(CH, send_mod)) of
1329	{error, Reason} ->
1330	    error_msg("unexpected result when retrieving send module for "
1331		      "own connection ~p: ~p. "
1332		      "~nexiting...", [CH, Reason]),
1333	    exit({invalid_connection, CH, Reason});
1334	{'EXIT', Reason} ->
1335	    error_msg("exit signal when retrieving send module for "
1336		      "own connection ~p: ~p. "
1337		      "~nexiting...", [CH, Reason]),
1338	    exit({invalid_connection, CH, Reason});
1339	SendMod when is_atom(SendMod) ->
1340	    SendMod:reset_stats()
1341    end.
1342
1343
1344mg_close_conn(CH) ->
1345    Reason = {self(), ignore},
1346    Pid        = megaco:conn_info(CH, control_pid),
1347    SendMod    = megaco:conn_info(CH, send_mod),
1348    SendHandle = megaco:conn_info(CH, send_handle),
1349    megaco:disconnect(CH, Reason),
1350    case SendMod of
1351	megaco_tcp -> megaco_tcp:close(SendHandle);
1352	megaco_udp -> megaco_udp:close(SendHandle);
1353	SendMod    -> exit(Pid, Reason)
1354    end.
1355
1356
1357mg_parse_receive_info(RI, RH) ->
1358    d("mg_parse_receive_info -> get encoding module"),
1359    EM = get_encoding_module(RI),
1360    d("mg_parse_receive_info -> get encoding config"),
1361    EC = get_encoding_config(RI, EM),
1362    d("mg_parse_receive_info -> get transport module"),
1363    TM = get_transport_module(RI),
1364    d("mg_parse_receive_info -> get transport port"),
1365    TP = get_transport_port(RI),
1366    RH1 = RH#megaco_receive_handle{send_mod        = TM,
1367				   encoding_mod    = EM,
1368				   encoding_config = EC},
1369    {TP, RH1}.
1370
1371
1372mg_start_transport(MgcPort,
1373		   #megaco_receive_handle{send_mod = megaco_tcp} = RH) ->
1374    mg_start_tcp(MgcPort,RH);
1375mg_start_transport(MgcPort,
1376		   #megaco_receive_handle{send_mod = megaco_udp} = RH) ->
1377    mg_start_udp(MgcPort,RH);
1378mg_start_transport(_, #megaco_receive_handle{send_mod = Mod}) ->
1379    throw({error, {bad_send_mod, Mod}}).
1380
1381
1382mg_start_tcp(MgcPort, RH) ->
1383    i("start tcp transport"),
1384    case megaco_tcp:start_transport() of
1385	{ok, Sup} ->
1386	    {ok, LocalHost} = inet:gethostname(),
1387	    Opts = [{host,           LocalHost},
1388		    {port,           MgcPort},
1389		    {receive_handle, RH},
1390		    {tcp_options,    [{nodelay, true}]}],
1391            i("tcp transport started: attempt (tcp) connect to MGC at:"
1392              "~n   ~p", [LocalHost]),
1393	    case megaco_tcp:connect(Sup, Opts) of
1394		{ok, SendHandle, ControlPid} ->
1395                    PrelMgcMid = preliminary_mid,
1396                    i("tcp transport (tcp) connected: attempt (megaco) connect:"),
1397		    {ok, ConnHandle} =
1398			megaco:connect(RH, PrelMgcMid,
1399				       SendHandle, ControlPid),
1400		    ConnHandle;
1401		{error, Reason} ->
1402		    {error, {megaco_tcp_connect, Reason}}
1403	    end;
1404        {error, Reason} ->
1405            {error, {megaco_tcp_start_transport, Reason}}
1406    end.
1407
1408
1409mg_start_udp(MgcPort, RH) ->
1410    i("start udp transport"),
1411    case megaco_udp:start_transport() of
1412	{ok, Sup} ->
1413            %% Some linux (Ubuntu) has "crap" in their /etc/hosts, that
1414            %% causes problem for us in this case (UDP). So we can't use
1415            %% local host. Try instead to "figure out" tha actual address...
1416            LocalAddr = which_local_addr(),
1417	    Opts = [{port, 0}, {receive_handle, RH}],
1418            i("udp transport started: attempt (udp) open"),
1419	    case megaco_udp:open(Sup, Opts) of
1420		{ok, Handle, ControlPid} ->
1421                    MgcMid = preliminary_mid,
1422                    i("udp transport open: "
1423                      "now create send handle with MGC address: "
1424                      "~n   ~p", [LocalAddr]),
1425                    SendHandle = megaco_udp:create_send_handle(Handle,
1426							       LocalAddr,
1427							       MgcPort),
1428                    i("udp transport: attempt (megaco) connect to:"
1429                      "~n   ~p", [SendHandle]),
1430		    {ok, ConnHandle} =
1431			megaco:connect(RH, MgcMid,
1432				       SendHandle, ControlPid),
1433		    ConnHandle;
1434		{error, Reason} ->
1435                    {error, {megaco_udp_open, Reason}}
1436	    end;
1437        {error, Reason} ->
1438            {error, {megaco_udp_start_transport, Reason}}
1439    end.
1440
1441
1442mg_service_change(ConnHandle) ->
1443    mg_service_change(ConnHandle, restart, ?megaco_cold_boot).
1444
1445mg_service_change(ConnHandle, Method, Reason) ->
1446    SCP = #'ServiceChangeParm'{serviceChangeMethod = Method,
1447                               serviceChangeReason = [Reason]},
1448    TermId = [?megaco_root_termination_id],
1449    SCR = #'ServiceChangeRequest'{terminationID = TermId,
1450                                  serviceChangeParms = SCP},
1451    CR = #'CommandRequest'{command = {serviceChangeReq, SCR}},
1452    AR = #'ActionRequest'{contextId = ?megaco_null_context_id,
1453                          commandRequests = [CR]},
1454    megaco:cast(ConnHandle, [AR], []).
1455
1456
1457mg_notify_request(CH) ->
1458    TimeStamp = cre_timeNotation("19990729", "22000000"),
1459    Event     = cre_observedEvent("al/of",TimeStamp),
1460    Desc      = cre_observedEventsDesc(2222,[Event]),
1461    NotifyReq = cre_notifyReq([#megaco_term_id{id = ?A4444}],Desc),
1462    CmdReq    = cre_commandReq({notifyReq, NotifyReq}),
1463    ActReq    = cre_actionReq(?megaco_null_context_id, [CmdReq]),
1464    megaco:cast(CH, [ActReq], [{reply_data, Desc}]).
1465
1466mg_apply_load(#mg{conn_handle = CH}) ->
1467    mg_notify_request(CH).
1468
1469
1470cre_actionReq(Cid, Cmds) ->
1471    #'ActionRequest'{contextId = Cid,
1472		     commandRequests = Cmds}.
1473
1474cre_commandReq(Cmd) ->
1475    #'CommandRequest'{command = Cmd}.
1476
1477cre_notifyReq(Tid, EvsDesc) ->
1478    #'NotifyRequest'{terminationID = Tid, observedEventsDescriptor = EvsDesc}.
1479
1480cre_observedEventsDesc(Id, EvList) ->
1481    #'ObservedEventsDescriptor'{requestId = Id, observedEventLst = EvList}.
1482
1483cre_observedEvent(Name, Not) ->
1484    #'ObservedEvent'{eventName = Name, timeNotation = Not}.
1485
1486cre_timeNotation(D,T) ->
1487    #'TimeNotation'{date = D, time = T}.
1488
1489%% -----------------------
1490%% Handle megaco callbacks
1491%%
1492
1493mg_handle_request({handle_connect, CH, _PV},
1494		  #mg{state = connecting} = S) ->
1495    {ok, S#mg{conn_handle = CH}};
1496
1497mg_handle_request({handle_disconnect, CH, _PV, _R}, S) ->
1498    {ok, S#mg{conn_handle = CH}};
1499
1500mg_handle_request({handle_syntax_error, _RH, _PV, _ED}, S) ->
1501    {reply, S};
1502
1503mg_handle_request({handle_message_error, CH, _PV, _ED}, S) ->
1504    {no_reply, S#mg{conn_handle = CH}};
1505
1506mg_handle_request({handle_trans_request, CH, _PV, _AR}, S) ->
1507    ED =  #'ErrorDescriptor'{errorCode = ?megaco_not_implemented,
1508                             errorText = "Transaction requests not handled"},
1509    {{discard_ack, ED}, S#mg{conn_handle = CH}};
1510
1511mg_handle_request({handle_trans_long_request, CH, _PV, _RD}, S) ->
1512    ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented,
1513                            errorText = "Long transaction requests not handled"},
1514    {{discard_ack,  ED}, S#mg{conn_handle = CH}};
1515
1516mg_handle_request({handle_trans_reply, CH, _PV, _AR, _RD},
1517		  #mg{parent = Pid, state = connecting} = S) ->
1518    %% Should really check this...
1519    Pid ! {service_change_reply, ok, self()},
1520    {ok, S#mg{conn_handle = CH, state = connected}};
1521mg_handle_request({handle_trans_reply, _CH, _PV, {error, ED}, RD},
1522		  #mg{parent = Pid, load_counter = 0} = S)
1523  when is_record(ED, 'ErrorDescriptor') andalso
1524       is_record(RD, 'ObservedEventsDescriptor') ->
1525    Pid ! {load_complete, self()},
1526    {ok, S};
1527mg_handle_request({handle_trans_reply, _CH, _PV, {error, ED}, RD},
1528		  #mg{load_counter = N} = S)
1529  when is_record(ED, 'ErrorDescriptor') andalso
1530       is_record(RD, 'ObservedEventsDescriptor') ->
1531    apply_load_timer(),
1532    {ok, S#mg{load_counter = N-1}};
1533
1534mg_handle_request({handle_trans_ack, _CH, _PV, _AS, _AD}, S) ->
1535    {ok, S}.
1536
1537
1538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1539
1540await_started(Pid) ->
1541    receive
1542	{started, Pid} ->
1543	    d("await_started ~p: ok", [Pid]),
1544	    {ok, Pid};
1545	{'EXIT', Pid, Reason} ->
1546	    i("await_started ~p: received exit signal: ~p", [Pid, Reason]),
1547	    exit({failed_starting, Pid, Reason})
1548    after 10000 ->
1549	    i("await_started ~p: timeout", [Pid]),
1550	    exit({error, timeout})
1551    end.
1552
1553stop(Pid) ->
1554    d("stop ~p", [Pid]),
1555    Pid ! {stop, self()},
1556    receive
1557	{stopped, Pid} ->
1558	    d("stop -> received stopped from ~p", [Pid]),
1559	    ok;
1560	{'EXIT', Pid, Reason} ->
1561	    i("stop ~p: received exit signal: ~p", [Pid, Reason]),
1562	    exit({failed_stopping, Pid, Reason})
1563    after 10000 ->
1564	    exit({error, timeout})
1565    end.
1566
1567get_stats(Pid, No) ->
1568    d("get_stats ~p", [Pid]),
1569    Pid ! {statistics, No, self()},
1570    receive
1571	{statistics, No, Stats, Pid} ->
1572	    {ok, Stats};
1573	{'EXIT', Pid, Reason} ->
1574	    i("get_stats ~p: received exit signal: ~p", [Pid, Reason]),
1575	    exit({failed_getting_stats, Pid, Reason})
1576    after 10000 ->
1577	    exit({error, timeout})
1578    end.
1579
1580service_change(Pid) ->
1581    d("service_change ~p", [Pid]),
1582    Pid ! {service_change, self()},
1583    receive
1584	{service_change_reply, Res, Pid} ->
1585	    {ok, Res};
1586	{'EXIT', Pid, Reason} ->
1587	    i("service_change ~p: received exit signal: ~p", [Pid, Reason]),
1588	    exit({failed_service_change, Pid, Reason})
1589    after 10000 ->
1590	    exit({error, timeout})
1591    end.
1592
1593
1594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595
1596notify_started(Parent) ->
1597    Parent ! {started, self()}.
1598
1599
1600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601
1602%% The megaco user callback interface
1603
1604handle_connect(CH, PV, Pid) ->
1605%     i("handle_connect -> entry with"
1606%       "~n    CH:  ~p"
1607%       "~n    PV:  ~p"
1608%       "~n    Pid: ~p", [CH, PV, Pid]),
1609    case CH#megaco_conn_handle.remote_mid of
1610        preliminary_mid ->
1611	    %% Avoids deadlock
1612	    ok;
1613	_ ->
1614	    Reply = request(Pid, {handle_connect, CH, PV}),
1615% 	    d("handle_connect -> Reply:~n~p", [Reply]),
1616	    Reply
1617    end.
1618
1619handle_disconnect(_CH, _PV,
1620		  {user_disconnect, {Pid, ignore}},
1621		  Pid) ->
1622%     i("handle_disconnect(ignore) -> entry with"
1623%       "~n   CH: ~p"
1624%       "~n   PV: ~p", [CH, PV]),
1625    %% Avoids deadlock
1626    ok;
1627handle_disconnect(CH, PV, R, Pid) ->
1628%     i("handle_disconnect -> entry with"
1629%       "~n   CH: ~p"
1630%       "~n   PV: ~p"
1631%       "~n   R:  ~p", [CH, PV, R]),
1632    request(Pid, {handle_disconnect, CH, PV, R}).
1633
1634handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor, Pid) ->
1635%     i("handle_syntax_error -> entry with"
1636%       "~n   ReceiveHandle:   ~p"
1637%       "~n   ProtocolVersion: ~p"
1638%       "~n   ErrorDescriptor: ~p",
1639%       [ReceiveHandle, ProtocolVersion, ErrorDescriptor]),
1640    Req = {handle_syntax_error, ReceiveHandle, ProtocolVersion,
1641	   ErrorDescriptor},
1642    request(Pid, Req).
1643
1644handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor, Pid) ->
1645%     i("handle_message_error -> entry with"
1646%       "~n   ConnHandle:      ~p"
1647%       "~n   ProtocolVersion: ~p"
1648%       "~n   ErrorDescriptor: ~p",
1649%       [ConnHandle, ProtocolVersion, ErrorDescriptor]),
1650    Req = {handle_message_error, ConnHandle, ProtocolVersion, ErrorDescriptor},
1651    request(Pid, Req).
1652
1653handle_trans_request(CH, PV, AR, Pid) ->
1654%     i("handle_trans_request -> entry with"
1655%       "~n   CH:  ~p"
1656%       "~n   PV:  ~p"
1657%       "~n   AR:  ~p"
1658%       "~n   Pid: ~p", [CH, PV, AR, Pid]),
1659    Reply = request(Pid, {handle_trans_request, CH, PV, AR}),
1660%     i("handle_trans_request -> Reply:~n~p", [Reply]),
1661    Reply.
1662
1663handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Pid) ->
1664%     i("handle_trans_long_request -> entry with"
1665%       "~n   ConnHandle:      ~p"
1666%       "~n   ProtocolVersion: ~p"
1667%       "~n   ReqData:         ~p", [ConnHandle, ProtocolVersion, ReqData]),
1668    Req = {handle_trans_long_request, ConnHandle, ProtocolVersion, ReqData},
1669    request(Pid, Req).
1670
1671handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData, Pid) ->
1672%     i("handle_trans_reply -> entry with"
1673%       "~n   ConnHandle:      ~p"
1674%       "~n   ProtocolVersion: ~p"
1675%       "~n   ActualReply:     ~p"
1676%       "~n   ReplyData:       ~p",
1677%       [ConnHandle, ProtocolVersion, ActualReply, ReplyData]),
1678    Req = {handle_trans_reply, ConnHandle, ProtocolVersion,
1679	   ActualReply, ReplyData},
1680    request(Pid, Req).
1681
1682handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Pid) ->
1683%     i("handle_trans_ack -> entry with"
1684%       "~n   ConnHandle:      ~p"
1685%       "~n   ProtocolVersion: ~p"
1686%       "~n   AckStatus:       ~p"
1687%       "~n   AckData:         ~p",
1688%       [ConnHandle, ProtocolVersion, AckStatus, AckData]),
1689    Req = {handle_trans_ack, ConnHandle, ProtocolVersion, AckStatus, AckData},
1690    request(Pid, Req).
1691
1692
1693request(Pid, Request) ->
1694    Pid ! {request, Request, self()},
1695    receive
1696	{reply, Reply, Pid} ->
1697	    Reply
1698    end.
1699
1700
1701%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702
1703error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
1704
1705
1706get_encoding_module(RI) ->
1707    case (catch get_conf(encoding_module, RI)) of
1708	{error, _} ->
1709	    undefined;
1710	Val ->
1711	    Val
1712    end.
1713
1714get_encoding_config(RI, EM) ->
1715    case text_codec(EM) of
1716	true ->
1717	    case megaco:system_info(text_config) of
1718		[Conf] when is_list(Conf) ->
1719		    Conf;
1720		_ ->
1721		    []
1722	    end;
1723
1724	false ->
1725	    get_conf(encoding_config, RI)
1726    end.
1727
1728text_codec(megaco_compact_text_encoder) ->
1729    true;
1730text_codec(megaco_pretty_text_encoder) ->
1731    true;
1732text_codec(_) ->
1733    false.
1734
1735
1736get_transport_module(RI) ->
1737    get_conf(transport_module, RI).
1738
1739get_transport_port(RI) ->
1740    get_conf(port, RI).
1741
1742
1743get_conf(Key, Config) ->
1744    case lists:keysearch(Key, 1, Config) of
1745	{value, {Key, Val}} ->
1746	    Val;
1747	_ ->
1748	    exit({error, {not_found, Key, Config}})
1749    end.
1750
1751
1752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753
1754%% Tries to find a valid local address...
1755which_local_addr() ->
1756    case inet:getifaddrs() of
1757        {ok, IFs} ->
1758            which_local_addr(IFs);
1759        {error, Reason} ->
1760            i("Failed get local address: "
1761              "~n   ~p", [Reason]),
1762            ?SKIP({failed_get_local_addr, Reason})
1763    end.
1764
1765%% We explicitly skip some interfaces that we know is not "valid"
1766%% (docker stuff)
1767which_local_addr([]) ->
1768    ?SKIP(failed_get_local_addr);
1769which_local_addr([{"br-" ++ _ = _IfName, _IfOpts}|IFs]) ->
1770    which_local_addr(IFs);
1771which_local_addr([{"docker" ++ _ = _IfName, _IfOpts}|IFs]) ->
1772    which_local_addr(IFs);
1773which_local_addr([{_IfName, IfOpts}|IFs]) ->
1774    case which_local_addr2(IfOpts) of
1775        {ok, Addr} ->
1776            Addr;
1777        error ->
1778            which_local_addr(IFs)
1779    end.
1780
1781which_local_addr2(IfOpts) ->
1782    case if_is_running(IfOpts) of
1783        true ->
1784            which_local_addr3(IfOpts);
1785        false ->
1786            error
1787    end.
1788
1789if_is_running(If) ->
1790    lists:keymember(flags, 1, If) andalso
1791        begin
1792            {value, {flags, Flags}} = lists:keysearch(flags, 1, If),
1793            (not lists:member(loopback, Flags)) andalso lists:member(running, Flags)
1794        end.
1795
1796which_local_addr3([]) ->
1797    error;
1798which_local_addr3([{addr, Addr}|_])
1799  when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) ->
1800    {ok, Addr};
1801which_local_addr3([_|T]) ->
1802    which_local_addr3(T).
1803
1804
1805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1806
1807try_tc(TCName, Pre, Case, Post) ->
1808    try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post).
1809
1810try_tc(TCName, Name, Verbosity, Pre, Case, Post) ->
1811    ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post).
1812
1813
1814
1815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1816
1817%% p(F) ->
1818%%     p(F, []).
1819
1820p(F, A) ->
1821    p(get(sname), F, A).
1822
1823p(S, F, A) when is_list(S) ->
1824    io:format("*** [~s] ~p ~s ***"
1825	      "~n   " ++ F ++ "~n",
1826	      [?FTS(), self(), S | A]);
1827p(_S, F, A) ->
1828    io:format("*** [~s] ~p *** "
1829	      "~n   " ++ F ++ "~n",
1830	      [?FTS(), self() | A]).
1831
1832
1833i(F) ->
1834    i(F, []).
1835
1836i(F, A) ->
1837    print(info, get(verbosity), "", F, A).
1838
1839
1840d(F) ->
1841    d(F, []).
1842
1843d(F, A) ->
1844    print(debug, get(verbosity), "DBG: ", F, A).
1845
1846
1847printable(_, debug)   -> true;
1848printable(info, info) -> true;
1849printable(_,_)        -> false.
1850
1851print(Severity, Verbosity, P, F, A) ->
1852    print(printable(Severity,Verbosity), P, F, A).
1853
1854print(true, P, F, A) ->
1855    io:format("~s~p:~s:~s: " ++ F ++ "~n", [P, self(), get(sname), ?FTS() | A]);
1856print(_, _, _, _) ->
1857    ok.
1858
1859
1860progress(F) ->
1861    progress(F, []).
1862
1863progress(F, A) ->
1864    io:format("~s " ++ F ++ "~n", [?FTS()|A]),
1865    io:format(user, "~s " ++ F ++ "~n", [?FTS()|A]).
1866
1867
1868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1869
1870random() ->
1871    10 * rand:uniform(50).
1872
1873apply_load_timer() ->
1874    erlang:send_after(random(), self(), apply_load_timeout).
1875
1876