diff options
Diffstat (limited to 'lib/megaco/test/megaco_mib_test.erl')
-rw-r--r-- | lib/megaco/test/megaco_mib_test.erl | 1619 |
1 files changed, 1619 insertions, 0 deletions
diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl new file mode 100644 index 0000000000..2da6aa3bf3 --- /dev/null +++ b/lib/megaco/test/megaco_mib_test.erl @@ -0,0 +1,1619 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Verify the application specifics of the Megaco application +%%---------------------------------------------------------------------- +-module(megaco_mib_test). + +-compile(export_all). + +-include("megaco_test_lib.hrl"). +-include_lib("megaco/include/megaco.hrl"). +-include_lib("megaco/include/megaco_message_v1.hrl"). + +-define(TEST_VERBOSITY, info). % silence | info | debug +-define(MGC_VERBOSITY, info). +-define(MG_VERBOSITY, info). + +-define(LOAD_COUNTER_START, 100). +-define(A4444, ["11111111", "00000000", "00000000"]). + +-record(mgc, {parent = undefined, + tcp_sup = undefined, + udp_sup = undefined, + mid = undefined, + mg = []}). +-record(mg, {parent = undefined, + mid = undefined, + conn_handle = undefined, + state = initiated, + load_counter = 0}). + +t() -> megaco_test_lib:t(?MODULE). +t(Case) -> megaco_test_lib:t({?MODULE, Case}). + + +%% Test server callbacks +init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + case Case of + traffic -> + Conf0 = lists:keydelete(tc_timeout, 1, Config), + Conf = [{tc_timeout, timer:minutes(5)}|Conf0], + megaco_test_lib:init_per_testcase(Case, Conf); + _ -> + megaco_test_lib:init_per_testcase(Case, Config) + end. + +fin_per_testcase(Case, Config) -> + process_flag(trap_exit, false), + megaco_test_lib:fin_per_testcase(Case, Config). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all(suite) -> + Cases = + [ + plain, + connect, + traffic + ], + Cases. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +plain(suite) -> + []; +plain(doc) -> + ["Test case for the basic statistics counter handling. "]; +plain(Config) when is_list(Config) -> + io:format("create test table 1~n", []), + Tab1 = megaco_test_cnt1, + megaco_stats:init(Tab1), + + io:format("~ncreate test table 2~n", []), + Tab2 = megaco_test_cnt2, + megaco_stats:init(Tab2, [kalle, hobbe]), + + io:format("~ntable 1 increments~n", []), + H1 = #megaco_conn_handle{local_mid = {deviceName, "a"}, + remote_mid = {deviceName, "b"}}, + H2 = #megaco_conn_handle{local_mid = {deviceName, "a"}, + remote_mid = {deviceName, "c"}}, + 1 = megaco_stats:inc(Tab1, H1, sune), + 2 = megaco_stats:inc(Tab1, H2, sune, 2), + 3 = megaco_stats:inc(Tab1, H1, gurka, 3), + 4 = megaco_stats:inc(Tab1, H2, sune, 2), + 4 = megaco_stats:inc(Tab1, H1, gurka), + + io:format("~ntable 2 increments~n", []), + H3 = #megaco_conn_handle{local_mid = {deviceName, "e"}, + remote_mid = {deviceName, "c"}}, + H4 = #megaco_conn_handle{local_mid = {deviceName, "e"}, + remote_mid = {deviceName, "d"}}, + 1 = megaco_stats:inc(Tab2, H3, tomat), + 4 = megaco_stats:inc(Tab2, H3, tomat, 3), + 5 = megaco_stats:inc(Tab2, H4, paprika, 5), + + io:format("~ntable 2 global increments~n", []), + 1 = megaco_stats:inc(Tab2, kalle), + 1 = megaco_stats:inc(Tab2, hobbe), + 2 = megaco_stats:inc(Tab2, hobbe), + 2 = megaco_stats:inc(Tab2, kalle), + 3 = megaco_stats:inc(Tab2, kalle), + 4 = megaco_stats:inc(Tab2, hobbe, 2), + + io:format("~ntable 1 stats~n", []), + {ok, Stats1} = megaco_stats:get_stats(Tab1), + io:format("Stats1 = ~p~n", [Stats1]), + {value, {H1, H1Stats}} = lists:keysearch(H1, 1, Stats1), + Stats1_2 = lists:keydelete(H1, 1, Stats1), + {value, {H2, H2Stats}} = lists:keysearch(H2, 1, Stats1_2), + Stats1_3 = lists:keydelete(H2, 1, Stats1_2), + [] = Stats1_3, + io:format("H1Stats = ~p~n", [H1Stats]), + io:format("H2Stats = ~p~n", [H2Stats]), + + {value, {sune, 1}} = lists:keysearch(sune, 1, H1Stats), + H1Stats_2 = lists:keydelete(sune, 1, H1Stats), + {value, {gurka, 4}} = lists:keysearch(gurka, 1, H1Stats_2), + H1Stats_3 = lists:keydelete(gurka, 1, H1Stats_2), + [] = H1Stats_3, + + {value, {sune, 4}} = lists:keysearch(sune, 1, H2Stats), + H2Stats_2 = lists:keydelete(sune, 1, H2Stats), + [] = H2Stats_2, + + + %% -- + io:format("~ntable 2 stats~n", []), + {ok, Stats2} = megaco_stats:get_stats(Tab2), + io:format("Stats2 = ~p~n", [Stats2]), + {ok, 3} = megaco_stats:get_stats(Tab2, kalle), + {ok, 4} = megaco_stats:get_stats(Tab2, hobbe), + + + %% -- + io:format("~ntable 1 reset stats for ~p~n", [H1]), + megaco_stats:reset_stats(Tab1, H1), + {ok, Stats1_4} = megaco_stats:get_stats(Tab1), + io:format("Stats1_4 = ~p~n", [Stats1_4]), + {ok, 0} = megaco_stats:get_stats(Tab1, H1, sune), + {ok, 0} = megaco_stats:get_stats(Tab1, H1, gurka), + + + %% -- + io:format("~ntable 2 reset stats for kalle and ~p~n", [H4]), + megaco_stats:reset_stats(Tab2, kalle), + megaco_stats:reset_stats(Tab2, H4), + {ok, Stats2_2} = megaco_stats:get_stats(Tab2), + io:format("Stats2_2 = ~p~n", [Stats2_2]), + {ok, 0} = megaco_stats:get_stats(Tab2, kalle), + {ok, 4} = megaco_stats:get_stats(Tab2, hobbe), + {ok, 4} = megaco_stats:get_stats(Tab2, H3, tomat), + {ok, 0} = megaco_stats:get_stats(Tab2, H4, paprika), + {ok, Stats4_4} = megaco_stats:get_stats(Tab2, H4), + io:format("Stats4_4 = ~p~n", [Stats4_4]), + + %% -- + io:format("~ntable 2 stats for nonexisting counters~n", []), + {error, _} = megaco_stats:get_stats(Tab2, kalla), + {error, _} = megaco_stats:get_stats(Tab2, H3, paprika), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +connect(suite) -> + []; +connect(doc) -> + []; +connect(Config) when is_list(Config) -> + put(verbosity, ?TEST_VERBOSITY), + put(sname, "TEST"), + i("connect -> starting"), + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + d("connect -> Nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]), + ok = megaco_test_lib:start_nodes([MgcNode, Mg1Node, Mg2Node], + ?FILE, ?LINE), + + %% Start the MGC and MGs + ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], + {ok, Mgc} = + start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), + {ok, Mg1} = + start_mg(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), + {ok, Mg2} = + start_mg(Mg2Node, {deviceName, "mg2"}, binary, udp, ?MG_VERBOSITY), + + %% Collect the initial statistics (should be zero if anything) + {ok, Mg1Stats0} = get_stats(Mg1, 1), + d("connect -> stats for Mg1: ~n~p", [Mg1Stats0]), + {ok, Mg2Stats0} = get_stats(Mg2, 1), + d("connect -> stats for Mg2: ~n~p", [Mg2Stats0]), + {ok, MgcStats0} = get_stats(Mgc, 1), + d("connect -> stats for Mgc: ~n~p", [MgcStats0]), + + %% Ask Mg1 to do a service change + {ok, Res1} = service_change(Mg1), + d("connect -> (Mg1) service change result: ~p", [Res1]), + + %% Collect the statistics + {ok, Mg1Stats1} = get_stats(Mg1, 1), + d("connect -> stats for Mg1: ~n~p", [Mg1Stats1]), + {ok, MgcStats1} = get_stats(Mgc, 1), + d("connect -> stats (1) for Mgc: ~n~p", [MgcStats1]), + {ok, MgcStats2} = get_stats(Mgc, 2), + d("connect -> stats (2) for Mgc: ~n~p", [MgcStats2]), + + %% Ask Mg2 to do a service change + {ok, Res2} = service_change(Mg2), + d("connect -> (Mg2) service change result: ~p", [Res2]), + + %% Collect the statistics + {ok, Mg2Stats1} = get_stats(Mg2, 1), + d("connect -> stats for Mg1: ~n~p", [Mg2Stats1]), + {ok, MgcStats3} = get_stats(Mgc, 1), + d("connect -> stats (1) for Mgc: ~n~p", [MgcStats3]), + {ok, MgcStats4} = get_stats(Mgc, 2), + d("connect -> stats (2) for Mgc: ~n~p", [MgcStats4]), + + %% Tell Mg1 to stop + stop(Mg1), + + %% Collect the statistics + {ok, MgcStats5} = get_stats(Mgc, 1), + d("connect -> stats (1) for Mgc: ~n~p", [MgcStats5]), + {ok, MgcStats6} = get_stats(Mgc, 2), + d("connect -> stats (2) for Mgc: ~n~p", [MgcStats6]), + + %% Tell Mg2 to stop + stop(Mg2), + + %% Collect the statistics + {ok, MgcStats7} = get_stats(Mgc, 1), + d("connect -> stats (1) for Mgc: ~n~p", [MgcStats7]), + {ok, MgcStats8} = get_stats(Mgc, 2), + d("connect -> stats (2) for Mgc: ~n~p", [MgcStats8]), + + %% Tell Mgc to stop + stop(Mgc), + + i("connect -> done", []), + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +traffic(suite) -> + []; +traffic(doc) -> + []; +traffic(Config) when is_list(Config) -> + put(verbosity, ?TEST_VERBOSITY), + put(sname, "TEST"), + i("traffic -> starting"), + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + Mg3Node = make_node_name(mg3), + Mg4Node = make_node_name(mg4), + d("traffic -> Nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p" + "~n Mg3Node: ~p" + "~n Mg4Node: ~p", + [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), + ok = megaco_test_lib:start_nodes([MgcNode, + Mg1Node, Mg2Node, Mg3Node, Mg4Node], + ?FILE, ?LINE), + + %% Start the MGC and MGs + i("traffic -> start the MGC"), + ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], + {ok, Mgc} = + start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), + + i("traffic -> start and connect the MGs"), + MgConf0 = [{Mg1Node, "mg1", text, tcp}, + {Mg2Node, "mg2", text, udp}, + {Mg3Node, "mg3", binary, tcp}, + {Mg4Node, "mg4", binary, udp}], + MgConf = traffic_connect_mg(MgConf0, []), + + %% Collect and check the MGs statistics + i("traffic -> collect and check the MGs stats"), + traffic_verify_mg_stats(MgConf, 1, 1), + + %% Collect and check the MGC statistics + i("traffic -> collect and check the MGC stats"), + {ok, MgcStats1} = get_stats(Mgc, 1), + d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats1]), + traffic_verify_mgc_stats(Mgc, 1, 1), + + + sleep(1000), + + + %% And apply some load + i("traffic -> apply traffic load"), + ok = traffic_apply_load(MgConf), + + %% Await completion of load part and the collect traffic + i("traffic -> await load competion"), + ok = traffic_await_load_complete(MgConf), + + + sleep(1000), + + + i("traffic -> collect and check the MGs statistics"), + traffic_verify_mg_stats(MgConf, + 1 + ?LOAD_COUNTER_START, + 1 + ?LOAD_COUNTER_START), + + i("traffic -> collect and check the MGC statistics"), + {ok, MgcStats3} = get_stats(Mgc, 1), + d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats3]), + traffic_verify_mgc_stats(Mgc, + 1 + ?LOAD_COUNTER_START, + 1 + ?LOAD_COUNTER_START), + + + sleep(1000), + + + %% Reset counters + i("traffic -> reset the MGs statistics"), + traffic_reset_mg_stats(MgConf), + i("traffic -> collect and check the MGs statistics"), + traffic_verify_mg_stats(MgConf, 0, 0), + + i("traffic -> reset the MGC statistics"), + traffic_reset_mgc_stats(Mgc), + i("traffic -> collect and check the MGC statistics"), + traffic_verify_mgc_stats(Mgc, 0, 0), + + + sleep(1000), + + + %% And apply some load + i("traffic -> apply traffic load"), + ok = traffic_apply_load(MgConf), + + %% Await completion of load part and the collect traffic + i("traffic -> await load competion"), + ok = traffic_await_load_complete(MgConf), + + + sleep(1000), + + + i("traffic -> collect and check the MGs statistics"), + traffic_verify_mg_stats(MgConf, + ?LOAD_COUNTER_START, + ?LOAD_COUNTER_START), + + i("traffic -> collect and check the MGC statistics"), + traffic_verify_mgc_stats(Mgc, + ?LOAD_COUNTER_START, + ?LOAD_COUNTER_START), + + + sleep(1000), + + + %% Tell MGs to stop + i("traffic -> stop the MGs"), + traffic_stop_mg(MgConf), + + + sleep(1000), + + + %% Collect the statistics + i("traffic -> collect the MGC statistics"), + {ok, MgcStats7} = get_stats(Mgc, 1), + d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats7]), + {ok, MgcStats8} = get_stats(Mgc, 2), + d("traffic -> stats (2) for Mgc: ~n~p~n", [MgcStats8]), + + %% Tell Mgc to stop + i("traffic -> stop the MGC"), + stop(Mgc), + + i("traffic -> done", []), + ok. + + +traffic_verify_mgc_stats(Pid, Out, In) + when is_pid(Pid) andalso is_integer(Out) andalso is_integer(In) -> + d("traffic_verify_mgc_stats -> entry with" + "~n Out: ~p" + "~n In: ~p", [Out, In]), + {ok, Stats} = get_stats(Pid, 2), + d("traffic_verify_mgc_stats -> stats (2) for Mgc: ~n~p~n", [Stats]), + traffic_verify_mgc_stats(Stats, Out, In); + +traffic_verify_mgc_stats(Stats, Out, In) when is_list(Stats) -> + d("traffic_verify_mgc_stats -> checking stats"), + Gen = traffic_verify_get_stats(gen, Stats), + Trans = traffic_verify_get_stats(trans, Stats), + traffic_verify_mgc_stats_gen(Gen), + traffic_verify_mgc_stats_trans(Trans, Out, In). + +traffic_verify_mgc_stats_gen([]) -> + d("traffic_verify_mgc_stats_gen -> done"), + ok; +traffic_verify_mgc_stats_gen([{medGwyGatewayNumErrors, 0}|Stats]) -> + traffic_verify_mgc_stats_gen(Stats); +traffic_verify_mgc_stats_gen([{medGwyGatewayNumErrors, Val}|_]) -> + exit({global_error_counter, Val, mgc}); +traffic_verify_mgc_stats_gen([{Handle, Counters}|Stats]) -> + N = {mgc, Handle, Handle}, + traffic_verify_counter(N, medGwyGatewayNumErrors, Counters, 0), + traffic_verify_mgc_stats_gen(Stats). + + +traffic_verify_mgc_stats_trans([], _Out, _In) -> + ok; +traffic_verify_mgc_stats_trans([{Mod, Stats}|MgcStats], Out, In) -> + d("traffic_verify_mgc_stats_trans -> entry with" + "~n Mod: ~p" + "~n Stats: ~p", [Mod, Stats]), + traffic_verify_mgc_stats_trans(Mod, Stats, Out, In), + traffic_verify_mgc_stats_trans(MgcStats, Out, In). + +traffic_verify_mgc_stats_trans(_Mod, [], _Out, _In) -> + ok; +traffic_verify_mgc_stats_trans(Mod, [{Handle,Counters}|Stats], Out, In) -> + N = {mgc, Mod, Handle}, + traffic_verify_counter(N, medGwyGatewayNumErrors, Counters, 0), + traffic_verify_counter(N, medGwyGatewayNumOutMessages, Counters, Out), + traffic_verify_counter(N, medGwyGatewayNumInMessages, Counters, In), + traffic_verify_mgc_stats_trans(Mod, Stats, Out, In). + + +traffic_verify_mg_stats(MgConf, Out, In) + when is_integer(Out) andalso is_integer(In) -> + d("traffic_verify_mg_stats -> entry with" + "~n Out: ~p" + "~n In: ~p", [Out, In]), + Stats = traffic_get_mg_stats(MgConf, []), + d("traffic_verify_mg_stats -> stats for MGs: ~n~p", [Stats]), + traffic_verify_mg_stats1(Stats, Out, In). + +traffic_verify_mg_stats1([], _, _) -> + ok; +traffic_verify_mg_stats1([{Name, Stats}|MgStats], Out, In) -> + d("traffic_verify_mg_stats1 -> entry with" + "~n Name: ~s" + "~n Stats: ~p", [Name, Stats]), + Gen = traffic_verify_get_stats(gen, Stats), + Trans = traffic_verify_get_stats(trans, Stats), + traffic_verify_mg_stats_gen(Name, Gen), + traffic_verify_mg_stats_trans(Name, Trans, Out, In), + traffic_verify_mg_stats1(MgStats, Out, In). + +traffic_verify_mg_stats_gen(Mg, []) -> + d("traffic_verify_mg_stats_gen -> ~s checked out OK",[Mg]), + ok; +traffic_verify_mg_stats_gen(Mg, [{medGwyGatewayNumErrors, 0}|Stats]) -> + traffic_verify_mg_stats_gen(Mg, Stats); +traffic_verify_mg_stats_gen(Mg, [{medGwyGatewayNumErrors, Val}|_]) -> + exit({global_error_counter, Val, Mg}); +traffic_verify_mg_stats_gen(Mg, [{_Handle, Counters}|Stats]) -> + traffic_verify_counter(Mg, medGwyGatewayNumErrors, Counters, 0), + traffic_verify_mg_stats_gen(Mg, Stats). + +traffic_verify_mg_stats_trans(Mg, Counters, Out, In) -> + traffic_verify_counter(Mg, medGwyGatewayNumErrors, Counters, 0), + traffic_verify_counter(Mg, medGwyGatewayNumOutMessages, Counters, Out), + traffic_verify_counter(Mg, medGwyGatewayNumInMessages, Counters, In). + + +traffic_verify_get_stats(S, Stats) -> + case lists:keysearch(S, 1, Stats) of + {value, {S, Val}} -> + Val; + false -> + exit({not_found, S, Stats}) + end. + +traffic_verify_counter(Name, Counter, Counters, Expected) -> + case lists:keysearch(Counter, 1, Counters) of + {value, {Counter, Expected}} -> + ok; + {value, {Counter, Val}} -> + exit({illegal_counter_value, Counter, Val, Expected, Name}); + false -> + exit({not_found, Counter, Counters, Name, Expected}) + end. + + +traffic_connect_mg([], Acc) -> + lists:reverse(Acc); +traffic_connect_mg([{Node, Name, Coding, Trans}|Mg], Acc) -> + Pid = traffic_connect_mg(Node, Name, Coding, Trans), + traffic_connect_mg(Mg, [{Name, Pid}|Acc]). + +traffic_connect_mg(Node, Name, Coding, Trans) -> + Mid = {deviceName, Name}, + {ok, Pid} = start_mg(Node, Mid, Coding, Trans, ?MG_VERBOSITY), + + %% Ask the MGs to do a service change + {ok, Res} = service_change(Pid), + d("traffic_connect_mg -> (~s) service change result: ~p", [Name,Res]), + + Pid. + + +traffic_stop_mg(MGs) -> + [stop(Pid) || {_Name, Pid} <- MGs]. + + +traffic_get_mg_stats([], Acc) -> + lists:reverse(Acc); +traffic_get_mg_stats([{Name, Pid}|Mgs], Acc) -> + {ok, Stats} = get_stats(Pid, 1), + d("traffic_get_mg_stats -> stats for ~s: ~n~p~n", [Name, Stats]), + traffic_get_mg_stats(Mgs, [{Name, Stats}|Acc]). + + +traffic_apply_load([]) -> + ok; +traffic_apply_load([{_,MG}|MGs]) -> + MG ! {apply_load, self(), ?LOAD_COUNTER_START}, + receive + {apply_load_ack, MG} -> + traffic_apply_load(MGs); + {'EXIT', MG, Reason} -> + exit({mg_exit, MG, Reason}) + after 10000 -> + exit({apply_load_ack_timeout, MG}) + end. + + +traffic_reset_mg_stats([]) -> + ok; +traffic_reset_mg_stats([{Name, Pid}|MGs]) -> + d("traffic_reset_mg_stats -> resetting ~s", [Name]), + traffic_reset_stats(Pid), + traffic_reset_mg_stats(MGs). + +traffic_reset_mgc_stats(Mgc) -> + d("traffic_reset_mgc_stats -> resetting ~p", [Mgc]), + traffic_reset_stats(Mgc). + +traffic_reset_stats(Pid) -> + Pid ! {reset_stats, self()}, + receive + {reset_stats_ack, Pid} -> + ok; + {'EXIT', Pid, Reason} -> + exit({client_exit, Pid, Reason}) + after 10000 -> + exit({reset_stats_ack_timeout, Pid}) + end. + + +traffic_await_load_complete([]) -> + ok; +traffic_await_load_complete(MGs0) -> + receive + {load_complete, Pid} -> + d("received load_complete from ~p", [Pid]), + MGs1 = lists:keydelete(Pid, 2, MGs0), + traffic_await_load_complete(lists:delete(Pid, MGs1)); + {'EXIT', Pid, Reason} -> + i("exit signal from ~p: ~p", [Pid, Reason]), + case lists:keymember(Pid, 2, MGs0) of + true -> + exit({mg_exit, Pid, Reason}); + false -> + MGs1 = lists:keydelete(Pid, 2, MGs0), + traffic_await_load_complete(lists:delete(Pid, MGs1)) + end + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +make_node_name(Name) -> + case string:tokens(atom_to_list(node()), [$@]) of + [_,Host] -> + list_to_atom(lists:concat([atom_to_list(Name) ++ "@" ++ Host])); + _ -> + exit("Test node must be started with '-sname'") + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_mgc(Node, Mid, ET, Verbosity) -> + d("start mgc[~p]: ~p", [Node, Mid]), + RI = {receive_info, mk_recv_info(ET)}, + Config = [{local_mid, Mid}, RI], + Pid = spawn_link(Node, ?MODULE, mgc, [self(), Verbosity, Config]), + await_started(Pid). + +mk_recv_info(ET) -> + mk_recv_info(ET, []). + +mk_recv_info([], Acc) -> + Acc; +mk_recv_info([{text,tcp}|ET], Acc) -> + RI = [{encoding_module, megaco_pretty_text_encoder}, + {encoding_config, []}, + {transport_module, megaco_tcp}, + {port, 2944}], + mk_recv_info(ET, [RI|Acc]); +mk_recv_info([{text,udp}|ET], Acc) -> + RI = [{encoding_module, megaco_pretty_text_encoder}, + {encoding_config, []}, + {transport_module, megaco_udp}, + {port, 2944}], + mk_recv_info(ET, [RI|Acc]); +mk_recv_info([{binary,tcp}|ET], Acc) -> + RI = [{encoding_module, megaco_ber_bin_encoder}, + {encoding_config, []}, + {transport_module, megaco_tcp}, + {port, 2945}], + mk_recv_info(ET, [RI|Acc]); +mk_recv_info([{binary,udp}|ET], Acc) -> + RI = [{encoding_module, megaco_ber_bin_encoder}, + {encoding_config, []}, + {transport_module, megaco_udp}, + {port, 2945}], + mk_recv_info(ET, [RI|Acc]); +mk_recv_info([ET|_], _) -> + throw({error, {invaalid_encoding_transport, ET}}). + +mgc(Parent, Verbosity, Config) -> + process_flag(trap_exit, true), + put(verbosity, Verbosity), + put(sname, "MGC"), + i("mgc -> starting"), + {Mid, TcpSup, UdpSup} = mgc_init(Config), + notify_started(Parent), + S = #mgc{parent = Parent, + tcp_sup = TcpSup, udp_sup = UdpSup, mid = Mid}, + i("mgc -> started"), + mgc_loop(S). + +mgc_init(Config) -> + d("mgc_init -> entry"), + Mid = get_conf(local_mid, Config), + RI = get_conf(receive_info, Config), + d("mgc_init -> start megaco"), + application:start(megaco), + d("mgc_init -> start megaco user"), + megaco:start_user(Mid, []), + d("mgc_init -> update user info (user_mod)"), + megaco:update_user_info(Mid, user_mod, ?MODULE), + d("mgc_init -> update user info (user_args)"), + megaco:update_user_info(Mid, user_args, [self()]), + d("mgc_init -> get user info (receive_handle)"), + RH = megaco:user_info(Mid,receive_handle), + d("mgc_init -> parse receive info"), + ListenTo = mgc_parse_receive_info(RI, RH), + d("mgc_init -> start transports"), + {Tcp, Udp} = mgc_start_transports(ListenTo), + {Mid, Tcp, Udp}. + + +mgc_loop(S) -> + d("mgc_loop -> await request"), + receive + {stop, Parent} when S#mgc.parent == Parent -> + i("mgc_loop -> stopping", []), + Mid = S#mgc.mid, + (catch mgc_close_conns(Mid)), + megaco:stop_user(Mid), + application:stop(megaco), + i("mgc_loop -> stopped", []), + Parent ! {stopped, self()}, + exit(normal); + + + + %% Reset stats + {reset_stats, Parent} when S#mgc.parent == Parent -> + i("mgc_loop -> got request to reset stats counters"), + mgc_reset_stats(S#mgc.mid), + Parent ! {reset_stats_ack, self()}, + mgc_loop(S); + + + %% Give me statistics + {statistics, 1, Parent} when S#mgc.parent == Parent -> + i("mgc_loop -> got request for statistics 1"), + {ok, Gen} = megaco:get_stats(), + GetTrans = + fun(CH) -> + Reason = {statistics, CH}, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), + {ok, Stats} = + case SendMod of + megaco_tcp -> megaco_tcp:get_stats(SendHandle); + megaco_udp -> megaco_udp:get_stats(SendHandle); + SendMod -> exit(Pid, Reason) + end, + {SendHandle, Stats} + end, + Mid = S#mgc.mid, + Trans = + lists:map(GetTrans, megaco:user_info(Mid, connections)), + Parent ! {statistics, 1, [{gen, Gen}, {trans, Trans}], self()}, + mgc_loop(S); + + + {statistics, 2, Parent} when S#mgc.parent == Parent -> + i("mgc_loop -> got request for statistics 2"), + {ok, Gen} = megaco:get_stats(), + #mgc{tcp_sup = TcpSup, udp_sup = UdpSup} = S, + TcpStats = get_trans_stats(TcpSup, megaco_tcp), + UdpStats = get_trans_stats(UdpSup, megaco_udp), + Parent ! {statistics, 2, [{gen, Gen}, {trans, [TcpStats, UdpStats]}], self()}, + mgc_loop(S); + + + %% Megaco callback messages + {request, Request, From} -> + d("mgc_loop -> received megaco request: ~n~p~n From: ~p", + [Request, From]), + Reply = mgc_handle_request(Request), + d("mgc_loop -> send request reply: ~n~p", [Reply]), + From ! {reply, Reply, self()}, + mgc_loop(S); + + + {'EXIT', Pid, Reason} -> + error_msg("MGC received unexpected exit signal from ~p:~n~p", + [Pid, Reason]), + mgc_loop(S); + + + Invalid -> + i("mgc_loop -> received invalid request: ~p", [Invalid]), + mgc_loop(S) + end. + + +mgc_reset_stats(Mid) -> + megaco:reset_stats(), + mgc_reset_trans_stats(megaco:user_info(Mid, connections), []). + +mgc_reset_trans_stats([], _Reset) -> + ok; +mgc_reset_trans_stats([CH|CHs], Reset) -> + SendMod = megaco:conn_info(CH, send_mod), + case lists:member(SendMod, Reset) of + true -> + mgc_reset_trans_stats(CHs, Reset); + false -> + SendMod:reset_stats(), + mgc_reset_trans_stats(CHs, [SendMod|Reset]) + end. + + +mgc_close_conns(Mid) -> + Reason = {self(), ignore}, + Disco = fun(CH) -> + (catch mgc_close_conn(CH, Reason)) + end, + lists:map(Disco, megaco:user_info(Mid, connections)). + +mgc_close_conn(CH, Reason) -> + d("close connection to ~p", [CH#megaco_conn_handle.remote_mid]), + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), + megaco:disconnect(CH, Reason), + case SendMod of + megaco_tcp -> megaco_tcp:close(SendHandle); + megaco_udp -> megaco_udp:close(SendHandle); + SendMod -> exit(Pid, Reason) + end. + +get_trans_stats(P, SendMod) when is_pid(P) -> + case (catch SendMod:get_stats()) of + {ok, Stats} -> + {SendMod, Stats}; + Else -> + {SendMod, Else} + end; +get_trans_stats(_P, SendMod) -> + {SendMod, undefined}. + +mgc_parse_receive_info([], _RH) -> + throw({error, no_receive_info}); +mgc_parse_receive_info(RI, RH) -> + mgc_parse_receive_info(RI, RH, []). + +mgc_parse_receive_info([], _RH, ListenTo) -> + ListenTo; +mgc_parse_receive_info([RI|RIs], RH, ListenTo) -> + d("mgc_parse_receive_info -> parse receive info"), + RH1 = mgc_parse_receive_info1(RI, RH), + case (catch mgc_parse_receive_info1(RI, RH)) of + {error, Reason} -> + i("failed parsing receive info: ~p~n~p", [RI, Reason]), + exit({failed_parsing_recv_info, RI, Reason}); + RH1 -> + mgc_parse_receive_info(RIs, RH, [RH1|ListenTo]) + end. + +mgc_parse_receive_info1(RI, RH) -> + d("mgc_parse_receive_info1 -> get encoding module"), + EM = get_encoding_module(RI), + d("mgc_parse_receive_info1 -> get encoding config"), + EC = get_encoding_config(RI, EM), + d("mgc_parse_receive_info1 -> get transport module"), + TM = get_transport_module(RI), + d("mgc_parse_receive_info1 -> get transport port"), + TP = get_transport_port(RI), + RH1 = RH#megaco_receive_handle{send_mod = TM, + encoding_mod = EM, + encoding_config = EC}, + {TP, RH1}. + + +mgc_start_transports([]) -> + throw({error, no_transport}); +mgc_start_transports(ListenTo) -> + mgc_start_transports(ListenTo, undefined, undefined). + + +mgc_start_transports([], TcpSup, UdpSup) -> + {TcpSup, UdpSup}; +mgc_start_transports([{Port, RH}|ListenTo], TcpSup, UdpSup) + when RH#megaco_receive_handle.send_mod == megaco_tcp -> + TcpSup1 = mgc_start_tcp(RH, Port, TcpSup), + mgc_start_transports(ListenTo, TcpSup1, UdpSup); +mgc_start_transports([{Port, RH}|ListenTo], TcpSup, UdpSup) + when RH#megaco_receive_handle.send_mod == megaco_udp -> + UdpSup1 = mgc_start_udp(RH, Port, UdpSup), + mgc_start_transports(ListenTo, TcpSup, UdpSup1); +mgc_start_transports([{_Port, RH}|_ListenTo], _TcpSup, _UdpSup) -> + throw({error, {bad_send_mod, RH#megaco_receive_handle.send_mod}}). + + +mgc_start_tcp(RH, Port, undefined) -> + d("start tcp transport"), + case megaco_tcp:start_transport() of + {ok, Sup} -> + mgc_start_tcp(RH, Port, Sup); + Else -> + throw({error, {failed_starting_tcp_transport, Else}}) + end; +mgc_start_tcp(RH, Port, Sup) when is_pid(Sup) -> + d("tcp listen on ~p", [Port]), + Opts = [{port, Port}, + {receive_handle, RH}, + {tcp_options, [{nodelay, true}]}], + mgc_tcp_create_listen(Sup, Opts, 3). + +mgc_tcp_create_listen(Sup, Opts, N) -> + mgc_tcp_create_listen(Sup, Opts, N, 1, undefined). + +mgc_tcp_create_listen(_Sup, _Opts, N, N, InitialReason) -> + d("failed creating mgc tcp listen socket after ~p tries: ~p", + [N, InitialReason]), + throw({error, {failed_starting_tcp_listen, InitialReason}}); +mgc_tcp_create_listen(Sup, Opts, MaxN, N, _InitialReason) + when is_integer(N) andalso is_integer(MaxN) andalso (MaxN > N) -> + d("try create mgc tcp listen socket [~w]", [N]), + case megaco_tcp:listen(Sup, Opts) of + ok -> + Sup; + {error, {could_not_start_listener, {gen_tcp_listen, eaddrinuse} = Reason}} -> + sleep(N * 200), + mgc_tcp_create_listen(Sup, Opts, MaxN, N + 1, Reason); + {error, Reason} -> + throw({error, {failed_starting_tcp_listen, Reason}}); + Else -> + throw({error, {failed_starting_tcp_listen, Else}}) + end. + + +mgc_start_udp(RH, Port, undefined) -> + d("start udp transport"), + case megaco_udp:start_transport() of + {ok, Sup} -> + mgc_start_udp(RH, Port, Sup); + Else -> + throw({error, {failed_starting_udp_transport, Else}}) + end; +mgc_start_udp(RH, Port, Sup) -> + d("open udp ~p", [Port]), + Opts = [{port, Port}, {receive_handle, RH}], + case megaco_udp:open(Sup, Opts) of + {ok, _SendHandle, _ControlPid} -> + Sup; + Else -> + exit({error, {failed_starting_udp_listen, Else}}) + end. + + + +%% ----------------------- +%% Handle megaco callbacks +%% + +mgc_handle_request({handle_connect, _CH, _PV}) -> + ok; +mgc_handle_request({handle_disconnect, CH, _PV, R}) -> + megaco:cancel(CH, R), % Cancel the outstanding messages + ok; +mgc_handle_request({handle_syntax_error, _RH, _PV, _ED}) -> + reply; +mgc_handle_request({handle_message_error, _CH, _PV, _ED}) -> + no_reply; +mgc_handle_request({handle_trans_request, CH, PV, ARs}) -> + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Only single service change on null context handled"}, + case ARs of + [AR] -> + ContextId = AR#'ActionRequest'.contextId, + case AR#'ActionRequest'.commandRequests of + [CR] when ContextId == ?megaco_null_context_id -> + case CR#'CommandRequest'.command of + {serviceChangeReq, Req} -> + Rep = mgc_service_change(CH, PV, Req), + CmdRep = [{serviceChangeReply, Rep}], + {discard_ack, + [#'ActionReply'{contextId = ContextId, + commandReply = CmdRep}]}; + _ -> + {discard_ack, ED} + end; + _ -> + {discard_ack, ED} + end; + _ -> + {discard_ack, ED} + end; +mgc_handle_request({handle_trans_long_request, _CH, _PV, _RD}) -> + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Long transaction requests not handled"}, + {discard_ack, ED}; +mgc_handle_request({handle_trans_reply, _CH, _PV, _AR, _RD}) -> + ok; +mgc_handle_request({handle_trans_ack, _CH, _PV, _AS, _AD}) -> + ok. + +mgc_service_change(CH, _PV, SCR) -> + SCP = SCR#'ServiceChangeRequest'.serviceChangeParms, + #'ServiceChangeParm'{serviceChangeAddress = Address, + serviceChangeProfile = Profile, + serviceChangeReason = [_Reason]} = SCP, + TermId = SCR#'ServiceChangeRequest'.terminationID, + if + TermId == [?megaco_root_termination_id] -> + MyMid = CH#megaco_conn_handle.local_mid, + Res = {serviceChangeResParms, + #'ServiceChangeResParm'{serviceChangeMgcId = MyMid, + serviceChangeAddress = Address, + serviceChangeProfile = Profile}}, + #'ServiceChangeReply'{terminationID = TermId, + serviceChangeResult = Res}; + true -> + Res = {errorDescriptor, + #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Only handled for root"}}, + + #'ServiceChangeReply'{terminationID = TermId, + serviceChangeResult = Res} + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_mg(Node, Mid, Encoding, Transport, Verbosity) -> + d("start mg[~p]: ~p", [Node, Mid]), + RI1 = + case Encoding of + text -> + [{encoding_module, megaco_pretty_text_encoder}, + {encoding_config, []}, + {port,2944}]; + binary -> + [{encoding_module, megaco_ber_bin_encoder}, + {encoding_config, []}, + {port,2945}] + end, + RI2 = + case Transport of + tcp -> + [{transport_module, megaco_tcp}]; + udp -> + [{transport_module, megaco_udp}] + end, + RI = {receive_info, RI1 ++ RI2}, + Config = [{local_mid, Mid}, RI], + Pid = spawn_link(Node, ?MODULE, mg, [self(), Verbosity, Config]), + await_started(Pid). + + +mg(Parent, Verbosity, Config) -> + process_flag(trap_exit, true), + put(verbosity, Verbosity), + put(sname, "MG"), + i("mg -> starting"), + {Mid, ConnHandle} = mg_init(Config), + notify_started(Parent), + S = #mg{parent = Parent, mid = Mid, conn_handle = ConnHandle}, + i("mg -> started"), + mg_loop(S). + +mg_init(Config) -> + d("mg_init -> entry"), + random_init(), + Mid = get_conf(local_mid, Config), + RI = get_conf(receive_info, Config), + d("mg_init -> start megaco"), + application:start(megaco), + d("mg_init -> start megaco user"), + megaco:start_user(Mid, []), + d("mg_init -> update user info (user_mod)"), + megaco:update_user_info(Mid, user_mod, ?MODULE), + d("mg_init -> update user info (user_args)"), + megaco:update_user_info(Mid, user_args, [self()]), + d("mg_init -> get user info (receive_handle)"), + RH = megaco:user_info(Mid,receive_handle), + d("mg_init -> parse receive info"), + {MgcPort,RH1} = mg_parse_receive_info(RI, RH), + d("mg_init -> start transport with"), + ConnHandle = mg_start_transport(MgcPort, RH1), + {Mid, ConnHandle}. + +mg_loop(#mg{state = State} = S) -> + d("mg_loop(~p) -> await request", [State]), + receive + {stop, Parent} when S#mg.parent == Parent -> + i("mg_loop(~p) -> stopping", [State]), + mg_close_conn(S#mg.conn_handle), + megaco:stop_user(S#mg.mid), + application:stop(megaco), + i("mg_loop(~p) -> stopped", [State]), + Parent ! {stopped, self()}, + exit(normal); + + + {reset_stats, Parent} when S#mg.parent == Parent -> + i("mg_loop(~p) -> got request to reset stats counters", [State]), + %% mg_reset_stats(S#mgc.conn_handle), + mg_reset_stats(S#mg.conn_handle), + Parent ! {reset_stats_ack, self()}, + mg_loop(S); + + + + %% Give me statistics + {statistics, 1, Parent} when S#mg.parent == Parent -> + i("mg_loop(~p) -> got request for statistics 1", [State]), + {ok, Gen} = megaco:get_stats(), + CH = S#mg.conn_handle, + Reason = {statistics, CH}, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), + {ok, Trans} = + case SendMod of + megaco_tcp -> megaco_tcp:get_stats(SendHandle); + megaco_udp -> megaco_udp:get_stats(SendHandle); + SendMod -> exit(Pid, Reason) + end, + Parent ! {statistics, 1, [{gen, Gen}, {trans, Trans}], self()}, + mg_loop(S); + + + %% Do a service change + {service_change, Parent} when S#mg.parent == Parent, + State == initiated -> + i("mg_loop(~p) -> received request to perform service change", + [State]), + Res = mg_service_change(S#mg.conn_handle), + d("mg_loop(~p) -> result: ~p", [State, Res]), + mg_loop(S#mg{state = connecting}); + + + %% Apply some load + {apply_load, Parent, Times} when S#mg.parent == Parent -> + i("mg_loop(~p) -> received apply_load request", [State]), + apply_load_timer(), + Parent ! {apply_load_ack, self()}, + mg_loop(S#mg{load_counter = Times - 1}); + + + apply_load_timeout -> + d("mg_loop(~p) -> received apply_load timeout [~p]", + [State, S#mg.load_counter]), + mg_apply_load(S), + mg_loop(S); + + + %% Megaco callback messages + {request, Request, From} -> + d("mg_loop(~p) -> received megaco request: ~n~p~n From: ~p", + [State, Request, From]), + {Reply, NewS} = mg_handle_request(Request, S), + d("mg_loop(~p) -> send request reply: ~n~p", + [NewS#mg.state, Reply]), + From ! {reply, Reply, self()}, + mg_loop(NewS); + + + {'EXIT', Pid, Reason} -> + error_msg("MG ~p received unexpected exit signal from ~p:~n~p", + [S#mg.mid, Pid, Reason]), + mg_loop(S); + + + Invalid -> + i("mg_loop(~p) -> received invalid request: ~p", [State, Invalid]), + mg_loop(S) + + end. + + +mg_reset_stats(CH) -> + megaco:reset_stats(), + case (catch megaco:conn_info(CH, send_mod)) of + {error, Reason} -> + error_msg("unexpected result when retrieving send module for " + "own connection ~p: ~p. " + "~nexiting...", [CH, Reason]), + exit({invalid_connection, CH, Reason}); + {'EXIT', Reason} -> + error_msg("exit signal when retrieving send module for " + "own connection ~p: ~p. " + "~nexiting...", [CH, Reason]), + exit({invalid_connection, CH, Reason}); + SendMod when is_atom(SendMod) -> + SendMod:reset_stats() + end. + + +mg_close_conn(CH) -> + Reason = {self(), ignore}, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), + megaco:disconnect(CH, Reason), + case SendMod of + megaco_tcp -> megaco_tcp:close(SendHandle); + megaco_udp -> megaco_udp:close(SendHandle); + SendMod -> exit(Pid, Reason) + end. + +% display_tuple(T) when tuple(T), size(T) > 0 -> +% i("size(T): ~p", [size(T)]), +% display_tuple(T,1). + +% display_tuple(T,P) when P > size(T) -> +% ok; +% display_tuple(T,P) -> +% i("T[~p]: ~p", [P,element(P,T)]), +% display_tuple(T,P+1). + + +mg_parse_receive_info(RI, RH) -> + d("mg_parse_receive_info -> get encoding module"), + EM = get_encoding_module(RI), + d("mg_parse_receive_info -> get encoding config"), + EC = get_encoding_config(RI, EM), + d("mg_parse_receive_info -> get transport module"), + TM = get_transport_module(RI), + d("mg_parse_receive_info -> get transport port"), + TP = get_transport_port(RI), + RH1 = RH#megaco_receive_handle{send_mod = TM, + encoding_mod = EM, + encoding_config = EC}, + {TP, RH1}. + + +mg_start_transport(MgcPort, + #megaco_receive_handle{send_mod = megaco_tcp} = RH) -> + mg_start_tcp(MgcPort,RH); +mg_start_transport(MgcPort, + #megaco_receive_handle{send_mod = megaco_udp} = RH) -> + mg_start_udp(MgcPort,RH); +mg_start_transport(_, #megaco_receive_handle{send_mod = Mod}) -> + throw({error, {bad_send_mod, Mod}}). + + +mg_start_tcp(MgcPort, RH) -> + d("start tcp transport"), + case megaco_tcp:start_transport() of + {ok, Sup} -> + {ok, LocalHost} = inet:gethostname(), + Opts = [{host, LocalHost}, + {port, MgcPort}, + {receive_handle, RH}, + {tcp_options, [{nodelay, true}]}], + case megaco_tcp:connect(Sup, Opts) of + {ok, SendHandle, ControlPid} -> + PrelMgcMid = preliminary_mid, + {ok, ConnHandle} = + megaco:connect(RH, PrelMgcMid, + SendHandle, ControlPid), + ConnHandle; + {error, Reason} -> + {error, {megaco_tcp_connect, Reason}} + end; + {error, Reason} -> + {error, {megaco_tcp_start_transport, Reason}} + end. + + +mg_start_udp(MgcPort, RH) -> + d("start udp transport"), + case megaco_udp:start_transport() of + {ok, Sup} -> + {ok, LocalHost} = inet:gethostname(), + Opts = [{port, 0}, {receive_handle, RH}], + case megaco_udp:open(Sup, Opts) of + {ok, Handle, ControlPid} -> + MgcMid = preliminary_mid, + SendHandle = megaco_udp:create_send_handle(Handle, + LocalHost, + MgcPort), + {ok, ConnHandle} = + megaco:connect(RH, MgcMid, + SendHandle, ControlPid), + ConnHandle; + {error, Reason} -> + {error, {megaco_udp_open, Reason}} + end; + {error, Reason} -> + {error, {megaco_udp_start_transport, Reason}} + end. + + +mg_service_change(ConnHandle) -> + mg_service_change(ConnHandle, restart, ?megaco_cold_boot). + +mg_service_change(ConnHandle, Method, Reason) -> + SCP = #'ServiceChangeParm'{serviceChangeMethod = Method, + serviceChangeReason = [Reason]}, + TermId = [?megaco_root_termination_id], + SCR = #'ServiceChangeRequest'{terminationID = TermId, + serviceChangeParms = SCP}, + CR = #'CommandRequest'{command = {serviceChangeReq, SCR}}, + AR = #'ActionRequest'{contextId = ?megaco_null_context_id, + commandRequests = [CR]}, + megaco:cast(ConnHandle, [AR], []). + + +mg_notify_request(CH) -> + TimeStamp = cre_timeNotation("19990729", "22000000"), + Event = cre_observedEvent("al/of",TimeStamp), + Desc = cre_observedEventsDesc(2222,[Event]), + NotifyReq = cre_notifyReq([#megaco_term_id{id = ?A4444}],Desc), + CmdReq = cre_commandReq({notifyReq, NotifyReq}), + ActReq = cre_actionReq(?megaco_null_context_id, [CmdReq]), + megaco:cast(CH, [ActReq], [{reply_data, Desc}]). + +mg_apply_load(#mg{conn_handle = CH}) -> + mg_notify_request(CH). + + +cre_actionReq(Cid, Cmds) -> + #'ActionRequest'{contextId = Cid, + commandRequests = Cmds}. + +cre_commandReq(Cmd) -> + #'CommandRequest'{command = Cmd}. + +cre_notifyReq(Tid, EvsDesc) -> + #'NotifyRequest'{terminationID = Tid, observedEventsDescriptor = EvsDesc}. + +cre_observedEventsDesc(Id, EvList) -> + #'ObservedEventsDescriptor'{requestId = Id, observedEventLst = EvList}. + +cre_observedEvent(Name, Not) -> + #'ObservedEvent'{eventName = Name, timeNotation = Not}. + +cre_timeNotation(D,T) -> + #'TimeNotation'{date = D, time = T}. + +%% ----------------------- +%% Handle megaco callbacks +%% + +mg_handle_request({handle_connect, CH, _PV}, + #mg{state = connecting} = S) -> + {ok, S#mg{conn_handle = CH}}; + +mg_handle_request({handle_disconnect, CH, _PV, _R}, S) -> + {ok, S#mg{conn_handle = CH}}; + +mg_handle_request({handle_syntax_error, _RH, _PV, _ED}, S) -> + {reply, S}; + +mg_handle_request({handle_message_error, CH, _PV, _ED}, S) -> + {no_reply, S#mg{conn_handle = CH}}; + +mg_handle_request({handle_trans_request, CH, _PV, _AR}, S) -> + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Transaction requests not handled"}, + {{discard_ack, ED}, S#mg{conn_handle = CH}}; + +mg_handle_request({handle_trans_long_request, CH, _PV, _RD}, S) -> + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Long transaction requests not handled"}, + {{discard_ack, ED}, S#mg{conn_handle = CH}}; + +mg_handle_request({handle_trans_reply, CH, _PV, _AR, _RD}, + #mg{parent = Pid, state = connecting} = S) -> + %% Should really check this... + Pid ! {service_change_reply, ok, self()}, + {ok, S#mg{conn_handle = CH, state = connected}}; +mg_handle_request({handle_trans_reply, _CH, _PV, {error, ED}, RD}, + #mg{parent = Pid, load_counter = 0} = S) + when is_record(ED, 'ErrorDescriptor') andalso + is_record(RD, 'ObservedEventsDescriptor') -> + Pid ! {load_complete, self()}, + {ok, S}; +mg_handle_request({handle_trans_reply, _CH, _PV, {error, ED}, RD}, + #mg{load_counter = N} = S) + when is_record(ED, 'ErrorDescriptor') andalso + is_record(RD, 'ObservedEventsDescriptor') -> + apply_load_timer(), + {ok, S#mg{load_counter = N-1}}; + +mg_handle_request({handle_trans_ack, _CH, _PV, _AS, _AD}, S) -> + {ok, S}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +await_started(Pid) -> + receive + {started, Pid} -> + d("await_started ~p: ok", [Pid]), + {ok, Pid}; + {'EXIT', Pid, Reason} -> + i("await_started ~p: received exit signal: ~p", [Pid, Reason]), + exit({failed_starting, Pid, Reason}) + after 10000 -> + i("await_started ~p: timeout", [Pid]), + exit({error, timeout}) + end. + +stop(Pid) -> + d("stop ~p", [Pid]), + Pid ! {stop, self()}, + receive + {stopped, Pid} -> + d("stop -> received stopped from ~p", [Pid]), + ok; + {'EXIT', Pid, Reason} -> + i("stop ~p: received exit signal: ~p", [Pid, Reason]), + exit({failed_stopping, Pid, Reason}) + after 10000 -> + exit({error, timeout}) + end. + +get_stats(Pid, No) -> + d("get_stats ~p", [Pid]), + Pid ! {statistics, No, self()}, + receive + {statistics, No, Stats, Pid} -> + {ok, Stats}; + {'EXIT', Pid, Reason} -> + i("get_stats ~p: received exit signal: ~p", [Pid, Reason]), + exit({failed_getting_stats, Pid, Reason}) + after 10000 -> + exit({error, timeout}) + end. + +service_change(Pid) -> + d("service_change ~p", [Pid]), + Pid ! {service_change, self()}, + receive + {service_change_reply, Res, Pid} -> + {ok, Res}; + {'EXIT', Pid, Reason} -> + i("service_change ~p: received exit signal: ~p", [Pid, Reason]), + exit({failed_service_change, Pid, Reason}) + after 10000 -> + exit({error, timeout}) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +notify_started(Parent) -> + Parent ! {started, self()}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% The megaco user callback interface + +handle_connect(CH, PV, Pid) -> +% i("handle_connect -> entry with" +% "~n CH: ~p" +% "~n PV: ~p" +% "~n Pid: ~p", [CH, PV, Pid]), + case CH#megaco_conn_handle.remote_mid of + preliminary_mid -> + %% Avoids deadlock + ok; + _ -> + Reply = request(Pid, {handle_connect, CH, PV}), +% d("handle_connect -> Reply:~n~p", [Reply]), + Reply + end. + +handle_disconnect(_CH, _PV, + {user_disconnect, {Pid, ignore}}, + Pid) -> +% i("handle_disconnect(ignore) -> entry with" +% "~n CH: ~p" +% "~n PV: ~p", [CH, PV]), + %% Avoids deadlock + ok; +handle_disconnect(CH, PV, R, Pid) -> +% i("handle_disconnect -> entry with" +% "~n CH: ~p" +% "~n PV: ~p" +% "~n R: ~p", [CH, PV, R]), + request(Pid, {handle_disconnect, CH, PV, R}). + +handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor, Pid) -> +% i("handle_syntax_error -> entry with" +% "~n ReceiveHandle: ~p" +% "~n ProtocolVersion: ~p" +% "~n ErrorDescriptor: ~p", +% [ReceiveHandle, ProtocolVersion, ErrorDescriptor]), + Req = {handle_syntax_error, ReceiveHandle, ProtocolVersion, + ErrorDescriptor}, + request(Pid, Req). + +handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor, Pid) -> +% i("handle_message_error -> entry with" +% "~n ConnHandle: ~p" +% "~n ProtocolVersion: ~p" +% "~n ErrorDescriptor: ~p", +% [ConnHandle, ProtocolVersion, ErrorDescriptor]), + Req = {handle_message_error, ConnHandle, ProtocolVersion, ErrorDescriptor}, + request(Pid, Req). + +handle_trans_request(CH, PV, AR, Pid) -> +% i("handle_trans_request -> entry with" +% "~n CH: ~p" +% "~n PV: ~p" +% "~n AR: ~p" +% "~n Pid: ~p", [CH, PV, AR, Pid]), + Reply = request(Pid, {handle_trans_request, CH, PV, AR}), +% i("handle_trans_request -> Reply:~n~p", [Reply]), + Reply. + +handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Pid) -> +% i("handle_trans_long_request -> entry with" +% "~n ConnHandle: ~p" +% "~n ProtocolVersion: ~p" +% "~n ReqData: ~p", [ConnHandle, ProtocolVersion, ReqData]), + Req = {handle_trans_long_request, ConnHandle, ProtocolVersion, ReqData}, + request(Pid, Req). + +handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData, Pid) -> +% i("handle_trans_reply -> entry with" +% "~n ConnHandle: ~p" +% "~n ProtocolVersion: ~p" +% "~n ActualReply: ~p" +% "~n ReplyData: ~p", +% [ConnHandle, ProtocolVersion, ActualReply, ReplyData]), + Req = {handle_trans_reply, ConnHandle, ProtocolVersion, + ActualReply, ReplyData}, + request(Pid, Req). + +handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Pid) -> +% i("handle_trans_ack -> entry with" +% "~n ConnHandle: ~p" +% "~n ProtocolVersion: ~p" +% "~n AckStatus: ~p" +% "~n AckData: ~p", +% [ConnHandle, ProtocolVersion, AckStatus, AckData]), + Req = {handle_trans_ack, ConnHandle, ProtocolVersion, AckStatus, AckData}, + request(Pid, Req). + + +request(Pid, Request) -> + Pid ! {request, Request, self()}, + receive + {reply, Reply, Pid} -> + Reply + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +sleep(X) -> + receive after X -> ok end. + + +error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). + + +get_encoding_module(RI) -> + case (catch get_conf(encoding_module, RI)) of + {error, _} -> + undefined; + Val -> + Val + end. + +get_encoding_config(RI, EM) -> + case text_codec(EM) of + true -> + case megaco:system_info(text_config) of + [Conf] when is_list(Conf) -> + Conf; + _ -> + [] + end; + + false -> + get_conf(encoding_config, RI) + end. + +text_codec(megaco_compact_text_encoder) -> + true; +text_codec(megaco_pretty_text_encoder) -> + true; +text_codec(_) -> + false. + + +get_transport_module(RI) -> + get_conf(transport_module, RI). + +get_transport_port(RI) -> + get_conf(port, RI). + + +get_conf(Key, Config) -> + case lists:keysearch(Key, 1, Config) of + {value, {Key, Val}} -> + Val; + _ -> + exit({error, {not_found, Key, Config}}) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +i(F) -> + i(F, []). + +i(F, A) -> + print(info, get(verbosity), "", F, A). + + +d(F) -> + d(F, []). + +d(F, A) -> + print(debug, get(verbosity), "DBG: ", F, A). + + +printable(_, debug) -> true; +printable(info, info) -> true; +printable(_,_) -> false. + +print(Severity, Verbosity, P, F, A) -> + print(printable(Severity,Verbosity), P, F, A). + +print(true, P, F, A) -> + io:format("~s~p:~s: " ++ F ++ "~n", [P, self(), get(sname) | A]); +print(_, _, _, _) -> + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +random_init() -> + {A,B,C} = now(), + random:seed(A,B,C). + +random() -> + 10 * random:uniform(50). + +apply_load_timer() -> + erlang:send_after(random(), self(), apply_load_timeout). + |