diff options
Diffstat (limited to 'lib/snmp/src/agent/snmpa_mib.erl')
-rw-r--r-- | lib/snmp/src/agent/snmpa_mib.erl | 216 |
1 files changed, 160 insertions, 56 deletions
diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index ab1098514c..8e594213f9 100644 --- a/lib/snmp/src/agent/snmpa_mib.erl +++ b/lib/snmp/src/agent/snmpa_mib.erl @@ -40,14 +40,18 @@ which_cache_size/1 ]). -%% <BACKWARD-COMPAT> --export([load_mibs/2, unload_mibs/2]). -%% </BACKWARD-COMPAT> +%% Utility exports +-export([subscribe_gc_events/1, unsubscribe_gc_events/1]). %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% <BACKWARD-COMPAT> +-export([load_mibs/2, unload_mibs/2]). +%% </BACKWARD-COMPAT> + + -include_lib("kernel/include/file.hrl"). -include("snmpa_internal.hrl"). -include("snmp_types.hrl"). @@ -55,14 +59,15 @@ -include("snmp_debug.hrl"). --define(SERVER, ?MODULE). --define(NO_CACHE, no_mibs_cache). --define(DEFAULT_CACHE_USAGE, true). --define(CACHE_GC_TICKTIME, timer:minutes(1)). --define(DEFAULT_CACHE_AUTOGC, true). --define(DEFAULT_CACHE_GCLIMIT, 100). --define(DEFAULT_CACHE_AGE, timer:minutes(10)). --define(CACHE_GC_TRIGGER, cache_gc_trigger). +-define(SERVER, ?MODULE). +-define(NO_CACHE, no_mibs_cache). +-define(DEFAULT_CACHE_USAGE, true). +-define(CACHE_GC_TICKTIME, timer:minutes(1)). +-define(DEFAULT_CACHE_AUTOGC, true). +-define(DEFAULT_CACHE_GCLIMIT, infinity). % 100). +-define(DEFAULT_CACHE_GCVERBOSE, false). +-define(DEFAULT_CACHE_AGE, timer:minutes(10)). +-define(CACHE_GC_TRIGGER, cache_gc_trigger). @@ -85,7 +90,8 @@ %%----------------------------------------------------------------- -record(state, {data, meo, teo, backup, - cache, cache_tmr, cache_autogc, cache_gclimit, cache_age, + cache, cache_tmr, cache_autogc, cache_gclimit, cache_age, + cache_sub, cache_gcverbose = false, data_mod}). @@ -153,6 +159,13 @@ update_cache_opts(MibServer, Key, Value) -> call(MibServer, {update_cache_opts, Key, Value}). +subscribe_gc_events(MibServer) -> + call(MibServer, {subscribe_gc_events, self()}). + +unsubscribe_gc_events(MibServer) -> + call(MibServer, {unsubscribe_gc_events, self()}). + + %%----------------------------------------------------------------- %% Func: lookup/2 %% Purpose: Finds the mib entry corresponding to the Oid. If it is a @@ -277,7 +290,7 @@ do_init(Prio, Mibs, Opts) -> process_flag(trap_exit, true), put(sname, ms), put(verbosity, ?vvalidate(get_verbosity(Opts))), - ?vlog("starting",[]), + ?vlog("starting", []), %% Extract the cache options {Cache, CacheOptions} = @@ -291,9 +304,10 @@ do_init(Prio, Mibs, Opts) -> Bad -> throw({error, {bad_option, {cache, Bad}}}) end, - CacheAutoGC = get_cacheopt_autogc(Cache, CacheOptions), - CacheGcLimit = get_cacheopt_gclimit(Cache, CacheOptions), - CacheAge = get_cacheopt_age(Cache, CacheOptions), + CacheAutoGC = get_cacheopt_autogc(Cache, CacheOptions), + CacheGcLimit = get_cacheopt_gclimit(Cache, CacheOptions), + CacheAge = get_cacheopt_age(Cache, CacheOptions), + CacheGcVerb = get_cacheopt_gcverbose(Cache, CacheOptions), %% Maybe start the cache gc timer CacheGcTimer = @@ -322,15 +336,16 @@ do_init(Prio, Mibs, Opts) -> ?vdebug("started",[]), MibDataMod:sync(Data2), ?vdebug("mib data synced",[]), - {ok, #state{data = Data2, - teo = TeOverride, - meo = MeOverride, - cache = Cache, - cache_tmr = CacheGcTimer, - cache_autogc = CacheAutoGC, - cache_gclimit = CacheGcLimit, - cache_age = CacheAge, - data_mod = MibDataMod}}; + {ok, #state{data = Data2, + teo = TeOverride, + meo = MeOverride, + cache = Cache, + cache_tmr = CacheGcTimer, + cache_autogc = CacheAutoGC, + cache_gclimit = CacheGcLimit, + cache_age = CacheAge, + cache_gcverbose = CacheGcVerb, + data_mod = MibDataMod}}; {'aborted at', Mib, _NewData, Reason} -> ?vinfo("failed loading mib ~p: ~p",[Mib,Reason]), {error, {Mib, Reason}} @@ -418,9 +433,43 @@ handle_call({update_cache_opts, Key, Value}, _From, State) -> {Result, NewState} = handle_update_cache_opts(Key, Value, State), {reply, Result, NewState}; + +handle_call({subscribe_gc_events, Pid}, _From, + #state{cache_sub = Sub} = State) + when (Sub =:= undefined) -> + ?vdebug("subscribe_gc_events: ~p => ok", [Pid]), + {reply, ok, State#state{cache_sub = Pid}}; +handle_call({subscribe_gc_events, Pid}, _From, + #state{cache_sub = Pid} = State) -> + ?vinfo("subscribe_gc_events: ~p => error:already-subscribed", [Pid]), + {reply, {error, already_subscribed}, State}; +handle_call({subscribe_gc_events, Pid}, _From, + #state{cache_sub = Sub} = State) + when is_pid(Sub) andalso (Pid =/= Sub) -> + ?vinfo("subscribe_gc_events: ~p => error:already-subscribed ~p", + [Pid, Sub]), + {reply, {error, {already_subscribed, Sub}}, State}; + +handle_call({unsubscribe_gc_events, Pid}, _From, + #state{cache_sub = Pid} = State) -> + ?vdebug("unsubscribe_gc_events: ~p => ok", [Pid]), + {reply, ok, State#state{cache_sub = undefined}}; +handle_call({unsubscribe_gc_events, Pid}, _From, + #state{cache_sub = Sub} = State) + when (Sub =:= undefined) -> + ?vinfo("unsubscribe_gc_events: ~p => error:not-subscribed", [Pid]), + {reply, {error, not_subscribed}, State}; +handle_call({unsubscribe_gc_events, Pid}, _From, + #state{cache_sub = Sub} = State) + when is_pid(Sub) andalso (Pid =/= Sub) -> + ?vinfo("unsubscribe_gc_events: ~p => error:not-subscribed ~p", + [Pid, Sub]), + {reply, {error, {not_subscribed, Sub}}, State}; + + handle_call({lookup, Oid}, _From, #state{data = Data, cache = Cache, data_mod = Mod} = State) -> - ?vlog("lookup ~p", [Oid]), + ?vlog("lookup ~p", [Oid]), Key = {lookup, Oid}, {Reply, NewState} = case maybe_cache_lookup(Cache, Key) of @@ -431,7 +480,7 @@ handle_call({lookup, Oid}, _From, ets:insert(Cache, {Key, Rep, timestamp()}), {Rep, maybe_start_cache_gc_timer(State)}; [{Key, Rep, _}] -> - ?vdebug("lookup -> found in cache", []), + ?vtrace("lookup -> found in cache - update timestamp", []), ets:update_element(Cache, Key, {3, timestamp()}), {Rep, State} end, @@ -458,7 +507,7 @@ handle_call({next, Oid, MibView}, _From, ets:insert(Cache, {Key, Rep, timestamp()}), {Rep, maybe_start_cache_gc_timer(State)}; [{Key, Rep, _}] -> - ?vdebug("lookup -> found in cache", []), + ?vdebug("lookup -> found in cache - update timestamp", []), ets:update_element(Cache, Key, {3, timestamp()}), {Rep, State} end, @@ -570,7 +619,7 @@ handle_call(info, _From, #state{data = Data, Reply = case (catch Mod:info(Data)) of Info when is_list(Info) -> - [{cache, size_cache(Cache)} | Info]; + [{cache, cache_info(Cache)} | Info]; E -> [{error, E}] end, @@ -664,13 +713,21 @@ handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) -> gen_server:reply(From, Reply), {noreply, S#state{backup = undefined}}; -handle_info(?CACHE_GC_TRIGGER, #state{cache = Cache, - cache_age = Age, - cache_gclimit = GcLimit, - cache_autogc = true} = S) +handle_info(?CACHE_GC_TRIGGER, #state{cache = Cache, + cache_age = Age, + cache_gclimit = GcLimit, + cache_autogc = true, + cache_gcverbose = GcVerbose} = S) when (Cache =/= ?NO_CACHE) -> - ?vlog("cache gc trigger event", []), - maybe_gc_cache(Cache, Age, GcLimit), + gcvprint(GcVerbose, "GC: begin"), + case maybe_gc_cache(Cache, Age, GcLimit) of + {ok, NumDeleted} = Result when (NumDeleted > 0) -> + gcvprint(GcVerbose, + "GC: ~w elements deleted from cache", [NumDeleted]), + maybe_send_gc_result(S, Result); + _ -> + ok + end, Tmr = start_cache_gc_timer(), {noreply, S#state{cache_tmr = Tmr}}; @@ -749,14 +806,15 @@ get_cacheopt_autogc(Cache, CacheOpts) -> IsValid). get_cacheopt_gclimit(Cache, CacheOpts) -> - IsValid = fun(Limit) when ((is_integer(Limit) andalso (Limit > 0)) orelse - (Limit =:= infinity)) -> + IsValid = fun(Limit) when ((is_integer(Limit) andalso + (Limit > 0)) orelse + (Limit =:= infinity)) -> true; (_) -> false end, - get_cacheopt(Cache, gclimit, CacheOpts, - infinity, ?DEFAULT_CACHE_GCLIMIT, + get_cacheopt(Cache, gclimit, CacheOpts, + infinity, ?DEFAULT_CACHE_GCLIMIT, IsValid). get_cacheopt_age(Cache, CacheOpts) -> @@ -765,8 +823,18 @@ get_cacheopt_age(Cache, CacheOpts) -> (_) -> false end, - get_cacheopt(Cache, age, CacheOpts, - ?DEFAULT_CACHE_AGE, ?DEFAULT_CACHE_AGE, + get_cacheopt(Cache, age, CacheOpts, + ?DEFAULT_CACHE_AGE, ?DEFAULT_CACHE_AGE, + IsValid). + +get_cacheopt_gcverbose(Cache, CacheOpts) -> + IsValid = fun(Verbosity) when is_boolean(Verbosity) -> + true; + (_) -> + false + end, + get_cacheopt(Cache, gcverbose, CacheOpts, + ?DEFAULT_CACHE_GCVERBOSE, ?DEFAULT_CACHE_GCVERBOSE, IsValid). get_cacheopt(?NO_CACHE, _, _, NoCacheVal, _, _) -> @@ -843,21 +911,28 @@ start_cache_gc_timer() -> %% ---------------------------------------------------------------- +gcvprint(GcVerbose, F) -> + gcvprint(GcVerbose, F, []). + +gcvprint(true, F, A) -> + ?vinfo(F, A); +gcvprint(_, _, _) -> + ok. + maybe_gc_cache(?NO_CACHE, _Age) -> ?vtrace("cache not enabled", []), ok; maybe_gc_cache(Cache, Age) -> - MatchSpec = gc_cache_matchspec(Age), - Keys = ets:select(Cache, MatchSpec), - do_gc_cache(Cache, Keys), - {ok, length(Keys)}. + MatchSpec = gc_cache_matchspec_del(Age), + NumDeleted = ets:select_delete(Cache, MatchSpec), + {ok, NumDeleted}. maybe_gc_cache(?NO_CACHE, _Age, _GcLimit) -> ok; maybe_gc_cache(Cache, Age, infinity = _GcLimit) -> maybe_gc_cache(Cache, Age); maybe_gc_cache(Cache, Age, GcLimit) -> - MatchSpec = gc_cache_matchspec(Age), + MatchSpec = gc_cache_matchspec_key(Age), Keys = case ets:select(Cache, MatchSpec, GcLimit) of {Match, _Cont} -> @@ -868,14 +943,26 @@ maybe_gc_cache(Cache, Age, GcLimit) -> do_gc_cache(Cache, Keys), {ok, length(Keys)}. -gc_cache_matchspec(Age) -> - Oldest = timestamp() - Age, +gc_cache_matchspec_del(Age) -> + %% The entry is a 3-tuple: {Key, Value, Timestamp} + MatchHead = {'_', '_', '$2'}, + Return = true, + gc_cache_matchspec(Age, MatchHead, Return). + +gc_cache_matchspec_key(Age) -> + %% The entry is a 3-tuple: {Key, Value, Timestamp} MatchHead = {'$1', '_', '$2'}, + Return = '$1', + gc_cache_matchspec(Age, MatchHead, Return). + +gc_cache_matchspec(Age, MatchHead, Return) -> + Oldest = timestamp() - Age, Guard = [{'<', '$2', Oldest}], - MatchFunc = {MatchHead, Guard, ['$1']}, + MatchFunc = {MatchHead, Guard, [Return]}, MatchSpec = [MatchFunc], MatchSpec. + do_gc_cache(_, []) -> ok; do_gc_cache(Cache, [Key|Keys]) -> @@ -906,20 +993,37 @@ maybe_cache_lookup(?NO_CACHE, _) -> maybe_cache_lookup(Cache, Key) -> ets:lookup(Cache, Key). -size_cache(?NO_CACHE) -> + +cache_info(?NO_CACHE) -> undefined; -size_cache(Cache) -> - case (catch ets:info(Cache, memory)) of - Sz when is_integer(Sz) -> - Sz; - _ -> - undefined +cache_info(Cache) -> + try + begin + [ + {memory, ets:info(Cache, memory)}, + {size, ets:info(Cache, size)}, + {stats, ets:info(Cache, stats)} + ] + end + catch + _:_:_ -> + undefined end. + timestamp() -> snmp_misc:now(ms). +maybe_send_gc_result(S, Result) -> + maybe_send_gc_event(S, gc_result, Result). + +maybe_send_gc_event(#state{cache_sub = Sub}, Ev, Info) when is_pid(Sub) -> + Sub ! {self(), Ev, Info}; +maybe_send_gc_event(_, _, _) -> + ok. + + %% ---------------------------------------------------------------- get_opt(Key, Options) -> |