aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmp_shadow_table.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/agent/snmp_shadow_table.erl')
-rw-r--r--lib/snmp/src/agent/snmp_shadow_table.erl187
1 files changed, 187 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmp_shadow_table.erl b/lib/snmp/src/agent/snmp_shadow_table.erl
new file mode 100644
index 0000000000..34543d542b
--- /dev/null
+++ b/lib/snmp/src/agent/snmp_shadow_table.erl
@@ -0,0 +1,187 @@
+%%
+%% %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(snmp_shadow_table).
+
+-export([table_func/2, table_func/4]).
+
+-include("snmpa_internal.hrl").
+
+-record(time_stamp, {key, data}).
+
+-define(verify(Expr, Error), verify(catch Expr, Error, ?FILE, ?LINE)).
+
+verify(Res, Error, File, Line) ->
+ case Res of
+ {atomic, _} ->
+ Res;
+ ok ->
+ Res;
+ _ ->
+ error_msg("~s(~w): crashed ~p -> ~p ~p~n",
+ [File, Line, Error, Res, process_info(self())]),
+ Res
+ end.
+
+
+%%%-----------------------------------------------------------------
+%%% This module contains generic functions for implementing an SNMP
+%%% table as a 'shadow'-table in Mnesia. This means that for the
+%%% SNMP table, there exists one Mnesia table with all information
+%%% of the table.
+%%% The Mnesia table is updated whenever an SNMP request is issued
+%%% for the table and a specified amount of time has past since the
+%%% last update.
+%%% This is implemented as instrumentation functions to be used
+%%% for the table.
+%%%-----------------------------------------------------------------
+
+create_time_stamp_table() ->
+ Props = [{type, set},
+ {attributes, record_info(fields, time_stamp)}],
+ create_table(time_stamp, Props, ram_copies, false),
+ NRef =
+ case mnesia:dirty_read({time_stamp, ref_count}) of
+ [] -> 1;
+ [#time_stamp{data = Ref}] -> Ref + 1
+ end,
+ ok = mnesia:dirty_write(#time_stamp{key = ref_count, data = NRef}).
+
+delete_time_stamp_table() ->
+ Tab = time_stamp,
+ case catch mnesia:dirty_read({Tab, ref_count}) of
+ {'EXIT', _Reason} ->
+ delete_table(Tab);
+ [] ->
+ delete_table(Tab);
+ [#time_stamp{data = 1}] ->
+ delete_table(Tab);
+ [#time_stamp{data = Ref}] ->
+ ok = mnesia:dirty_write(#time_stamp{key = ref_count, data = Ref - 1})
+ end.
+
+update(Name, UpdateFunc, Interval) ->
+ CurrentTime = get_time(),
+ case mnesia:dirty_read({time_stamp, Name}) of
+ [#time_stamp{data = Expire}] when CurrentTime =< Expire -> ok;
+ _ ->
+ UpdateFunc(),
+ ok = mnesia:dirty_write(#time_stamp{key = Name,
+ data = CurrentTime + Interval})
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: table_func(Op, Extra)
+%% table_func(Op, RowIndex, Cols, Extra)
+%% Args: Extra = {Name, SnmpKey, Attributes, Interval, UpdateFunc}
+%% Purpose: Instrumentation function for the table.
+%% Name is the name of the table
+%% SnmpKey is the snmpkey as it should be specifed in order
+%% to create the Mnesia table as an SNMP table
+%% Attributes is the attributes as it should be specifed in order
+%% to create the Mnesia table as an SNMP table
+%% Interval is the minimum time in milliseconds between two
+%% updates of the table
+%% UpdateFunc is a function with no arguments that is called
+%% whenever the table must be updated
+%% Returns: As specified for an SNMP table instrumentation function.
+%%-----------------------------------------------------------------
+table_func(new, {Name, SnmpKey, Attribs, _Interval, _UpdateFunc}) ->
+ create_time_stamp_table(),
+ Props = [{type, set},
+ {snmp, [{key, SnmpKey}]},
+ {attributes, Attribs}],
+ create_table(Name, Props, ram_copies, true);
+table_func(delete, {Name, _SnmpKey, _Attribs, _Interval, _UpdateFunc}) ->
+ delete_time_stamp_table(),
+ delete_table(Name).
+
+table_func(Op, RowIndex, Cols,
+ {Name, _SnmpKey, _Attribs, Interval, UpdateFunc}) ->
+ update(Name, UpdateFunc, Interval),
+ snmp_generic:table_func(Op, RowIndex, Cols, {Name, mnesia}).
+
+get_time() ->
+ {M,S,U} = erlang:now(),
+ 1000000000 * M + 1000 * S + (U div 1000).
+
+%%-----------------------------------------------------------------
+%% Urrk.
+%% We want named tables, without schema info; the tables should
+%% be locally named, but if the node crashes, info about the
+%% table shouldn't be kept. We could use ets tables for this.
+%% BUT, we also want the snmp functionality, therefore we must
+%% use mnesia.
+%% The problem arises when the node that implements these tables
+%% crashes, and another node takes over the MIB-implementations.
+%% That node cannot create the shadow tables again, because they
+%% already exist (according to mnesia...). Therefore, we must
+%% check if we maybe must delete the table first, and then create
+%% it again.
+%%-----------------------------------------------------------------
+create_table(Tab, Props, Storage, DeleteAll) ->
+ case lists:member(Tab, mnesia:system_info(tables)) of
+ true ->
+ case mnesia:table_info(Tab, storage_type) of
+ unknown ->
+ ?verify(mnesia:add_table_copy(Tab, node(), Storage),
+ [add_table_copy, Tab, node(), Storage]);
+ Storage when DeleteAll == true ->
+ delete_all(Tab);
+ _ ->
+ ignore
+ end;
+ false ->
+ Nodes = [node()],
+ Props2 = [{local_content, true}, {Storage, Nodes}] ++ Props,
+ ?verify(mnesia:create_table(Tab, Props2),
+ [create_table, Tab, Props2])
+ end.
+
+delete_all(Tab) ->
+ delete_all(mnesia:dirty_first(Tab), Tab).
+
+delete_all('$end_of_table', _Tab) ->
+ ok;
+delete_all(Key, Tab) ->
+ ok = mnesia:dirty_delete({Tab, Key}),
+ delete_all(mnesia:dirty_next(Tab, Key), Tab).
+
+delete_table(Tab) ->
+ case lists:member(Tab, mnesia:system_info(tables)) of
+ true ->
+ case ?verify(mnesia:del_table_copy(Tab, node()),
+ [del_table_copy, Tab, node()]) of
+ {atomic, ok} ->
+ ok;
+ {aborted, _Reason} ->
+ catch delete_all(Tab),
+ ok
+ end;
+ false ->
+ ok
+ end.
+
+
+%%-----------------------------------------------------------------
+
+error_msg(F, A) ->
+ ?snmpa_error(F, A).
+
+