diff options
Diffstat (limited to 'lib/snmp')
-rw-r--r-- | lib/snmp/src/agent/modules.mk | 2 | ||||
-rw-r--r-- | lib/snmp/src/agent/snmpa_mib.erl | 103 | ||||
-rw-r--r-- | lib/snmp/src/agent/snmpa_mib_data.erl | 59 | ||||
-rw-r--r-- | lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 72 | ||||
-rw-r--r-- | lib/snmp/src/app/snmp.app.src | 1 | ||||
-rw-r--r-- | lib/snmp/src/app/snmp.appup.src | 12 | ||||
-rw-r--r-- | lib/snmp/vsn.mk | 2 |
7 files changed, 154 insertions, 97 deletions
diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index 23f53a772c..e03f60120a 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -28,6 +28,7 @@ BEHAVIOUR_MODULES = \ snmpa_notification_filter \ snmpa_set_mechanism +# snmpa_mib_data_ttln MODULES = \ $(BEHAVIOUR_MODULES) \ snmpa \ @@ -44,7 +45,6 @@ MODULES = \ snmpa_local_db \ snmpa_mib \ snmpa_mib_data_tttn \ - snmpa_mib_data_ttln \ snmpa_mib_lib \ snmpa_misc_sup \ snmpa_mpd \ diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index a5e4a4cb73..1bd7398a69 100644 --- a/lib/snmp/src/agent/snmpa_mib.erl +++ b/lib/snmp/src/agent/snmpa_mib.erl @@ -81,7 +81,7 @@ -record(state, {data, meo, teo, backup, cache, cache_tmr, cache_autogc, cache_gclimit, cache_age, - mib_data_mod}). + data_mod}). @@ -225,9 +225,9 @@ info(MibServer, Type) -> call(MibServer, {info, Type}). dump(MibServer) -> - call(MibServer, dump). + dump(MibServer, io). -dump(MibServer, File) when is_list(File) -> +dump(MibServer, File) when (File =:= io) orelse is_list(File) -> call(MibServer, {dump, File}). backup(MibServer, BackupDir) when is_list(BackupDir) -> @@ -290,15 +290,15 @@ do_init(Prio, Mibs, Opts) -> MeOverride = get_me_override(Opts), TeOverride = get_te_override(Opts), MibStorage = get_mib_storage(Opts), - MibDataMod = get_mib_data_mod(Otps), - Data = snmpa_mib_data:new(MibStorage), + MibDataMod = get_data_mod(Opts), + Data = MibDataMod:new(MibStorage), ?vtrace("init -> mib data created",[]), case (catch mib_operations(MibDataMod, load_mib, Mibs, Data, MeOverride, TeOverride, true)) of {ok, Data2} -> ?vdebug("started",[]), - snmpa_mib_data:sync(Data2), + MibDataMod:sync(Data2), ?vdebug("mib data synced",[]), {ok, #state{data = Data2, teo = TeOverride, @@ -308,7 +308,7 @@ do_init(Prio, Mibs, Opts) -> cache_autogc = CacheAutoGC, cache_gclimit = CacheGcLimit, cache_age = CacheAge, - mib_data_mib = MibDataMib}}; + data_mod = MibDataMod}}; {'aborted at', Mib, _NewData, Reason} -> ?vinfo("failed loading mib ~p: ~p",[Mib,Reason]), {error, {Mib, Reason}} @@ -341,12 +341,12 @@ mib_operation(Mod, Operation, Mib, Data0, MeOverride, TeOverride, Force) when is_list(Mib) -> ?vtrace("mib operation on mib ~p", [Mib]), case apply(Mod, Operation, [Data0, Mib, MeOverride, TeOverride]) of - {error, 'already loaded'} when (Operation =:= load_mib) andalso + {error, already_loaded} when (Operation =:= load_mib) andalso (Force =:= true) -> ?vlog("ignore mib ~p -> already loaded", [Mib]), Data0; - {error, 'not loaded'} when (Operation =:= unload_mib) andalso - (Force =:= true) -> + {error, not_loaded} when (Operation =:= unload_mib) andalso + (Force =:= true) -> ?vlog("ignore mib ~p -> not loaded", [Mib]), Data0; {error, Reason} -> @@ -401,7 +401,7 @@ handle_call({update_cache_opts, Key, Value}, _From, State) -> {reply, Result, NewState}; handle_call({lookup, Oid}, _From, - #state{data = Data, cache = Cache, mib_data_mod = Mod} = State) -> + #state{data = Data, cache = Cache, data_mod = Mod} = State) -> ?vlog("lookup ~p", [Oid]), Key = {lookup, Oid}, {Reply, NewState} = @@ -421,14 +421,14 @@ handle_call({lookup, Oid}, _From, {reply, Reply, NewState}; handle_call({which_mib, Oid}, _From, - #state{data = Data, mib_data_mod = Mod} = State) -> + #state{data = Data, data_mod = Mod} = State) -> ?vlog("which_mib ~p",[Oid]), Reply = Mod:which_mib(Data, Oid), ?vdebug("which_mib: ~p",[Reply]), {reply, Reply, State}; handle_call({next, Oid, MibView}, _From, - #state{data = Data, cache = Cache, mib_data_mod = Mod} = State) -> + #state{data = Data, cache = Cache, data_mod = Mod} = State) -> ?vlog("next ~p [~p]", [Oid, MibView]), Key = {next, Oid, MibView}, {Reply, NewState} = @@ -452,18 +452,18 @@ handle_call({load_mibs, Mibs}, _From, teo = TeOverride, meo = MeOverride, cache = Cache, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("load mibs ~p",[Mibs]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), - {NData,Reply} = + {NData, Reply} = case (catch mib_operations(Mod, load_mib, Mibs, Data, MeOverride, TeOverride)) of {'aborted at', Mib, NewData, Reason} -> ?vlog("aborted at ~p for reason ~p",[Mib,Reason]), - {NewData,{error, {'load aborted at', Mib, Reason}}}; + {NewData, {error, {'load aborted at', Mib, Reason}}}; {ok, NewData} -> - {NewData,ok} + {NewData, ok} end, Mod:sync(NData), {reply, Reply, State#state{data = NData, cache = NewCache}}; @@ -473,38 +473,39 @@ handle_call({unload_mibs, Mibs}, _From, teo = TeOverride, meo = MeOverride, cache = Cache, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("unload mibs ~p",[Mibs]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), %% Unload mib(s) - {NData,Reply} = + {NData, Reply} = case (catch mib_operations(Mod, unload_mib, Mibs, Data, MeOverride, TeOverride)) of {'aborted at', Mib, NewData, Reason} -> - ?vlog("aborted at ~p for reason ~p",[Mib,Reason]), + ?vlog("aborted at ~p for reason ~p", [Mib,Reason]), {NewData, {error, {'unload aborted at', Mib, Reason}}}; {ok, NewData} -> - {NewData,ok} + {NewData, ok} end, Mod:sync(NData), {reply, Reply, State#state{data = NData, cache = NewCache}}; -handle_call(which_mibs, _From, #state{data = Data} = State) -> +handle_call(which_mibs, _From, #state{data = Data, data_mod = Mod} = State) -> ?vlog("which mibs",[]), Reply = Mod:which_mibs(Data), {reply, Reply, State}; handle_call({whereis_mib, Mib}, _From, #state{data = Data, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("whereis mib: ~p",[Mib]), Reply = Mod:whereis_mib(Data, Mib), {reply, Reply, State}; handle_call({register_subagent, Oid, Pid}, _From, - #state{data = Data, cache = Cache, - mib_data_mod = Mod} = State) -> + #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("register subagent ~p, ~p",[Oid,Pid]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), @@ -512,29 +513,31 @@ handle_call({register_subagent, Oid, Pid}, _From, {error, Reason} -> ?vlog("registration failed: ~p",[Reason]), {reply, {error, Reason}, State#state{cache = NewCache}}; - NewData -> + {ok, NewData} -> {reply, ok, State#state{data = NewData, cache = NewCache}} end; handle_call({unregister_subagent, OidOrPid}, _From, - #state{data = Data, cache = Cache, - mib_data_mod = Mod} = State) -> + #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("unregister subagent ~p",[OidOrPid]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), case Mod:unregister_subagent(Data, OidOrPid) of + {ok, NewData} -> + {reply, ok, State#state{data = NewData, cache = NewCache}}; {ok, NewData, DeletedSubagentPid} -> {reply, {ok, DeletedSubagentPid}, State#state{data = NewData, cache = NewCache}}; {error, Reason} -> ?vlog("unregistration failed: ~p",[Reason]), - {reply, {error, Reason}, State#state{cache = NewCache}}; - NewData -> - {reply, ok, State#state{data = NewData, cache = NewCache}} + {reply, {error, Reason}, State#state{cache = NewCache}} end; -handle_call(info, _From, #state{data = Data, cache = Cache, - mib_data_mod = Mod} = State) -> +handle_call(info, _From, #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("info",[]), Reply = case (catch Mod:info(Data)) of @@ -546,8 +549,8 @@ handle_call(info, _From, #state{data = Data, cache = Cache, {reply, Reply, State}; handle_call({info, Type}, _From, - #state{data = Data, - mib_data_mod = Mod} = State) -> + #state{data = Data, + data_mod = Mod} = State) -> ?vlog("info ~p",[Type]), Reply = case (catch Mod:info(Data, Type)) of @@ -558,13 +561,8 @@ handle_call({info, Type}, _From, end, {reply, Reply, State}; -handle_call(dump, _From, #state{mib_data_mod = Mod} = _State) -> - ?vlog("dump",[]), - Reply = Mod:dump(State#state.data), - {reply, Reply, State}; - handle_call({dump, File}, _From, - #state{data = Data, mib_data_mod = Mod} = State) -> + #state{data = Data, data_mod = Mod} = State) -> ?vlog("dump on ~s",[File]), Reply = Mod:dump(Data, File), {reply, Reply, State}; @@ -573,8 +571,9 @@ handle_call({dump, File}, _From, %% done in the master agent process, but just in case a user issues %% a backup call to this process directly, we add a similar check here. handle_call({backup, BackupDir}, From, - #state{backup = undefined, - data = Data, mib_data_mod = Mod} = State) -> + #state{backup = undefined, + data = Data, + data_mod = Mod} = State) -> ?vlog("backup to ~s", [BackupDir]), Pid = self(), V = get(verbosity), @@ -655,7 +654,7 @@ handle_info(Info, State) -> warning_msg("received unknown info: ~n~p", [Info]), {noreply, State}. -terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> +terminate(_Reason, #state{data = Data, data_mod = Mod}) -> catch Mod:close(Data), ok. @@ -673,6 +672,11 @@ terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> %% S2 = {state, Data, MEO, TEO, B}, %% {ok, S2}; +code_change({down, Vsn}, #state{data = Data0, data_mod = Mod} = State, Extra) -> + Data = Mod:code_change(down, Vsn, Extra, Data0), + {ok, State#state{data = Data}}; + + %% %% upgrade %% %% %% code_change(_Vsn, S1, upgrade_from_pre_4_12) -> @@ -681,12 +685,8 @@ terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> %% S2 = #state{data = Data, meo = MEO, teo = TEO, backup = B, cache = Cache}, %% {ok, S2}; -code_change({down, _Vsn}, #state{mib_data_mod = Mod} = State, _Extra) -> - Data = Mod:code_change(down, Data0), - {ok, State#state{data = Data}}; - -code_change(_Vsn, #state{mib_data_mod = Mod} = State, _Extra) -> - Data = Mod:code_change(up, Data0), +code_change(Vsn, #state{data = Data0, data_mod = Mod} = State, Extra) -> + Data = Mod:code_change(up, Vsn, Extra, Data0), {ok, State#state{data = Data}}. @@ -706,6 +706,9 @@ get_te_override(Options) -> get_mib_storage(Options) -> get_opt(mib_storage, Options, ets). +get_data_mod(Options) -> + get_opt(data_module, Options, snmpa_mib_data_tttn). + get_cacheopt_autogc(Cache, CacheOpts) -> IsValid = fun(AutoGC) when ((AutoGC =:= true) orelse (AutoGC =:= false)) -> diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index 790b056065..30078db3cf 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -18,11 +18,54 @@ %% -module(snmpa_mib_data). +-include_lib("snmp/include/snmp_types.hrl"). + %%%----------------------------------------------------------------- %%% This is the behaviour for the MIB server backend internal %%% data storage. %%%----------------------------------------------------------------- +%% These types should really be defined elsewhere... +-export_type([ + oid/0, + + mib_storage/0, + mib_storage_dir/0, + mib_storage_action/0, + + mib_view/0, + mib_view_elem/0, + mib_view_mask/0, + mib_view_inclusion/0 + ]). + +-type oid() :: [non_neg_integer()]. + +-type mib_storage() :: ets | + {ets, Dir :: mib_storage_dir()} | + {ets, Dir :: mib_storage_dir(), Action :: mib_storage_action()} | + dets | + {dets, Dir :: mib_storage_dir()} | + {dets, Dir :: mib_storage_dir(), Action :: mib_storage_action()} | + mnesia | + {mnesia, Nodes :: [node()]} | + {mnesia, Nodes :: [node()], + Action :: mib_storage_action()}. + +-type mib_storage_dir() :: default | string(). +-type mib_storage_action() :: clear | keep. + +-type mib_view() :: [mib_view_elem()]. +-type mib_view_elem() :: {SubTree :: oid(), + Mask :: [non_neg_integer()], + Inclusion :: mib_view_inclusion()}. +-type mib_view_mask() :: [non_neg_integer()]. +-type mib_view_inclusion() :: 1 | 2. % 1 = included, 2 = excluded + +-type me() :: #me{}. + +-type filename() :: file:filename(). + -callback new(MibStorage :: mib_storage()) -> State :: term(). @@ -33,12 +76,12 @@ -callback load_mib(State :: term(), FileName :: string(), MeOverride :: boolean(), TeOverride :: boolean()) -> - {ok, NewState :: term()} | {error, already_loaded | Reason :: term()}. + {ok, NewState :: term()} | {error, Reason :: already_loaded | term()}. -callback unload_mib(State :: term(), FileName :: string(), MeOverride :: boolean(), TeOverride :: boolean()) -> - {ok, NewState :: term()} | {error, not_loaded | Reason :: term()}. + {ok, NewState :: term()} | {error, Reason :: not_loaded | term()}. -callback lookup(State :: term(), Oid :: oid()) -> {false, Reason :: term()} | @@ -50,14 +93,14 @@ endOfView | false | {subagent, SubAgentPid :: pid(), SAOid :: oid()} | {variable, MibEntry :: me(), VarOid :: oid()} | - {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()} + {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()}. -callback register_subagent(State :: term(), Oid :: oid(), Pid :: pid()) -> {ok, NewState :: term()} | {error, Reason :: term()}. -callback unregister_subagent(State :: term(), - Pid :: pid() | Oid :: oid()) -> - {ok, NewState :: term()} | % When second arg wa a pid() + PidOrOid :: pid() | oid()) -> + {ok, NewState :: term()} | % When second arg was a pid() {ok, NewState :: term(), Pid :: pid()} | % When second arg was a oid() {error, Reason :: term()}. @@ -74,12 +117,14 @@ {ok, Filename :: string()} | {error, Reason :: term()}. -callback info(State :: term()) -> list(). --callback info(State :: term(), Item :: atom()) -> term(). -callback backup(State :: term(), BackupDir :: string()) -> ok | {error, Reason :: term()}. --callback code_change(Direction :: up | down, State :: term()) -> +-callback code_change(Direction :: up | down, + Vsn :: term(), + Extra :: term(), + State :: term()) -> NewState :: term(). diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index 561e97deee..c24cff43e5 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -19,6 +19,9 @@ -module(snmpa_mib_data_tttn). %%%----------------------------------------------------------------- +%%% +%%% TTTN - TupleTreeTupleNodes +%%% %%% This module implements the MIB internal data structures. %%% An MIB Data Structure consists of three items; an ets-table, %%% a tree and a list of registered subagents. @@ -34,16 +37,16 @@ -include("snmp_types.hrl"). -include("snmp_debug.hrl"). --define(VMODULE,"MDATA"). +-define(VMODULE,"MDATA_TTTN"). -include("snmp_verbosity.hrl"). --define(MIB_DATA,snmpa_mib_data). --define(MIB_NODE,snmpa_mib_node). --define(MIB_TREE,snmpa_mib_tree). --define(DUMMY_TREE_GENERATION,1). --define(DEFAULT_TREE,{tree,{undefined_node},internal}). -%%-define(DUMMY_TREE_DB,dummy_tree_db). -%%-define(DUMMY_TREE_DB_INIT,{?DUMMY_TREE_DB,?DEFAULT_TREE}). +-behaviour(snmpa_mib_data). + +-define(MIB_DATA, snmpa_mib_data). +-define(MIB_NODE, snmpa_mib_node). +-define(MIB_TREE, snmpa_mib_tree). +-define(DUMMY_TREE_GENERATION, 1). +-define(DEFAULT_TREE, {tree,{undefined_node},internal}). %%%----------------------------------------------------------------- @@ -69,6 +72,7 @@ %% tree is the root node (same as ref_tree but with the subagents added). %% subagents is a list of {SAPid, Oid} %%---------------------------------------------------------------------- + -record(mib_data, {mib_db, % table of #mib_info node_db, % table of #node_info tree_db, % table of #tree @@ -80,16 +84,21 @@ %% API --export([new/0, new/1, sync/1, close/1, - load_mib/4, unload_mib/4, which_mibs/1, whereis_mib/2, +-export([new/1, + close/1, + sync/1, + load_mib/4, + unload_mib/4, + lookup/2, + next/3, + register_subagent/3, + unregister_subagent/2, + dump/2, + which_mib/2, which_mibs/1, + whereis_mib/2, info/1, info/2, - dump/1, dump/2, backup/2, - lookup/2, next/3, which_mib/2, - register_subagent/3, unregister_subagent/2]). - -%% Internal exports --export([code_change/2]). + code_change/4]). %%----------------------------------------------------------------- @@ -123,8 +132,6 @@ %% Func: new/0, new/1 %% Returns: A representation of mib data. %%----------------------------------------------------------------- -new() -> - new(ets). %% Where -> A list of nodes where the tables will be created new(Storage) -> @@ -408,10 +415,12 @@ whereis_mib(#mib_data{mib_db = Db}, Name) -> %% Purpose: Deletes SA with Pid from all subtrees it handles. %% Returns: NewMibData. %%---------------------------------------------------------------------- -unregister_subagent(MibData, Pid) when is_pid(Pid) -> +unregister_subagent(#mib_data{subagents = SAs} = MibData, Pid) + when is_pid(Pid) -> SAs = MibData#mib_data.subagents, case lists:keysearch(Pid, 1, SAs) of - false -> MibData; + false -> + {ok, MibData}; {value, {Pid, Oid}} -> % we should never get an error since Oid is found in MibData. {ok, NewMibData, _DeletedSA} = unregister_subagent(MibData, Oid), @@ -424,7 +433,7 @@ unregister_subagent(MibData, Pid) when is_pid(Pid) -> %% Returns: {error, Reason} | {ok, NewMibData, DeletedSubagentPid} %%---------------------------------------------------------------------- unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) -> - case catch delete_subagent(T#tree.root, Oid) of + case (catch delete_subagent(T#tree.root, Oid)) of {tree, Tree, Info} -> OldSAs = MibData#mib_data.subagents, {value, {Pid, _Oid}} = lists:keysearch(Oid, 2, OldSAs), @@ -434,7 +443,7 @@ unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) -> MibData#mib_data{tree = T2, subagents = SAs}, Pid}; _ -> - {error, {'invalid oid', Oid}} + {error, {invalid_oid, Oid}} end. %%---------------------------------------------------------------------- @@ -479,6 +488,7 @@ old_format(LoadedMibs) -> %%---------------------------------------------------------------------- %% A total dump for debugging. %%---------------------------------------------------------------------- + dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, io) -> @@ -1346,14 +1356,16 @@ maybe_drop_me(_) -> true. %% Code change functions %%---------------------------------------------------------------------- -code_change(down, State) -> - ?d("code_change(down) -> entry",[]), - State; - -code_change(up, State) -> - ?d("code_change(up)",[]), +code_change(down, _Vsn, _Extra, State) -> + ?d("code_change(down) -> entry when" + "~n Vsn: ~p" + "~n Extra: ~p", [_Vsn, _Extra]), State; -code_change(_Vsn, State) -> +code_change(up, _Vsn, _Extra, State) -> + ?d("code_change(up) -> entry when" + "~n Vsn: ~p" + "~n Extra: ~p", [_Vsn, _Extra]), State. + diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index b11c1ef934..92a06b0c9f 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -52,6 +52,7 @@ snmpa_local_db, snmpa_mib, snmpa_mib_data, + snmpa_mib_data_tttn, snmpa_mib_lib, snmpa_misc_sup, snmpa_mpd, diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 4c5f14da90..7ffa4a725d 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,19 +22,15 @@ %% ----- U p g r a d e ------------------------------------------------------- [ - {"4.23", - [ - ] - } + {"4.23.1", [{restart_application, snmp}]}, + {"4.23", [{restart_application, snmp}]} ], %% ------D o w n g r a d e --------------------------------------------------- [ - {"4.23", - [ - ] - } + {"4.23.1", [{restart_application, snmp}]}, + {"4.23", [{restart_application, snmp}]} ] }. diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index fb7aa52402..0e48e7ea56 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.23.1 +SNMP_VSN = 4.24 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" |