From de19bb31eea2840f5b446237f41f0ea6b9762398 Mon Sep 17 00:00:00 2001 From: Alexander Kuleshov Date: Wed, 21 Jul 2021 22:22:26 +0600 Subject: [PATCH] TODO --- src/eradius_client.erl | 91 ++++++++++++++++++++++++++++++++--- test/eradius_client_SUITE.erl | 6 ++- test/eradius_test_handler.erl | 4 +- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/eradius_client.erl b/src/eradius_client.erl index f237b484..e0c03f4c 100644 --- a/src/eradius_client.erl +++ b/src/eradius_client.erl @@ -80,13 +80,35 @@ send_request({IP, Port, Secret}, Request, Options) when ?GOOD_CMD(Request) andal TS1 = eradius_lib:timestamp(milli_seconds), ServerName = proplists:get_value(server_name, Options, undefined), MetricsInfo = make_metrics_info(Options, {IP, Port}), - update_client_requests(MetricsInfo), Retries = proplists:get_value(retries, Options, ?DEFAULT_RETRIES), Timeout = proplists:get_value(timeout, Options, ?DEFAULT_TIMEOUT), - Peer = {ServerName, {IP, Port}}, - {Socket, ReqId} = gen_server:call(?SERVER, {wanna_send, Peer, MetricsInfo}), - Response = send_request_loop(Socket, ReqId, Peer, Request#radius_request{reqid = ReqId, secret = Secret}, Retries, Timeout, MetricsInfo), - proceed_response(Request, Response, Peer, TS1, MetricsInfo, Options); + SendFun = fun () -> + Peer = {ServerName, {IP, Port}}, + update_client_requests(MetricsInfo), + {Socket, ReqId} = gen_server:call(?SERVER, {wanna_send, Peer, MetricsInfo}), + Response = send_request_loop(Socket, ReqId, Peer, + Request#radius_request{reqid = ReqId, secret = Secret}, + Retries, Timeout, MetricsInfo), + proceed_response(Request, Response, Peer, TS1, MetricsInfo, Options) + end, + % If we have other RADIUS upstream servers check current one, + % maybe it is already marked as inactive and try to find another + % one if it is + case proplists:get_value(failover, Options, []) of + [] -> + SendFun(); + UpstreamServers -> + case find_suitable_peer([{IP, Port, Secret} | UpstreamServers]) of + [] -> + error(no_active_servers); + {{IP, Port, Secret}, _NewPool} -> + SendFun(); + {NewPeer, NewPool} -> + % current server is not in list of active servers, so use another one + NewOptions = lists:keyreplace(failover, 1, Options, {failover, NewPool}), + send_request(NewPeer, Request, NewOptions) + end + end; send_request({_IP, _Port, _Secret}, _Request, _Options) -> error(badarg). @@ -385,7 +407,9 @@ configure(State) -> %% private prepare_pools() -> ets:new(?MODULE, [ordered_set, public, named_table, {keypos, 1}, {write_concurrency,true}]), - lists:foreach(fun({_PoolName, Servers}) -> prepare_pool(Servers) end, application:get_env(eradius, servers_pool, [])). + lists:foreach(fun({_PoolName, Servers}) -> prepare_pool(Servers) end, application:get_env(eradius, servers_pool, [])), + lists:foreach(fun(Server) -> store_upstream_servers(Server) end, application:get_env(eradius, servers, [])). + prepare_pool([]) -> ok; prepare_pool([{Addr, Port, _, Opts} | Servers]) -> Retries = proplists:get_value(retries, Opts, ?DEFAULT_RETRIES), @@ -395,6 +419,61 @@ prepare_pool([{Addr, Port, _} | Servers]) -> store_radius_server_from_pool(Addr, Port, ?DEFAULT_RETRIES), prepare_pool(Servers). +get_routes_info(HandlerOpts) -> + DefaultRoute = lists:keyfind(default_route, 1, HandlerOpts), + Routes = lists:keyfind(routes, 1, HandlerOpts), + Options = lists:keyfind(options, 1, HandlerOpts), + Retries = case Options of + false -> + ?DEFAULT_RETRIES; + {options, Opts} -> + proplists:get_value(retries, Opts, ?DEFAULT_RETRIES) + end, + {DefaultRoute, Routes, Retries}. + +put_default_route_to_pool(false, _) -> ok; +put_default_route_to_pool({default_route, {Host, Port, _Secret}}, Retries) -> + store_radius_server_from_pool(Host, Port, Retries); +put_default_route_to_pool({default_route, {Host, Port, _Secret}, _PoolName}, Retries) -> + store_radius_server_from_pool(Host, Port, Retries); +put_default_route_to_pool(_, _) -> ok. + +put_routes_to_pool(false, _Retries) -> ok; +put_routes_to_pool({routes, Routes}, Retries) -> + lists:foreach(fun (Route) -> + case Route of + {_RouteName, {Host, Port, _Secret}} -> + store_radius_server_from_pool(Host, Port, Retries); + {_RouteName, {Host, Port, _Secret}, _Pool} -> + store_radius_server_from_pool(Host, Port, Retries); + {Host, Port, _Secret, _Opts} -> + store_radius_server_from_pool(Host, Port, Retries); + _ -> ok + end + end, Routes). + +store_upstream_servers({Server, _}) -> + store_upstream_servers(Server); +store_upstream_servers({Server, _, _}) -> + store_upstream_servers(Server); +store_upstream_servers(Server) -> + HandlerDefinitions = application:get_env(eradius, Server, []), + F = fun (HandlerOpts) -> + {DefaultRoute, Routes, Retries} = get_routes_info(HandlerOpts), + put_default_route_to_pool(DefaultRoute, Retries), + put_routes_to_pool(Routes, Retries) + end, + lists:foreach(fun (HandlerDefinition) -> + case HandlerDefinition of + {{_, []}, _} -> ok; + {{_, _, []}, _} -> ok; + {{_, HandlerOpts}, _} -> F(HandlerOpts); + {{_, _, HandlerOpts}, _} -> F(HandlerOpts); + _HandlerDefinition -> ok + end + end, + HandlerDefinitions). + %% private store_radius_server_from_pool(Addr, Port, Retries) when is_tuple(Addr) and is_integer(Port) and is_integer(Retries) -> ets:insert(?MODULE, {{Addr, Port}, Retries, Retries}); diff --git a/test/eradius_client_SUITE.erl b/test/eradius_client_SUITE.erl index e550df4f..61e33020 100644 --- a/test/eradius_client_SUITE.erl +++ b/test/eradius_client_SUITE.erl @@ -24,7 +24,10 @@ -include("test/eradius_test.hrl"). -define(BAD_SERVER_IP, {eradius_test_handler:localhost(ip), 1820, "secret"}). --define(BAD_SERVER_TUPLE, {{eradius_test_handler:localhost(tuple), 1820}, 3, 3}). +-define(BAD_SERVER_INITIAL_RETRIES, 3). +-define(BAD_SERVER_TUPLE, {{eradius_test_handler:localhost(tuple), 1820}, + ?BAD_SERVER_INITIAL_RETRIES - 1, + ?BAD_SERVER_INITIAL_RETRIES}). -define(BAD_SERVER_IP_ETS_KEY, {eradius_test_handler:localhost(tuple), 1820}). all() -> [ @@ -218,7 +221,6 @@ reconf_ports_10(_Config) -> send_request_failover(_Config) -> ?equal(accept, eradius_test_handler:send_request_failover(?BAD_SERVER_IP)), - ?equal([], ets:lookup(eradius_client, ?BAD_SERVER_IP_ETS_KEY)), {ok, Timeout} = application:get_env(eradius, unreachable_timeout), timer:sleep(Timeout * 1000), ?equal([?BAD_SERVER_TUPLE], ets:lookup(eradius_client, ?BAD_SERVER_IP_ETS_KEY)), diff --git a/test/eradius_test_handler.erl b/test/eradius_test_handler.erl index 44d53271..692f6f70 100644 --- a/test/eradius_test_handler.erl +++ b/test/eradius_test_handler.erl @@ -15,7 +15,9 @@ start() -> application:set_env(eradius, one, [{{"ONE", []}, [{localhost(ip), "secret"}]}]), application:set_env(eradius, servers, [{one, {localhost(ip), [1812]}}]), application:set_env(eradius, unreachable_timeout, 2), - application:set_env(eradius, servers_pool, [{test_pool, [{localhost(tuple), 1812, "secret"}]}]), + application:set_env(eradius, servers_pool, [{test_pool, [{localhost(tuple), 1812, "secret"}, + % fake upstream server for fail-over + {localhost(tuple), 1820, "secret"}]}]), application:ensure_all_started(eradius), eradius:modules_ready([?MODULE]).