1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(snmpa_mib_storage_mnesia).
21
22
23-behaviour(snmpa_mib_storage).
24
25%%%-----------------------------------------------------------------
26%%% This module implements the snmpa_mib_storage behaviour.
27%%% It uses mnesia for storage.
28%%%-----------------------------------------------------------------
29
30-export([
31	 open/5,
32	 close/1,
33	 read/2,
34	 write/2,
35	 delete/1,
36	 delete/2,
37	 match_object/2,
38	 match_delete/2,
39	 tab2list/1,
40	 info/1, info/2,
41	 sync/1,
42	 backup/2
43	]).
44
45
46-define(VMODULE,"MS-MNESIA").
47-include("snmp_verbosity.hrl").
48
49-record(tab, {id}).
50
51
52%% ---------------------------------------------------------------
53%% open
54%%
55%% Open or create a mnesia table.
56%%
57%% Opts    - A list of implementation dependent options
58%%           mnesia_open_options() = [mnesia_open_option()]
59%%           mnesia_open_option()  = {action, keep | clear} |
60%%                                   {nodes,  [node()]}
61%%
62%% ---------------------------------------------------------------
63
64open(Name, RecName, Fields, Type, Opts) ->
65    ?vtrace("open ~p table ~p for record ~p",
66	    [Type, Name, RecName]),
67    Action  = get_action(Opts),
68    Nodes   = get_nodes(Opts),
69    case table_exists(Name) of
70	true when (Action =:= keep) ->
71	    ?vtrace("open table ~p - exist (keep)", [Name]),
72	    {ok, #tab{id = Name}};
73	true when (Action =:= clear) ->
74	    ?vtrace("open table ~p - exist (clear)", [Name]),
75	    F = fun() -> mnesia:clear_table(Name) end,
76	    case mnesia:transaction(F) of
77		{aborted, Reason} ->
78		    {error, {clear, Reason}};
79		{atomic, _} ->
80		    {ok, #tab{id = Name}}
81	    end;
82	false ->
83	    ?vtrace("open table ~p - does not exist", [Name]),
84	    Args = [{record_name, RecName},
85		    {attributes,  Fields},
86		    {type,        Type},
87		    {disc_copies, Nodes}],
88	    case mnesia:create_table(Name, Args) of
89		{atomic, ok} ->
90		    ?vtrace("open table ~p - ok", [Name]),
91		    {ok, #tab{id = Name}};
92		{aborted, Reason} ->
93		    ?vinfo("open table ~p - aborted"
94			   "~n   Reason: ~p", [Name, Reason]),
95		    {error, {create, Reason}}
96	    end
97    end.
98
99table_exists(Name) ->
100    case (catch mnesia:table_info(Name, type)) of
101	{'EXIT', _Reason} ->
102	    false;
103	_ ->
104	    true
105    end.
106
107
108%% ---------------------------------------------------------------
109%% close
110%%
111%% Close the mib-storage table.
112%% This does nothing in the mnesia case.
113%% ---------------------------------------------------------------
114
115close(_) ->
116    ?vtrace("close mib-storage - ignore",[]),
117    ok.
118
119
120%% ---------------------------------------------------------------
121%% read
122%%
123%% Retrieve a record from the mib-storage table.
124%% ---------------------------------------------------------------
125
126read(#tab{id = ID}, Key) ->
127    ?vtrace("read (dirty) from database ~p: ~p", [ID, Key]),
128    case (catch mnesia:dirty_read(ID, Key)) of
129	[Rec|_] -> {value,Rec};
130	_ -> false
131    end.
132
133
134%% ---------------------------------------------------------------
135%% write
136%%
137%% Write a record to the mib-storage table.
138%% ---------------------------------------------------------------
139
140write(#tab{id = ID}, Rec) ->
141    ?vtrace("write to database ~p", [ID]),
142    F = fun() -> mnesia:write(ID, Rec, write) end,
143    case mnesia:transaction(F) of
144	{aborted, _Reason} = ABORTED ->
145	    {error, ABORTED};
146	{atomic,_} ->
147	    ok
148    end.
149
150
151%% ---------------------------------------------------------------
152%% delete
153%%
154%% Delete the mib-storage table.
155%% ---------------------------------------------------------------
156
157delete(#tab{id = ID}) ->
158    ?vtrace("delete database: ~p", [ID]),
159    mnesia:delete_table(ID).
160
161
162%% ---------------------------------------------------------------
163%% delete
164%%
165%% Delete a record from the mib-storage table.
166%% ---------------------------------------------------------------
167
168delete(#tab{id = ID}, Key) ->
169    ?vtrace("delete from database ~p: ~p", [ID, Key]),
170    F = fun() -> mnesia:delete(ID, Key, write) end,
171    case mnesia:transaction(F) of
172	{aborted, _Reason} = ABORTED ->
173	    {error, ABORTED};
174	{atomic, _} ->
175	    ok
176    end.
177
178
179%% ---------------------------------------------------------------
180%% match_object
181%%
182%% Search the mib-storage table for records witch matches
183%% the pattern.
184%% ---------------------------------------------------------------
185
186match_object(#tab{id = ID}, Pattern) ->
187    ?vtrace("match_object in ~p of ~p", [ID, Pattern]),
188    F = fun() -> mnesia:match_object(ID, Pattern, read) end,
189    case mnesia:transaction(F) of
190	{aborted, _Reason} = ABORTED ->
191	    {error, ABORTED};
192	{atomic, Rs} ->
193	    Rs
194    end.
195
196
197%% ---------------------------------------------------------------
198%% match_delete
199%%
200%% Search the mib-storage table for records witch matches
201%% the pattern and deletes them from the table.
202%% ---------------------------------------------------------------
203
204match_delete(#tab{id = ID}, Pattern) ->
205    ?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]),
206    F = fun() ->
207		Recs = mnesia:match_object(ID, Pattern, read),
208		lists:foreach(fun(Rec) ->
209				      mnesia:delete_object(ID, Rec, write)
210			      end, Recs),
211		Recs
212	end,
213    case mnesia:transaction(F) of
214	{aborted, _Reason} = ABORTED ->
215	    {error, ABORTED};
216	{atomic, Rs} ->
217	    Rs
218    end.
219
220
221%% ---------------------------------------------------------------
222%% tab2list
223%%
224%% Return all records in the mib-storage table in the form of
225%% a list.
226%% ---------------------------------------------------------------
227
228tab2list(#tab{id = ID} = Tab) ->
229    ?vtrace("tab2list -> list of ~p", [ID]),
230    match_object(Tab, mnesia:table_info(ID, wild_pattern)).
231
232
233%% ---------------------------------------------------------------
234%% info
235%%
236%% Retrieve implementation dependent mib-storage table
237%% information.
238%% ---------------------------------------------------------------
239
240info(#tab{id = ID}) ->
241    case (catch mnesia:table_info(ID, all)) of
242	Info when is_list(Info) ->
243	    Info;
244	{'EXIT', {aborted, Reason}} ->
245	    {error, Reason}
246    end.
247
248
249info(#tab{id = ID}, Item) ->
250    mnesia:table_info(ID, Item).
251
252
253%% ---------------------------------------------------------------
254%% sync
255%%
256%% Ignore
257%% ---------------------------------------------------------------
258
259sync(_) ->
260    ok.
261
262
263%% ---------------------------------------------------------------
264%% backup
265%%
266%% Ignore. Mnesia handles its own backups.
267%% ---------------------------------------------------------------
268
269backup(_, _) ->
270    ok.
271
272
273%%----------------------------------------------------------------------
274
275get_action(Opts) ->
276    snmp_misc:get_option(action, Opts, keep).
277
278get_nodes(Opts) ->
279    case snmp_misc:get_option(nodes, Opts, erlang:nodes()) of
280	[] ->
281	    [node()];
282	Nodes when is_list(Nodes) ->
283	    Nodes;
284	all ->
285	    erlang:nodes();
286	visible ->
287	    erlang:nodes(visible);
288	connected ->
289	    erlang:nodes(connected);
290	db_nodes ->
291	    try mnesia:system_info(db_nodes) of
292		DbNodes when is_list(DbNodes) ->
293		    DbNodes;
294		_ ->
295		    erlang:nodes()
296	    catch
297		_:_ ->
298		    erlang:nodes()
299	    end
300    end.
301
302%% user_err(F, A) ->
303%%     snmpa_error:user_err(F, A).
304