From 6dc7a981b5f76b1057a62c820963b20c53047d6a Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 3 Jul 2012 14:20:24 +0200 Subject: Encode fix The header is used when incrementing counters. Sending answers in the list form was broken because of this. --- lib/diameter/src/base/diameter_codec.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index fe1212b7e0..fb109fe271 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -91,7 +91,8 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) -> Flags = make_flags(0, Hdr), - Pkt#diameter_packet{bin = < Date: Thu, 19 Jul 2012 17:52:38 +0200 Subject: Fix counter typo --- lib/diameter/src/base/diameter_service.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3dfdcee2b2..adf903d6d7 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -2218,7 +2218,7 @@ a(#diameter_packet{errors = []} packet = P} = Req) -> try - incr(in, Pkt, TPid) + incr(recv, Pkt, TPid) of _ -> cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]) -- cgit v1.2.3 From 0af94314f1f61fb39e87e9a173cf7a9d69155695 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 22 Aug 2012 22:46:16 +0200 Subject: Count incoming answers containing AVP decode errors --- lib/diameter/src/base/diameter_service.erl | 66 ++++++++++++++++-------------- 1 file changed, 35 insertions(+), 31 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index adf903d6d7..27ee99a876 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -2200,44 +2200,37 @@ handle_answer(SvcName, _, {error, Req, Reason}) -> handle_answer(SvcName, AnswerErrors, {answer, #request{dictionary = Dict} = Req, Pkt}) -> - a(examine(diameter_codec:decode(Dict, Pkt)), - SvcName, - AnswerErrors, - Req). + answer(examine(diameter_codec:decode(Dict, Pkt)), + SvcName, + AnswerErrors, + Req). %% We don't really need to do a full decode if we're a relay and will %% just resend with a new hop by hop identifier, but might a proxy %% want to examine the answer? -a(#diameter_packet{errors = []} - = Pkt, - SvcName, - AE, - #request{transport = TPid, - caps = Caps, - packet = P} - = Req) -> +answer(Pkt, SvcName, AE, #request{transport = TPid} = Req) -> try incr(recv, Pkt, TPid) of - _ -> - cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]) + _ -> a(Pkt, SvcName, AE, Req) catch exit: {invalid_error_bit, _} = E -> - e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req) - end; - -a(#diameter_packet{} = Pkt, SvcName, AE, Req) -> - e(Pkt, SvcName, AE, Req). + a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req) + end. -e(Pkt, SvcName, callback, #request{transport = TPid, - caps = Caps, - packet = Pkt} - = Req) -> - cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]); -e(Pkt, SvcName, report, Req) -> +a(#diameter_packet{errors = Es} = Pkt, SvcName, AE, #request{transport = TPid, + caps = Caps, + packet = P} + = Req) + when [] == Es; + callback == AE -> + cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]); + +a(Pkt, SvcName, report, Req) -> x(errors, handle_answer, [SvcName, Req, Pkt]); -e(Pkt, SvcName, discard, Req) -> + +a(Pkt, SvcName, discard, Req) -> x({errors, handle_answer, [SvcName, Req, Pkt]}). %% Note that we don't check that the application id in the answer's @@ -2252,8 +2245,10 @@ e(Pkt, SvcName, discard, Req) -> incr(_, #diameter_packet{msg = undefined}, _) -> ok; -incr(Dir, Pkt, TPid) - when is_pid(TPid) -> +incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, TPid) -> + incr(TPid, {diameter_codec:msg_id(H), D, error}); + +incr(Dir, Pkt, TPid) -> #diameter_packet{header = #diameter_header{is_error = E} = Hdr, msg = Rec} @@ -2267,15 +2262,24 @@ incr(Dir, Pkt, TPid) orelse (E andalso PE) orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]), - Ctr = rc_counter(Rec, RC), - is_tuple(Ctr) - andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}). + incr(TPid, Hdr, Dir, rc_counter(Rec, RC)). + +%% incr/4 + +incr(TPid, Hdr, Dir, Ctr) + when is_tuple(Ctr) -> + incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}); + +incr(_, _, _, _) -> + false. %% incr/2 incr(TPid, Counter) -> diameter_stats:incr(Counter, TPid, 1). +%% error_counter/2 + %% RFC 3588, 7.6: %% %% All Diameter answer messages defined in vendor-specific -- cgit v1.2.3 From cf4120168389b7267db857265faafbc8429a16cd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 22 Aug 2012 23:58:28 +0200 Subject: Fix result code statistics for arbitrary dictionaries The code assumed the common dictionary, which was just plain wrong. --- lib/diameter/src/base/diameter_service.erl | 52 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 27ee99a876..aa75ed3dd9 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1942,7 +1942,7 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = Es, when [] == Es; is_record(hd(Msg), diameter_header) -> Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)), - incr(send, Pkt, TPid), %% count result codes in sent answers + incr(send, Pkt, Dict, TPid), %% count result codes in sent answers send(TPid, Pkt#diameter_packet{transport_data = TD}); %% Or not: set Result-Code and Failed-AVP AVP's. @@ -2209,9 +2209,11 @@ handle_answer(SvcName, %% just resend with a new hop by hop identifier, but might a proxy %% want to examine the answer? -answer(Pkt, SvcName, AE, #request{transport = TPid} = Req) -> +answer(Pkt, SvcName, AE, #request{transport = TPid, + dictionary = Dict} + = Req) -> try - incr(recv, Pkt, TPid) + incr(recv, Pkt, Dict, TPid) of _ -> a(Pkt, SvcName, AE, Req) catch @@ -2242,19 +2244,19 @@ a(Pkt, SvcName, discard, Req) -> %% Increment a stats counter for an incoming or outgoing message. %% TODO: fix -incr(_, #diameter_packet{msg = undefined}, _) -> +incr(_, #diameter_packet{msg = undefined}, _, _) -> ok; -incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, TPid) -> +incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) -> incr(TPid, {diameter_codec:msg_id(H), D, error}); -incr(Dir, Pkt, TPid) -> +incr(Dir, Pkt, Dict, TPid) -> #diameter_packet{header = #diameter_header{is_error = E} = Hdr, msg = Rec} = Pkt, - RC = int(get_avp_value(?BASE, 'Result-Code', Rec)), + RC = int(get_avp_value(Dict, 'Result-Code', Rec)), PE = is_protocol_error(RC), %% Check that the E bit is set only for 3xxx result codes. @@ -2262,16 +2264,13 @@ incr(Dir, Pkt, TPid) -> orelse (E andalso PE) orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]), - incr(TPid, Hdr, Dir, rc_counter(Rec, RC)). + irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)). -%% incr/4 +irc(_, _, _, undefined) -> + false; -incr(TPid, Hdr, Dir, Ctr) - when is_tuple(Ctr) -> - incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}); - -incr(_, _, _, _) -> - false. +irc(TPid, Hdr, Dir, Ctr) -> + incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}). %% incr/2 @@ -2289,26 +2288,27 @@ incr(TPid, Counter) -> %% Maintain statistics assuming one or the other, not both, which is %% surely the intent of the RFC. -rc_counter(_, RC) - when is_integer(RC) -> - {'Result-Code', RC}; -rc_counter(Rec, _) -> - rcc(get_avp_value(?BASE, 'Experimental-Result', Rec)). +rc_counter(Dict, Rec, undefined) -> + er(get_avp_value(Dict, 'Experimental-Result', Rec)); +rc_counter(_, _, RC) -> + {'Result-Code', RC}. %% Outgoing answers may be in any of the forms messages can be sent %% in. Incoming messages will be records. We're assuming here that the %% arity of the result code AVP's is 0 or 1. -rcc([{_,_,RC} = T]) - when is_integer(RC) -> +er([{_,_,N} = T | _]) + when is_integer(N) -> T; -rcc({_,_,RC} = T) - when is_integer(RC) -> +er({_,_,N} = T) + when is_integer(N) -> T; -rcc(_) -> +er(_) -> undefined. -int([N]) +%% Extract the first good looking integer. There's no guarantee +%% that what we're looking for has arity 1. +int([N|_]) when is_integer(N) -> N; int(N) -- cgit v1.2.3 From 1ace7fbbb6fb7303bb7e4f41dda9c360f7b66ccd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 09:42:07 +0200 Subject: vsn -> 1.2 --- lib/diameter/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index f6dc786417..c69133a178 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.1 +DIAMETER_VSN = 1.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)" -- cgit v1.2.3 From ae78b0ef5dea1806ab186e351de1ba3054c63c5f Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 27 Jul 2012 08:54:24 +0200 Subject: Allow multiple transport_module and transport_config Transports are started one after the other if a connection is not established with the timeout that can now be specified with transport_config. For example, try an SCTP connect first, a TCP connect if it doesn't succeed. --- lib/diameter/src/base/diameter.erl | 1 + lib/diameter/src/base/diameter_peer.erl | 113 +++++++++++++++++---- lib/diameter/src/base/diameter_peer_fsm.erl | 149 ++++++++++++++++++++++------ 3 files changed, 210 insertions(+), 53 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 336f0c1f2d..6703841f80 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -312,6 +312,7 @@ call(SvcName, App, Message) -> -type transport_opt() :: {transport_module, atom()} | {transport_config, any()} + | {transport_timeout, non_neg_integer() | infinity} | {applications, [app_alias()]} | {capabilities, [capability()]} | {capabilities_cb, evaluable()} diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl index 3e78c4caef..bfd59bee8e 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.erl @@ -27,7 +27,7 @@ up/2]). %% ... and the stack. --export([start/3, +-export([start/1, send/2, close/1, abort/1, @@ -57,6 +57,11 @@ %% Server state. -record(state, {id = now()}). +%% Default transport_module/config. +-define(DEFAULT_TMOD, diameter_tcp). +-define(DEFAULT_TCFG, []). +-define(DEFAULT_TTMO, infinity). + %%% --------------------------------------------------------------------------- %%% # notify/2 %%% --------------------------------------------------------------------------- @@ -65,12 +70,95 @@ notify(SvcName, T) -> rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}). %%% --------------------------------------------------------------------------- -%%% # start/3 +%%% # start/1 %%% --------------------------------------------------------------------------- -start(T, Opts, #diameter_service{} = Svc) -> - {Mod, Cfg} = split_transport(Opts), - apply(Mod, start, [T, Svc, Cfg]). +%% Initial start. +start({T, Opts, #diameter_service{} = Svc}) -> + start(T, Svc, pair(Opts, [], []), []); + +%% Subsequent start. +start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs). + +%% pair/3 +%% +%% Pair transport modules with config. + +%% Another transport_module: accumulate it. +pair([{transport_module, M} | Rest], Mods, Acc) -> + pair(Rest, [M|Mods], Acc); + +%% Another transport_config: accumulate another tuple. +pair([{transport_config = T, C} | Rest], Mods, Acc) -> + pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc); +pair([{transport_config, C, Tmo} | Rest], Mods, Acc) -> + pair(Rest, [], acc({Mods, C, Tmo}, Acc)); + +pair([_ | Rest], Mods, Acc) -> + pair(Rest, Mods, Acc); + +%% No transport_module or transport_config: defaults. +pair([], [], []) -> + [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}]; + +%% One transport_module, one transport_config. +pair([], [M], [{[], Cfg, Tmo}]) -> + [{[M], Cfg, Tmo}]; + +%% Trailing transport_module: default transport_config. +pair([], [_|_] = Mods, Acc) -> + lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc)); + +pair([], [], Acc) -> + lists:reverse(def(Acc)). + +%% acc/2 + +acc(T, Acc) -> + [T | def(Acc)]. + +%% def/1 +%% +%% Default module of previous pair if none were specified. + +def([{[], Cfg, Tmo} | Acc]) -> + [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc]; +def(Acc) -> + Acc. + +%% start/4 + +start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs); + +%% One transport: return the bare error for backwards compatibility. +start(_, _, [], [Err]) -> + {Err}; + +%% Or not: return list of errors. +start(_, _, [], Errs) -> + {{error, Errs}}. + +%% start/7 + +start(T, [], _, Svc, _, Rest, Errs) -> + start(T, Svc, Rest, Errs); + +start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) -> + case start(M, [T, Svc, Cfg]) of + {ok, TPid} -> + {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + {ok, TPid, [_|_] = Addrs} -> + {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + E -> + start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs]) + end. + +%% start/2 + +start(Mod, Args) -> + apply(Mod, start, Args). %%% --------------------------------------------------------------------------- %%% # up/[12] @@ -204,21 +292,6 @@ bang(undefined = No, _) -> bang(Pid, T) -> Pid ! T. -%% split_transport/1 -%% -%% Split options into transport module, transport config and -%% remaining options. - -split_transport(Opts) -> - {[M,C], _} = proplists:split(Opts, [transport_module, - transport_config]), - {value(M, diameter_tcp), value(C, [])}. - -value([{_,V}], _) -> - V; -value([], V) -> - V. - %% call/1 call(Request) -> diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 99644814d2..d5e2d690cf 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -54,6 +54,12 @@ -define(NO_INBAND_SECURITY, 0). -define(TLS, 1). +%% Keys in process dictionary. +-define(CB_KEY, cb). %% capabilities callback +-define(DWA_KEY, dwa). %% outgoing DWA +-define(Q_KEY, q). %% transport start queue +-define(START_KEY, start). %% start of connected transport + %% A 2xxx series Result-Code. Not necessarily 2001. -define(IS_SUCCESS(N), 2 == (N) div 1000). @@ -143,18 +149,17 @@ init(T) -> proc_lib:init_ack({ok, self()}), gen_server:enter_loop(?MODULE, [], i(T)). -i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> - putr(dwa, dwa(Caps)), +i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc}) -> + putr(?DWA_KEY, dwa(Caps)), {M, Ref} = T, {[Ts], Rest} = proplists:split(Opts, [capabilities_cb]), - putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}), - {ok, TPid, Svc} = start_transport(T, Rest, Svc0), - erlang:monitor(process, TPid), + putr(?CB_KEY, {Ref, [F || {_,F} <- Ts]}), erlang:monitor(process, WPid), + {TPid, Addrs} = start_transport(T, Rest, Svc), #state{parent = WPid, transport = TPid, mode = M, - service = Svc}. + service = svc(Svc, Addrs)}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when @@ -164,18 +169,53 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> %% watchdog start (start/2) succeeds regardless so as not to crash the %% service. -start_transport(T, Opts, Svc) -> - case diameter_peer:start(T, Opts, Svc) of - {ok, TPid} -> - {ok, TPid, Svc}; - {ok, TPid, [_|_] = Addrs} -> - #diameter_service{capabilities = Caps0} = Svc, - Caps = Caps0#diameter_caps{host_ip_address = Addrs}, - {ok, TPid, Svc#diameter_service{capabilities = Caps}}; - No -> +start_transport(T, Opts, #diameter_service{capabilities = Caps} = Svc) -> + Addrs0 = Caps#diameter_caps.host_ip_address, + start_transport(Addrs0, {T, Opts, Svc}). + +start_transport(Addrs0, T) -> + case diameter_peer:start(T) of + {TPid, Addrs, Tmo, Data} -> + erlang:monitor(process, TPid), + q_next(TPid, Addrs0, Tmo, Data), + {TPid, addrs(Addrs, Addrs0)}; + {No} -> exit({shutdown, No}) end. +addrs([], Addrs0) -> + Addrs0; +addrs(Addrs, _) -> + Addrs. + +svc(Svc, []) -> + Svc; +svc(Svc, Addrs) -> + readdr(Svc, Addrs). + +readdr(#diameter_service{capabilities = Caps0} = Svc, Addrs) -> + Caps = Caps0#diameter_caps{host_ip_address = Addrs}, + Svc#diameter_service{capabilities = Caps}. + +%% The 4-tuple Data returned from diameter_peer:start/1 identifies the +%% transport module/config use to start the transport process in +%% question as well as any alternates to try if a connection isn't +%% established within Tmo. +q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) -> + send_after(Tmo, {connection_timeout, TPid}), + putr(?Q_KEY, {Addrs0, Tmo, Data}). + +%% Connection has been established: retain the started module/config +%% in the process dictionary. +keep_transport() -> + {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY), + putr(?START_KEY, T). + +send_after(infinity, _) -> + ok; +send_after(Tmo, T) -> + erlang:send_after(Tmo, self(), T). + %% handle_call/3 handle_call(_, _, State) -> @@ -240,25 +280,48 @@ eraser(Key) -> %% Connection to peer. transition({diameter, {TPid, connected, Remote}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M} = S) -> 'Wait-Conn-Ack' = PS, %% assert connect = M, %% - send_CER(S#state{mode = {M, Remote}, - transport = TPid}); + keep_transport(), + send_CER(S#state{mode = {M, Remote}}); %% Connection from peer. transition({diameter, {TPid, connected}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M, parent = Pid} = S) -> 'Wait-Conn-Ack' = PS, %% assert accept = M, %% + keep_transport(), Pid ! {accepted, self()}, - start_timer(S#state{state = recv_CER, - transport = TPid}); + start_timer(S#state{state = recv_CER}); + +%% Connection established after receiving a connection_timeout +%% message. This may be followed by an incoming message which arrived +%% before the transport was killed and this can't be distinguished +%% from one from the transport that's been started to replace it. +transition({diameter, {_, connected}}, _) -> + {stop, connection_timeout}; +transition({diameter, {_, connected, _}}, _) -> + {stop, connection_timeout}; + +%% Connection has timed out: start an alternate. +transition({connection_timeout = T, TPid}, + #state{transport = TPid, + state = 'Wait-Conn-Ack'} + = S) -> + exit(TPid, {shutdown, T}), + start_next(S); + +%% Connect timeout after connection or alternate start: ignore. +transition({connection_timeout, _}, _) -> + ok; %% Incoming message from the transport. transition({diameter, {recv, Pkt}}, S) -> @@ -305,14 +368,21 @@ transition({resolve_port, _Pid} = T, #state{transport = TPid}) -> TPid ! T, ok; -%% Parent or transport has died. -transition({'DOWN', _, process, P, _}, - #state{parent = Pid, - transport = TPid}) - when P == Pid; - P == TPid -> +%% Parent has died. +transition({'DOWN', _, process, WPid, _}, + #state{parent = WPid}) -> stop; +%% Transport has died before connection timeout. +transition({'DOWN', _, process, TPid, _}, + #state{transport = TPid} + = S) -> + start_next(S); + +%% Transport has died after connection timeout. +transition({'DOWN', _, process, _, _}, _) -> + ok; + %% State query. transition({state, Pid}, #state{state = S, transport = TPid}) -> Pid ! {self(), [S, TPid]}, @@ -320,6 +390,19 @@ transition({state, Pid}, #state{state = S, transport = TPid}) -> %% Crash on anything unexpected. +%% start_next/1 + +start_next(#state{service = Svc0} = S) -> + case getr(?Q_KEY) of + {Addrs0, Tmo, Data} -> + Svc = readdr(Svc0, Addrs0), + {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}), + S#state{transport = TPid, + service = svc(Svc, Addrs)}; + undefined -> + stop + end. + %% send_CER/1 send_CER(#state{mode = {connect, Remote}, @@ -649,7 +732,7 @@ rc([RC|_]) -> %% answer/2 answer('DWR', _) -> - getr(dwa); + getr(?DWA_KEY); answer(Name, #state{service = #diameter_service{capabilities = Caps}}) -> a(Name, Caps). @@ -749,15 +832,15 @@ caps(#state{service = Svc}) -> %% caps_cb/1 caps_cb(Caps) -> - {Ref, Ts} = eraser(capabilities_cb), - ccb(Ts, [Ref, Caps]). + {Ref, Ts} = eraser(?CB_KEY), + caps_cb(Ts, [Ref, Caps]). -ccb([], _) -> +caps_cb([], _) -> ok; -ccb([F | Rest], T) -> +caps_cb([F | Rest], T) -> case diameter_lib:eval([F|T]) of ok -> - ccb(Rest, T); + caps_cb(Rest, T); N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately N; Res -> -- cgit v1.2.3 From 28f5ad5aa80c987b7aa98d2d9e4ec6f75850b929 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 12:47:41 +0200 Subject: Add diameter_transport for transport start indirection Module contains a transport start function that calls an a specified function, and more. --- lib/diameter/src/modules.mk | 1 + lib/diameter/src/transport/diameter_transport.erl | 55 +++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 lib/diameter/src/transport/diameter_transport.erl (limited to 'lib/diameter') diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index 7a700a6d53..5d3c4157ae 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -58,6 +58,7 @@ RT_MODULES = \ transport/diameter_tcp_sup \ transport/diameter_sctp \ transport/diameter_sctp_sup \ + transport/diameter_transport \ transport/diameter_transport_sup # Handwritten (compile time) modules not included in the app file. diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl new file mode 100644 index 0000000000..ff4b6bbc6d --- /dev/null +++ b/lib/diameter/src/transport/diameter_transport.erl @@ -0,0 +1,55 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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% +%% + +-module(diameter_transport). + +%% +%% This module implements a transport start function that +%% evaluates its config argument. +%% + +%% Transport start functions +-export([start/3, + select/3, + eval/3]). + +%% start/3 + +%% Call a start function in this module ... +start(T, Svc, {F,A}) -> + start(T, Svc, {?MODULE, F, [A]}); + +%% ... or some other. +start(T, Svc, F) -> + diameter_lib:eval([F, T, Svc]). + +%% select/3 +%% +%% A start function that whose config argument is expected to return a +%% new start function. + +select(T, Svc, F) -> + start(T, Svc, diameter_lib:eval([F, T, Svc])). + +%% eval/3 +%% +%% A start function that simply evaluates its config argument. + +eval(_, _, F) -> + diameter_lib:eval(F). -- cgit v1.2.3 From 6e84ba43a344599ec94852086f40f9b0eacc2c32 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 29 Jul 2012 02:29:55 +0200 Subject: Update documentation --- lib/diameter/doc/src/diameter.xml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'lib/diameter') diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 93e2603c10..ec7bd2012e 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -764,15 +764,45 @@ Defaults to diameter_tcp if unspecified.

The interface required of a transport module is documented in diameter_transport(3).

+ +

+Multiple transport_module and transport_config +options are allowed. +The order of these is significant in this case (and only in this case), +a transport_module being paired with the first +transport_config following it in the options list, or the +default value for trailing modules. +Transport starts will be attempted with each of the +modules in order until one establishes a connection within the +corresponding timeout (see transport_config below) or all fail.

{transport_config, term()} +{transport_config, term(), Unsigned32()}

A term passed as the third argument to the start/3 function of the relevant transport_module in order to start a transport process. Defaults to the empty list if unspecified.

+ +

+The 3-tuple form additionally specifies an interval, in milliseconds, +after which a started transport process should be terminated if it has +not yet established a connection. +For example, the following options on a connecting transport +request a connection with one peer over SCTP or another +(typically the same) over TCP.

+ + +{transport_module, diameter_sctp} +{transport_config, SctpOpts, 5000} +{transport_module, diameter_tcp} +{transport_config, TcpOpts} + + +

+To listen on both SCTP and TCP, define one transport for each.

{applications, [application_alias()]} -- cgit v1.2.3 From 30412e11d96828a519e11c200393dafb898010bc Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 30 Jul 2012 01:47:23 +0200 Subject: Update example code --- lib/diameter/examples/code/peer.erl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/diameter') diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl index b07cd32b98..8fdeba57bf 100644 --- a/lib/diameter/examples/code/peer.erl +++ b/lib/diameter/examples/code/peer.erl @@ -117,6 +117,11 @@ server(T) -> %% %% Return config for a connecting transport. +client({all, LA, RA, RP}) -> + [[M,{K,C}], T] + = [client({P, LA, RA, RP}) || P <- [sctp,tcp]], + [M, {K,C,2000} | T]; + client({T, LA, RA, RP}) -> [{transport_module, tmod(T)}, {transport_config, [{ip, addr(LA)}, -- cgit v1.2.3 From a9ec9ebbbcbed454c9bee618ffca81534322213b Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 09:31:40 +0200 Subject: Fix Destination-Host/Realm extraction for arbitrary dictionaries --- lib/diameter/src/base/diameter_service.erl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index aa75ed3dd9..a4160fddbd 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -72,6 +72,8 @@ -define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests -define(RESTART_TC, 1000). %% if restart was this recent +-define(RELAY, ?DIAMETER_DICT_RELAY). + %% Used to be able to swap this with anything else dict-like but now %% rely on the fact that a service's #state{} record does not change %% in storing in it ?STATE table and not always going through the @@ -2353,8 +2355,11 @@ rt(#request{packet = #diameter_packet{msg = undefined}}, _) -> false; %% TODO: Not what we should do. %% ... or not. -rt(#request{packet = #diameter_packet{msg = Msg}} = Req, S) -> - find_transport(get_destination(Msg), Req, S). +rt(#request{packet = #diameter_packet{msg = Msg}, + dictionary = Dict} + = Req, + S) -> + find_transport(get_destination(Dict, Msg), Req, S). %%% --------------------------------------------------------------------------- %%% # report_status/5 @@ -2466,12 +2471,12 @@ find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) -> find_transport(#diameter_app{} = App, Msg, Opts, S) -> ft(App, Msg, Opts, S). -ft(#diameter_app{module = Mod} = App, Msg, Opts, S) -> +ft(#diameter_app{module = Mod, dictionary = Dict} = App, Msg, Opts, S) -> #options{filter = Filter, extra = Xtra} = Opts, pick_peer(App#diameter_app{module = Mod ++ Xtra}, - get_destination(Msg), + get_destination(Dict, Msg), Filter, S); ft(false = No, _, _, _) -> @@ -2507,11 +2512,11 @@ find_transport([_,_] = RH, Filter, S). -%% get_destination/1 +%% get_destination/2 -get_destination(Msg) -> - [str(get_avp_value(?BASE, 'Destination-Realm', Msg)), - str(get_avp_value(?BASE, 'Destination-Host', Msg))]. +get_destination(Dict, Msg) -> + [str(get_avp_value(Dict, 'Destination-Realm', Msg)), + str(get_avp_value(Dict, 'Destination-Host', Msg))]. %% This is not entirely correct. The avp could have an arity 1, in %% which case an empty list is a DiameterIdentity of length 0 rather @@ -2535,6 +2540,9 @@ str(T) -> %% question. The third form allows messages to be sent as is, without %% a dictionary, which is needed in the case of relay agents, for one. +get_avp_value(?RELAY, Name, Msg) -> + get_avp_value(?BASE, Name, Msg); + get_avp_value(Dict, Name, [#diameter_header{} | Avps]) -> try {Code, _, VId} = Dict:avp_header(Name), -- cgit v1.2.3 From 60fb617d030a24afb86fe9779479509f11179578 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 01:00:54 +0200 Subject: Set Result-Code as an optional AVP in reply to request containing errors Previously assumed it had arity 1, which is not necessarily the case. Whether or not we should do this is probably debatable. There should at least be a way for the user to roll their own. --- lib/diameter/src/base/diameter_service.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index a4160fddbd..167b2c1f79 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1985,7 +1985,10 @@ rc(RC) -> rc(Rec, RC, Failed, Dict) when is_integer(RC) -> - set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict). + set(Rec, + lists:append([rc(Rec, {'Result-Code', RC}, Dict), + failed_avp(Rec, Failed, Dict)]), + Dict). %% Reply as name and tuple list ... set([_|_] = Ans, Avps, _) -> @@ -1995,6 +1998,22 @@ set([_|_] = Ans, Avps, _) -> set(Rec, Avps, Dict) -> Dict:'#set-'(Avps, Rec). +%% rc/3 +%% +%% Turn the result code into a list if its optional and only set it if +%% the arity is 1 or {0,1}. In other cases (which probably shouldn't +%% exist in practise) we can't know what's appropriate. + +rc([MsgName | _], {'Result-Code' = K, RC} = T, Dict) -> + case Dict:avp_arity(MsgName, 'Result-Code') of + 1 -> [T]; + {0,1} -> [{K, [RC]}]; + _ -> [] + end; + +rc(Rec, T, Dict) -> + rc([Dict:rec2msg(element(1, Rec))], T, Dict). + %% failed_avp/3 failed_avp(_, [] = No, _) -> -- cgit v1.2.3 From de0cbe83cd96c17d75baf353e84c883b78980f98 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 01:20:00 +0200 Subject: Allow an answer to opt out of setting Result-Code/Failed-AVP By returning it in a length 1 list from a handle_request callback. This is the aforementioned roll your own. --- lib/diameter/src/base/diameter_service.erl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 167b2c1f79..ced6fe605c 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1937,6 +1937,12 @@ is_loop(Code, Vid, OH, Avps) -> %% %% Send a locally originating reply. +%% Skip the setting of Result-Code and Failed-AVP's below. +reply([Msg], Dict, TPid, Pkt) + when is_list(Msg); + is_tuple(Msg) -> + reply(Msg, Dict, TPid, Pkt#diameter_packet{errors = []}); + %% No errors or a diameter_header/avp list. reply(Msg, Dict, TPid, #diameter_packet{errors = Es, transport_data = TD} -- cgit v1.2.3 From 1c3884894917a7e84d95f7c82c61c6205d802717 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 22 Aug 2012 19:56:02 +0200 Subject: Deal with the fact that capabilities config may be incomplete A transport can be configured before its service so handle insufficient configuration instead of crashing at CER/CEA encode. --- lib/diameter/src/base/diameter_codec.erl | 6 +++--- lib/diameter/src/base/diameter_peer_fsm.erl | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index fe1212b7e0..88aabc1e4a 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -63,9 +63,9 @@ encode(Mod, #diameter_packet{} = Pkt) -> e(Mod, Pkt) catch error: Reason -> - %% Be verbose rather than letting the emulator truncate the - %% error report. - X = {Reason, ?STACK}, + %% Be verbose since a crash report may be truncated and + %% encode errors are self-inflicted. + X = {?MODULE, encode, {Reason, ?STACK}}, diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}), exit(X) end; diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 99644814d2..588ebfb4e8 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -202,14 +202,27 @@ handle_info(T, #state{} = State) -> ?LOG(stop, T), x(T, State) catch + exit: {diameter_codec, encode, _} = Reason -> + close_wd(Reason, State#state.parent), + ?LOG(stop, Reason), + %% diameter_codec:encode/2 emits an error report. Only + %% indicate the probable reason here. + diameter_lib:info_report(probable_configuration_error, + insufficient_capabilities), + {stop, {shutdown, Reason}, State}; {?MODULE, Tag, Reason} -> ?LOG(Tag, {Reason, T}), {stop, {shutdown, Reason}, State} end. -%% The form of the exception caught here is historical. It's +%% The form of the throw caught here is historical. It's %% significant that it's not a 2-tuple, as in ?FAILURE(Reason), %% since these are caught elsewhere. +%% Note that there's no guarantee that the service and transport +%% capabilities are good enough to build a CER/CEA that can be +%% succesfully encoded. It's not checked at diameter:add_transport/2 +%% since this can be called before creating the service. + x(Reason, #state{} = S) -> close_wd(Reason, S), {stop, {shutdown, Reason}, S}. -- cgit v1.2.3 From e93b194345b563e9f5bc6264e577ef839d2f1ab2 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 20:53:53 +0200 Subject: Statistics fixes Statistics are deleted as a consequence of diameter:remove_transport/2. --- lib/diameter/src/base/diameter_config.erl | 3 +- lib/diameter/src/base/diameter_peer_fsm.erl | 6 +- lib/diameter/src/base/diameter_service.erl | 21 +-- lib/diameter/src/base/diameter_stats.erl | 249 +++++++++++----------------- 4 files changed, 109 insertions(+), 170 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 9253af0de2..eb06045ace 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -519,6 +519,7 @@ rm(SvcName, L) -> Refs = lists:map(fun(#transport{ref = R}) -> R end, L), case stop_transport(SvcName, Refs) of ok -> + diameter_stats:flush(Refs), lists:foreach(fun delete_object/1, L); {error, _} = No -> No diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 99644814d2..433983c1cb 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -120,11 +120,10 @@ %% specified on the transport in question. Check here that the list is %% still non-empty. -start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) -> +start({_,_} = Type, Opts, #diameter_service{applications = Apps} = Svc) -> [] /= Apps orelse ?ERROR({no_apps, Type, Opts}), T = {self(), Type, Opts, Svc}, {ok, Pid} = diameter_peer_fsm_sup:start_child(T), - diameter_stats:reg(Pid, Ref), Pid. start_link(T) -> @@ -146,6 +145,7 @@ init(T) -> i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> putr(dwa, dwa(Caps)), {M, Ref} = T, + diameter_stats:reg(Ref), {[Ts], Rest} = proplists:split(Opts, [capabilities_cb]), putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}), {ok, TPid, Svc} = start_transport(T, Rest, Svc0), diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3dfdcee2b2..591b99d7cc 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -43,8 +43,7 @@ subscriptions/0, services/0, services/1, - whois/1, - flush_stats/1]). + whois/1]). %% test/debug -export([call_module/3, @@ -388,15 +387,6 @@ whois(SvcName) -> undefined end. -%%% --------------------------------------------------------------------------- -%%% # flush_stats/1 -%%% -%%% Output: list of {{SvcName, Alias, Counter}, Value} -%%% --------------------------------------------------------------------------- - -flush_stats(TPid) -> - diameter_stats:flush(TPid). - %% =========================================================================== %% =========================================================================== @@ -2820,11 +2810,10 @@ complete(Pre) -> end. info_stats(#state{peerT = PeerT}) -> - Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'}, - [{'is_pid', '$2'}], - [['$1', '$2']]}]), - diameter_stats:read(lists:append(Peers)). -%% TODO: include peer identities in return value + MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'}, + [{'is_pid', '$2'}], + [['$1', '$2']]}], + diameter_stats:read(lists:append(ets:select(PeerT, MatchSpec))). info_transport(#state{peerT = PeerT, connT = ConnT}) -> dict:fold(fun it/3, diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl index 71479afa95..8d4b456bd2 100644 --- a/lib/diameter/src/base/diameter_stats.erl +++ b/lib/diameter/src/base/diameter_stats.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -22,14 +22,13 @@ %% -module(diameter_stats). --compile({no_auto_import, [monitor/2]}). -behaviour(gen_server). -export([reg/1, reg/2, - incr/1, incr/2, incr/3, + incr/1, incr/3, read/1, - flush/0, flush/1]). + flush/1]). %% supervisor callback -export([start_link/0]). @@ -48,123 +47,104 @@ -include("diameter_internal.hrl"). -%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref}, -%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and -%% Pid death causes counters keyed on {Counter, Pid} to be deleted and -%% added to those keyed on {Counter, Ref}. +%% ets table containing 2-tuple stats. reg(Pid, Ref) inserts a {Pid, +%% Ref}, incr(Counter, X, N) updates the counter keyed at {Counter, +%% X}, and Pid death causes counters keyed on {Counter, Pid} to be +%% deleted and added to those keyed on {Counter, Ref}. -define(TABLE, ?MODULE). %% Name of registered server. -define(SERVER, ?MODULE). -%% Entries in the table. --define(REC(Key, Value), {Key, Value}). - %% Server state. -record(state, {id = now()}). -type counter() :: any(). --type contrib() :: any(). - -%%% --------------------------------------------------------------------------- -%%% # reg(Pid, Contrib) -%%% -%%% Description: Register a process as a contributor of statistics -%%% associated with a specified term. Statistics can be -%%% contributed by specifying either Pid or Contrib as -%%% the second argument to incr/3. Statistics contributed -%%% by Pid are folded into the corresponding entry for -%%% Contrib when the process dies. -%%% -%%% Contrib can be any term but should not be a pid -%%% passed as the first argument to reg/2. Subsequent -%%% registrations for the same Pid overwrite the association -%%% --------------------------------------------------------------------------- - --spec reg(pid(), contrib()) - -> true. +-type ref() :: any(). + +%% --------------------------------------------------------------------------- +%% # reg(Pid, Ref) +%% +%% Register a process as a contributor of statistics associated with a +%% specified term. Statistics can be contributed by specifying either +%% Pid or Ref as the second argument to incr/3. Statistics contributed +%% by Pid are folded into the corresponding entry for Ref when the +%% process dies. +%% --------------------------------------------------------------------------- + +-spec reg(pid(), ref()) + -> boolean(). -reg(Pid, Contrib) +reg(Pid, Ref) when is_pid(Pid) -> - call({reg, Pid, Contrib}). + call({reg, Pid, Ref}). --spec reg(contrib()) +-spec reg(ref()) -> true. reg(Ref) -> reg(self(), Ref). -%%% --------------------------------------------------------------------------- -%%% # incr(Counter, Contrib, N) -%%% -%%% Description: Increment a counter for the specified contributor. -%%% -%%% Contrib will typically be an argument passed to reg/2 -%%% but there's nothing that requires this. In particular, -%%% if Contrib is a pid that hasn't been registered then -%%% counters are unaffected by the death of the process. -%%% --------------------------------------------------------------------------- - --spec incr(counter(), contrib(), integer()) - -> integer(). - -incr(Ctr, Contrib, N) -> - update_counter({Ctr, Contrib}, N). +%% --------------------------------------------------------------------------- +%% # incr(Counter, Ref, N) +%% +%% Increment a counter for the specified contributor. +%% +%% Ref will typically be an argument passed to reg/2 but there's +%% nothing that requires this. Only registered pids can contribute +%% counters however, otherwise incr/3 is a no-op. +%% --------------------------------------------------------------------------- -incr(Ctr, N) - when is_integer(N) -> - incr(Ctr, self(), N); +-spec incr(counter(), ref(), integer()) + -> integer(). -incr(Ctr, Contrib) -> - incr(Ctr, Contrib, 1). +incr(Ctr, Ref, N) -> + update_counter({Ctr, Ref}, N). incr(Ctr) -> incr(Ctr, self(), 1). -%%% --------------------------------------------------------------------------- -%%% # read(Contribs) -%%% -%%% Description: Retrieve counters for the specified contributors. -%%% --------------------------------------------------------------------------- +%% --------------------------------------------------------------------------- +%% # read(Refs) +%% +%% Retrieve counters for the specified contributors. +%% --------------------------------------------------------------------------- + +-spec read([ref()]) + -> [{ref(), [{counter(), integer()}]}]. --spec read([contrib()]) - -> [{contrib(), [{counter(), integer()}]}]. +read(Refs) -> + read(Refs, false). -read(Contribs) -> - lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end, +read(Refs, B) -> + MatchSpec = [{{{'_', '$1'}, '_'}, + [?ORCOND([{'=:=', '$1', {const, R}} + || R <- Refs])], + ['$_']}], + L = ets:select(?TABLE, MatchSpec), + B andalso delete(L), + lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,N}, D) end, orddict:new(), - ets:select(?TABLE, [{?REC({'_', '$1'}, '_'), - [?ORCOND([{'=:=', '$1', {const, C}} - || C <- Contribs])], - ['$_']}])). - -%%% --------------------------------------------------------------------------- -%%% # flush(Contrib) -%%% -%%% Description: Retrieve and delete statistics for the specified -%%% contributor. -%%% -%%% If Contrib is a pid registered with reg/2 then statistics -%%% for both and its associated contributor are retrieved. -%%% --------------------------------------------------------------------------- - --spec flush(contrib()) - -> [{counter(), integer()}]. + L). + +%% --------------------------------------------------------------------------- +%% # flush(Refs) +%% +%% Retrieve and delete statistics for the specified contributors. +%% --------------------------------------------------------------------------- + +-spec flush([ref()]) + -> [{ref(), {counter(), integer()}}]. -flush(Contrib) -> +flush(Refs) -> try - call({flush, Contrib}) + call({flush, Refs}) catch exit: _ -> [] end. -flush() -> - flush(self()). - -%%% --------------------------------------------------------- -%%% EXPORTED INTERNAL FUNCTIONS -%%% --------------------------------------------------------- +%% =========================================================================== start_link() -> ServerName = {local, ?SERVER}, @@ -179,18 +159,16 @@ state() -> uptime() -> call(uptime). -%%% ---------------------------------------------------------- -%%% # init(_) -%%% -%%% Output: {ok, State} -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # init/1 +%% ---------------------------------------------------------- init([]) -> ets:new(?TABLE, [named_table, ordered_set, public]), {ok, #state{}}. %% ---------------------------------------------------------- -%% handle_call(Request, From, State) +%% # handle_call/3 %% ---------------------------------------------------------- handle_call(state, _, State) -> @@ -199,19 +177,20 @@ handle_call(state, _, State) -> handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; -handle_call({reg, Pid, Contrib}, _From, State) -> - monitor(not ets:member(?TABLE, Pid), Pid), - {reply, insert(?REC(Pid, Contrib)), State}; +handle_call({reg, Pid, Ref}, _From, State) -> + B = ets:insert_new(?TABLE, {Pid, Ref}), + B andalso erlang:monitor(process, Pid), + {reply, B, State}; -handle_call({flush, Contrib}, _From, State) -> - {reply, fetch(Contrib), State}; +handle_call({flush, Refs}, _From, State) -> + {reply, read(Refs, true), State}; handle_call(Req, From, State) -> ?UNEXPECTED([Req, From]), {reply, nok, State}. %% ---------------------------------------------------------- -%% handle_cast(Request, State) +%% # handle_cast/2 %% ---------------------------------------------------------- handle_cast({incr, Rec}, State) -> @@ -223,7 +202,7 @@ handle_cast(Msg, State) -> {noreply, State}. %% ---------------------------------------------------------- -%% handle_info(Request, State) +%% # handle_info/2 %% ---------------------------------------------------------- handle_info({'DOWN', _MRef, process, Pid, _}, State) -> @@ -235,91 +214,61 @@ handle_info(Info, State) -> {noreply, State}. %% ---------------------------------------------------------- -%% terminate(Reason, State) +%% # terminate/2 %% ---------------------------------------------------------- terminate(_Reason, _State) -> ok. %% ---------------------------------------------------------- -%% code_change(OldVsn, State, Extra) +%% # code_change/3 %% ---------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. -%%% --------------------------------------------------------- -%%% INTERNAL FUNCTIONS -%%% --------------------------------------------------------- - -%% monitor/2 - -monitor(true, Pid) -> - erlang:monitor(process, Pid); -monitor(false = No, _) -> - No. +%% =========================================================================== %% down/1 down(Pid) -> - L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')), - [?REC(_, Ref) = T] = lookup(Pid), + down(lookup(Pid), ets:match_object(?TABLE, {{'_', Pid}, '_'})). + +down([{_, Ref} = T], L) -> fold(Ref, L), - delete_object(T), + delete([T|L]); +down([], L) -> %% flushed delete(L). -%% Fold Pid-based entries into Ref-based ones. +%% Fold pid-based entries into ref-based ones. fold(Ref, L) -> - lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end, - L). - -delete(Objs) -> - lists:foreach(fun delete_object/1, Objs). - -%% fetch/1 - -fetch(X) -> - MatchSpec = [{?REC({'_', '$1'}, '_'), - [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])], - ['$_']}], - L = ets:select(?TABLE, MatchSpec), - delete(L), - D = lists:foldl(fun sum/2, dict:new(), L), - dict:to_list(D). - -sum({{Ctr, _}, N}, Dict) -> - dict:update(Ctr, fun(V) -> V+N end, N, Dict). - -ref(Pid) - when is_pid(Pid) -> - ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]); -ref(_) -> - []. + lists:foreach(fun({{K, _}, V}) -> update_counter({{K, Ref}, V}) end, L). %% update_counter/2 %% -%% From an arbitrary request process. Cast to the server process to -%% insert a new element if the counter doesn't exists so that two -%% processes don't do so simultaneously. +%% From an arbitrary process. Cast to the server process to insert a +%% new element if the counter doesn't exists so that two processes +%% don't do so simultaneously. update_counter(Key, N) -> try ets:update_counter(?TABLE, Key, N) catch error: badarg -> - cast({incr, ?REC(Key, N)}) + cast({incr, {Key, N}}) end. %% update_counter/1 %% %% From the server process. -update_counter(?REC(Key, N) = T) -> +update_counter({{_Ctr, Ref} = Key, N} = T) -> try ets:update_counter(?TABLE, Key, N) catch error: badarg -> - insert(T) + (not is_pid(Ref) orelse ets:member(?TABLE, Ref)) + andalso insert(T) end. insert(T) -> @@ -328,8 +277,8 @@ insert(T) -> lookup(Key) -> ets:lookup(?TABLE, Key). -delete_object(T) -> - ets:delete_object(?TABLE, T). +delete(Objs) -> + lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs). %% cast/1 -- cgit v1.2.3 From 34920eaa016af20226da0a6dc44d3cd8b9cf4abe Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 24 Aug 2012 03:17:10 +0200 Subject: Improve statistics test cases --- lib/diameter/src/base/diameter_stats.erl | 32 +++++------ lib/diameter/test/diameter_stats_SUITE.erl | 92 +++++++++++++++++++++--------- 2 files changed, 80 insertions(+), 44 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl index 8d4b456bd2..70727d068e 100644 --- a/lib/diameter/src/base/diameter_stats.erl +++ b/lib/diameter/src/base/diameter_stats.erl @@ -25,8 +25,8 @@ -behaviour(gen_server). --export([reg/1, reg/2, - incr/1, incr/3, +-export([reg/2, reg/1, + incr/3, incr/1, read/1, flush/1]). @@ -96,9 +96,10 @@ reg(Ref) -> %% --------------------------------------------------------------------------- -spec incr(counter(), ref(), integer()) - -> integer(). + -> integer() | false. -incr(Ctr, Ref, N) -> +incr(Ctr, Ref, N) + when is_integer(N) -> update_counter({Ctr, Ref}, N). incr(Ctr) -> @@ -177,6 +178,9 @@ handle_call(state, _, State) -> handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; +handle_call({incr, T}, _, State) -> + {reply, update_counter(T), State}; + handle_call({reg, Pid, Ref}, _From, State) -> B = ets:insert_new(?TABLE, {Pid, Ref}), B andalso erlang:monitor(process, Pid), @@ -193,10 +197,6 @@ handle_call(Req, From, State) -> %% # handle_cast/2 %% ---------------------------------------------------------- -handle_cast({incr, Rec}, State) -> - update_counter(Rec), - {noreply, State}; - handle_cast(Msg, State) -> ?UNEXPECTED([Msg]), {noreply, State}. @@ -246,21 +246,22 @@ fold(Ref, L) -> %% update_counter/2 %% -%% From an arbitrary process. Cast to the server process to insert a +%% From an arbitrary process. Call to the server process to insert a %% new element if the counter doesn't exists so that two processes -%% don't do so simultaneously. +%% don't insert simultaneously. update_counter(Key, N) -> try ets:update_counter(?TABLE, Key, N) catch error: badarg -> - cast({incr, {Key, N}}) + call({incr, {Key, N}}) end. %% update_counter/1 %% -%% From the server process. +%% From the server process, when update_counter/2 failed due to a +%% non-existent entry. update_counter({{_Ctr, Ref} = Key, N} = T) -> try @@ -268,7 +269,7 @@ update_counter({{_Ctr, Ref} = Key, N} = T) -> catch error: badarg -> (not is_pid(Ref) orelse ets:member(?TABLE, Ref)) - andalso insert(T) + andalso begin insert(T), N end end. insert(T) -> @@ -280,11 +281,6 @@ lookup(Key) -> delete(Objs) -> lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs). -%% cast/1 - -cast(Msg) -> - gen_server:cast(?SERVER, Msg). - %% call/1 call(Request) -> diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl index e7807fd360..4a93d4f748 100644 --- a/lib/diameter/test/diameter_stats_SUITE.erl +++ b/lib/diameter/test/diameter_stats_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -30,11 +30,12 @@ end_per_suite/1]). %% testcases --export([an/1, - twa/1]). +-export([reg/1, + incr/1, + read/1, + flush/1]). -define(stat, diameter_stats). --define(util, diameter_util). %% =========================================================================== @@ -49,8 +50,10 @@ groups() -> [{all, [], tc()}]. tc() -> - [an, - twa]. + [reg, + incr, + read, + flush]. init_per_suite(Config) -> ok = diameter:start(), @@ -61,25 +64,62 @@ end_per_suite(_Config) -> %% =========================================================================== -an(_) -> - Ref = {'_', make_ref()}, +reg(_) -> + Ref = '$1', true = ?stat:reg(Ref), - true = ?stat:reg(Ref), %% duplicate - ok = ?stat:incr(x), - ok = ?stat:incr(x, Ref), - ok = ?stat:incr(y, 2), - ok = ?stat:incr(y, Ref), - %% Flushing a pid flushes even stats on the registered reference. - [{x,2},{y,3}] = lists:sort(?stat:flush()), - [] = ?stat:flush(Ref), - [] = ?stat:flush(). - -twa(_) -> + false = ?stat:reg(Ref). %% duplicate + +incr(_) -> + Ref = '_', + Ctr = x, + false = ?stat:incr(Ctr), %% not registered, + 1 = ?stat:incr(Ctr, Ref, 1), %% only pids need register + true = ?stat:reg(Ref), + spawn(fun() -> + true = ?stat:reg(Ref), + 2 = ?stat:incr(Ctr, self(), 2) + end), + ok = fold(Ctr, Ref, 3), %% folded + ?stat:flush([self(), Ref]). + +read(_) -> + Ref = make_ref(), + C1 = {a,b}, + C2 = {b,a}, + true = ?stat:reg(Ref), + 1 = ?stat:incr(C1), + 1 = ?stat:incr(C2), + 2 = ?stat:incr(C1), + 7 = ?stat:incr(C1, Ref, 7), + Self = self(), + [{Ref, [{C1,7}]}, {Self, [{C1,2}, {C2,1}]}] + = lists:sort(?stat:read([self(), Ref, make_ref()])), + [] = ?stat:read([]), + [] = ?stat:read([make_ref()]), + ?stat:flush([self(), Ref, make_ref()]). + +flush(_) -> Ref = make_ref(), - ok = ?stat:incr(x, 8), - ok = ?stat:incr(x, Ref, 7), - %% Flushing a reference doesn't affect registered pids. - [{x,7}] = ?stat:flush(Ref), - [] = ?stat:flush(Ref), - [{x,8}] = ?stat:flush(), - [] = ?stat:flush(). + Ctr = '_', + true = ?stat:reg(Ref), + 1 = ?stat:incr(Ctr), + 3 = ?stat:incr(Ctr, self(), 2), + 2 = ?stat:incr(Ctr, Ref, 2), + Self = self(), + [{Self, [{Ctr, 3}]}] = ?stat:flush([self()]), + 1 = ?stat:incr(Ctr), + [{Ref, [{Ctr, 2}]}] = ?stat:flush([Ref]), + [{Self, [{Ctr, 1}]}] = ?stat:flush([self()]), + [] = ?stat:flush([self(), Ref]). + +%% =========================================================================== + +%% Keep incremented until a fold results in the specified value. +fold(Ctr, Ref, N) -> + case ?stat:incr(Ctr, Ref, 0) of + N -> + ok; + M when M < N -> + erlang:yield(), + fold(Ctr, Ref, N) + end. -- cgit v1.2.3 From b1cdee0b11c6fde89ba586b1c04724daf4a96a07 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 22 Aug 2012 16:45:31 +0200 Subject: Add events for watchdog state transitions --- lib/diameter/doc/src/diameter.xml | 13 ++++++++ lib/diameter/src/base/diameter_service.erl | 8 +++++ lib/diameter/src/base/diameter_watchdog.erl | 48 +++++++++++++++++++++++------ 3 files changed, 60 insertions(+), 9 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 93e2603c10..4dcbac4889 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -692,6 +692,19 @@ The packet record contains the CEA in question.

+{watchdog, Ref, PeerRef, {From, To}, Config} + + +Ref = transport_ref() +PeerRef = diameter_app:peer_ref() +From, To = initial | okay | suspect | down | reopen +Config = {connect|listen, [transport_opt()]} + + +

+An RFC 3539 watchdog state machine has changed state.

+
+

diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3dfdcee2b2..c9c3179630 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -516,6 +516,14 @@ transition({reconnect, Pid}, S) -> reconnect(Pid, S), ok; +%% Watchdog is sending notification of a state transition. +transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName, + peerT = PeerT}) -> + #peer{ref = Ref, type = T, options = Opts} + = fetch(PeerT, Pid), + send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}), + ok; + %% Monitor process has died. Just die with a reason that tells %% diameter_config about the happening. If a cleaner shutdown is %% required then someone should stop us. diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index fb22fd8275..b615bed860 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -54,7 +54,7 @@ %% number of DWAs received during reopen %% end PCB parent = self() :: pid(), - transport :: pid(), + transport :: pid() | undefined, tref :: reference(), %% reference for current watchdog timer message_data}). %% term passed into diameter_service with message @@ -64,6 +64,14 @@ %% that a failed capabilities exchange produces the desired exit %% reason. +-spec start(Type, {RecvData, Opts, SvcName, Svc}) + -> pid() + when Type :: {connect|accept, reference()}, + RecvData :: term(), + Opts :: list(), + SvcName :: term(), + Svc :: #diameter_service{}. + start({_,_} = Type, T) -> Ref = make_ref(), {ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}), @@ -102,7 +110,7 @@ i({_, Pid, _} = T) -> %% from old code erlang:monitor(process, Pid), make_state(T). -make_state({T, Pid, {ConnT, +make_state({T, Pid, {RecvData, Opts, SvcName, #diameter_service{applications = Apps, @@ -116,7 +124,7 @@ make_state({T, Pid, {ConnT, tw = proplists:get_value(watchdog_timer, Opts, ?DEFAULT_TW_INIT), - message_data = {ConnT, SvcName, Apps}}. + message_data = {RecvData, SvcName, Apps}}. %% handle_call/3 @@ -134,14 +142,36 @@ handle_info(T, State) -> case transition(T, State) of ok -> {noreply, State}; - #watchdog{status = X} = S -> - ?LOGC(X =/= State#watchdog.status, transition, X), + #watchdog{} = S -> + event(State, S), {noreply, S}; stop -> ?LOG(stop, T), + event(State, State#watchdog{status = down}), {stop, {shutdown, T}, State} end. +event(#watchdog{status = T}, #watchdog{status = T}) -> + ok; + +event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) -> + ok; + +event(#watchdog{status = From, transport = F, parent = Pid}, + #watchdog{status = To, transport = T}) -> + E = {tpid(F,T), From, To}, + notify(Pid, E), + ?LOG(transition, {self(), E}). + +tpid(_, Pid) + when is_pid(Pid) -> + Pid; +tpid(Pid, _) -> + Pid. + +notify(Pid, E) -> + Pid ! {watchdog, self(), E}. + %% terminate/2 terminate(_, _) -> @@ -251,8 +281,8 @@ transition({'DOWN', _, process, TPid, _}, status = initial}) -> stop; -transition({'DOWN', _, process, Pid, _}, - #watchdog{transport = Pid} +transition({'DOWN', _, process, TPid, _}, + #watchdog{transport = TPid} = S) -> failover(S), close(S), @@ -385,7 +415,7 @@ recv(Name, Pkt, S) -> rcv(Name, Pkt, S), NS catch - throw: {?MODULE, throwaway, #watchdog{} = NS} -> + {?MODULE, throwaway, #watchdog{} = NS} -> NS end. -- cgit v1.2.3 From bde27ba6edd16e1892318f7354d0b1956859a7c4 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 09:17:56 +0200 Subject: Fix timing issue with subscribe in test suites Has to happen before add_transport to be sure of getting the subsequent event. --- lib/diameter/test/diameter_util.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index 0c42f955ad..c3f5cbf0e2 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -275,8 +275,8 @@ connect(Client, Prot, LRef) -> connect(Client, Prot, LRef, Opts) -> [PortNr] = lport(Prot, LRef, 20), - Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), true = diameter:subscribe(Client), + Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), ok = receive {diameter_event, Client, {up, Ref, _, _, _}} -> ok after 2000 -> -- cgit v1.2.3 From 942bd62d9b83f2697b25de0be1f452d9dff4cefa Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 09:47:01 +0200 Subject: Lighten up on timetraps in test suites Some look to be optimistic when running in slow virtual environments. (With bad time keeping?) --- lib/diameter/test/diameter_capx_SUITE.erl | 6 +++--- lib/diameter/test/diameter_compiler_SUITE.erl | 4 ++-- lib/diameter/test/diameter_dict_SUITE.erl | 4 ++-- lib/diameter/test/diameter_failover_SUITE.erl | 4 ++-- lib/diameter/test/diameter_reg_SUITE.erl | 4 ++-- lib/diameter/test/diameter_relay_SUITE.erl | 4 ++-- lib/diameter/test/diameter_stats_SUITE.erl | 4 ++-- lib/diameter/test/diameter_sync_SUITE.erl | 4 ++-- lib/diameter/test/diameter_tls_SUITE.erl | 4 ++-- lib/diameter/test/diameter_traffic_SUITE.erl | 4 ++-- lib/diameter/test/diameter_util.erl | 6 +++--- 11 files changed, 24 insertions(+), 24 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl index 54a161d606..ae128b8203 100644 --- a/lib/diameter/test/diameter_capx_SUITE.erl +++ b/lib/diameter/test/diameter_capx_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -93,12 +93,12 @@ -define(fail(T), erlang:error({T, process_info(self(), messages)})). --define(TIMEOUT, 2000). +-define(TIMEOUT, 10000). %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, start_services, diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl index 3b4c9706e0..4b792b5426 100644 --- a/lib/diameter/test/diameter_compiler_SUITE.erl +++ b/lib/diameter/test/diameter_compiler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -339,7 +339,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 5}}]. + [{timetrap, {minutes, 2}}]. all() -> [format, diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl index 5cf8506d3f..3cc65c0257 100644 --- a/lib/diameter/test/diameter_dict_SUITE.erl +++ b/lib/diameter/test/diameter_dict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -48,7 +48,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl index 53398dd93e..ed31670031 100644 --- a/lib/diameter/test/diameter_failover_SUITE.erl +++ b/lib/diameter/test/diameter_failover_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -101,7 +101,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl index ec6a0ca731..4939019f7a 100644 --- a/lib/diameter/test/diameter_reg_SUITE.erl +++ b/lib/diameter/test/diameter_reg_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -43,7 +43,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl index 70e1866791..134239d9b6 100644 --- a/lib/diameter/test/diameter_relay_SUITE.erl +++ b/lib/diameter/test/diameter_relay_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -112,7 +112,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl index e7807fd360..ab7ac55008 100644 --- a/lib/diameter/test/diameter_stats_SUITE.erl +++ b/lib/diameter/test/diameter_stats_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -39,7 +39,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_sync_SUITE.erl b/lib/diameter/test/diameter_sync_SUITE.erl index ab629fb1c1..457efab8ae 100644 --- a/lib/diameter/test/diameter_sync_SUITE.erl +++ b/lib/diameter/test/diameter_sync_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -43,7 +43,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl index 85b953dc1a..6cc34b20c5 100644 --- a/lib/diameter/test/diameter_tls_SUITE.erl +++ b/lib/diameter/test/diameter_tls_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -127,7 +127,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start_ssl, diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 6eed8d3b5d..99b4fc7f63 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -159,7 +159,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, start_services, add_transports, result_codes] diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index c3f5cbf0e2..38073e9916 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -279,7 +279,7 @@ connect(Client, Prot, LRef, Opts) -> Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), ok = receive {diameter_event, Client, {up, Ref, _, _, _}} -> ok - after 2000 -> + after 10000 -> {Client, Prot, PortNr, process_info(self(), messages)} end, Ref. @@ -295,7 +295,7 @@ disconnect(Client, Ref, Server, LRef) -> ok = diameter:remove_transport(Client, Ref), ok = receive {diameter_event, Server, {down, LRef, _, _}} -> ok - after 2000 -> + after 10000 -> {Client, Ref, Server, LRef, process_info(self(), messages)} end. -- cgit v1.2.3 From 94af89e62b9c277b9d32c86e68deb40d8fe532f9 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 10:00:30 +0200 Subject: Exercise service_info in test suites --- lib/diameter/test/diameter_relay_SUITE.erl | 7 ++++++- lib/diameter/test/diameter_util.erl | 29 +++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl index 134239d9b6..f10d82bdf8 100644 --- a/lib/diameter/test/diameter_relay_SUITE.erl +++ b/lib/diameter/test/diameter_relay_SUITE.erl @@ -48,6 +48,7 @@ send_loop/1, send_timeout_1/1, send_timeout_2/1, + info/1, disconnect/1, stop_services/1, stop/1]). @@ -136,7 +137,8 @@ tc() -> send4, send_loop, send_timeout_1, - send_timeout_2]. + send_timeout_2, + info]. %% =========================================================================== %% start/stop testcases @@ -224,6 +226,9 @@ send_timeout(Tmo) -> {'Re-Auth-Request-Type', ?AUTHORIZE_ONLY}], call(Req, [{filter, realm}, {timeout, Tmo}]). +info(_Config) -> + [] = ?util:info(). + %% =========================================================================== realm(Host) -> diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index 38073e9916..890d24f6f8 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -35,7 +35,8 @@ lport/3, listen/2, listen/3, connect/3, connect/4, - disconnect/4]). + disconnect/4, + info/0]). %% common_test-specific -export([write_priv/3, @@ -262,7 +263,10 @@ listen(SvcName, Prot) -> listen(SvcName, Prot, []). listen(SvcName, Prot, Opts) -> - add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}). + SvcName = diameter:service_info(SvcName, name), %% assert + Ref = add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}), + true = transport(SvcName, Ref), %% assert + Ref. %% --------------------------------------------------------------------------- %% connect/2-3 @@ -275,8 +279,11 @@ connect(Client, Prot, LRef) -> connect(Client, Prot, LRef, Opts) -> [PortNr] = lport(Prot, LRef, 20), + Client = diameter:service_info(Client, name), %% assert true = diameter:subscribe(Client), Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), + true = transport(Client, Ref), %% assert + ok = receive {diameter_event, Client, {up, Ref, _, _, _}} -> ok after 10000 -> @@ -284,6 +291,10 @@ connect(Client, Prot, LRef, Opts) -> end, Ref. +transport(SvcName, Ref) -> + [Ref] == [R || [{ref, R} | _] <- diameter:service_info(SvcName, transport), + R == Ref]. + %% --------------------------------------------------------------------------- %% disconnect/4 %% @@ -320,3 +331,17 @@ opts(listen) -> []; opts(PortNr) -> [{raddr, ?ADDR}, {rport, PortNr}]. + +%% --------------------------------------------------------------------------- +%% info/0 + +info() -> + [_|_] = Svcs = diameter:services(), %% assert + run([[fun info/1, S] || S <- Svcs]). + +info(S) -> + [_|_] = Keys = diameter:service_info(S, keys), + [] = run([[fun info/2, K, S] || K <- Keys]). + +info(Key, SvcName) -> + [{Key, _}] = diameter:service_info(SvcName, [Key]). -- cgit v1.2.3 From 26721653adec1e9d4a1032e766f8243085687bf3 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 24 Aug 2012 23:59:19 +0200 Subject: Maintain watchdog states in service_info --- lib/diameter/src/base/diameter_service.erl | 89 ++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 16 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index c9c3179630..0bba8117a5 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -65,9 +65,27 @@ -include_lib("diameter/include/diameter.hrl"). -include("diameter_internal.hrl"). +%% The "old" states maintained in this module historically. -define(STATE_UP, up). -define(STATE_DOWN, down). +-type op_state() :: ?STATE_UP + | ?STATE_DOWN. + +%% The RFC 3539 watchdog states that are now maintained, albeit +%% along with the old up/down. okay = up, else down. +-define(WD_INITIAL, initial). +-define(WD_OKAY, okay). +-define(WD_SUSPECT, suspect). +-define(WD_DOWN, down). +-define(WD_REOPEN, reopen). + +-type wd_state() :: ?WD_INITIAL + | ?WD_OKAY + | ?WD_SUSPECT + | ?WD_DOWN + | ?WD_REOPEN. + -define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1 -define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests -define(RESTART_TC, 1000). %% if restart was this recent @@ -117,7 +135,8 @@ type :: match(connect | accept), ref :: match(reference()), %% key into diameter_config options :: match([diameter:transport_opt()]),%% from start_transport - op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP), + op_state = {?STATE_DOWN, ?WD_INITIAL} + :: match(op_state() | {op_state(), wd_state()}), started = now(), %% at process start conn = false :: match(boolean() | pid())}). %% true at accept, pid() at connection_up (connT key) @@ -516,13 +535,33 @@ transition({reconnect, Pid}, S) -> reconnect(Pid, S), ok; -%% Watchdog is sending notification of a state transition. +%% Watchdog is sending notification of a state transition. Note that +%% the connection_up/down messages are pre-date this message and are +%% still used. A 'watchdog' message will follow these and communicate +%% the same state as was set in handling connection_up/down. transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName, peerT = PeerT}) -> - #peer{ref = Ref, type = T, options = Opts} + #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}} + = P = fetch(PeerT, Pid), + insert(PeerT, P#peer{op_state = {OS, To}}), send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}), ok; +%% Death of a peer process results in the removal of it's peer and any +%% associated conn record when 'DOWN' is received (after this) but the +%% states will be {?STATE_UP, ?WD_DOWN} for a short time. (No real +%% problem since ?WD_* is only used in service_info.) We set ?WD_OKAY +%% as a consequence of connection_up since we know a watchdog is +%% coming. We can't set anything at connection_down since we don't +%% know if the subsequent watchdog message will be ?WD_DOWN or +%% ?WD_SUSPECT. We don't (yet) set ?STATE_* as a consequence of a +%% watchdog message since this requires changing some of the matching +%% on ?STATE_*. +%% +%% Death of a conn process results in connection_down followed by +%% watchdog ?WD_DOWN. The latter doesn't result in the conn record +%% being deleted since 'DOWN' from death of its peer doesn't (yet) +%% deal with the record having been removed. %% Monitor process has died. Just die with a reason that tells %% diameter_config about the happening. If a cleaner shutdown is @@ -887,7 +926,14 @@ accepted(Pid, _TPid, #state{peerT = PeerT} = S) -> fetch(Tid, Key) -> [T] = ets:lookup(Tid, Key), - T. + case T of + #peer{op_state = ?STATE_UP} = P -> + P#peer{op_state = {?STATE_UP, ?WD_OKAY}}; + #peer{op_state = ?STATE_DOWN} = P -> + P#peer{op_state = {?STATE_DOWN, ?WD_DOWN}}; + _ -> + T + end. %%% --------------------------------------------------------------------------- %%% # connection_up/3 @@ -933,12 +979,12 @@ connection_up(T, P, C, #state{peerT = PeerT, service = #diameter_service{applications = Apps}} = S) -> - #peer{conn = TPid, op_state = ?STATE_DOWN} + #peer{conn = TPid, op_state = {?STATE_DOWN, _}} = P, #conn{apps = SApps, caps = Caps} = C, - insert(PeerT, P#peer{op_state = ?STATE_UP}), + insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}), request_peer_up(TPid), report_status(up, P, C, S, T), @@ -987,22 +1033,22 @@ peer_cb(MFA, Alias) -> connection_down(Pid, #state{peerT = PeerT, connT = ConnT} = S) -> - #peer{op_state = ?STATE_UP, %% assert + #peer{op_state = {?STATE_UP, WS}, %% assert conn = TPid} = P = fetch(PeerT, Pid), C = fetch(ConnT, TPid), - insert(PeerT, P#peer{op_state = ?STATE_DOWN}), + insert(PeerT, P#peer{op_state = {?STATE_DOWN, WS}}), connection_down(P,C,S). %% connection_down/3 -connection_down(#peer{op_state = ?STATE_DOWN}, _, S) -> +connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, S) -> S; connection_down(#peer{conn = TPid, - op_state = ?STATE_UP} + op_state = {?STATE_UP, _}} = P, #conn{caps = Caps, apps = SApps} @@ -1051,7 +1097,7 @@ peer_down(Pid, Reason, #state{peerT = PeerT} = S) -> %% Send an event at connection establishment failure. closed({shutdown, {close, _TPid, Reason}}, - #peer{op_state = ?STATE_DOWN, + #peer{op_state = {?STATE_DOWN, _}, ref = Ref, type = Type, options = Opts}, @@ -2858,15 +2904,26 @@ it_acc(ConnT, Acc, #peer{pid = Pid, op_state = OS, started = T, conn = TPid}) -> + WS = wd_state(OS), dict:append(Ref, [{type, Type}, {options, Opts}, - {watchdog, {Pid, T, OS}} - | info_conn(ConnT, TPid)], + {watchdog, {Pid, T, WS}} + | info_conn(ConnT, TPid, WS /= ?WD_DOWN)], Acc). -info_conn(ConnT, TPid) -> - info_conn(ets:lookup(ConnT, TPid)). +info_conn(ConnT, TPid, true) + when is_pid(TPid) -> + info_conn(ets:lookup(ConnT, TPid)); +info_conn(_, _, _) -> + []. + +wd_state({_,S}) -> + S; +wd_state(?STATE_UP) -> + ?WD_OKAY; +wd_state(?STATE_DOWN) -> + ?WD_DOWN. info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) -> [{peer, {Pid, T}}, -- cgit v1.2.3 From 2c6efd882cb748abbd7f4bea696d8d6211ea5ffa Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 25 Aug 2012 18:12:49 +0200 Subject: Add 'connections' and 'peers' service_info These provide alternates to 'transport' that group information, and present statistics, per transport established transport connection and peer Origin-Host instead of per reference returned by diameter:add_transport/2. --- lib/diameter/src/base/diameter_service.erl | 148 +++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 28 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 0bba8117a5..ad5a853a0d 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -2800,11 +2800,17 @@ transports(#state{peerT = PeerT}) -> 'Vendor-Specific-Application-Id', 'Firmware-Revision']). +%% The config returned by diameter:service_info(SvcName, all). -define(ALL_INFO, [capabilities, applications, transport, - pending, - statistics]). + pending]). + +%% The rest. +-define(OTHER_INFO, [connections, + name, + peers, + statistics]). service_info(Items, S) when is_list(Items) -> @@ -2857,22 +2863,26 @@ service_info(Item, #state{service = Svc} = S, Complete) -> applications -> info_apps(S); transport -> info_transport(S); pending -> info_pending(S); - statistics -> info_stats(S); - keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test + keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO; all -> service_info(?ALL_INFO, S); + statistics -> info_stats(S); + connections -> info_connections(S); + peers -> info_peers(S); _ when Complete -> service_info(complete(Item), S, false); _ -> undefined end. complete(Pre) -> P = atom_to_list(Pre), - case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO, + case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO, lists:prefix(P, atom_to_list(I))] of [I] -> I; _ -> Pre end. +%% info_stats/1 + info_stats(#state{peerT = PeerT}) -> Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'}, [{'is_pid', '$2'}], @@ -2880,30 +2890,47 @@ info_stats(#state{peerT = PeerT}) -> diameter_stats:read(lists:append(Peers)). %% TODO: include peer identities in return value -info_transport(#state{peerT = PeerT, connT = ConnT}) -> - dict:fold(fun it/3, +%% info_transport/1 +%% +%% One entry per configured transport. Statistics for each entry are +%% the accumulated values for the ref and associated peer pids. + +info_transport(S) -> + PeerD = peer_dict(S), + RefsD = dict:map(fun(_, Ls) -> [P || L <- Ls, {peer, {P,_}} <- L] end, + PeerD), + Refs = lists:append(dict:fold(fun(R, Ps, A) -> [[R|Ps] | A] end, + [], + RefsD)), + Stats = diameter_stats:read(Refs), + dict:fold(fun(R, Ls, A) -> + Ps = dict:fetch(R, RefsD), + [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)] + | A] + end, [], - ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end, - dict:new(), - PeerT)). - -it(Ref, [[{type, connect} | _] = L], Acc) -> - [[{ref, Ref} | L] | Acc]; -it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) -> - [[{ref, Ref}, - {type, listen}, - {options, Opts}, - {accept, [lists:nthtail(2,A) || A <- L]}] - | Acc]. -%% Each entry has the same Opts. (TODO) - -it_acc(ConnT, Acc, #peer{pid = Pid, - type = Type, - ref = Ref, - options = Opts, - op_state = OS, - started = T, - conn = TPid}) -> + PeerD). + +transport([[{type, connect} | _] = L]) -> + L; + +transport([[{type, accept}, {options, Opts} | _] | _] = Ls) -> + [{type, listen}, + {options, Opts}, + {accept, [lists:nthtail(2,L) || L <- Ls]}]. +%% Note that all peer records for a listening transport (ie. same Ref) +%% have the same options. (TODO) + +peer_dict(#state{peerT = PeerT, connT = ConnT}) -> + ets:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, dict:new(), PeerT). + +peer_acc(ConnT, Acc, #peer{pid = Pid, + type = Type, + ref = Ref, + options = Opts, + op_state = OS, + started = T, + conn = TPid}) -> WS = wd_state(OS), dict:append(Ref, [{type, Type}, @@ -2932,6 +2959,11 @@ info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) -> info_conn([] = No) -> No. +%% Use the fields names from diameter_caps instead of +%% diameter_base_CER to distinguish between the 2-tuple values +%% compared to the single capabilities values. Note also that the +%% returned list is tagged 'caps' rather than 'capabilities' to +%% emphasize the difference. info_caps(#diameter_caps{} = C) -> lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))). @@ -2947,6 +2979,10 @@ mk_app(#diameter_app{alias = Alias, {module, ModX}, {id, Id}]. +%% info_pending/1 +%% +%% One entry for each outgoing request whose answer is outstanding. + info_pending(#state{} = S) -> MatchSpec = [{{'$1', #request{transport = '$2', @@ -2960,3 +2996,59 @@ info_pending(#state{} = S) -> {{from, '$3'}}]}}]}], ets:select(?REQUEST_TABLE, MatchSpec). + +%% info_connections/1 +%% +%% One entry per transport connection. Statistics for each entry are +%% for the peer pid only. + +info_connections(S) -> + ConnL = conn_list(S), + Stats = diameter_stats:read([P || L <- ConnL, {peer, {P,_}} <- L]), + [L ++ [stats([P], Stats)] || L <- ConnL, {peer, {P,_}} <- L]. + +conn_list(S) -> + lists:append(dict:fold(fun conn_acc/3, [], peer_dict(S))). + +conn_acc(Ref, Peers, Acc) -> + [[[{ref, Ref} | L] || L <- Peers, lists:keymember(peer, 1, L)] + | Acc]. + +stats(Refs, Stats) -> + {statistics, dict:to_list(lists:foldl(fun(R,D) -> + stats_acc(R, D, Stats) + end, + dict:new(), + Refs))}. + +stats_acc(Ref, Dict, Stats) -> + lists:foldl(fun({C,N}, D) -> dict:update_counter(C, N, D) end, + Dict, + proplists:get_value(Ref, Stats, [])). + +%% info_peers/1 +%% +%% One entry per peer Origin-Host. Statistics for each entry are +%% accumulated values for all associated transport refs and peer pids. + +info_peers(S) -> + ConnL = conn_list(S), + {PeerD, RefD} = lists:foldl(fun peer_acc/2, + {dict:new(), dict:new()}, + ConnL), + Refs = lists:append(dict:fold(fun(_, Rs, A) -> [lists:append(Rs) | A] end, + [], + RefD)), + Stats = diameter_stats:read(Refs), + dict:fold(fun(OH, Cs, A) -> + Rs = lists:append(dict:fetch(OH, RefD)), + [{OH, [{connections, Cs}, stats(Rs, Stats)]} + | A] + end, + [], + PeerD). + +peer_acc(Peer, {PeerD, RefD}) -> + [Ref, {TPid, _}, [{origin_host, {_, OH}} | _]] + = [proplists:get_value(K, Peer) || K <- [ref, peer, caps]], + {dict:append(OH, Peer, PeerD), dict:append(OH, [Ref, TPid], RefD)}. -- cgit v1.2.3 From 158d95e6b8575be8983ae6024ffa85c639ecdfda Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 02:55:41 +0200 Subject: Make service_info behave with nested item lists and non-atoms --- lib/diameter/src/base/diameter_service.erl | 41 ++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index ad5a853a0d..6aff03ec1d 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -2812,14 +2812,33 @@ transports(#state{peerT = PeerT}) -> peers, statistics]). -service_info(Items, S) - when is_list(Items) -> - [{complete(I), service_info(I,S)} || I <- Items]; service_info(Item, S) when is_atom(Item) -> - service_info(Item, S, true). + case tagged_info(Item, S) of + {_, T} -> T; + undefined = No -> No + end; + +service_info(Items, S) -> + tagged_info(Items, S). + +tagged_info(Item, S) + when is_atom(Item) -> + case complete(Item) of + {value, I} -> + {I, complete_info(I,S)}; + false -> + undefined + end; + +tagged_info(Items, S) + when is_list(Items) -> + [T || I <- Items, T <- [tagged_info(I,S)], T /= undefined, T /= []]; + +tagged_info(_, _) -> + undefined. -service_info(Item, #state{service = Svc} = S, Complete) -> +complete_info(Item, #state{service = Svc} = S) -> case Item of name -> S#state.service_name; @@ -2867,18 +2886,20 @@ service_info(Item, #state{service = Svc} = S, Complete) -> all -> service_info(?ALL_INFO, S); statistics -> info_stats(S); connections -> info_connections(S); - peers -> info_peers(S); - _ when Complete -> service_info(complete(Item), S, false); - _ -> undefined + peers -> info_peers(S) end. +complete(I) + when I == keys; + I == all -> + {value, I}; complete(Pre) -> P = atom_to_list(Pre), case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO, lists:prefix(P, atom_to_list(I))] of - [I] -> I; - _ -> Pre + [I] -> {value, I}; + _ -> false end. %% info_stats/1 -- cgit v1.2.3 From f430c8c706de300122d10c64b316d18917e176b3 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 8 Jul 2012 21:04:25 +0200 Subject: Turn last field of #diameter_app{} into an options list To make for easier adding of future options. The record is only passed into transport modules so the only compatibility issue is with these. (No issue for diameter_{tcp,sctp} and unlikely but theoretically possible for any other implementations, which probably don't exist at this point.) --- lib/diameter/include/diameter.hrl | 3 +-- lib/diameter/src/base/diameter_config.erl | 2 +- lib/diameter/src/base/diameter_service.erl | 9 +++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl index 4273262015..47f9d1240f 100644 --- a/lib/diameter/include/diameter.hrl +++ b/lib/diameter/include/diameter.hrl @@ -139,7 +139,6 @@ init_state, %% option 'state', initial callback state id, %% 32-bit unsigned application identifier = Dict:id() mutable = false, %% boolean(), do traffic callbacks modify state? - answer_errors = report}). %% | callback | discard - %% how to handle containing errors? + options = [{answer_errors, report}]}). %% | callback | discard -endif. %% -ifdef(diameter_hrl). diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 9253af0de2..2095e926c3 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -600,7 +600,7 @@ app_acc({application, Opts}, Acc) -> module = init_mod(Mod), init_state = ModS, mutable = init_mutable(M), - answer_errors = init_answers(A)} + options = [{answer_errors, init_answers(A)}]} | Acc]; app_acc(_, Acc) -> Acc. diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3dfdcee2b2..c7b216c6f7 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -953,7 +953,12 @@ init_conn(Id, Alias, TC, {SvcName, Apps}) -> peer_cb({ModX, peer_up, [SvcName, TC]}, Alias). find_app(Alias, Apps) -> - lists:keyfind(Alias, #diameter_app.alias, Apps). + case lists:keyfind(Alias, #diameter_app.alias, Apps) of + #diameter_app{options = E} = A when is_atom(E) -> %% upgrade + A#diameter_app{options = [{answer_errors, E}]}; + A -> + A + end. %% A failing peer callback brings down the service. In the case of %% peer_up we could just kill the transport and emit an error but for @@ -1352,7 +1357,7 @@ send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) -> #diameter_app{alias = Alias, dictionary = Dict, module = ModX, - answer_errors = AE} + options = [{answer_errors, AE} | _]} = App, EPkt = encode(Dict, Pkt), -- cgit v1.2.3 From 4b6328306abd9fd55c1d83169350c0fbd35a8e3b Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 11 Jul 2012 02:19:25 +0200 Subject: Don't let peer_up/peer_down take down the service process This would previously have resulted in all of a service's connections going down, especially bad for a server. --- lib/diameter/src/base/diameter_service.erl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index c7b216c6f7..77f7c6dd8e 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -945,12 +945,15 @@ ilp({Id, Alias}, {TC, SA}, LDict) -> init_conn(Id, Alias, TC, SA), ?Dict:append(Alias, TC, LDict). -init_conn(Id, Alias, TC, {SvcName, Apps}) -> +init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) -> #diameter_app{module = ModX, id = Id} %% assert = find_app(Alias, Apps), - peer_cb({ModX, peer_up, [SvcName, TC]}, Alias). + peer_cb({ModX, peer_up, [SvcName, TC]}, Alias) + orelse exit(TPid, kill). %% fake transport failure + +%% find_app/2 find_app(Alias, Apps) -> case lists:keyfind(Alias, #diameter_app.alias, Apps) of @@ -960,17 +963,17 @@ find_app(Alias, Apps) -> A end. -%% A failing peer callback brings down the service. In the case of -%% peer_up we could just kill the transport and emit an error but for -%% peer_down we have no way to cleanup any state change that peer_up -%% may have introduced. +%% Don't bring down the service (and all associated connections) +%% regardless of what happens. peer_cb(MFA, Alias) -> try state_cb(MFA, Alias) of ModS -> - mod_state(Alias, ModS) + mod_state(Alias, ModS), + true catch - E: Reason -> - ?ERROR({E, Reason, MFA, ?STACK}) + E:R -> + diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA), + false end. %%% --------------------------------------------------------------------------- -- cgit v1.2.3 From 9a671bf0c1b0000f6fd8f35f44eb25348e0b415d Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 12 Oct 2011 15:42:31 +0200 Subject: Use gen_sctp:peeloff/2 to transfer association ownership The transport process is now controlling process even in the accept case. --- lib/diameter/src/transport/diameter_sctp.erl | 88 ++++++++++++++++------------ 1 file changed, 52 insertions(+), 36 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 68b0342cd5..e0cb30e22a 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -62,6 +62,7 @@ -record(transport, {parent :: pid(), mode :: {accept, pid()} + | accept | {connect, {list(inet:ip_address()), uint(), list()}} %% {RAs, RP, Errors} | connect, @@ -324,6 +325,11 @@ code_change(_, State, _) -> terminate(_, #transport{assoc_id = undefined}) -> ok; +terminate(_, #transport{socket = Sock, + mode = accept, + assoc_id = Id}) -> + close(Sock, Id); + terminate(_, #transport{socket = Sock, mode = {accept, _}, assoc_id = Id}) -> @@ -356,13 +362,16 @@ start_timer(S) -> %% Incoming message from SCTP. l({sctp, Sock, _RA, _RP, Data} = Msg, #listener{socket = Sock} = S) -> - setopts(Sock), - case find(Data, S) of + Id = assoc_id(Data), + + try find(Id, Data, S) of {TPid, NewS} -> - TPid ! Msg, + TPid ! {peeloff, peeloff(Sock, Id, TPid), Msg}, NewS; false -> S + after + setopts(Sock) end; %% Transport is asking message to be sent. See send/3 for why the send @@ -430,15 +439,19 @@ t(T,S) -> %% transition/2 -%% Incoming message. -transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock, - mode = {accept, _}} - = S) -> - recv(Data, S); +%% Listening process is transfering ownership of an association. +transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg}, + #transport{mode = {accept, _}, + socket = LSock} + = S) -> + transition(Msg, S#transport{socket = Sock}); -transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) -> +%% Incoming message. +transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) -> setopts(Sock), recv(Data, S); +%% Don't match on Sock since in R15B01 it can be the listening socket +%% in the (peeled-off) accept case, which is likely a bug. %% Outgoing message. transition({diameter, {send, Msg}}, S) -> @@ -456,13 +469,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) -> transition({diameter, {tls, _Ref, _Type, _Bool}}, _) -> stop; +%% Parent process has died. +transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) -> + stop; + %% Listener process has died. transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) -> stop; -%% Parent process has died. -transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) -> - stop; +%% Ditto but we have ownership of the association. It might be that +%% we'll go down anyway though. +transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) -> + ok; %% Request for the local port number. transition({resolve_port, Pid}, #transport{socket = Sock}) @@ -521,14 +539,6 @@ send(Bin, #transport{streams = {_, OS}, %% send/3 -%% Messages have to be sent from the controlling process, which is -%% probably a bug. Sending from here causes an inet_reply, Sock, -%% Status} message to be sent to the controlling process while -%% gen_sctp:send/4 here hangs. -send(StreamId, Bin, #transport{assoc_id = AId, - mode = {accept, LPid}}) -> - LPid ! {send, AId, StreamId, Bin}; - send(StreamId, Bin, #transport{socket = Sock, assoc_id = AId}) -> send(Sock, AId, StreamId, Bin). @@ -608,21 +618,15 @@ up(#transport{parent = Pid, S#transport{mode = C}; up(#transport{parent = Pid, - mode = {accept, _}} + mode = {accept = A, _}} = S) -> diameter_peer:up(Pid), - S. + S#transport{mode = A}. -%% find/2 +%% find/3 -find({[#sctp_sndrcvinfo{assoc_id = Id}], _} - = Data, - #listener{tmap = T} - = S) -> - f(ets:lookup(T, Id), Data, S); - -find({_, Rec} = Data, #listener{tmap = T} = S) -> - f(ets:lookup(T, assoc_id(Rec)), Data, S). +find(Id, Data, #listener{tmap = T} = S) -> + f(ets:lookup(T, Id), Data, S). %% New association and a transport waiting for one: use it. f([], @@ -663,17 +667,29 @@ f([], _, _) -> %% assoc_id/1 -assoc_id(#sctp_shutdown_event{assoc_id = Id}) -> +assoc_id({[#sctp_sndrcvinfo{assoc_id = Id}], _}) -> + Id; +assoc_id({_, Rec}) -> + id(Rec). + +id(#sctp_shutdown_event{assoc_id = Id}) -> Id; -assoc_id(#sctp_assoc_change{assoc_id = Id}) -> +id(#sctp_assoc_change{assoc_id = Id}) -> Id; -assoc_id(#sctp_sndrcvinfo{assoc_id = Id}) -> +id(#sctp_sndrcvinfo{assoc_id = Id}) -> Id; -assoc_id(#sctp_paddr_change{assoc_id = Id}) -> +id(#sctp_paddr_change{assoc_id = Id}) -> Id; -assoc_id(#sctp_adaptation_event{assoc_id = Id}) -> +id(#sctp_adaptation_event{assoc_id = Id}) -> Id. +%% peeloff/3 + +peeloff(LSock, Id, TPid) -> + {ok, Sock} = gen_sctp:peeloff(LSock, Id), + ok = gen_sctp:controlling_process(Sock, TPid), + Sock. + %% connect/4 connect(_, [], _, Reasons) -> -- cgit v1.2.3 From c8cf385ec5d8b8e451aac064590860079b334f79 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 23 Aug 2012 14:45:42 +0200 Subject: Add any target to test/Makefile To run all test suites but without stopping if one fails (like all). --- lib/diameter/test/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile index ab5b45ff3d..5203ac4854 100644 --- a/lib/diameter/test/Makefile +++ b/lib/diameter/test/Makefile @@ -67,8 +67,13 @@ ERL_COMPILE_FLAGS += +warn_export_vars \ # Targets # ---------------------------------------------------- +# Require success ... all: opt +# ... or not. +any: opt + $(MAKE) -i $(SUITES) + run: $(SUITES) debug opt: $(TARGET_FILES) @@ -113,7 +118,7 @@ help: @echo " Echo some relevant variables." @echo ======================================== -.PHONY: all run clean debug docs help info opt realclean +.PHONY: all any run clean debug docs help info opt realclean # ---------------------------------------------------- # Special Targets -- cgit v1.2.3 From 872db12d02ee0d9954ca52cad3fe2dcc2344fb21 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 11:37:25 +0200 Subject: Add realclean target to src/Makefile To clean everything out of ebin since $(TARGET_FILES) isn't constant. --- lib/diameter/src/Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index dbfaa4e140..6a5cb1a106 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2011. All Rights Reserved. +# Copyright Ericsson AB 2010-2012. 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 @@ -181,6 +181,10 @@ clean: rm -f $(TARGET_FILES) gen/* rm -f depend.mk +realclean: clean + rm -f ../ebin/* +# Not $(EBIN) just to be a bit paranoid + # ---------------------------------------------------- # Release targets # ---------------------------------------------------- @@ -245,7 +249,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile -include depend.mk -.PHONY: app clean depend dict info release_subdir +.PHONY: app clean realclean depend dict info release_subdir .PHONY: debug opt release_docs_spec release_spec .PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%) .PHONY: $(EXAMPLE_DIRS:%/=release_examples_%) -- cgit v1.2.3 From 92813e780248755e720eda0749aa6232bae8724e Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 21:32:11 +0200 Subject: Minor spec fix --- lib/diameter/src/base/diameter_watchdog.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index b615bed860..d7474e5c56 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -64,13 +64,12 @@ %% that a failed capabilities exchange produces the desired exit %% reason. --spec start(Type, {RecvData, Opts, SvcName, Svc}) - -> pid() - when Type :: {connect|accept, reference()}, +-spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}}) + -> {reference(), pid()} + when Type :: {connect|accept, diameter:transport_ref()}, RecvData :: term(), - Opts :: list(), - SvcName :: term(), - Svc :: #diameter_service{}. + Opt :: diameter:transport_opt(), + SvcName :: diameter:service_name(). start({_,_} = Type, T) -> Ref = make_ref(), -- cgit v1.2.3 From aa62ff77cee327bedb42a8d43320c0c40c99f3d3 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 21:18:47 +0200 Subject: Minor spec and backwards compatibility fix --- lib/diameter/src/base/diameter_peer.erl | 31 +++++++++++++++++++++++------ lib/diameter/src/base/diameter_peer_fsm.erl | 7 ++++++- 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl index bfd59bee8e..a2a1c567d8 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.erl @@ -33,6 +33,9 @@ abort/1, notify/2]). +%% Old interface only called from old code. +-export([start/3]). %% < diameter-1.2 (R15B02) + %% Server start. -export([start_link/0]). @@ -69,10 +72,31 @@ notify(SvcName, T) -> rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}). +%%% --------------------------------------------------------------------------- +%%% # start/3 +%%% --------------------------------------------------------------------------- + +%% From old code: make is restart. +start(_T, _Opts, #diameter_service{}) -> + {error, restart}. + %%% --------------------------------------------------------------------------- %%% # start/1 %%% --------------------------------------------------------------------------- +-spec start({T, [Opt], #diameter_service{}}) + -> {TPid, [Addr], Tmo, Data} + | {error, [term()]} + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(), + TPid :: pid(), + Addr :: inet:ip_address(), + Tmo :: non_neg_integer(), + Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]}, + Mod :: module(), + Cfg :: term(), + Err :: term(). + %% Initial start. start({T, Opts, #diameter_service{} = Svc}) -> start(T, Svc, pair(Opts, [], []), []); @@ -132,13 +156,8 @@ def(Acc) -> start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) -> start(T, Ms, Cfg, Svc, Tmo, Rest, Errs); -%% One transport: return the bare error for backwards compatibility. -start(_, _, [], [Err]) -> - {Err}; - -%% Or not: return list of errors. start(_, _, [], Errs) -> - {{error, Errs}}. + {error, Errs}. %% start/7 diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index d5e2d690cf..638d905195 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -121,6 +121,11 @@ %%% Output: Pid %%% --------------------------------------------------------------------------- +-spec start(T, [Opt], #diameter_service{}) + -> pid() + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(). + %% diameter_config requires a non-empty list of applications on the %% service but diameter_service then constrains the list to any %% specified on the transport in question. Check here that the list is @@ -179,7 +184,7 @@ start_transport(Addrs0, T) -> erlang:monitor(process, TPid), q_next(TPid, Addrs0, Tmo, Data), {TPid, addrs(Addrs, Addrs0)}; - {No} -> + No -> exit({shutdown, No}) end. -- cgit v1.2.3 From ce2b8dfdfdf2cd67884a59a44bbd834bc7c4d872 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 22:14:47 +0200 Subject: Add plt/dialyze targets to src/Makefile --- lib/diameter/src/.gitignore | 1 + lib/diameter/src/Makefile | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'lib/diameter') diff --git a/lib/diameter/src/.gitignore b/lib/diameter/src/.gitignore index feeb378fd8..cc06720fd1 100644 --- a/lib/diameter/src/.gitignore +++ b/lib/diameter/src/.gitignore @@ -1,2 +1,3 @@ /depend.mk +/otp.plt diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 6a5cb1a106..26f5ae480e 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -185,6 +185,27 @@ realclean: clean rm -f ../ebin/* # Not $(EBIN) just to be a bit paranoid +PLT = ./otp.plt + +plt: + dialyzer --build_plt \ + --apps erts stdlib kernel \ + xmerl ssl public_key crypto \ + compiler syntax_tools runtime_tools \ + --output_plt $(PLT) \ + --verbose + +dialyze: opt $(PLT) + dialyzer --plt $(PLT) \ + --verbose \ + -Wno_improper_lists \ + $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \ + $(patsubst %, $(EBIN)/%.$(EMULATOR), \ + $(notdir $(RT_MODULES) $(CT_MODULES))) +# Omit all but the common dictionary module since these +# (diameter_gen_relay in particular) generate warning depending on how +# much of the included diameter_gen.hrl they use. + # ---------------------------------------------------- # Release targets # ---------------------------------------------------- @@ -253,6 +274,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile .PHONY: debug opt release_docs_spec release_spec .PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%) .PHONY: $(EXAMPLE_DIRS:%/=release_examples_%) +.PHONY: plt dialyze # Keep intermediate files. .SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl) -- cgit v1.2.3 From fef602372214f864ab1d685ee6053d36d9dcc605 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Aug 2012 21:34:20 +0200 Subject: Update appup --- lib/diameter/src/base/diameter.appup.src | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src index 2ebdad598f..9b2a7d18ab 100644 --- a/lib/diameter/src/base/diameter.appup.src +++ b/lib/diameter/src/base/diameter.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -22,13 +22,28 @@ [ {"0.9", [{restart_application, diameter}]}, {"0.10", [{restart_application, diameter}]}, - {"1.0", [{update, diameter_service}, - {update, diameter_watchdog}]} + {"1.0", [{restart_application, diameter}]}, + {"1.1", [%% new code + {add_module, diameter_transport}, + %% modified code + {load, diameter_sctp}, + {load, diameter_stats}, + {load, diameter_service}, + {load, diameter_config}, + {load, diameter_codec}, + {load, diameter_watchdog}, + {load, diameter_peer}, + {load, diameter_peer_fsm}, + {load, diameter}, + %% unmodified but including modified diameter.hrl + {load, diameter_callback}, + {load, diameter_capx}, + {load, diameter_types}]} ], [ {"0.9", [{restart_application, diameter}]}, {"0.10", [{restart_application, diameter}]}, - {"1.0", [{update, diameter_watchdog}, - {update, diameter_service}]} + {"1.0", [{restart_application, diameter}]}, + {"1.1", [{restart_application, diameter}]} ] }. -- cgit v1.2.3 From 12febf1392d53cefe072860fdc60df85809ea90d Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 27 Aug 2012 09:11:44 +0200 Subject: Increase buffer sizes in gen_sctp suite This improves the situation with long turnaround times but doesn't completely solve the problem. --- lib/diameter/test/diameter_gen_sctp_SUITE.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl index 7f435a6b7a..5e65b84b56 100644 --- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl +++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl @@ -341,8 +341,15 @@ receive_what_was_sent(_Config) -> %% open/0 open() -> - gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary]). + open([]). +%% open/1 + +open(Opts) -> + gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary, + {recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16} + | Opts]). + %% assoc/1 assoc(Sock) -> -- cgit v1.2.3 From a918d79217b65a638ff5f5e4bf88e0a18af3c691 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 27 Aug 2012 09:36:51 +0200 Subject: Maintain pid of started transport process in process dictionary --- lib/diameter/src/base/diameter_peer_fsm.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 638d905195..bbda62c32b 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -210,11 +210,14 @@ q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) -> send_after(Tmo, {connection_timeout, TPid}), putr(?Q_KEY, {Addrs0, Tmo, Data}). -%% Connection has been established: retain the started module/config -%% in the process dictionary. -keep_transport() -> +%% Connection has been established: retain the started +%% pid/module/config in the process dictionary. This is a part of the +%% interface defined by this module, so that the transport pid can be +%% found when constructing service_info (in order to extract further +%% information from it). +keep_transport(TPid) -> {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY), - putr(?START_KEY, T). + putr(?START_KEY, {TPid, T}). send_after(infinity, _) -> ok; @@ -291,7 +294,7 @@ transition({diameter, {TPid, connected, Remote}}, = S) -> 'Wait-Conn-Ack' = PS, %% assert connect = M, %% - keep_transport(), + keep_transport(TPid), send_CER(S#state{mode = {M, Remote}}); %% Connection from peer. @@ -303,7 +306,7 @@ transition({diameter, {TPid, connected}}, = S) -> 'Wait-Conn-Ack' = PS, %% assert accept = M, %% - keep_transport(), + keep_transport(TPid), Pid ! {accepted, self()}, start_timer(S#state{state = recv_CER}); -- cgit v1.2.3 From 48d846f8ec8120e6351c10681a46e874a0546918 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 27 Aug 2012 12:53:24 +0200 Subject: Include transport-specific service info --- lib/diameter/src/base/diameter_service.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 6aff03ec1d..f586cef11a 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -2976,10 +2976,21 @@ wd_state(?STATE_DOWN) -> info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) -> [{peer, {Pid, T}}, {apps, SApps}, - {caps, info_caps(Caps)}]; + {caps, info_caps(Caps)} + | try [{port, info_port(Pid)}] catch _:_ -> [] end]; info_conn([] = No) -> No. +%% Extract information that the processes involved are expected to +%% "publish" in their process dictionaries. Simple but backhanded. +info_port(Pid) -> + {_, PD} = process_info(Pid, dictionary), + {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD), + {TPid, {_Type, TMod, _Cfg}} = T, + {_, TD} = process_info(TPid, dictionary), + {_, Data} = lists:keyfind({TMod, info}, 1, TD), + [{owner, TPid}, {module, TMod} | [_|_] = TMod:info(Data)]. + %% Use the fields names from diameter_caps instead of %% diameter_base_CER to distinguish between the 2-tuple values %% compared to the single capabilities values. Note also that the -- cgit v1.2.3 From a907e66c93e54ffd9b4c8c84f500cd267a28ddd8 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 27 Aug 2012 10:26:08 +0200 Subject: Maintain service_info callback data in process dictionary To be used by diameter_service in constructing service_info. --- lib/diameter/src/transport/diameter_etcp.erl | 22 ++++++-- lib/diameter/src/transport/diameter_sctp.erl | 41 ++++++++++++--- lib/diameter/src/transport/diameter_tcp.erl | 77 ++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 22 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl index d925d62545..cd62cf34fa 100644 --- a/lib/diameter/src/transport/diameter_etcp.erl +++ b/lib/diameter/src/transport/diameter_etcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -36,7 +36,9 @@ send/2, close/1, setopts/2, - port/1]). + sockname/1, + peername/1, + getstat/1]). %% child start -export([start_link/1]). @@ -113,10 +115,20 @@ close(Pid) -> setopts(_, _) -> ok. -%% port/1 +%% sockname/1 -port(_) -> - 3868. %% We have no local port: fake it. +sockname(_) -> + {error, ?MODULE}. + +%% peername/1 + +peername(_) -> + {error, ?MODULE}. + +%% getstat/1 + +getstat(_) -> + {error, ?MODULE}. %% start_link/1 diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 68b0342cd5..9a65834647 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -37,12 +37,18 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("kernel/include/inet_sctp.hrl"). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). %% The default port for a listener. @@ -133,6 +139,24 @@ start_link(T) -> infinity, diameter_lib:spawn_opts(server, [])). +%% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({gen_sctp, Sock}) -> + lists:flatmap(fun(K) -> info(K, Sock) end, + [{socket, sockname}, + {peer, peername}, + {statistics, getstat}]). + +info({K,F}, Sock) -> + case inet:F(Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + %% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -157,7 +181,7 @@ i({connect, Pid, Opts, Addrs, Ref}) -> RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As], [RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps], {LAs, Sock} = open(Addrs, Rest, 0), - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self(), LAs}), erlang:monitor(process, Pid), #transport{parent = Pid, @@ -169,7 +193,7 @@ i({connect, _, _, _} = T) -> %% from old code %% An accepting transport spawned by diameter. i({accept, Pid, LPid, Sock, Ref}) when is_pid(Pid) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), erlang:monitor(process, Pid), erlang:monitor(process, LPid), @@ -181,7 +205,7 @@ i({accept, _, _, _} = T) -> %% from old code %% An accepting transport spawned at association establishment. i({accept, Ref, LPid, Sock, Id}) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), MRef = erlang:monitor(process, LPid), %% Wait for a signal that the transport has been started before @@ -554,10 +578,9 @@ recv({_, #sctp_assoc_change{state = comm_up, mode = {T, _}, socket = Sock} = S) -> - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code - andalso - (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})), + andalso publish(T, Ref, Id, Sock), up(S#transport{assoc_id = Id, streams = {IS, OS}}); @@ -599,6 +622,10 @@ recv({_, #sctp_paddr_change{}}, _) -> recv({_, #sctp_pdapi_event{}}, _) -> ok. +publish(T, Ref, Id, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}), + putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1 + %% up/1 up(#transport{parent = Pid, diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 78dbda6888..597f2f14d7 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -37,11 +37,17 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). -define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1 @@ -110,6 +116,33 @@ start_link(T) -> infinity, diameter_lib:spawn_opts(server, [])). +%% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({Mod, Sock}) -> + lists:flatmap(fun(K) -> info(Mod, K, Sock) end, + [{socket, fun sockname/2}, + {peer, fun peername/2}, + {statistics, fun getstat/2} + | ssl_info(Mod, Sock)]). + +info(Mod, {K,F}, Sock) -> + case F(Mod, Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + +ssl_info(ssl = M, Sock) -> + [{M, ssl_info(Sock)}]; +ssl_info(_, _) -> + []. + +ssl_info(Sock) -> + [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]]. + %% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, setopts(M, Sock), - putr(ref, Ref), + putr(?REF_KEY, Ref), #transport{parent = Pid, module = M, socket = Sock, @@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) -> {LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid), Sock; @@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) -> RPort = get_port(RP), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid, {RAddr, RPort}), Sock. +publish(Mod, T, Ref, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + putr(?INFO_KEY, {Mod, Sock}). %% for info/1 + ok({ok, T}) -> T; ok(No) -> @@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock, ssl = Opts} = S) -> {ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]), - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code andalso (true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})), @@ -696,12 +733,32 @@ setopts(M, Sock) -> portnr(gen_tcp, Sock) -> inet:port(Sock); -portnr(ssl, Sock) -> - case ssl:sockname(Sock) of +portnr(M, Sock) -> + case M:sockname(Sock) of {ok, {_Addr, PortNr}} -> {ok, PortNr}; {error, _} = No -> No - end; -portnr(M, Sock) -> - M:port(Sock). + end. + +%% sockname/2 + +sockname(gen_tcp, Sock) -> + inet:sockname(Sock); +sockname(M, Sock) -> + M:sockname(Sock). + +%% peername/2 + +peername(gen_tcp, Sock) -> + inet:peername(Sock); +peername(M, Sock) -> + M:peername(Sock). + +%% getstat/2 + +getstat(gen_tcp, Sock) -> + inet:getstat(Sock); +getstat(M, Sock) -> + M:getstat(Sock). +%% Note that ssl:getstat/1 doesn't yet exist in R15B01. -- cgit v1.2.3