%%%------------------------------------------------------------------- %%% @author vdasari %%% @copyright (C) 2018, %%% @doc %%% %%% @end %%% Created : 18. Jan 2018 9:56 AM %%%------------------------------------------------------------------- -module(lldp_manager). -author("vdasari"). -behaviour(gen_server). -include("logger.hrl"). %% API -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([info/0, info/1, dashboard/1]). -export([start_handler/3, stop_handler/1, get_neighbors/2]). -define(SERVER, ?MODULE). -define(APPNAME, lldp). -define(call(A),gen_server:call(?MODULE, A)). -define(cast(A),gen_server:cast(?MODULE, A)). -record(state, { handler_map = #{} }). %%%=================================================================== %%% API %%%=================================================================== info() -> io:format("~s~n", [?call(info)]). info(ProcName) -> io:format("~s~n", [?call({info,ProcName})]). dashboard(ProcName) -> io:format("~s~n", [gen_server:call(ProcName, {info, dashboard})]). load_config() -> case application:get_env(?APPNAME, netlink) of {ok, Config} -> start_handler(lldp_netlink, lldp_netlink, Config); _ -> ok end. get_neighbors(ProcName, IfInfo) -> gen_server:call(ProcName, {neighbors, get, IfInfo}). start_handler(Name, Module, Config) -> ?cast({start_handler, Name, Module, Config}). stop_handler(Name) -> ?cast({stop_handler, Name}). %%-------------------------------------------------------------------- %% @doc %% Starts the server %% %% @end %%-------------------------------------------------------------------- -spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %%-------------------------------------------------------------------- %% @private %% @doc %% Initializes the server %% %% @spec init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -spec(init(Args :: term()) -> {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | {stop, Reason :: term()} | ignore). init([]) -> self() ! {init}, {ok, #state{}}. handle_call(Request, From, State) -> try process_call(Request, State) of {reply, ok, _} = Return -> ?DEBUG("call: Request From ~p, Returns ~p~n~p", [From, ok, Request]), Return; {reply, NotOk, _} = Return when is_atom(NotOk) -> ?INFO("call: Request From ~p, Returns ~p~n~p", [From, NotOk, Request]), Return; Return -> Return catch Error:Reason:StackTrace -> ?ERROR("Failed:~n Request ~p~n From ~p~n Error ~p, Reason ~p~n StackTrace ~n~s", [Request, From, Error, Reason, lldp_utils:pretty_print(StackTrace)]), {reply, Error, State} end. handle_cast(Request, State) -> ?DEBUG("cast: Request ~p", [Request]), try process_cast(Request, State) of Return -> Return catch Error:Reason:StackTrace -> ?ERROR("Failed:~n Request ~p~n Error ~p, Reason ~p~n StackTrace ~n~s", [Request, Error, Reason, lldp_utils:pretty_print(StackTrace)]), {noreply, State} end. handle_info(Info, State) -> ?DEBUG("info: Request ~p", [Info]), try process_info_msg(Info, State) of Return -> Return catch Error:Reason:StackTrace -> ?ERROR("Failed:~n Request ~p~n Error ~p, Reason ~p~n StackTrace ~n~s", [Info, Error, Reason, lldp_utils:pretty_print(StackTrace)]), {noreply, State} end. terminate(_Reason, _State) -> ?INFO("~s going down: ~p", [?MODULE, _Reason]), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== process_call(info, #state{handler_map = Map} = State) -> Header = io_lib:format("~16c ~16c ~4c ~4c~n", [$-, $-, $-, $-]) ++ io_lib:format("~16s ~16s ~4s ~4s~n", ["Name", "Module", "Ifs", "Nbrs"]) ++ io_lib:format("~16c ~16c ~4c ~4c~n", [$-, $-, $-, $-]), Return = maps:fold(fun (_, Pid, Acc) -> gen_lldp:info(Pid, {info, brief}) ++ Acc end, [], Map), {reply, lists:flatten(Header ++ Return), State}; process_call(Request, #state{handler_map = Map} = State) when element(1, Request) == info -> HandlerName = element(2, Request), #{HandlerName := Pid} = Map, {reply, gen_lldp:info(Pid, Request), State}; process_call(Request, State) -> ?INFO("call: Unhandled Request ~p", [Request]), {reply, ok, State}. process_cast({start_handler, Name, Module, Config}, State) -> do_start_handler(Name, Module, Config, State); process_cast({stop_handler, Name}, State) -> do_stop_handler(Name, State); process_cast(Request, State) -> ?INFO("cast: Request~n~p", [Request]), {noreply, State}. process_info_msg({init}, State) -> load_config(), {noreply, State}; process_info_msg(Request, State) -> ?INFO("info: Request~n~p", [Request]), {noreply, State}. %%%=================================================================== %%% Worker functions %%%=================================================================== do_start_handler(Name, Module, Config, #state{handler_map = Map} = State) -> case Module:init(Config) of {ok, ChildState} -> {ok, Pid} = lldp_handler_sup:start_child(Name, Module, ChildState), true = erlang:link(Pid), {noreply, State#state{ handler_map = Map#{Name => Pid} }}; _ -> {noreply, State} end. do_stop_handler(Name, #state{handler_map = Map} = State) -> #{Name := Pid} = Map, lldp_handler_sup:stop_child(Pid), {noreply, State#state{ handler_map = maps:remove(Name, Map) }}.