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