aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmpa_general_db.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/agent/snmpa_general_db.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/agent/snmpa_general_db.erl')
-rw-r--r--lib/snmp/src/agent/snmpa_general_db.erl578
1 files changed, 578 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmpa_general_db.erl b/lib/snmp/src/agent/snmpa_general_db.erl
new file mode 100644
index 0000000000..795c353a9e
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_general_db.erl
@@ -0,0 +1,578 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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(snmpa_general_db).
+
+
+%%%-----------------------------------------------------------------
+%%% This module implements a very simple "generic" MIB database
+%%% interface. It contains the most common functions performed.
+%%% It is generic in the sense that it implements both an interface
+%%% to mnesia and ets.
+%%%
+%%% Note that this module has nothing to do with the snmp_generic
+%%% and snmp_generic_mnesia modules.
+%%%-----------------------------------------------------------------
+
+-export([open/5, close/1, read/2, write/2, delete/1, delete/2]).
+-export([sync/1, backup/2]).
+-export([match_object/2, match_delete/2]).
+-export([tab2list/1, info/1, info/2]).
+
+
+-define(VMODULE,"GDB").
+-include("snmp_verbosity.hrl").
+
+
+%% ---------------------------------------------------------------
+%% open(Info,Name,RecName,Attr,Type) -> term()
+%% Info -> ets | {ets, Dir} |
+%% {dets, Dir} | {dets, Dir, Action} |
+%% {mnesia, Nodes} | {mnesia, Nodes, Action}
+%% Name -> atom()
+%% RecName -> Name of the record to store
+%% Attr -> Attributes of the record stored in the table
+%% Type -> set | bag
+%% Dir -> string()
+%% Nodes -> [node()]
+%% Action -> keep | clear
+%%
+%% Open or create a database table. In the mnesia/dets case,
+%% where the table is stored on disc, it could be of interest
+%% to clear the table. This is controlled by the Action parameter.
+%% An empty node list ([]), is traslated into a list containing
+%% only the own node.
+%% ---------------------------------------------------------------
+open({mnesia,Nodes,clear}, Name, RecName, Attr, Type) when is_list(Nodes) ->
+ ?vtrace("[mnesia] open ~p database ~p for ~p on ~p; clear",
+ [Type, Name, RecName, Nodes]),
+ mnesia_open(mnesia_table_check(Name), Nodes, RecName, Attr, Type, clear);
+open({mnesia,Nodes,_}, Name, RecName, Attr, Type) ->
+ ?vtrace("[mnesia] open ~p database ~p for ~p on ~p; keep",
+ [Type, Name, RecName, Nodes]),
+ open({mnesia,Nodes}, Name, RecName, Attr, Type);
+open({mnesia,Nodes}, Name, RecName, Attr, Type) when is_list(Nodes) ->
+ ?vtrace("[mnesia] open ~p database ~p for ~p on ~p",
+ [Type, Name, RecName, Nodes]),
+ mnesia_open(mnesia_table_check(Name), Nodes, RecName, Attr, Type, keep);
+
+open({dets, Dir, Action}, Name, _RecName, _Attr, Type) ->
+ dets_open(Name, dets_filename(Name, Dir), Type, Action);
+open({dets, Dir}, Name, _RecName, _Attr, Type) ->
+ dets_open(Name, dets_filename(Name, Dir), Type, keep);
+
+%% This function creates the ets table
+open(ets, Name, _RecName, _Attr, Type) ->
+ ?vtrace("[ets] open ~p database ~p", [Type, Name]),
+ Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
+ {ets, Ets, undefined};
+open({ets, Dir}, Name, _RecName, _Attr, Type) ->
+ ets_open(Name, Dir, keep, Type);
+open({ets, Dir, Action}, Name, _RecName, _Attr, Type) ->
+ ets_open(Name, Dir, Action, Type).
+
+ets_open(Name, Dir, keep, Type) ->
+ ?vtrace("[ets] open ~p database ~p", [Type, Name]),
+ File = filename:join(Dir, atom_to_list(Name) ++ ".db"),
+ case file:read_file_info(File) of
+ {ok, _} ->
+ case ets:file2tab(File) of
+ {ok, Tab} ->
+ {ets, Tab, File};
+ {error, Reason} ->
+ user_err("failed converting file to (ets-) tab: "
+ "File: ~p"
+ "~n~p", [File, Reason]),
+ Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
+ {ets, Ets, File}
+ end;
+ {error, _} ->
+ Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
+ {ets, Ets, File}
+ end;
+ets_open(Name, Dir, clear, Type) ->
+ File = filename:join(Dir, atom_to_list(Name) ++ ".db"),
+ Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
+ {ets, Ets, File}.
+
+
+
+mnesia_open({table_exist,Name},_Nodes,_RecName,_Attr,_Type,clear) ->
+ ?vtrace("[mnesia] database ~p already exists; clear content",[Name]),
+ Pattern = '_',
+ F = fun() ->
+ Recs = mnesia:match_object(Name,Pattern,read),
+ lists:foreach(fun(Rec) ->
+ mnesia:delete_object(Name,Rec,write)
+ end, Recs),
+ Recs
+ end,
+ case mnesia:transaction(F) of
+ {aborted,Reason} ->
+ exit({aborted,Reason});
+ {atomic,_} ->
+ {mnesia,Name}
+ end;
+mnesia_open({table_exist,Name},_Nodes,_RecName,_Attr,_Type,keep) ->
+ ?vtrace("[mnesia] database ~p already exists; keep content",[Name]),
+ {mnesia,Name};
+mnesia_open({no_table,Name},[],Type,RecName,Attr,Action) ->
+ mnesia_open({no_table,Name},[node()],Type,RecName,Attr,Action);
+mnesia_open({no_table,Name},Nodes,RecName,Attr,Type,_) ->
+ ?vtrace("[mnesia] no database ~p: create for ~p of type ~p",
+ [Name,RecName,Type]),
+ %% Ok, we assume that this means that the table does not exist
+ Args = [{record_name,RecName}, {attributes,Attr},
+ {type,Type}, {disc_copies,Nodes}],
+ case mnesia:create_table(Name,Args) of
+ {atomic,ok} ->
+ {mnesia,Name};
+ {aborted,Reason} ->
+ %% ?vinfo("[mnesia] aborted: ~p", [Reason]),
+ exit({failed_create_mnesia_table,Reason})
+ end.
+
+
+mnesia_table_check(Name) ->
+ ?vtrace("[mnesia] check existens of database ~p",[Name]),
+ case (catch mnesia:table_info(Name,type)) of
+ {'EXIT', _Reason} ->
+ {no_table, Name};
+ _ ->
+ {table_exist, Name}
+ end.
+
+
+dets_open(Name, File, Type, Action) ->
+ ?vtrace("[dets] open database ~p (~p)", [Name, Action]),
+ N = dets_open1(Name, File, Type),
+ dets_open2(N, Action).
+
+dets_open1(Name, File, Type) ->
+ ?vtrace("[dets] open database ~p of type ~p",[Name, Type]),
+ {ok, N} = dets:open_file(Name, [{file, File}, {type, Type}, {keypos, 2}]),
+ N.
+
+dets_open2(N, clear) ->
+ dets:match_delete(N,'_'),
+ {dets, N};
+dets_open2(N, _) ->
+ {dets, N}.
+
+%% dets_table_check(Name, Dir) ->
+%% Filename = dets_filename(Name, Dir),
+%% ?vtrace("[dets] check existens of database: "
+%% "~n ~p -> ~s"
+%% "~n ~p"
+%% "~n ~p"
+%% ,
+%% [Name, Filename, file:list_dir(Dir), file:read_file_info(Filename)]),
+%% case (catch dets:info(Filename, size)) of
+%% {'EXIT', Reason} ->
+%% {no_table, Name, Filename};
+%% undefined -> %% Introduced in R8
+%% {no_table, Name, Filename};
+%% _ ->
+%% {table_exist, Name, Filename}
+%% end.
+
+
+dets_filename(Name, Dir) ->
+ Dir1 = dets_filename1(Dir),
+ Dir2 = string:strip(Dir1, right, $/),
+ io_lib:format("~s/~p.dat", [Dir2, Name]).
+
+dets_filename1([]) -> ".";
+dets_filename1(Dir) -> Dir.
+
+
+%% ---------------------------------------------------------------
+%% close(DbRef) ->
+%% DbRef -> term()
+%%
+%% Close the database. This does nothing in the mnesia case, but
+%% deletes the table in the ets case.
+%% ---------------------------------------------------------------
+close({mnesia,_}) ->
+ ?vtrace("[mnesia] close database: NO ACTION",[]),
+ ok;
+close({dets, Name}) ->
+ ?vtrace("[dets] close database ~p",[Name]),
+ dets:close(Name);
+close({ets, Name, undefined}) ->
+ ?vtrace("[ets] close (delete) table ~p",[Name]),
+ ets:delete(Name);
+close({ets, Name, File}) ->
+ ?vtrace("[ets] close (delete) table ~p",[Name]),
+ write_ets_file(Name, File),
+ ets:delete(Name).
+
+
+%% ---------------------------------------------------------------
+%% read(DbRef,Key) -> false | {value,Rec}
+%% DbRef -> term()
+%% Rec -> tuple()
+%%
+%% Retrieve a record from the database.
+%% ---------------------------------------------------------------
+read({mnesia, Name}, Key) ->
+ ?vtrace("[mnesia] read (dirty) from database ~p: ~p",[Name,Key]),
+ case (catch mnesia:dirty_read(Name,Key)) of
+ [Rec|_] -> {value,Rec};
+ _ -> false
+ end;
+read({dets, Name}, Key) ->
+ ?vtrace("[dets] read from table ~p: ~p",[Name,Key]),
+ case dets:lookup(Name, Key) of
+ [Rec|_] -> {value, Rec};
+ _ -> false
+ end;
+read({ets, Name, _}, Key) ->
+ ?vtrace("[ets] read from table ~p: ~p",[Name,Key]),
+ case ets:lookup(Name, Key) of
+ [Rec|_] -> {value, Rec};
+ _ -> false
+ end.
+
+
+%% ---------------------------------------------------------------
+%% write(DbRef,Rec) -> ok
+%% DbRef -> term()
+%% Rec -> tuple()
+%%
+%% Write a record to the database.
+%% ---------------------------------------------------------------
+write({mnesia, Name}, Rec) ->
+ ?vtrace("[mnesia] write to database ~p",[Name]),
+ F = fun() -> mnesia:write(Name, Rec, write) end,
+ case mnesia:transaction(F) of
+ {aborted, Reason} ->
+ exit({aborted, Reason});
+ {atomic,_} ->
+ ok
+ end;
+write({dets, Name}, Rec) ->
+ ?vtrace("[dets] write to table ~p",[Name]),
+ dets:insert(Name, Rec);
+write({ets, Name, _}, Rec) ->
+ ?vtrace("[ets] write to table ~p",[Name]),
+ ets:insert(Name, Rec).
+
+
+%% ---------------------------------------------------------------
+%% delete(DbRef) ->
+%% DbRef -> term()
+%%
+%% Delete the database.
+%% ---------------------------------------------------------------
+delete({mnesia, Name}) ->
+ ?vtrace("[mnesia] delete database: ~p",[Name]),
+ mnesia:delete_table(Name);
+delete({dets, Name}) ->
+ ?vtrace("[dets] delete database ~p",[Name]),
+ File = dets:info(Name, filename),
+ case dets:close(Name) of
+ ok ->
+ file:delete(File);
+ Error ->
+ Error
+ end;
+delete({ets, Name, undefined}) ->
+ ?vtrace("[dets] delete table ~p",[Name]),
+ ets:delete(Name);
+delete({ets, Name, File}) ->
+ ?vtrace("[dets] delete table ~p",[Name]),
+ file:delete(File),
+ ets:delete(Name).
+
+
+%% ---------------------------------------------------------------
+%% delete(DbRef, Key) -> ok
+%% DbRef -> term()
+%% Key -> term()
+%%
+%% Delete a record from the database.
+%% ---------------------------------------------------------------
+delete({mnesia, Name}, Key) ->
+ ?vtrace("[mnesia] delete from database ~p: ~p", [Name, Key]),
+ F = fun() -> mnesia:delete(Name, Key, write) end,
+ case mnesia:transaction(F) of
+ {aborted,Reason} ->
+ exit({aborted,Reason});
+ {atomic,_} ->
+ ok
+ end;
+delete({dets, Name}, Key) ->
+ ?vtrace("[dets] delete from table ~p: ~p", [Name, Key]),
+ dets:delete(Name, Key);
+delete({ets, Name, _}, Key) ->
+ ?vtrace("[ets] delete from table ~p: ~p", [Name, Key]),
+ ets:delete(Name, Key).
+
+
+%% ---------------------------------------------------------------
+%% match_object(DbRef,Pattern) -> [tuple()]
+%% DbRef -> term()
+%% Pattern -> tuple()
+%%
+%% Search the database for records witch matches the pattern.
+%% ---------------------------------------------------------------
+match_object({mnesia, Name}, Pattern) ->
+ ?vtrace("[mnesia] match_object in ~p of ~p",[Name, Pattern]),
+ F = fun() -> mnesia:match_object(Name, Pattern, read) end,
+ case mnesia:transaction(F) of
+ {aborted, Reason} ->
+ exit({aborted, Reason});
+ {atomic, Recs} ->
+ Recs
+ end;
+match_object({dets, Name}, Pattern) ->
+ ?vtrace("[dets] match_object in ~p of ~p",[Name, Pattern]),
+ dets:match_object(Name, Pattern);
+match_object({ets, Name, _}, Pattern) ->
+ ?vtrace("[ets] match_object in ~p of ~p",[Name, Pattern]),
+ ets:match_object(Name, Pattern).
+
+
+%% ---------------------------------------------------------------
+%% match_delete(DbRef,Pattern) ->
+%% DbRef -> term()
+%% Pattern -> tuple()
+%%
+%% Search the database for records witch matches the pattern and
+%% deletes them from the database.
+%% ---------------------------------------------------------------
+match_delete({mnesia, Name}, Pattern) ->
+ ?vtrace("[mnesia] match_delete in ~p with pattern ~p",[Name,Pattern]),
+ F = fun() ->
+ Recs = mnesia:match_object(Name, Pattern, read),
+ lists:foreach(fun(Rec) ->
+ mnesia:delete_object(Name, Rec, write)
+ end, Recs),
+ Recs
+ end,
+ case mnesia:transaction(F) of
+ {aborted, Reason} ->
+ exit({aborted, Reason});
+ {atomic,R} ->
+ R
+ end;
+match_delete({dets, Name}, Pattern) ->
+ ?vtrace("[dets] match_delete in ~p with pattern ~p",[Name,Pattern]),
+ Recs = dets:match_object(Name, Pattern),
+ dets:match_delete(Name, Pattern),
+ Recs;
+match_delete({ets, Name, _}, Pattern) ->
+ ?vtrace("[ets] match_delete in ~p with pattern ~p",[Name,Pattern]),
+ Recs = ets:match_object(Name, Pattern),
+ ets:match_delete(Name, Pattern),
+ Recs.
+
+
+%% ---------------------------------------------------------------
+%% tab2list(DbRef) -> [tuple()]
+%% DbRef -> term()
+%%
+%% Return all records in the table in the form of a list.
+%% ---------------------------------------------------------------
+tab2list({mnesia, Name}) ->
+ ?vtrace("[mnesia] tab2list -> list of ~p", [Name]),
+ match_object({mnesia, Name}, mnesia:table_info(Name, wild_pattern));
+tab2list({dets, Name}) ->
+ ?vtrace("[dets] tab2list -> list of ~p", [Name]),
+ match_object({dets, Name}, '_');
+tab2list({ets, Name, _}) ->
+ ?vtrace("[ets] tab2list -> list of ~p", [Name]),
+ ets:tab2list(Name).
+
+
+
+%% ---------------------------------------------------------------
+%% info(Db) -> taglist()
+%% info(Db, Item) -> Info
+%% Db -> term()
+%% Item -> atom()
+%% tablist() -> [{key(),value()}]
+%% key() -> atom()
+%% value() -> term()
+%%
+%% Retrieve table information.
+%% ---------------------------------------------------------------
+info({mnesia, Name}) ->
+ case (catch mnesia:table_info(Name, all)) of
+ Info when is_list(Info) ->
+ Info;
+ {'EXIT', {aborted, Reason}} ->
+ {error, Reason};
+ Else ->
+ {error, Else}
+ end;
+info({dets, Name}) ->
+ dets:info(Name);
+info({ets, Name, _}) ->
+ case ets:info(Name) of
+ undefined ->
+ [];
+ L ->
+ L
+ end.
+
+
+info({mnesia, Name}, Item) ->
+ case (catch mnesia:table_info(Name, Item)) of
+ {'EXIT', {aborted, Reason}} ->
+ {error, Reason};
+ Info ->
+ Info
+ end;
+info({dets, Name}, memory) ->
+ dets:info(Name, file_size);
+info({dets, Name}, Item) ->
+ dets:info(Name, Item);
+info({ets, Name, _}, Item) ->
+ ets:info(Name, Item).
+
+
+%% ---------------------------------------------------------------
+%% sync(Db) -> ok | {error, Reason}
+%% Db -> term()
+%% Reason -> term()
+%%
+%% Dump table to disc (if possible)
+%% ---------------------------------------------------------------
+
+sync({mnesia, _}) ->
+ ok;
+sync({dets, Name}) ->
+ dets:sync(Name);
+sync({ets, _Name, undefined}) ->
+ ok;
+sync({ets, Name, File}) ->
+ write_ets_file(Name, File).
+
+
+%% ---------------------------------------------------------------
+%% backup(Db, BackupDir) -> ok | {error, Reason}
+%% Db -> term()
+%% Reason -> term()
+%%
+%% Make a backup copy of the DB (only valid for det and ets)
+%% ---------------------------------------------------------------
+
+backup({mnesia, _}, _) ->
+ ok;
+backup({dets, Name}, BackupDir) ->
+ case dets:info(Name, filename) of
+ undefined ->
+ {error, no_file};
+ Filename ->
+ case filename:dirname(Filename) of
+ BackupDir ->
+ {error, db_dir};
+ _ ->
+ Type = dets:info(Name, type),
+ KP = dets:info(Name, keypos),
+ dets_backup(Name,
+ filename:basename(Filename),
+ BackupDir, Type, KP)
+ end
+ end;
+backup({ets, _Name, undefined}, _BackupDir) ->
+ ok;
+backup({ets, Name, File}, BackupDir) ->
+ Filename = filename:basename(File),
+ case filename:join(BackupDir, Filename) of
+ File ->
+ %% Oups: backup-dir and db-dir the same
+ {error, db_dir};
+ BackupFile ->
+ write_ets_file(Name, BackupFile)
+ end.
+
+
+dets_backup(Name, Filename, BackupDir, Type, KP) ->
+ ?vtrace("dets_backup -> entry with"
+ "~n Name: ~p"
+ "~n Filename: ~p"
+ "~n BackupDir: ~p"
+ "~n Type: ~p"
+ "~n KP: ~p", [Name, Filename, BackupDir, Type, KP]),
+ BackupFile = filename:join(BackupDir, Filename),
+ ?vtrace("dets_backup -> "
+ "~n BackupFile: ~p", [BackupFile]),
+ Backup = list_to_atom(atom_to_list(Name) ++ "_backup"),
+ Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
+ case dets:open_file(Backup, Opts) of
+ {ok, B} ->
+ ?vtrace("dets_backup -> create fun", []),
+ F = fun(Arg) ->
+ dets_backup(Arg, start, Name, B)
+ end,
+ dets:safe_fixtable(Name, true),
+ Res = dets:init_table(Backup, F, [{format, bchunk}]),
+ dets:safe_fixtable(Name, false),
+ ?vtrace("dets_backup -> Res: ~p", [Res]),
+ Res;
+ Error ->
+ ?vinfo("dets_backup -> open_file failed: "
+ "~n ~p", [Error]),
+ Error
+ end.
+
+
+dets_backup(close, _Cont, _Name, B) ->
+ dets:close(B),
+ ok;
+dets_backup(read, Cont1, Name, B) ->
+ case dets:bchunk(Name, Cont1) of
+ {Cont2, Data} ->
+ F = fun(Arg) ->
+ dets_backup(Arg, Cont2, Name, B)
+ end,
+ {Data, F};
+ '$end_of_table' ->
+ dets:close(B),
+ end_of_input;
+ Error ->
+ Error
+ end.
+
+
+%%----------------------------------------------------------------------
+
+write_ets_file(Name, File) ->
+ TmpFile = File ++ ".tmp",
+ case ets:tab2file(Name, TmpFile) of
+ ok ->
+ case file:rename(TmpFile, File) of
+ ok ->
+ ok;
+ Else ->
+ user_err("Warning: could not move file ~p"
+ " (~p)", [File, Else])
+ end;
+ {error, Reason} ->
+ user_err("Warning: could not save file ~p (~p)",
+ [File, Reason])
+ end.
+
+
+%%----------------------------------------------------------------------
+
+user_err(F, A) ->
+ snmpa_error:user_err(F, A).