aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmpa_supervisor.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/agent/snmpa_supervisor.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/agent/snmpa_supervisor.erl')
-rw-r--r--lib/snmp/src/agent/snmpa_supervisor.erl564
1 files changed, 564 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl
new file mode 100644
index 0000000000..5ef5914e18
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_supervisor.erl
@@ -0,0 +1,564 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(snmpa_supervisor).
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start_link/2]).
+-export([start_sub_sup/1, start_master_sup/1]).
+-export([start_sub_agent/3, stop_sub_agent/1]).
+
+%% Internal exports
+-export([init/1, config/2]).
+
+
+-define(SERVER, ?MODULE).
+
+-include("snmpa_internal.hrl").
+-include("snmp_verbosity.hrl").
+-include("snmp_debug.hrl").
+
+
+%%-----------------------------------------------------------------
+%% Process structure
+%% =================
+%%
+%% ___________________ supervisor __________________
+%% / | | \ \
+%% ___misc_sup___ target_cache symbolic_store local_db agent_sup
+%% / | \ | |
+%% mib net_if note_store MA - SA
+%%
+%% The supervisor (one at each node) starts:
+%% snmpa_symbolic_store (one at each node)
+%% snmpa_local_db (one at each node)
+%% snmpa_target_cache (one at each node)
+%% MA - which starts
+%% own mib (hangs it under misc_sup)
+%% net_if (hangs it under misc_sup)
+%% note_store (hangs it under misc_sup)
+%% SAs - which starts
+%% own mib (hangs it under misc_sup)
+%%
+%% This structure is intended to make the agent fault tolerant. The
+%% agent processes (by far most code is in these processes) can be
+%% restarted in case of a failure, as all data needed by these procs
+%% are stored in the other procs. Any other process crash leads to
+%% that the entire snmpa_supervisor crashes. If it is restarted, all
+%% dynamically loaded mibs must be reloaded.
+%%
+%% It is up to the supervisor to configure the internal and
+%% external mibs. Note that the
+%% agent group (internal MIB) and sysObjectID *must* be configured
+%% in some way.
+%%-----------------------------------------------------------------
+
+start_link(Type, Opts) ->
+ ?d("start_link -> entry with"
+ "~n Type. ~p"
+ "~n Opts. ~p", [Type, Opts]),
+ start_link(get_agent_type(Opts), Opts, Type).
+
+start_link(sub, Opts, _Type) ->
+ start_sub_sup(Opts);
+start_link(master, Opts, normal) ->
+ start_master_sup(Opts);
+start_link(master, Opts, {takeover, Node}) ->
+ case start_master_sup(Opts) of
+ {ok, Pid} ->
+ OwnMibNames = get_own_loaded_mibs(),
+ try_load_other_loaded_mibs(Node, OwnMibNames),
+ {ok, Pid};
+ Else ->
+ Else
+ end.
+
+get_own_loaded_mibs() ->
+ AgentInfo = snmpa:info(snmp_master_agent),
+ [ Name || {Name, _, _} <- loaded_mibs(AgentInfo) ].
+
+try_load_other_loaded_mibs(Node, OwnMibs) ->
+ case rpc:call(Node, snmpa, info, [snmp_master_agent]) of
+ {badrpc, R} ->
+ error_msg("could not takeover loaded mibs: ~p", [R]);
+ AgentInfo ->
+ LoadedMibs = loaded_mibs(AgentInfo),
+ MibsToLoad = mibs_to_load(LoadedMibs, OwnMibs),
+ lists:foreach(fun(M) -> takeover_mib(M) end, MibsToLoad)
+ end.
+
+loaded_mibs(AgentInfo) ->
+ {value, {_, MibInfo}} = key1search(mib_server, AgentInfo),
+ {value, {_, LoadedMibs}} = key1search(loaded_mibs, MibInfo),
+ LoadedMibs.
+
+mibs_to_load(OtherMibs, OwnMibs) ->
+ [{N, S, F} || {N, S, F} <- OtherMibs, not lists:member(N, OwnMibs)].
+
+takeover_mib({'STANDARD-MIB', _Symbolic, _FileName}) ->
+ ok;
+takeover_mib({'SNMPv2-MIB', _Symbolic, _FileName}) ->
+ ok;
+takeover_mib({_MibName, _Symbolic, FileName}) ->
+ case snmpa:load_mibs(snmp_master_agent, [FileName]) of
+ ok ->
+ ok;
+ {error, R} ->
+ error_msg("could not reload mib ~p: ~p", [FileName, R])
+ end.
+
+
+%% ----------------------------------------------------------------
+
+start_sub_sup(Opts) ->
+ ?d("start_sub_sup -> entry with"
+ "~n Opts: ~p", [Opts]),
+ (catch do_start_sub_sup(Opts)).
+
+do_start_sub_sup(Opts) ->
+ verify_mandatory([db_dir], Opts),
+ ?d("do_start_sub_sup -> start (sub) supervisor",[]),
+ supervisor:start_link({local, ?SERVER}, ?MODULE, [sub, Opts]).
+
+start_master_sup(Opts) ->
+ (catch do_start_master_sup(Opts)).
+
+do_start_master_sup(Opts) ->
+ verify_mandatory([db_dir], Opts),
+ supervisor:start_link({local, ?SERVER}, ?MODULE, [master, Opts]).
+
+verify_mandatory([], _) ->
+ ok;
+verify_mandatory([Key|Keys], Opts) ->
+ case lists:keymember(Key, 1, Opts) of
+ true ->
+ verify_mandatory(Keys, Opts);
+ false ->
+ throw({error, {missing_mandatory_option, Key}})
+ end.
+
+
+%% ----------------------------------------------------------------
+
+start_sub_agent(ParentAgent, Subtree, Mibs)
+ when is_pid(ParentAgent) andalso is_list(Mibs) ->
+ ?d("start_sub_agent -> entry with"
+ "~n ParentAgent: ~p"
+ "~n Subtree: ~p"
+ "~n Mibs: ~p", [ParentAgent, Subtree, Mibs]),
+ snmpa_agent_sup:start_subagent(ParentAgent, Subtree, Mibs).
+
+stop_sub_agent(SubAgentPid) ->
+ snmpa_agent_sup:stop_subagent(SubAgentPid).
+
+
+%% ----------------------------------------------------------------
+
+init([AgentType, Opts]) ->
+ ?d("init -> entry with"
+ "~n AgentType: ~p"
+ "~n Opts: ~p", [AgentType, Opts]),
+
+ put(sname, asup),
+ put(verbosity,get_verbosity(Opts)),
+
+ ?vlog("starting",[]),
+
+ ?vdebug("create agent table",[]),
+ ets:new(snmp_agent_table, [set, public, named_table]),
+
+ ?vdebug("create community cache",[]),
+ ets:new(snmp_community_cache, [bag, public, named_table]),
+
+ %% Get restart type for the agent
+ Restart = get_opt(restart_type, Opts, permanent),
+ ?vdebug("agent restart type: ~w", [Restart]),
+
+ %% -- Agent type --
+ ets:insert(snmp_agent_table, {agent_type, AgentType}),
+
+ %% -- Prio --
+ Prio = get_opt(priority, Opts, normal),
+ ?vdebug("[agent table] store priority: ~p",[Prio]),
+ ets:insert(snmp_agent_table, {priority, Prio}),
+
+ %% -- Versions --
+ Vsns = get_opt(versions, Opts, [v1,v2,v3]),
+ ?vdebug("[agent table] store versions: ~p",[Vsns]),
+ ets:insert(snmp_agent_table, {versions, Vsns}),
+
+ %% -- DB-directory --
+ DbDir = get_opt(db_dir, Opts),
+ ?vdebug("[agent table] store db_dir: ~n ~p",[DbDir]),
+ ets:insert(snmp_agent_table, {db_dir, filename:join([DbDir])}),
+
+ DbInitError = get_opt(db_init_error, Opts, terminate),
+ ?vdebug("[agent table] store db_init_error: ~n ~p",[DbInitError]),
+ ets:insert(snmp_agent_table, {db_init_error, DbInitError}),
+
+ %% -- Error report module --
+ ErrorReportMod = get_opt(error_report_mod, Opts, snmpa_error_logger),
+ ?vdebug("[agent table] store error report module: ~w",[ErrorReportMod]),
+ ets:insert(snmp_agent_table, {error_report_mod, ErrorReportMod}),
+
+ %% -- mib storage --
+ MibStorage =
+ case get_opt(mib_storage, Opts, ets) of
+ dets ->
+ {dets, DbDir};
+ {dets, default} ->
+ {dets, DbDir};
+ {dets, default, Act} ->
+ {dets, DbDir, Act};
+ {ets, default} ->
+ {ets, DbDir};
+ mnesia ->
+ {mnesia, erlang:nodes()};
+ {mnesia, visible} ->
+ {mnesia, erlang:nodes(visible)};
+ {mnesia, connected} ->
+ {mnesia, erlang:nodes(connected)};
+ Other ->
+ Other
+ end,
+ ?vdebug("[agent table] store mib storage: ~w",[MibStorage]),
+ ets:insert(snmp_agent_table, {mib_storage, MibStorage}),
+
+ %% -- Agent mib storage --
+ AgentMibStorage = get_opt(agent_mib_storage, Opts, persistent),
+ %% ?vdebug("[agent table] store agent mib storage: ~w",[AgentMibStorage]),
+ ets:insert(snmp_agent_table, {agent_mib_storage, AgentMibStorage}),
+
+ %% -- System start time --
+ ?vdebug("[agent table] store system start time",[]),
+ ets:insert(snmp_agent_table, {system_start_time, snmp_misc:now(cs)}),
+
+ %% -- Symbolic store options --
+ SsOpts = get_opt(symbolic_store, Opts, []),
+ ?vdebug("[agent table] store symbolic store options: ~w",[SsOpts]),
+ ets:insert(snmp_agent_table, {symbolic_store, SsOpts}),
+
+ %% -- Local DB options --
+ LdbOpts = get_opt(local_db, Opts, []),
+ ?vdebug("[agent table] store local db options: ~w",[LdbOpts]),
+ ets:insert(snmp_agent_table, {local_db, LdbOpts}),
+
+ %% -- Target cache options --
+ TargetCacheOpts = get_opt(target_cache, Opts, []),
+ ?vdebug("[agent table] store target cache options: ~w",[TargetCacheOpts]),
+ ets:insert(snmp_agent_table, {target_cache, TargetCacheOpts}),
+
+ %% -- Specs --
+ SupFlags = {one_for_all, 0, 3600},
+
+ MiscSupSpec =
+ sup_spec(snmpa_misc_sup, [], Restart, infinity),
+
+ SymStoreOpts = [{mib_storage, MibStorage} | SsOpts],
+ SymStoreArgs = [Prio, SymStoreOpts],
+ SymStoreSpec =
+ worker_spec(snmpa_symbolic_store, SymStoreArgs, Restart, 2000),
+
+ LdbArgs = [Prio, DbDir, LdbOpts],
+ LocalDbSpec =
+ worker_spec(snmpa_local_db, LdbArgs, Restart, 5000),
+
+ ?vdebug("init VACM",[]),
+ snmpa_vacm:init(DbDir, DbInitError),
+
+ TargetCacheArgs = [Prio, TargetCacheOpts],
+ TargetCacheSpec =
+ worker_spec(snmpa_target_cache, TargetCacheArgs, transient, 2000, []),
+
+ Rest =
+ case AgentType of
+ master ->
+ % If we're starting the master, we must also
+ % configure the tables.
+
+ %% -- Config --
+ ConfOpts = get_opt(config, Opts, []),
+ ?vdebug("[agent table] store config options: ~p", [ConfOpts]),
+ ets:insert(snmp_agent_table, {config, ConfOpts}),
+
+ ConfigArgs = [Vsns, ConfOpts],
+ ConfigSpec =
+ worker_spec(config, ?MODULE, config, ConfigArgs,
+ transient, 2000, [?MODULE]),
+
+ %% -- Agent verbosity --
+ AgentVerb = get_opt(agent_verbosity, Opts, silence),
+
+ %% -- Discovery processing --
+ DiscoOpts = get_opt(discovery, Opts, []),
+ ?vdebug("[agent table] store discovery options: ~p", [DiscoOpts]),
+ ets:insert(snmp_agent_table, {discovery, DiscoOpts}),
+
+ %% -- Mibs --
+ Mibs = get_mibs(get_opt(mibs, Opts, []), Vsns),
+ ?vdebug("[agent table] store mibs: ~n ~p",[Mibs]),
+ ets:insert(snmp_agent_table, {mibs, Mibs}),
+
+ Ref = make_ref(),
+
+ %% -- Set module --
+ SetModule = get_opt(set_mechanism, Opts, snmpa_set),
+ ?vdebug("[agent table] store set-module: ~p",[SetModule]),
+ ets:insert(snmp_agent_table, {set_mechanism, ConfOpts}),
+
+ %% -- Authentication service --
+ AuthModule = get_opt(authentication_service, Opts, snmpa_acm),
+ ?vdebug("[agent table] store authentication service: ~w",
+ [AuthModule]),
+ ets:insert(snmp_agent_table,
+ {authentication_service, AuthModule}),
+
+ %% -- Multi-threaded --
+ MultiT = get_opt(multi_threaded, Opts, false),
+ ?vdebug("[agent table] store multi-threaded: ~p",[MultiT]),
+ ets:insert(snmp_agent_table, {multi_threaded, MultiT}),
+
+ %% -- Audit trail log --
+ case get_opt(audit_trail_log, Opts, not_found) of
+ not_found ->
+ ?vdebug("[agent table] no audit trail log",[]),
+ ok;
+ AtlOpts ->
+ ?vdebug("[agent table] "
+ "store audit trail log options: ~p",
+ [AtlOpts]),
+ ets:insert(snmp_agent_table,
+ {audit_trail_log, AtlOpts}),
+ ok
+ end,
+
+ %% -- MIB server --
+ MibsOpts = get_opt(mib_server, Opts, []),
+ ?vdebug("[agent table] store mib-server options: "
+ "~n ~p", [MibsOpts]),
+ ets:insert(snmp_agent_table, {mib_server, MibsOpts}),
+
+ %% -- Network interface --
+ NiOpts = get_opt(net_if, Opts, []),
+ ?vdebug("[agent table] store net-if options: "
+ "~n ~p", [NiOpts]),
+ ets:insert(snmp_agent_table, {net_if, NiOpts}),
+
+ %% -- Note store --
+ NsOpts = get_opt(note_store, Opts, []),
+ ?vdebug("[agent table] store note-store options: "
+ "~n ~p",[NsOpts]),
+ ets:insert(snmp_agent_table, {note_store, NsOpts}),
+
+ AgentOpts =
+ [{verbosity, AgentVerb},
+ {mibs, Mibs},
+ {mib_storage, MibStorage},
+ {set_mechanism, SetModule},
+ {authentication_service, AuthModule},
+ {multi_threaded, MultiT},
+ {versions, Vsns},
+ {net_if, NiOpts},
+ {mib_server, MibsOpts},
+ {note_store, NsOpts}|
+ get_opt(master_agent_options, Opts, [])],
+
+ AgentSpec =
+ worker_spec(snmpa_agent,
+ [Prio,snmp_master_agent,none,Ref,AgentOpts],
+ Restart, 15000),
+ AgentSupSpec =
+ sup_spec(snmpa_agent_sup, [AgentSpec],
+ Restart, infinity),
+ [ConfigSpec, AgentSupSpec];
+ _ ->
+ ?vdebug("[sub agent] spec for the agent supervisor",[]),
+ AgentSupSpec =
+ sup_spec(snmpa_agent_sup, [], Restart, infinity),
+ [AgentSupSpec]
+ end,
+ ?vdebug("init done",[]),
+ {ok, {SupFlags, [MiscSupSpec, SymStoreSpec, LocalDbSpec, TargetCacheSpec |
+ Rest]}}.
+
+get_mibs(Mibs, Vsns) ->
+ MibDir = filename:join(code:priv_dir(snmp), "mibs"),
+ StdMib =
+ case (lists:member(v2, Vsns) or lists:member(v3, Vsns)) of
+ true -> filename:join([MibDir, "SNMPv2-MIB"]);
+ false -> filename:join([MibDir, "STANDARD-MIB"])
+ end,
+ ?vdebug("add standard and v2 mibs",[]),
+ NMibs = add_mib(StdMib, Mibs, ["SNMPv2-MIB", "STANDARD-MIB"]),
+ case lists:member(v3, Vsns) of
+ true ->
+ ?vdebug("add v3 mibs",[]),
+ add_v3_mibs(MibDir, NMibs);
+ false ->
+ NMibs
+ end.
+
+add_v3_mibs(MibDir, Mibs) ->
+ NMibs = add_mib(filename:join(MibDir, "SNMP-FRAMEWORK-MIB"),
+ Mibs, ["SNMP-FRAMEWORK-MIB"]),
+ add_mib(filename:join(MibDir, "SNMP-MPD-MIB"),
+ NMibs, ["SNMP-MPD-MIB"]).
+
+add_mib(DefaultMib, [], _BaseNames) -> [DefaultMib];
+add_mib(DefaultMib, [Mib | T], BaseNames) ->
+ case lists:member(filename:basename(Mib), BaseNames) of
+ true -> [Mib | T]; % The user defined his own version of the mib
+ false -> [Mib | add_mib(DefaultMib, T, BaseNames)]
+ end.
+
+
+config(Vsns, Opts) ->
+ ?d("config -> entry with"
+ "~n Vsns. ~p"
+ "~n Opts. ~p", [Vsns, Opts]),
+ Verbosity = get_opt(verbosity, Opts, silence),
+ put(sname, conf),
+ put(verbosity, Verbosity),
+
+ ?vlog("starting", []),
+
+ ConfDir = get_opt(dir, Opts),
+ ForceLoad = get_opt(force_load, Opts, false),
+
+ case (catch conf(ConfDir, Vsns, ForceLoad)) of
+ ok ->
+ ?d("config -> done", []),
+ ignore;
+ {'EXIT', Reason} ->
+ ?vlog("configure failed (exit) with reason: ~p",[Reason]),
+ {error, {config_error, Reason}}
+ end.
+
+conf(Dir, Vsns, true) ->
+ conf1(Dir, Vsns, reconfigure);
+conf(Dir, Vsns, false) ->
+ conf1(Dir, Vsns, configure).
+
+conf1(Dir, Vsns, Func) ->
+ ?vlog("start ~w",[Func]),
+
+ ?vdebug("~w snmp_standard_mib",[Func]),
+ snmp_standard_mib:Func(Dir),
+ ?vdebug("init snmp_framework_mib",[]),
+ snmp_framework_mib:init(),
+ ?vdebug("configure snmp_framework_mib",[]),
+ snmp_framework_mib:configure(Dir),
+ ?vdebug("~w snmp_target_mib",[Func]),
+ snmp_target_mib:Func(Dir),
+ ?vdebug("~w snmp_notification_mib",[Func]),
+ snmp_notification_mib:Func(Dir),
+ ?vdebug("~w snmp_view_based_acm_mib",[Func]),
+ snmp_view_based_acm_mib:Func(Dir),
+ case lists:member(v1, Vsns) or lists:member(v2, Vsns) of
+ true ->
+ ?vdebug("we need to handle v1 and/or v2 =>~n"
+ " ~w snmp_community_mib",[Func]),
+ snmp_community_mib:Func(Dir);
+ false ->
+ ?vdebug("no need to handle v1 or v2",[]),
+ ok
+ end,
+ case lists:member(v3, Vsns) of
+ true ->
+ ?vdebug("we need to handle v3 =>~n"
+ " ~w snmp_user_based_sm_mib",[Func]),
+ snmp_user_based_sm_mib:Func(Dir);
+ false ->
+ ?vdebug("no need to handle v3",[]),
+ ok
+ end,
+ ?vdebug("conf done",[]),
+ ok.
+
+
+%% -------------------------------------
+
+sup_spec(Name, Args, Type, Time) ->
+ ?d("sup_spec -> entry with"
+ "~n Name: ~p"
+ "~n Args: ~p"
+ "~n Type: ~p"
+ "~n Time: ~p", [Name, Args, Type, Time]),
+ {Name,
+ {Name, start_link, Args},
+ Type, Time, supervisor, [Name, supervisor]}.
+
+worker_spec(Name, Args, Type, Time) ->
+ worker_spec(Name, Name, Args, Type, Time, []).
+
+worker_spec(Name, Args, Type, Time, Modules) ->
+ worker_spec(Name, Name, Args, Type, Time, Modules).
+
+worker_spec(Name, Mod, Args, Type, Time, Modules) ->
+ worker_spec(Name, Mod, start_link, Args, Type, Time, Modules).
+
+worker_spec(Name, Mod, Func, Args, Type, Time, Modules)
+ when is_atom(Name) andalso
+ is_atom(Mod) andalso
+ is_atom(Func) andalso
+ is_list(Args) andalso
+ is_atom(Type) andalso
+ is_list(Modules) ->
+ ?d("worker_spec -> entry with"
+ "~n Name: ~p"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p"
+ "~n Type: ~p"
+ "~n Time: ~p"
+ "~n Modules: ~p", [Name, Mod, Func, Args, Type, Time, Modules]),
+ {Name,
+ {Mod, Func, Args},
+ Type, Time, worker, [Name] ++ Modules}.
+
+
+get_verbosity(Opts) ->
+ SupOpts = get_opt(supervisor, Opts, []),
+ get_opt(verbosity, SupOpts, silence).
+
+
+get_agent_type(Opts) ->
+ get_opt(agent_type, Opts, master).
+
+get_opt(Key, Opts) ->
+ snmp_misc:get_option(Key, Opts).
+
+get_opt(Key, Opts, Def) ->
+ snmp_misc:get_option(Key, Opts, Def).
+
+key1search(Key, List) ->
+ lists:keysearch(Key, 1, List).
+
+
+%%-----------------------------------------------------------------
+
+error_msg(F, A) ->
+ ?snmpa_error(F, A).
+
+% i(F) ->
+% i(F, []).
+
+% i(F, A) ->
+% io:format("~p:~p: " ++ F ++ "~n", [node(),?MODULE|A]).