diff options
Diffstat (limited to 'lib/snmp/src/agent/snmp_generic_mnesia.erl')
-rw-r--r-- | lib/snmp/src/agent/snmp_generic_mnesia.erl | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmp_generic_mnesia.erl b/lib/snmp/src/agent/snmp_generic_mnesia.erl new file mode 100644 index 0000000000..a73aad5b33 --- /dev/null +++ b/lib/snmp/src/agent/snmp_generic_mnesia.erl @@ -0,0 +1,400 @@ +%% +%% %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_generic_mnesia). + +-export([variable_get/1, variable_set/2, variable_inc/2]). +-export([table_func/2, table_func/4, + table_set_cols/4, table_set_element/4, table_set_elements/3, + table_get_elements/4, table_get_row/2, table_get_row/3, + table_next/2,table_set_status/7, + table_try_make_consistent/2, + table_delete_row/2]). + +-include("STANDARD-MIB.hrl"). +-include("snmp_types.hrl"). +%% -include("snmp_generic.hrl"). + +%%%----------------------------------------------------------------- +%%% Generic functions for implementing software tables +%%% and variables. Mnesia is used. +%%%----------------------------------------------------------------- + +%%------------------------------------------------------------------ +%% Theses functions could be in the MIB for simple +%% variables or tables, i.e. vars without complex +%% set-operations. If there are complex set op, an +%% extra layer-function should be added, and that +%% function should be in the MIB, and it can call these +%% functions. +%%------------------------------------------------------------------ + +%%------------------------------------------------------------------ +%% Variables +%%------------------------------------------------------------------ +%%------------------------------------------------------------------ +%% This is the default function for variables. +%%------------------------------------------------------------------ +variable_get(Name) -> + case mnesia:dirty_read({snmp_variables, Name}) of + [{_Db, _Name, Val}] -> {value, Val}; + _ -> undefined + end. + +variable_set(Name, Val) -> + mnesia:dirty_write({snmp_variables, Name, Val}), + true. + +variable_inc(Name, N) -> + case mnesia:dirty_update_counter({snmp_variables, Name}, N) of + NewVal when NewVal < 4294967296 -> + ok; + NewVal -> + mnesia:dirty_write({snmp_variables, Name, NewVal rem 4294967296}) + end. + +%%------------------------------------------------------------------ +%% Tables +%% Assumes the RowStatus is the last column in the +%% table. +%%------------------------------------------------------------------ +%%------------------------------------------------------------------ +%% This is the default function for tables. +%% +%% Name is the name of the table (atom) +%% RowIndex is a flat list of the indexes for the row. +%% Cols is a list of column numbers. +%%------------------------------------------------------------------ +table_func(new, _Name) -> + ok; + +table_func(delete, _Name) -> + ok. + +table_func(get, RowIndex, Cols, Name) -> + TableInfo = snmp_generic:table_info(Name), + snmp_generic:handle_table_get({Name, mnesia}, RowIndex, Cols, + TableInfo#table_info.first_own_index); + +%%------------------------------------------------------------------ +%% Returns: List of endOfTable | {NextOid, Value}. +%% Implements the next operation, with the function +%% handle_table_next. Next should return the next accessible +%% instance, which cannot be a key (well, it could, but it +%% shouldn't). +%%------------------------------------------------------------------ +table_func(get_next, RowIndex, Cols, Name) -> + #table_info{first_accessible = FirstCol, first_own_index = FOI, + nbr_of_cols = LastCol} = snmp_generic:table_info(Name), + snmp_generic:handle_table_next({Name,mnesia},RowIndex,Cols, + FirstCol, FOI, LastCol); + +table_func(is_set_ok, RowIndex, Cols, Name) -> + snmp_generic:table_try_row({Name, mnesia}, nofunc, RowIndex, Cols); + +%%------------------------------------------------------------------ +%% Cols is here a list of {ColumnNumber, NewValue} +%% This function must only be used by tables with a RowStatus col! +%% Other tables should use table_set_cols/4. +%% All set functionality is handled within a transaction. +%% +%% GenericMnesia uses its own table_set_status and own table_try_make_consistent +%% for performance reasons. +%%------------------------------------------------------------------ +table_func(set, RowIndex, Cols, Name) -> + case mnesia:transaction( + fun() -> + snmp_generic:table_set_row( + {Name, mnesia}, nofunc, + {snmp_generic_mnesia, table_try_make_consistent}, + RowIndex, Cols) + end) of + {atomic, Value} -> + Value; + {aborted, Reason} -> + user_err("set transaction aborted. Tab ~w, RowIndex" + " ~w, Cols ~w. Reason ~w", + [Name, RowIndex, Cols, Reason]), + {Col, _Val} = hd(Cols), + {commitFailed, Col} + end; + +table_func(undo, _RowIndex, _Cols, _Name) -> + {noError, 0}. + + +table_get_row(Name, RowIndex) -> + case mnesia:snmp_get_row(Name, RowIndex) of + {ok, DbRow} -> + TableInfo = snmp_generic:table_info(Name), + make_row(DbRow, TableInfo#table_info.first_own_index); + undefined -> + undefined + end. +table_get_row(Name, RowIndex, FOI) -> + case mnesia:snmp_get_row(Name, RowIndex) of + {ok, DbRow} -> + make_row(DbRow, FOI); + undefined -> + undefined + end. + +%%----------------------------------------------------------------- +%% Returns: [Val | noacc | noinit] | undefined +%%----------------------------------------------------------------- +table_get_elements(Name, RowIndex, Cols, FirstOwnIndex) -> + case mnesia:snmp_get_row(Name, RowIndex) of + {ok, DbRow} -> + Row = make_row(DbRow, FirstOwnIndex), + get_elements(Cols, Row); + undefined -> + undefined + end. + +get_elements([Col | Cols], Row) -> + [element(Col, Row) | get_elements(Cols, Row)]; +get_elements([], _Row) -> []. + +%%----------------------------------------------------------------- +%% Args: DbRow is a mnesia row ({name, Keys, Cols, ...}). +%% Returns: A tuple with a SNMP-table row. Each SNMP-col is one +%% element, list or int. +%%----------------------------------------------------------------- +make_row(DbRow, 0) -> + [_Name, _Keys | Cols] = tuple_to_list(DbRow), + list_to_tuple(Cols); +make_row(DbRow, FirstOwnIndex) -> + list_to_tuple(make_row2(make_row_list(DbRow), FirstOwnIndex)). +make_row2(RowList, 1) -> RowList; +make_row2([_OtherIndex | RowList], N) -> + make_row2(RowList, N-1). + +make_row_list(Row) -> + make_row_list(size(Row), Row, []). +make_row_list(N, Row, Acc) when N > 2 -> + make_row_list(N-1, Row, [element(N, Row) | Acc]); +make_row_list(2, Row, Acc) -> + case element(2, Row) of + Keys when is_tuple(Keys) -> + lists:append(tuple_to_list(Keys), Acc); + Key -> + [Key | Acc] + end. + +%% createAndGo +table_set_status(Name, RowIndex, ?'RowStatus_createAndGo', _StatusCol, Cols, + ChangedStatusFunc, _ConsFunc) -> + Row = table_construct_row(Name, RowIndex, ?'RowStatus_active', Cols), + mnesia:write(Row), + snmp_generic:try_apply(ChangedStatusFunc, [Name, ?'RowStatus_createAndGo', + RowIndex, Cols]); + +%%------------------------------------------------------------------ +%% createAndWait - set status to notReady, and try to +%% make row consistent. +%%------------------------------------------------------------------ +table_set_status(Name, RowIndex, ?'RowStatus_createAndWait', _StatusCol, + Cols, ChangedStatusFunc, ConsFunc) -> + Row = table_construct_row(Name, RowIndex, ?'RowStatus_notReady', Cols), + mnesia:write(Row), + case snmp_generic:try_apply(ConsFunc, [RowIndex, Row]) of + {noError, 0} -> snmp_generic:try_apply(ChangedStatusFunc, + [Name, ?'RowStatus_createAndWait', + RowIndex, Cols]); + Error -> Error + end; + +%% destroy +table_set_status(Name, RowIndex, ?'RowStatus_destroy', _StatusCol, Cols, + ChangedStatusFunc, _ConsFunc) -> + case snmp_generic:try_apply(ChangedStatusFunc, + [Name, ?'RowStatus_destroy', RowIndex, Cols]) of + {noError, 0} -> + #table_info{index_types = Indexes} = snmp_generic:table_info(Name), + Key = + case snmp_generic:split_index_to_keys(Indexes, RowIndex) of + [Key1] -> Key1; + KeyList -> list_to_tuple(KeyList) + end, + mnesia:delete({Name, Key}), + {noError, 0}; + Error -> Error + end; + +%% Otherwise, active or notInService +table_set_status(Name, RowIndex, Val, _StatusCol, Cols, + ChangedStatusFunc, ConsFunc) -> + table_set_cols(Name, RowIndex, Cols, ConsFunc), + snmp_generic:try_apply(ChangedStatusFunc, [Name, Val, RowIndex, Cols]). + +table_delete_row(Name, RowIndex) -> + case mnesia:snmp_get_mnesia_key(Name, RowIndex) of + {ok, Key} -> + mnesia:delete({Name, Key}); + undefined -> + ok + end. + + +%%------------------------------------------------------------------ +%% This function is a simple consistency check +%% function which could be used by the user-defined +%% table functions. +%% Check if the row has all information needed to +%% make row notInService (from notReady). This is +%% a simple check, which just checks if some col +%% in the row has the value 'noinit'. +%% If it has the information, the status is changed +%% to notInService. +%%------------------------------------------------------------------ +table_try_make_consistent(RowIndex, NewDbRow) -> + Name = element(1, NewDbRow), + #table_info{first_own_index = FirstOwnIndex, + status_col = StatusCol, index_types = IT} = + snmp_generic:table_info(Name), + if + is_integer(StatusCol) -> + NewRow = make_row(NewDbRow, FirstOwnIndex), + StatusVal = element(StatusCol, NewRow), + AddCol = if + FirstOwnIndex == 0 -> 2; + true -> 1 + FirstOwnIndex - length(IT) + end, + table_try_make_consistent(Name, RowIndex, NewRow, NewDbRow, + AddCol, StatusCol, StatusVal); + true -> + {noError, 0} + end. + + +table_try_make_consistent(Name, RowIndex, NewRow, NewDbRow, + AddCol, StatusCol, ?'RowStatus_notReady') -> + case lists:member(noinit, tuple_to_list(NewRow)) of + true -> {noError, 0}; + false -> + table_set_element(Name, RowIndex, StatusCol, + ?'RowStatus_notInService'), + NewDbRow2 = set_new_row([{StatusCol, ?'RowStatus_notInService'}], + AddCol, NewDbRow), + mnesia:write(NewDbRow2), + {noError, 0} + end; + +table_try_make_consistent(_Name, _RowIndex, _NewRow, _NewDBRow, + _AddCol, _StatusCol, _StatusVal) -> + {noError, 0}. + +%%------------------------------------------------------------------ +%% Constructs a row that is to be stored in Mnesia, i.e. +%% {Name, Key, Col1, ...} | +%% {Name, {Key1, Key2, ..}, ColN, ColN+1...} +%% dynamic key values are stored without length first. +%% RowIndex is a list of the first elements. RowStatus is needed, +%% because the provided value may not be stored, e.g. createAndGo +%% should be active. If a value isn't specified in the Col list, +%% then the corresponding value will be noinit. +%%------------------------------------------------------------------ +table_construct_row(Name, RowIndex, Status, Cols) -> + #table_info{nbr_of_cols = LastCol, index_types = Indexes, + defvals = Defs, status_col = StatusCol, + first_own_index = FirstOwnIndex, not_accessible = NoAccs} = + snmp_generic:table_info(Name), + KeyList = snmp_generic:split_index_to_keys(Indexes, RowIndex), + OwnKeyList = snmp_generic:get_own_indexes(FirstOwnIndex, KeyList), + StartCol = length(OwnKeyList) + 1, + RowList = snmp_generic:table_create_rest(StartCol, LastCol, + StatusCol, Status, Cols, NoAccs), + L = snmp_generic:init_defaults(Defs, RowList, StartCol), + Keys = case KeyList of + [H] -> H; + _ -> list_to_tuple(KeyList) + end, + list_to_tuple([Name, Keys | L]). + +%%------------------------------------------------------------------ +%% table_set_cols/4 +%% can be used by the set procedure of all tables +%% to set all columns in Cols, one at a time. +%% ConsFunc is a check-consistency function, which will +%% be called with the RowIndex of this row, when +%% all columns are set. This is useful when the RowStatus +%% could change, e.g. if the manager has provided all +%% mandatory columns in this set operation. +%% If ConsFunc is nofunc, no function will be called after all +%% sets. +%% Returns: {noError, 0} | {Error, Col} +%%------------------------------------------------------------------ +table_set_cols(Name, RowIndex, Cols, ConsFunc) -> + table_set_elements(Name, RowIndex, Cols, ConsFunc). + +%%----------------------------------------------------------------- +%% Col is _not_ a key column. A row in the db is stored as +%% {Name, {Key1, Key2,...}, Col1, Col2, ...} +%%----------------------------------------------------------------- +table_set_element(Name, RowIndex, Col, NewVal) -> + #table_info{index_types = Indexes, first_own_index = FirstOwnIndex} = + snmp_generic:table_info(Name), + DbCol = if + FirstOwnIndex == 0 -> Col + 2; + true -> 1 + FirstOwnIndex - length(Indexes) + Col + end, + case mnesia:snmp_get_row(Name, RowIndex) of + {ok, DbRow} -> + NewDbRow = setelement(DbCol, DbRow, NewVal), + mnesia:write(NewDbRow), + true; + undefined -> + false + end. + +table_set_elements(Name, RowIndex, Cols) -> + case table_set_elements(Name, RowIndex, Cols, nofunc) of + {noError, 0} -> true; + _ -> false + end. +table_set_elements(Name, RowIndex, Cols, ConsFunc) -> + #table_info{index_types = Indexes, first_own_index = FirstOwnIndex} = + snmp_generic:table_info(Name), + AddCol = if + FirstOwnIndex == 0 -> 2; + true -> 1 + FirstOwnIndex - length(Indexes) + end, + case mnesia:snmp_get_row(Name, RowIndex) of + {ok, DbRow} -> + NewDbRow = set_new_row(Cols, AddCol, DbRow), + mnesia:write(NewDbRow), + snmp_generic:try_apply(ConsFunc, [RowIndex, NewDbRow]); + undefined -> + {Col, _Val} = hd(Cols), + {commitFailed, Col} + end. + +set_new_row([{Col, Val} | Cols], AddCol, Row) -> + set_new_row(Cols, AddCol, setelement(Col+AddCol, Row, Val)); +set_new_row([], _AddCol, Row) -> + Row. + +table_next(Name, RestOid) -> + case mnesia:snmp_get_next_index(Name, RestOid) of + {ok, NextIndex} -> NextIndex; + endOfTable -> endOfTable + end. + + +user_err(F, A) -> + snmpa_error:user_err(F, A). |