%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% Purpose: Implements an "MG" used by the test suite
%%----------------------------------------------------------------------
-module(megaco_test_mg).
-export([start/5, start/6, stop/1,
get_stats/1, reset_stats/1,
user_info/1, user_info/2,
update_user_info/3,
conn_info/1, conn_info/2,
update_conn_info/3,
service_change/1,
ack_info/2, rep_info/2,
group_requests/2,
notify_request/1,
await_notify_reply/1,
notify_request_and_reply/1,
cancel_request/2,
apply_load/2,
apply_multi_load/3,
enable_test_code/4,
encode_ar_first/2,
verbosity/2]).
-export([mg/3, notify_request_handler_main/5]).
-export([loader_main/4]).
%% Megaco callback api
-export([
handle_connect/4,
handle_disconnect/5,
handle_syntax_error/5,
handle_message_error/5,
handle_trans_request/5,
handle_trans_long_request/5,
handle_trans_reply/6,
handle_trans_ack/6
]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v1.hrl").
-define(A4444, tid(255*256*256) ).
-define(A4445, tid(255*256*256 + 255) ).
-define(A5555, tid(255*256*256 + 255*256) ).
-define(A5556, tid(255*256*256 + 255*256 + 255) ).
-record(mg, {mid = undefined,
state = initiated,
req_handler = undefined,
call_mode = async,
group_size = 1,
encode_ar_first = false,
ack_info = undefined,
rep_info = undefined,
load_counter = 0,
reply_counter = 0,
mload_info = undefined,
parent = undefined,
dsi_timer,
evs = []}).
-define(EVS_MAX, 10).
%%% --------------------------------------------------------------------
start(Node, Mid, Encoding, Transport, Verbosity) ->
%% Conf = [{megaco_trace, io}],
%% Conf = [{megaco_trace, "megaco-mg.trace"}],
Conf = [{megaco_trace, false}],
start(Node, Mid, Encoding, Transport, Conf, Verbosity).
start(Node, Mid, Encoding, Transport, Conf, Verbosity) ->
d("start mg[~p]: ~p"
"~n Encoding: ~p"
"~n Transport: ~p"
"~n Conf: ~p", [Node, Mid, Encoding, Transport, Conf]),
RI1 = encoding_config(Encoding),
RI2 = transport_config(Transport),
{RI3, Conf1} = transport_opts(Conf),
RI = {receive_info, RI1 ++ RI2 ++ RI3},
Config = [{local_mid, Mid}, RI] ++ Conf1,
Self = self(),
Fun =
fun() ->
io:format("LOADER(~p,~p) started~n", [self(),node()]),
case (catch mg(Self, Verbosity, Config)) of
{'EXIT', Reason} ->
io:format("LOADER(~p,~p) terminating with exit"
"~n~p"
"~n", [self(), node(), Reason]),
exit(Reason);
Else ->
io:format("LOADER(~p,~p) terminating with"
"~n~p"
"~n", [self(), node(), Else]),
Else
end
end,
true = erlang:monitor_node(Node, true),
Pid = spawn_link(Node, Fun),
%% Pid = spawn_link(Node, ?MODULE, mg, [self(), Verbosity, Config]),
MonRef = (catch erlang:monitor(process, Pid)),
NodePing = net_adm:ping(Node),
ProcInfo = (catch proc_info(Pid)),
i("start -> "
"~n self(): ~p"
"~n node(): ~p"
"~n net_adm:ping(~p): ~p"
"~n Loader: ~p"
"~n Monitor ref: ~p"
"~n Process info: ~p",
[self(), node(),
Node, NodePing,
Pid,
MonRef, ProcInfo]),
await_started(Node, MonRef, Pid).
proc_info(Pid) ->
rpc:call(node(Pid), erlang, process_info, [Pid]).
encoding_config({Encoding, EC}) when is_atom(Encoding) andalso is_list(EC) ->
{Mod, Port} = select_encoding(Encoding),
[{encoding_module, Mod},
{encoding_config, EC},
{port, Port}];
encoding_config(Encoding) when is_atom(Encoding) ->
{Mod, Port} = select_encoding(Encoding),
[{encoding_module, Mod},
{encoding_config, []},
{port, Port}];
encoding_config(Encoding) ->
throw({error, {invalid_encoding, Encoding}}).
select_encoding(text) ->
{megaco_pretty_text_encoder, 2944};
select_encoding(pretty_text) ->
{megaco_pretty_text_encoder, 2944};
select_encoding(compact_text) ->
{megaco_compact_text_encoder, 2944};
select_encoding(binary) ->
{megaco_ber_encoder, 2945};
select_encoding(erl_dist) ->
{megaco_erl_dist_encoder, 2946};
select_encoding(Encoding) ->
throw({error, {invalid_encoding, Encoding}}).
transport_config(tcp) ->
[{transport_module, megaco_tcp}];
transport_config(udp) ->
[{transport_module, megaco_udp}];
transport_config(TransportConfig) when is_list(TransportConfig) ->
{value, {transport, Trans}} =
lists:keysearch(transport, 1, TransportConfig),
transport_config(Trans) ++
case lists:keysearch(host, 1, TransportConfig) of
{value, Value} ->
[Value];
false ->
[]
end.
transport_opts(Config) ->
case lists:keysearch(transport_opts, 1, Config) of
{value, TO} ->
Config1 = lists:keydelete(transport_opts, 1, Config),
{[TO], Config1};
false ->
{[], Config}
end.
await_started(Node, MonRef, Pid) ->
i("await_started -> entry with"
"~n MonRef: ~p"
"~n Pid: ~p", [MonRef, Pid]),
receive
{started, Pid} ->
d("await_started ~p - started"
"~n Process info: ~p", [Pid, (catch proc_info(Pid))]),
true = erlang:monitor_node(Node, false),
erlang:demonitor(MonRef),
{ok, Pid};
{nodedown, Node} ->
i("await_started ~p - received node down", [Pid]),
exit({node_down, Node});
{'DOWN', MonRef, process, Pid, Info} ->
i("await_started ~p - received down signal: ~p",
[Pid, Info]),
true = erlang:monitor_node(Node, false),
exit({failed_starting, Pid, Info});
{'EXIT', Pid, Reason} ->
i("await_started ~p - received exit signal: ~p", [Pid, Reason]),
true = erlang:monitor_node(Node, false),
exit({failed_starting, Pid, Reason})
%% This timeout was originally 10 secs, but on some debug compiled
%% platforms, it was simply not long enough
after 20000 ->
NodePing = net_adm:ping(Node),
ProcInfo = (catch proc_info(Pid)),
FlushQ = megaco_test_lib:flush(),
i("await_started ~p - timeout: "
"~n net_adm:ping(~p): ~p"
"~n Process info: ~p"
"~n Messages in my queue: ~p",
[Pid, Node, NodePing, ProcInfo, FlushQ]),
true = erlang:monitor_node(Node, false),
exit({error, timeout})
end.
verbosity(Pid, V) ->
Pid ! {verbosity, V, self()}.
stop(Pid) ->
server_request(Pid, stop, stopped).
get_stats(Pid) ->
server_request(Pid, statistics, statistics_reply).
reset_stats(Pid) ->
server_request(Pid, reset_stats, reset_stats_ack).
user_info(Pid) ->
server_request(Pid, {user_info, all}, user_info_ack).
user_info(Pid, Tag) when is_atom(Tag) ->
server_request(Pid, {user_info, Tag}, user_info_ack).
update_user_info(Pid, Tag, Val) ->
server_request(Pid, {update_user_info, Tag, Val}, update_user_info_ack).
conn_info(Pid) ->
server_request(Pid, {conn_info, all}, conn_info_ack).
conn_info(Pid, Tag) when is_atom(Tag) ->
server_request(Pid, {conn_info, Tag}, conn_info_ack).
update_conn_info(Pid, Tag, Val) ->
server_request(Pid, {update_conn_info, Tag, Val}, update_conn_info_ack).
enable_test_code(Pid, Module, Where, Fun)
when is_atom(Module) andalso is_atom(Where) andalso is_function(Fun) ->
Tag = {Module, Where},
server_request(Pid, {enable_test_code, Tag, Fun}, enable_test_code_reply).
encode_ar_first(Pid, New) when is_atom(New) ->
server_request(Pid, {encode_ar_first, New}, encode_ar_first_reply).
service_change(Pid) ->
server_request(Pid, service_change, service_change_reply).
group_requests(Pid, N) ->
server_request(Pid, {group_requests, N}, group_requests_reply).
ack_info(Pid, InfoPid) ->
Pid ! {{ack_info, InfoPid}, self()}.
rep_info(Pid, InfoPid) ->
Pid ! {{rep_info, InfoPid}, self()}.
notify_request(Pid) ->
Pid ! {notify_request, self()}.
await_notify_reply(Pid) ->
await_reply(Pid, notify_request_reply).
notify_request_and_reply(Pid) ->
notify_request(Pid),
await_notify_reply(Pid).
cancel_request(Pid, Reason) ->
server_request(Pid, cancel_request, Reason, cancel_request_reply).
apply_load(Pid, CounterStart) ->
server_request(Pid, apply_load, CounterStart, apply_load_ack).
apply_multi_load(Pid, NumLoaders, NumReq) ->
server_request(Pid, apply_multi_load, {NumLoaders, NumReq}, apply_multi_load_ack).
server_request(Pid, Req, ReplyTag) ->
Pid ! {Req, self()},
await_reply(Pid, ReplyTag).
server_request(Pid, Req, ReqData, ReplyTag) ->
Pid ! {Req, ReqData, self()},
await_reply(Pid, ReplyTag).
await_reply(Pid, ReplyTag) ->
await_reply(Pid, ReplyTag, infinity).
await_reply(Pid, ReplyTag, Timeout) ->
receive
{ReplyTag, Reply, Pid} ->
Reply;
{'EXIT', Pid, Reason} ->
exit({failed, ReplyTag, Pid, Reason})
after Timeout ->
exit({timeout, ReplyTag, Pid})
end.
server_reply(Pid, ReplyTag, Reply) ->
Pid ! {ReplyTag, Reply, self()}.
%%% --------------------------------------------------------------------
mg(Parent, Verbosity, Config) ->
process_flag(trap_exit, true),
put(sname, "MG"),
%% put(verbosity, Verbosity),
put(verbosity, debug), % Enable debug printouts during init
i("mg -> starting"),
%% megaco:enable_trace(max, io),
case (catch init(Config)) of
{error, _} = Error ->
exit(Error);
{'EXIT', Reason} ->
exit({init_failed, Reason});
{ok, Mid, DSITimer} ->
notify_started(Parent),
MG = #mg{parent = Parent, mid = Mid, dsi_timer = DSITimer},
i("mg -> started"),
put(verbosity, Verbosity),
case (catch loop(evs(MG, started))) of
{'EXIT', normal} ->
exit(normal);
{'EXIT', Reason} ->
i("mg failed with reason:~n ~p", [Reason]),
exit(Reason);
Else ->
i("mg terminated: ~n ~p", [Else]),
exit({unexpected, Else})
end
end.
init(Config) ->
d("init -> entry with"
"~n Config: ~p", [Config]),
random_init(),
d("init -> random initiated", []),
Mid = get_conf(local_mid, Config),
d("init -> Mid: ~p", [Mid]),
RI = get_conf(receive_info, Config),
d("init -> RI: ~p", [RI]),
d("init -> maybe start the display system info timer"),
DSITimer =
case get_conf(display_system_info, Config, undefined) of
Time when is_integer(Time) ->
d("init -> creating display system info timer"),
create_timer(Time, display_system_info);
_ ->
undefined
end,
Conf0 = lists:keydelete(display_system_info, 1, Config),
d("init -> start megaco"),
application:start(megaco),
d("init -> possibly enable megaco trace"),
case lists:keysearch(megaco_trace, 1, Conf0) of
{value, {megaco_trace, true}} ->
megaco:enable_trace(max, io);
{value, {megaco_trace, io}} ->
megaco:enable_trace(max, io);
{value, {megaco_trace, File}} when is_list(File) ->
megaco:enable_trace(max, File);
_ ->
ok
end,
Conf1 = lists:keydelete(megaco_trace, 1, Conf0),
d("init -> start megaco user"),
Conf2 = lists:keydelete(local_mid, 1, Conf1),
Conf3 = lists:keydelete(receive_info, 1, Conf2),
ok = megaco:start_user(Mid, Conf3),
d("init -> update user info (user_mod)"),
ok = megaco:update_user_info(Mid, user_mod, ?MODULE),
d("init -> update user info (user_args)"),
ok = megaco:update_user_info(Mid, user_args, [self(), Mid]),
d("init -> get user info (receive_handle)"),
RH = megaco:user_info(Mid, receive_handle),
d("init -> parse receive info"),
{MgcPort, MgcHost, RH1, TO} = parse_receive_info(RI, RH),
d("init -> start transport (with ~p)", [TO]),
{ok, _CH} = start_transport(MgcPort, MgcHost, RH1, TO),
{ok, Mid, DSITimer}.
loop(#mg{parent = Parent, mid = Mid} = S) ->
d("loop -> await request", []),
receive
{display_system_info, Time} ->
display_system_info(S#mg.mid),
NewTimer = create_timer(Time, display_system_info),
loop(evs(S#mg{dsi_timer = NewTimer}, {dsi, Time}));
{verbosity, V, Parent} ->
i("loop -> received new verbosity: ~p", [V]),
put(verbosity,V),
loop(evs(S, {verb, V}));
{stop, Parent} ->
i("loop -> stopping", []),
display_system_info(S#mg.mid, "at finish "),
cancel_timer(S#mg.dsi_timer),
Res = do_stop(Mid),
d("loop -> stop result: ~p", [Res]),
server_reply(Parent, stopped, {ok, Res}),
done(evs(S, stop), normal);
{{enable_test_code, Tag, Fun}, Parent} ->
i("loop -> enable_test_code: ~p, ~p", [Tag, Fun]),
Reply = (catch ets:insert(megaco_test_data, {Tag, Fun})),
d("loop -> enable_test_code -> "
"~n Reply: ~p"
"~n ets:tab2list(megaco_test_data): ~p",
[Reply,ets:tab2list(megaco_test_data)]),
server_reply(Parent, enable_test_code_reply, Reply),
loop(evs(S, {enable_test_code, Tag}));
{{encode_ar_first, EAF}, Parent} ->
i("loop -> encode_ar_first: ~p", [EAF]),
{Reply, S1} = handle_encode_ar_first(S, EAF),
server_reply(Parent, encode_ar_first_reply, Reply),
loop(evs(S1#mg{encode_ar_first = EAF}, {enc_arf, EAF}));
%% Give me statistics
{statistics, Parent} ->
i("loop -> got request for statistics", []),
Stats = do_get_statistics(Mid),
server_reply(Parent, statistics_reply, {ok, Stats}),
loop(evs(S, stats));
{reset_stats, Parent} ->
i("loop -> got request to reset stats counters", []),
do_reset_stats(Mid),
server_reply(Parent, reset_stats_ack, ok),
loop(evs(S, rst_stats));
{{user_info, Tag}, Parent} ->
i("loop -> got user_info request for ~w", [Tag]),
Res = do_get_user_info(Mid, Tag),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, user_info_ack, Res),
loop(evs(S, {ui, Tag}));
{{update_user_info, Tag, Val}, Parent} ->
i("loop -> got update_user_info: ~w -> ~p", [Tag, Val]),
Res = do_update_user_info(Mid, Tag, Val),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, update_user_info_ack, Res),
loop(evs(S, {uui, {Tag, Val}}));
{{conn_info, Tag}, Parent} ->
i("loop -> got conn_info request for ~w", [Tag]),
Res = do_get_conn_info(Mid, Tag),
server_reply(Parent, conn_info_ack, Res),
loop(evs(S, {ci, Tag}));
{{update_conn_info, Tag, Val}, Parent} ->
i("loop -> got update_conn_info: ~w -> ~p", [Tag, Val]),
Res = do_update_conn_info(Mid, Tag, Val),
server_reply(Parent, update_conn_info_ack, Res),
loop(evs(S, {uci, {Tag, Val}}));
%% Do a service change
%% No server-reply here. Since the service change is
%% async, the reply (from the MGC) will come later.
{service_change, Parent} ->
i("loop -> received request to perform service change", []),
S1 =
case (catch do_service_change(S)) of
{ok, MG} ->
d("loop -> service change initiated", []),
MG;
Error ->
d("loop -> service change failed: ~p", [Error]),
server_reply(Parent, service_change_reply, Error),
S
end,
loop(evs(S1, svc_ch));
{{group_requests, N}, Parent} when N > 0 ->
i("loop -> received group_requests ~p", [N]),
Reply = {ok, S#mg.group_size},
server_reply(Parent, group_requests_reply, Reply),
loop(evs(S#mg{group_size = N}, {grp_reqs, N}));
{{ack_info, To}, Parent} ->
i("loop -> received request to inform about received ack's ", []),
loop(evs(S#mg{ack_info = To}, {acki, To}));
{{rep_info, To}, Parent} ->
i("loop -> received request to inform about received rep's ", []),
loop(evs(S#mg{rep_info = To}, {repi, To}));
%% Make a sync-call
{notify_request, Parent} ->
i("loop -> received request to send notify request ", []),
{Res, S1} = do_handle_notify_request(S),
d("loop -> notify request result: ~p", [Res]),
loop(evs(S1, not_req));
%% sync-call complete
{notify_request_complete, NotifyReply, Pid} ->
i("loop -> received notify request complete from "
"~n ~p with"
"~n NotifyReply: ~p",
[Pid, NotifyReply]),
server_reply(Parent, notify_request_reply, NotifyReply),
loop(evs(S#mg{req_handler = undefined}, {not_reqc, NotifyReply}));
%% cancel requests
{cancel_request, Reason, Parent} ->
i("loop -> received request to cancel (all) megaco requests ", []),
Res = do_cancel_requests(Mid, Reason),
server_reply(Parent, cancel_request_reply, Res),
loop(evs(S, {creq, Reason}));
%% Apply multi-load
{apply_multi_load, {NL, NR}, Parent} ->
i("loop -> received apply_multi_load request: ~w, ~w", [NL, NR]),
S1 = start_loaders(S, NL, NR),
loop(evs(S1, {apply_mload, {NL, NR}}));
%% Apply some load
{apply_load, Times, Parent} ->
i("loop -> received apply_load request: ~w", [Times]),
S1 =
case update_load_times(S, Times) of
{ok, MG} ->
apply_load_timer(),
server_reply(Parent, apply_load_ack, ok),
MG;
Error ->
server_reply(Parent, apply_load_ack, Error),
S
end,
loop(evs(S1, {apply_load, Times}));
{apply_load_timeout, _} ->
d("loop -> received apply_load timeout", []),
S1 = do_apply_load(S),
loop(evs(S1, apply_loadto));
%% Megaco callback messages
{request, Request, Mid, From} ->
d("loop -> received megaco request: ~n ~p"
"~n Mid: ~p"
"~n From: ~p",
[Request, Mid, From]),
{Reply, S1} = handle_megaco_request(S, Request),
d("loop -> send (megaco callback) request reply: ~n~p", [Reply]),
From ! {reply, Reply, self()},
loop(evs(S1, {req, {Request, Mid, From}}));
{'EXIT', Pid, Reason} ->
i("loop -> received exit signal from ~p: "
"~n ~p", [Pid, Reason]),
S1 = handle_exit(S, Pid, Reason),
loop(evs(S1, {exit, {Pid, Reason}}));
Invalid ->
error_msg("received invalid request: ~n~p", [Invalid]),
loop(evs(S, {invalid, Invalid}))
end.
evs(#mg{evs = EVS} = S, Ev) when (length(EVS) < ?EVS_MAX) ->
S#mg{evs = [{?FTS(), Ev}|EVS]};
evs(#mg{evs = EVS} = S, Ev) ->
S#mg{evs = [{?FTS(), Ev}|lists:droplast(EVS)]}.
done(#mg{evs = EVS}, Reason) ->
info_msg("Exiting with latest event(s): "
"~n ~p"
"~n", [EVS]),
exit(Reason).
handle_encode_ar_first(#mg{encode_ar_first = Old} = MG, New)
when (New =:= true) orelse (New =:= false) ->
{{ok, Old}, MG#mg{encode_ar_first = New}};
handle_encode_ar_first(MG, New) ->
{{error, {invalid_value, New}}, MG}.
%%
%% Stop user
%%
do_stop(Mid) ->
d("do_stop -> stopping user ~p", [Mid]),
Disco = fun close_conn/1,
lists:map(Disco, megaco:user_info(Mid, connections)),
megaco:stop_user(Mid).
close_conn(CH) ->
d("do_stop -> closing connection ~p", [CH]),
Reason = {stopped_by_user,self()},
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),
megaco:cancel(CH, Reason),
case SendMod of
megaco_tcp -> megaco_tcp:close(SendHandle);
megaco_udp -> megaco_udp:close(SendHandle);
SendMod -> exit(Pid, Reason)
end.
%%
%% Get statistics
%%
do_get_statistics(Mid) ->
case megaco:user_info(Mid, connections) of
[CH] ->
do_get_conn_statistics(CH);
[] ->
[]
end.
do_get_conn_statistics(CH) ->
{ok, Gen} = megaco:get_stats(),
%% 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)
end,
[{gen, Gen}, {trans, Trans}].
%%
%% reset user stats
%%
do_reset_stats(Mid) ->
%% We only have one connection
[CH] = megaco:user_info(Mid, connections),
do_reset_stats1(CH).
do_reset_stats1(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.
%%
%% Get user info for user
%%
do_get_user_info(Mid, all = Tag) ->
case (catch megaco:user_info(Mid, Tag)) of
L when is_list(L) ->
lists:sort(L);
Else ->
Else
end;
do_get_user_info(Mid, Tag) ->
(catch megaco:user_info(Mid, Tag)).
%%
%% Update user info for user
%%
do_update_user_info(Mid, Tag, Val) ->
(catch megaco:update_user_info(Mid, Tag, Val)).
%%
%% Get conn info
%%
do_get_conn_info(CH, all = Tag) when is_record(CH, megaco_conn_handle) ->
case (catch megaco:conn_info(CH, Tag)) of
L when is_list(L) ->
lists:sort(L);
Else ->
Else
end;
do_get_conn_info(CH, Tag) when is_record(CH, megaco_conn_handle) ->
(catch megaco:conn_info(CH, Tag));
do_get_conn_info(Mid, Tag) ->
case megaco:user_info(Mid, connections) of
[CH|_] ->
do_get_conn_info(CH, Tag);
[] ->
[]
end.
%%
%% Update conn info for user
%%
do_update_conn_info(Mid, Tag, Val) ->
%% We only have one connection
[CH] = megaco:user_info(Mid, connections),
(catch megaco:update_conn_info(CH, Tag, Val)).
%%
%% Perform service change
%%
do_service_change(#mg{mid = Mid,
state = initiated,
encode_ar_first = EAF} = MG) ->
%% We only have one connection
d("do service change for ~p", [Mid]),
[CH] = megaco:user_info(Mid, connections),
Method = restart,
Reason = ?megaco_cold_boot,
case do_service_change(CH, Method, EAF, Reason) of
ok ->
{ok, MG#mg{state = connecting}};
Error ->
d("service change for ~p failed: ~n~p", [Mid, Error]),
Error
end;
do_service_change(#mg{state = State} = MG) ->
{{error, {invalid_state, State}}, MG}.
do_service_change(ConnHandle, Method, EAF, Reason) ->
d("send service change using:"
"~n ConnHandle: ~p"
"~n Method: ~p"
"~n EAF: ~p"
"~n Reason: ~p", [ConnHandle, Method, EAF, Reason]),
SCP = cre_serviceChangeParm(Method, [Reason]),
TermId = [?megaco_root_termination_id],
SCR = cre_serviceChangeReq(TermId, SCP),
CR = cre_commandReq({serviceChangeReq, SCR}),
AR = cre_actionReq(?megaco_null_context_id,[CR]),
send_async(EAF, ConnHandle, [AR], []).
%% Make a sync call
do_handle_notify_request(#mg{mid = Mid,
group_size = N,
encode_ar_first = EAF,
state = connected} = MG) ->
d("do_handle_notify_request -> entry"),
[CH] = megaco:user_info(Mid, connections),
Pid = start_notify_request_handler(EAF, CH, N),
{ok, MG#mg{req_handler = Pid}};
do_handle_notify_request(#mg{state = State} = MG) ->
d("do_handle_notify_request -> entry with"
"~n State: ~p", [State]),
{{error, {invalid_state, State}}, MG}.
%%
%% Cancel requests
%%
do_cancel_requests(Mid, Reason) ->
[CH] = megaco:user_info(Mid, connections),
megaco:cancel(CH, Reason).
%%
%% Apply multi load
%%
start_loaders(#mg{mid = Mid, encode_ar_first = EAF} = MG, NumLoaders, Times) ->
[CH] = megaco:user_info(Mid, connections),
Env = get(),
Loaders = start_loaders1(NumLoaders, [], [Env, EAF, Times, CH]),
d("start_loaders -> Loaders: ~n~w", [Loaders]),
MG#mg{mload_info = {Loaders, 0, 0}}.
start_loaders1(0, Acc, _) ->
Acc;
start_loaders1(N, Acc, Args) ->
Pid = spawn_link(?MODULE, loader_main, Args),
start_loaders1(N-1, [Pid|Acc], Args).
loader_main(Env, EAF, N, CH) ->
lists:foreach(fun({Tag,Val}) -> put(Tag,Val) end, Env),
loader_main(EAF, N, CH).
loader_main(_EAF, 0, _) ->
d("loader_main -> done"),
exit(loader_done);
loader_main(EAF, N, CH) ->
d("loader_main -> entry with: ~w", [N]),
{Act, _} = make_notify_request(),
_Res = send_sync(EAF, CH, Act, []),
loader_main(EAF, N-1, CH).
handle_exit(#mg{parent = Pid} = S, Pid, Reason) ->
error_msg("received exit from the parent:"
"~n ~p", [Reason]),
done(S, {parent_terminated, Reason});
handle_exit(#mg{parent = Parent, req_handler = Pid} = MG, Pid, Reason) ->
error_msg("received unexpected exit from the request handler:"
"~n ~p", [Reason]),
server_reply(Parent, notify_request_reply,
{error, {request_handler_exit, Reason}}),
MG#mg{req_handler = undefined};
handle_exit(#mg{parent = Parent, mload_info = {Loaders0, Ok, Err}} = MG,
Pid, loader_done) ->
d("handle_exit(loader_done) -> entry when"
"~n Loaders0: ~p"
"~n Ok: ~p"
"~n Err: ~p", [Loaders0, Ok, Err]),
Loaders = lists:delete(Pid, Loaders0),
LoadInfo =
case Loaders of
[] ->
d("handle_exit -> multi load done", []),
server_reply(Parent, apply_multi_load_ack, {ok, Ok+1, Err}),
undefined;
_ ->
{Loaders, Ok+1, Err}
end,
MG#mg{mload_info = LoadInfo};
handle_exit(#mg{parent = Parent, mload_info = {Loaders, Ok, Err}} = MG,
Pid, Reason)
when length(Loaders) > 0 ->
d("handle_exit -> entry when"
"~n Reason: ~p"
"~n Loaders: ~p"
"~n Ok: ~p"
"~n Err: ~p", [Reason, Loaders, Ok, Err]),
case lists:delete(Pid, Loaders) of
[] ->
%% since we cannot be empty prior the delete,
%% the last one exited...
server_reply(Parent, apply_multi_load, {ok, Ok, Err+1}),
MG#mg{mload_info = undefined};
Loaders ->
%% Could not be this MG, so go on to the next
error_msg("received unexpected exit signal from ~p:~n~p",
[Pid, Reason]);
Loaders1 ->
%% Not empty, but we removed one
MG#mg{mload_info = {Loaders1,Ok,Err+1}}
end;
handle_exit(_MG, Pid, Reason) ->
error_msg("received unexpected exit signal from ~p:~n~p",
[Pid, Reason]).
parse_receive_info(RI, RH) ->
d("parse_receive_info -> get encoding module"),
EM = get_encoding_module(RI),
d("parse_receive_info -> get encoding config"),
EC = get_encoding_config(RI, EM),
d("parse_receive_info -> get transport module"),
TM = get_transport_module(RI),
d("parse_receive_info -> get transport port"),
TP = get_transport_port(RI),
d("parse_receive_info -> get transport host"),
TH = get_transport_host(RI),
d("parse_receive_info -> get transport opts"),
TO = get_transport_opts(RI),
RH1 = RH#megaco_receive_handle{send_mod = TM,
encoding_mod = EM,
encoding_config = EC},
{TP, TH, RH1, TO}.
start_transport(MgcPort, MgcHost,
#megaco_receive_handle{send_mod = megaco_tcp} = RH, TO) ->
start_tcp(MgcPort, MgcHost, RH, TO);
start_transport(MgcPort, MgcHost,
#megaco_receive_handle{send_mod = megaco_udp} = RH, TO) ->
start_udp(MgcPort, MgcHost, RH, TO);
start_transport(_, _, #megaco_receive_handle{send_mod = Mod}, _TO) ->
throw({error, {bad_send_mod, Mod}}).
start_tcp(MgcPort, MgcHost, RH, TO) ->
d("start tcp transport: "
"~n MGC Port: ~p"
"~n MGC Host: ~p"
"~n Receive handle: ~p"
"~n Transport options: ~p", [MgcPort, MgcHost, RH, TO]),
case megaco_tcp:start_transport() of
{ok, Sup} ->
d("tcp transport started: ~p", [Sup]),
start_tcp_connect(TO, RH, MgcPort, MgcHost, Sup);
{error, Reason} ->
throw({error, {megaco_tcp_start_transport, Reason}})
end.
start_tcp_connect(TO, RH, Port, Host, Sup) ->
d("try tcp connecting to: ~p:~p", [Host, Port]),
Opts = [{host, Host},
{port, Port},
{receive_handle, RH},
{tcp_options, [{nodelay, true}]}] ++ TO,
try_start_tcp_connect(RH, Sup, Opts, 250, noError).
try_start_tcp_connect(RH, Sup, Opts, Timeout, Error0) when (Timeout < 5000) ->
Sleep = random(Timeout) + 100,
d("try tcp connect (~p,~p)", [Timeout, Sleep]),
case megaco_tcp:connect(Sup, Opts) of
{ok, SendHandle, ControlPid} ->
d("tcp connected: ~p, ~p", [SendHandle, ControlPid]),
megaco_tcp_connect(RH, SendHandle, ControlPid);
Error1 when Error0 =:= noError -> % Keep the first error
d("failed connecting [1]: ~p", [Error1]),
sleep(Sleep),
try_start_tcp_connect(RH, Sup, Opts, Timeout*2, Error1);
Error2 ->
d("failed connecting [2]: ~p", [Error2]),
sleep(Sleep),
try_start_tcp_connect(RH, Sup, Opts, Timeout*2, Error0)
end;
try_start_tcp_connect(_RH, Sup, _Opts, _Timeout, Error) ->
megaco_tcp:stop_transport(Sup),
throw({error, {megaco_tcp_connect, Error}}).
megaco_tcp_connect(RH, SendHandle, ControlPid) ->
PrelMgcMid = preliminary_mid,
d("megaco connect", []),
{ok, CH} = megaco:connect(RH, PrelMgcMid, SendHandle, ControlPid),
d("megaco connected: ~p", [CH]),
{ok, CH}.
start_udp(MgcPort, MgcHost, RH, TO) ->
d("start udp transport (~p)", [MgcPort]),
case megaco_udp:start_transport() of
{ok, Sup} ->
d("udp transport started: ~p", [Sup]),
Opts = [{port, 0}, {receive_handle, RH}] ++ TO,
d("udp open", []),
case megaco_udp:open(Sup, Opts) of
{ok, Handle, ControlPid} ->
d("udp opened: ~p, ~p", [Handle, ControlPid]),
megaco_udp_connect(MgcPort, MgcHost,
RH, Handle, ControlPid);
{error, Reason} ->
throw({error, {megaco_udp_open, Reason}})
end;
{error, Reason} ->
throw({error, {megaco_udp_start_transport, Reason}})
end.
megaco_udp_connect(MgcPort, MgcHost, RH, Handle, ControlPid) ->
MgcMid = preliminary_mid,
SendHandle = megaco_udp:create_send_handle(Handle, MgcHost, MgcPort),
d("megaco connect", []),
{ok, CH} = megaco:connect(RH, MgcMid, SendHandle, ControlPid),
d("megaco connected: ~p", [CH]),
{ok, CH}.
update_load_times(#mg{load_counter = 0} = MG, Times) ->
d("update_load_times(0) -> entry with"
"~n Times: ~p", [Times]),
{ok, MG#mg{load_counter = Times}};
update_load_times(#mg{load_counter = N}, Times) ->
d("update_load_times(~p) -> entry with"
"~n Times: ~p", [N, Times]),
{error, {already_counting, N}}.
do_apply_load(#mg{mid = Mid} = MG) ->
d("do_apply_load -> entry"),
case megaco:user_info(Mid, connections) of
[CH] ->
do_apply_load(MG, CH);
[] ->
i("failed to apply load: no connections for ~p", [Mid]),
MG
end.
do_apply_load(#mg{parent = Parent,
encode_ar_first = EAF,
call_mode = Mode,
group_size = Sz,
load_counter = N0} = MG, CH) ->
d("do_apply_load -> entry with"
"~n Mode: ~p"
"~n Sz: ~p"
"~n N0: ~p", [Mode, Sz, N0]),
{NofSent, Actions, ReplyData} = make_notify_request(N0, Sz),
d("do_apply_load -> notifications constructed:"
"~n NofSent: ~p"
"~n Actions: ~p"
"~n ReplyData: ~p", [NofSent, Actions, ReplyData]),
N = N0 - NofSent,
case Mode of
sync ->
Result = send_sync(EAF, CH, Actions, []),
d("do_apply_load -> call result when N = ~p: ~n~p", [N,Result]),
case N of
0 ->
d("do_apply_load -> load complete"),
Parent ! {load_complete, self()},
MG#mg{call_mode = async, load_counter = 0};
_ ->
d("do_apply_load -> make another round"),
apply_load_timer(),
MG#mg{call_mode = async, load_counter = N}
end;
async ->
Result = send_async(EAF, CH, Actions, [{reply_data, ReplyData}]),
d("do_apply_load -> cast result:~n ~p", [Result]),
MG#mg{call_mode = sync,
load_counter = N,
reply_counter = NofSent} % Outstanding replies
end.
start_notify_request_handler(EAF, CH, N) ->
d("start_notify_request_handler -> entry with"
"~n EAF: ~p"
"~n CH: ~p"
"~n N: ~p", [EAF, CH, N]),
Env = get(),
spawn_link(?MODULE, notify_request_handler_main, [self(), Env, EAF, CH, N]).
notify_request_handler_main(Parent, Env, EAF, CH, N) ->
F = fun({Tag, Val}) -> put(Tag, Val) end,
lists:foreach(F, Env),
d("notify_request_handler_main -> entry with"
"~n Parent: ~p"
"~n EAF: ~p"
"~n CH: ~p"
"~n N: ~p", [Parent, EAF, CH, N]),
Res = do_notify_request(EAF, CH, N),
d("notify_request_handler_main -> notify complete:"
"~n Res: ~p", [Res]),
Parent ! {notify_request_complete, {ok, Res}, self()},
unlink(Parent),
exit(normal).
do_notify_request(_EAF, _CH, N) when N =< 0 ->
d("do_notify_request(~p) -> ignoring", [N]),
ignore;
do_notify_request(EAF, CH, 1) ->
d("do_notify_request(1) -> entry with"),
{Action, _} = make_notify_request(),
send_sync(EAF, CH, Action, []);
do_notify_request(EAF, CH, N) ->
d("do_notify_request(~p) -> entry with", [N]),
{N, Actions, _} = make_notify_request(N,N),
send_sync(EAF, CH, Actions, []).
make_notify_request(N, Sz) when (N >= Sz) andalso (Sz > 0) ->
{Req, ReplyData} = make_notify_request(N, Sz, [], []),
{Sz, Req, ReplyData};
make_notify_request(N, _Sz) when N > 0 ->
{Req, ReplyData} = make_notify_request(N, N, [], []),
{N, Req, ReplyData}.
make_notify_request(_Offset, 0, Actions, ReplyDatas) ->
{lists:reverse(Actions), lists:reverse(ReplyDatas)};
make_notify_request(Offset, N, Actions, ReplyDatas) when N > 0 ->
TimeStamp = cre_timeNotation(),
Event = cre_observedEvent("al/of", TimeStamp),
Desc = cre_observedEventsDesc(2000 + N, [Event]),
TidNum = 2#10000000 + Offset - N,
NotifyReq = cre_notifyReq([#megaco_term_id{id = tid(TidNum)}],Desc),
CmdReq = cre_commandReq({notifyReq, NotifyReq}),
ActReq = cre_actionReq(?megaco_null_context_id, [CmdReq]),
make_notify_request(Offset, N-1, [[ActReq]|Actions], [Desc|ReplyDatas]).
make_notify_request() ->
TimeStamp = cre_timeNotation("19990729", "22000000"),
Event = cre_observedEvent("al/of", TimeStamp),
Desc1 = cre_observedEventsDesc(2221, [Event]),
Desc2 = cre_observedEventsDesc(2222, [Event]),
Desc3 = cre_observedEventsDesc(2223, [Event]),
Desc4 = cre_observedEventsDesc(2224, [Event]),
NotifyReq1 = cre_notifyReq([#megaco_term_id{id = ?A4444}], Desc1),
NotifyReq2 = cre_notifyReq([#megaco_term_id{id = ?A4445}], Desc2),
CmdReq1 = cre_commandReq({notifyReq, NotifyReq1}),
CmdReq2 = cre_commandReq({notifyReq, NotifyReq2}),
ActReq = cre_actionReq(?megaco_null_context_id, [CmdReq1,CmdReq2]),
{[ActReq], [Desc3,Desc4]}.
cre_actionReq(Cid, Cmds) ->
#'ActionRequest'{contextId = Cid,
commandRequests = Cmds}.
cre_commandReq(Cmd) ->
#'CommandRequest'{command = Cmd}.
cre_serviceChangeReq(TermId, Parms) ->
#'ServiceChangeRequest'{terminationID = TermId,
serviceChangeParms = Parms}.
cre_serviceChangeParm(Method, Reason) ->
#'ServiceChangeParm'{serviceChangeMethod = Method,
serviceChangeReason = Reason}.
cre_notifyReq(Tid, EvsDesc) ->
#'NotifyRequest'{terminationID = Tid, observedEventsDescriptor = EvsDesc}.
% cre_notifyRep(Tid) ->
% #'NotifyReply'{terminationID = [Tid]}.
% cre_notifyRep(Tid,Err) ->
% #'NotifyReply'{terminationID = [Tid], errorDescriptor = Err}.
cre_observedEventsDesc(Id, EvList) ->
#'ObservedEventsDescriptor'{requestId = Id, observedEventLst = EvList}.
cre_observedEvent(Name, Not) ->
#'ObservedEvent'{eventName = Name, timeNotation = Not}.
cre_timeNotation() ->
{{Year,Month,Day},{Hour,Min,Sec}} = calendar:universal_time(),
D = lists:flatten(io_lib:format("~4..0w~2..0w~2..0w", [Year,Month,Day])),
T = lists:flatten(io_lib:format("~2..0w~2..0w~4..0w", [Hour,Min,Sec])),
cre_timeNotation(D, T).
cre_timeNotation(D,T) ->
#'TimeNotation'{date = D, time = T}.
cre_error_descr(Code,Text) ->
#'ErrorDescriptor'{errorCode = Code, errorText = Text}.
% cre_error_descr(Code,FormatString,Args) ->
% Text = lists:flatten(io_lib:format(FormatString,Args)),
% cre_error_descr(Code,Text).
%% -----------------------
%% Handle megaco callbacks
%%
handle_megaco_request(#mg{state = connecting} = MG,
{handle_connect, _CH, _PV}) ->
d("handle_megaco_request(handle_connect,connecting) -> entry"),
{ok, MG};
handle_megaco_request(#mg{state = S} = MG,
{handle_connect, _CH, _PV}) ->
d("handle_megaco_request(handle_connect) -> entry"),
Desc =
lists:flatten(io_lib:format("not ready for connect in state ~p", [S])),
ED = cre_error_descr(?megaco_internal_gateway_error, Desc),
{{discard_ack, ED}, MG};
handle_megaco_request(#mg{req_handler = Pid} = MG,
{handle_disconnect, _CH, _PV, R})
when is_pid(Pid) ->
d("handle_megaco_request(handle_disconnect) -> entry with"
"~n Pid: ~p", [Pid]),
Error = {error, {disconnected, R}},
self() ! {notify_request_complete, Error, Pid},
unlink(Pid),
exit(Pid, kill),
{ok, MG#mg{req_handler = undefined, state = initiated}};
handle_megaco_request(MG, {handle_disconnect, _CH, _PV, _R}) ->
d("handle_megaco_request(handle_disconnect) -> entry"),
{ok, MG#mg{state = initiated}};
handle_megaco_request(MG,
{handle_syntax_error, _RH, _PV, _ED}) ->
{reply, MG};
handle_megaco_request(#mg{req_handler = Pid} = MG,
{handle_message_error, CH, PV, ED})
when is_pid(Pid) ->
d("handle_megaco_request(handle_message_error) -> entry with"
"~n Pid: ~p"
"~n CH: ~p"
"~n PV: ~p"
"~n ED: ~p", [Pid, CH, PV, ED]),
self() ! {notify_request_complete, ED, Pid},
unlink(Pid),
exit(Pid, kill),
{no_reply, MG#mg{req_handler = undefined}};
handle_megaco_request(MG, {handle_message_error, CH, PV, ED}) ->
d("handle_megaco_request(handle_message_error) -> entry with"
"~n CH: ~p"
"~n PV: ~p"
"~n ED: ~p", [CH, PV, ED]),
{no_reply, MG};
handle_megaco_request(MG, {handle_trans_request, _CH, _PV, _AR}) ->
ED = cre_error_descr(?megaco_not_implemented,
"Transaction requests not handled"),
{{discard_ack, ED}, MG};
handle_megaco_request(MG,
{handle_trans_long_request, _CH, _PV, _RD}) ->
ED = cre_error_descr(?megaco_not_implemented,
"Long transaction requests not handled"),
{{discard_ack, ED}, MG};
handle_megaco_request(#mg{rep_info = P} = MG,
{handle_trans_reply, CH, PV, AR, RD}) when is_pid(P) ->
P ! {rep_received, self(), AR},
do_handle_trans_reply(MG, CH, PV, AR, RD);
handle_megaco_request(MG, {handle_trans_reply, CH, PV, AR, RD}) ->
do_handle_trans_reply(MG, CH, PV, AR, RD);
handle_megaco_request(#mg{ack_info = P} = MG,
{handle_trans_ack, _CH, _PV, AS, _AD}) when is_pid(P) ->
d("handle_megaco_request(handle_trans_ack,~p) -> entry",[P]),
P ! {ack_received, self(), AS},
{ok, MG};
handle_megaco_request(MG, {handle_trans_ack, _CH, _PV, _AS, _AD}) ->
d("handle_megaco_request(handle_trans_ack) -> entry"),
{ok, MG}.
do_handle_trans_reply(#mg{parent = Parent, state = connecting} = MG,
CH, _PV, {ok, Rep}, _RD) ->
d("do_handle_trans_reply(connecting) -> entry with"
"~n CH: ~p"
"~n Rep: ~p", [CH, Rep]),
server_reply(Parent, service_change_reply, ok),
{ok, MG#mg{state = connected}};
do_handle_trans_reply(#mg{parent = Parent, load_counter = 0} = MG,
CH, _PV, {ok, Rep}, _RD) ->
d("do_handle_trans_reply(load_counter = 0) -> entry with"
"~n CH: ~p"
"~n Rep: ~p", [CH, Rep, Parent]),
handle_trans_reply_verify_act(Rep),
server_reply(Parent, load_complete, ok),
{ok, MG#mg{reply_counter = 0}};
do_handle_trans_reply(#mg{reply_counter = 0} = MG,
CH, _PV, {ok, Rep}, _RD) ->
d("do_handle_trans_reply(reply_counter = 0) -> entry with"
"~n CH: ~p"
"~n Rep: ~p", [CH, Rep]),
handle_trans_reply_verify_act(Rep),
apply_load_timer(),
{ok, MG};
do_handle_trans_reply(#mg{reply_counter = N} = MG,
CH, _PV, {ok, Rep}, _RD) ->
d("do_handle_trans_reply(reply_counter = ~p) -> entry with"
"~n CH: ~p"
"~n Rep: ~p", [N, CH, Rep]),
handle_trans_reply_verify_act(Rep),
apply_load_timer(),
{ok, MG#mg{reply_counter = N-1}};
do_handle_trans_reply(MG, _CH, _PV, {error, ED}, _RD) ->
i("unexpected error transaction: ~p", [ED]),
{ok, MG}.
handle_trans_reply_verify_act([]) ->
ok;
handle_trans_reply_verify_act([#'ActionReply'{commandReply = Rep}|Reps]) ->
handle_trans_reply_verify_cmd(Rep),
handle_trans_reply_verify_act(Reps);
handle_trans_reply_verify_act([Rep|Reps]) ->
i("received 'propably' unexpected reply: ~n~p", [Rep]),
handle_trans_reply_verify_act(Reps).
handle_trans_reply_verify_cmd([]) ->
ok;
handle_trans_reply_verify_cmd([Cmd|Cmds]) ->
case Cmd of
{notifyReply, #'NotifyReply'{terminationID = [Tid]}} ->
d("received expected notification reply from ~n ~p", [Tid]);
Else ->
i("received unexpected notification reply ~n~p", [Else])
end,
handle_trans_reply_verify_cmd(Cmds).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
notify_started(Parent) ->
Parent ! {started, self()}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The megaco user callback interface
handle_connect(CH, PV, Pid, Mid) ->
case CH#megaco_conn_handle.remote_mid of
preliminary_mid ->
%% Avoids deadlock
ok;
_ ->
Reply = request(Pid, {handle_connect, CH, PV}, Mid),
Reply
end.
handle_disconnect(_CH, _PV,
{user_disconnect, {stopped_by_user, Pid}},
Pid, _Mid) ->
ok;
handle_disconnect(CH, PV, R, Pid, Mid) ->
request(Pid, {handle_disconnect, CH, PV, R}, Mid).
handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor, Pid, Mid) ->
Req = {handle_syntax_error, ReceiveHandle, ProtocolVersion,
ErrorDescriptor},
request(Pid, Req, Mid).
handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor, Pid, Mid) ->
Req = {handle_message_error, ConnHandle, ProtocolVersion, ErrorDescriptor},
request(Pid, Req, Mid).
handle_trans_request(CH, PV, AR, Pid, Mid) ->
Reply = request(Pid, {handle_trans_request, CH, PV, AR}, Mid),
Reply.
handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Pid, Mid) ->
Req = {handle_trans_long_request, ConnHandle, ProtocolVersion, ReqData},
request(Pid, Req, Mid).
handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData, Pid, Mid) ->
Req = {handle_trans_reply, ConnHandle, ProtocolVersion,
ActualReply, ReplyData},
request(Pid, Req, Mid).
handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Pid, Mid) ->
Req = {handle_trans_ack, ConnHandle, ProtocolVersion, AckStatus, AckData},
request(Pid, Req, Mid).
request(Pid, Request, Mid) ->
Pid ! {request, Request, Mid, self()},
receive
{reply, {delay, To, ED}, Pid} ->
sleep(To),
{discard_ack, ED};
{reply, Reply, Pid} ->
Reply
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
send_async(true, CH, Actions, Opts) ->
d("send_async(true) -> encode actions first"),
case megaco:encode_actions(CH, Actions, Opts) of
{ok, BinOrBins} ->
d("send_async(true) -> send message"),
megaco:cast(CH, BinOrBins, Opts);
Error ->
d("send_async(true) -> encode failed: ~n~p", [Error]),
Error
end;
send_async(_, CH, Actions, Opts) ->
d("send_async(true) -> send message"),
megaco:cast(CH, Actions, Opts).
send_sync(true, CH, Actions, Opts) ->
d("send_sync(true) -> encode actions first"),
case megaco:encode_actions(CH, Actions, Opts) of
{ok, BinOrBins} ->
d("send_sync(true) -> send message"),
megaco:call(CH, BinOrBins, Opts);
Error ->
d("send_sync(true) -> encode failed: ~n~p", [Error]),
Error
end;
send_sync(_, CH, Actions, Opts) ->
d("send_sync(false) -> send message"),
megaco:call(CH, Actions, Opts).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
sleep(X) ->
receive after X -> ok end.
info_msg(F,A) -> error_logger:info_msg("MG: " ++ F ++ "~n",A).
error_msg(F,A) -> error_logger:error_msg("MG: " ++ 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_transport_host(RI) ->
{ok, LocalHost} = inet:gethostname(),
get_conf(host, RI, LocalHost).
get_transport_opts(RI) ->
get_conf(transport_opts, RI, []).
get_conf(Key, Config) ->
case lists:keysearch(Key, 1, Config) of
{value, {Key, Val}} ->
Val;
_ ->
exit({error, {not_found, Key, Config}})
end.
get_conf(Key, Config, Default) ->
case lists:keysearch(Key, 1, Config) of
{value, {Key, Val}} ->
Val;
_ ->
Default
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tid(N) when N >= 0 ->
{Rem1, Val1} = num2str(N),
{Rem2, Val2} = num2str(Rem1),
{0, Val3} = num2str(Rem2),
[Val3, Val2, Val1].
num2str(N) when N >= 0 ->
num2str(N, []).
num2str(Rem, Val) when length(Val) == 8 ->
{Rem, Val};
num2str(N, Val) ->
D = N div 2,
case N rem 2 of
1 ->
num2str(D, [$1|Val]);
0 ->
num2str(D, [$0|Val])
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] ~s ~p ~s ***"
"~n " ++ F ++ "~n~n",
[?FTS(), P, self(), get(sname) | A]);
print(_, _, _, _) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
random_init() ->
ok.
random() ->
random(50).
random(N) ->
rand:uniform(N).
display_system_info(Mid) ->
display_system_info(Mid, "").
display_system_info(Mid, Pre) ->
TimeStr = ?FTS(),
MibStr = lists:flatten(io_lib:format("~p ", [Mid])),
megaco_test_lib:display_system_info(MibStr ++ Pre ++ TimeStr).
create_timer(Time, Event) ->
erlang:send_after(Time, self(), {Event, Time}).
cancel_timer(undefined) ->
ok;
cancel_timer(Ref) ->
erlang:cancel_timer(Ref).
apply_load_timer() ->
create_timer(random(), apply_load_timeout).