diff options
Diffstat (limited to 'lib/snmp/src/agent/snmp_shadow_table.erl')
-rw-r--r-- | lib/snmp/src/agent/snmp_shadow_table.erl | 187 |
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). + + |