./ct_report/coverage/mnesia_api.COVER.html

1 -module(mnesia_api).
2
3 -export([set_master/1,
4 backup_mnesia/1, restore_mnesia/1,
5 dump_mnesia/1, dump_table/2, load_mnesia/1,
6 install_fallback_mnesia/1,
7 mnesia_change_nodename/4,
8 restore/1, mnesia_info/1]).
9
10 -type info_result() :: {ok, #{binary() => binary() | [binary()] | integer()}}.
11 -type info_error() :: {{internal_server_error | bad_key_error, binary()}, #{key => binary()}}.
12 -type dump_error() :: table_does_not_exist | file_error | cannot_dump.
13 -type restore_error() :: cannot_restore | file_not_found | not_a_log_file_error |
14 table_does_not_exist.
15 -type backup_error() :: wrong_filename | cannot_backup.
16 -type load_error() :: cannot_load | bad_file_format | file_not_found.
17 -type change_error() :: file_not_found | bad_file_format | cannot_change.
18
19 -spec mnesia_info(Keys::[binary()]) -> {ok, [info_result() | info_error()]}.
20 mnesia_info(null) ->
21 2 Value = mnesia:system_info(all),
22 2 Result = lists:foldl(fun({Key, Result}, AllAcc) ->
23 84 AllAcc ++ [{ok, #{<<"result">> => convert_value(Result), <<"key">> => Key}}]
24 end, [], Value),
25 2 {ok, Result};
26 mnesia_info(Keys) ->
27 2 Result = lists:foldl(fun
28 (<<"all">>, Acc) ->
29
:-(
Acc ++ [{{bad_key_error, <<"Key \"all\" does not exist">>},
30 #{key => <<"all">>}}];
31 (Key, Acc) ->
32 86 try mnesia:system_info(binary_to_atom(Key)) of
33 Value ->
34 84 Acc ++ [{ok, #{<<"result">> => convert_value(Value), <<"key">> => Key}}]
35 catch
36 _:{_, {badarg, _}} ->
37 2 Acc ++ [{{bad_key_error, <<"Key \"", Key/binary, "\" does not exist">>},
38 #{key => Key}}];
39 _:_ ->
40
:-(
Acc ++ [{{internal_server_error, <<"Internal server error">>}, #{key => Key}}]
41 end
42 end, [], Keys),
43 2 {ok, Result}.
44
45 -spec dump_mnesia(file:name()) -> {dump_error(), io_lib:chars()} | {ok, []}.
46 dump_mnesia(Path) ->
47 6 Tabs = get_local_tables(),
48 6 dump_tables(Path, Tabs).
49
50 -spec dump_table(file:name(), string()) -> {dump_error(), io_lib:chars()} | {ok, []}.
51 dump_table(Path, STable) ->
52 6 Table = list_to_atom(STable),
53 6 dump_tables(Path, [Table]).
54
55 -spec backup_mnesia(file:name()) -> {backup_error(), io_lib:chars()} | {ok, []}.
56 backup_mnesia(Path) ->
57 8 case mnesia:backup(Path) of
58 ok ->
59 4 {ok, ""};
60 {error, {'EXIT', {error, enoent}}} ->
61 2 {wrong_filename, io_lib:format("Wrong filename: ~p", [Path])};
62 {error, Reason} ->
63 2 String = io_lib:format("Can't store backup in ~p at node ~p: ~p",
64 [filename:absname(Path), node(), Reason]),
65 2 {cannot_backup, String}
66 end.
67
68 -spec restore_mnesia(file:name()) -> {restore_error(), io_lib:chars()} | {ok, []}.
69 restore_mnesia(Path) ->
70 10 ErrorString=lists:flatten( io_lib:format("Can't restore backup from ~p at node ~p: ",
71 [filename:absname(Path), node()])),
72 10 case mnesia_api:restore(Path) of
73 {atomic, _} ->
74 2 {ok, ""};
75 {aborted, {no_exists, Table}} ->
76
:-(
String = io_lib:format("~sTable ~p does not exist.", [ErrorString, Table]),
77
:-(
{table_does_not_exist, String};
78 {aborted, enoent} ->
79 4 String = ErrorString ++ "File not found.",
80 4 {file_not_found, String};
81 {aborted, {not_a_log_file, Filename}} ->
82 2 String = "Wrong file " ++ Filename ++ " structure",
83 2 {not_a_log_file_error, String};
84 {aborted, Reason} ->
85 2 String = io_lib:format("~s~p", [ErrorString, Reason]),
86 2 {cannot_restore, String}
87 end.
88
89 -spec load_mnesia(file:name()) -> {load_error(), io_lib:chars()} | {ok, []}.
90 load_mnesia(Path) ->
91 8 case mnesia:load_textfile(Path) of
92 {atomic, ok} ->
93 2 {ok, ""};
94 {error, bad_header} ->
95 2 {bad_file_format, "File has wrong format"};
96 {error, read} ->
97 2 {bad_file_format, "File has wrong format"};
98 {error, open} ->
99 2 {file_not_found, "File was not found"};
100 {error, Reason} ->
101
:-(
String = io_lib:format("Can't load dump in ~p at node ~p: ~p",
102 [filename:absname(Path), node(), Reason]),
103
:-(
{cannot_load, String}
104 end.
105
106 -spec mnesia_change_nodename(node(), node(), _, _) -> {ok, _} | {change_error(), io_lib:chars()}.
107 mnesia_change_nodename(From, To, Source, Target) ->
108 6 Switch =
109 fun
110 (Node) when Node == From ->
111 32 io:format(" - Replacing nodename: '~p' with: '~p'~n", [From, To]),
112 32 To;
113 (Node) when Node == To ->
114
:-(
io:format(" - Node: '~p' will not be modified (it is already '~p')~n", [Node, To]),
115
:-(
Node;
116 (Node) ->
117
:-(
io:format(" - Node: '~p' will not be modified (it is not '~p')~n", [Node, From]),
118
:-(
Node
119 end,
120 6 Convert =
121 fun
122 ({schema, db_nodes, Nodes}, Acc) ->
123
:-(
io:format(" +++ db_nodes ~p~n", [Nodes]),
124
:-(
{[{schema, db_nodes, lists:map(Switch, Nodes)}], Acc};
125 ({schema, version, Version}, Acc) ->
126
:-(
io:format(" +++ version: ~p~n", [Version]),
127
:-(
{[{schema, version, Version}], Acc};
128 ({schema, cookie, Cookie}, Acc) ->
129
:-(
io:format(" +++ cookie: ~p~n", [Cookie]),
130
:-(
{[{schema, cookie, Cookie}], Acc};
131 ({schema, Tab, CreateList}, Acc) ->
132 32 io:format("~n * Checking table: '~p'~n", [Tab]),
133 32 Keys = [ram_copies, disc_copies, disc_only_copies],
134 32 OptSwitch =
135 fun({Key, Val}) ->
136 576 case lists:member(Key, Keys) of
137 true ->
138 96 io:format(" + Checking key: '~p'~n", [Key]),
139 96 {Key, lists:map(Switch, Val)};
140 480 false -> {Key, Val}
141 end
142 end,
143 32 Res = {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc},
144 32 Res;
145 (Other, Acc) ->
146 2 {[Other], Acc}
147 end,
148 6 case mnesia:traverse_backup(Source, Target, Convert, switched) of
149 2 {ok, _} = Result -> Result;
150 {error, Reason} ->
151 4 String = io_lib:format("Error while changing node's name ~p:~n~p",
152 [node(), Reason]),
153 4 case Reason of
154 {_, enoent} ->
155 2 {file_not_found, String};
156 {_, {not_a_log_file, _}} ->
157 2 {bad_file_format, String};
158 _ ->
159
:-(
{cannot_change, String}
160 end
161 end.
162
163 -spec install_fallback_mnesia(file:name()) ->
164 {cannot_fallback, io_lib:chars()} | {ok, []}.
165 install_fallback_mnesia(Path) ->
166 2 case mnesia:install_fallback(Path) of
167 ok ->
168
:-(
{ok, ""};
169 {error, Reason} ->
170 2 String = io_lib:format("Can't install fallback from ~p at node ~p: ~p",
171 [filename:absname(Path), node(), Reason]),
172 2 {cannot_fallback, String}
173 end.
174
175 -spec set_master(node()) -> {cannot_set, io_lib:chars()} | {ok, []}.
176 set_master(Node) ->
177 4 case mnesia:set_master_nodes([Node]) of
178 ok ->
179 4 {ok, ""};
180 {error, Reason} ->
181
:-(
String = io_lib:format("Can't set master node ~p at node ~p:~n~p",
182 [Node, node(), Reason]),
183
:-(
{cannot_set, String}
184 end.
185
186 %---------------------------------------------------------------------------------------------------
187 % Helpers
188 %---------------------------------------------------------------------------------------------------
189
190 -spec convert_value(any()) -> binary() | [{ok, any()}] | integer().
191 convert_value(Value) when is_binary(Value) ->
192
:-(
Value;
193 convert_value(Value) when is_integer(Value) ->
194 40 Value;
195 convert_value(Value) when is_atom(Value) ->
196 236 atom_to_binary(Value);
197 convert_value([Head | _] = Value) when is_integer(Head) ->
198 12 list_to_binary(Value);
199 convert_value(Value) when is_list(Value) ->
200 48 [{ok, convert_value(Item)} || Item <- Value];
201 convert_value(Value) ->
202 16 list_to_binary(io_lib:format("~p", [Value])).
203
204 -spec dump_tables(file:name(), list()) -> {dump_error(), io_lib:chars()} | {ok, []}.
205 dump_tables(File, Tabs) ->
206 12 case dump_to_textfile(Tabs, file:open(File, [write])) of
207 ok ->
208 6 {ok, ""};
209 {file_error, Reason} ->
210 4 String = io_lib:format("Can't store dump in ~p at node ~p: ~p",
211 [filename:absname(File), node(), Reason]),
212 4 {file_error, String};
213 {error, Reason} ->
214 2 String = io_lib:format("Can't store dump in ~p at node ~p: ~p",
215 [filename:absname(File), node(), Reason]),
216 2 case Reason of
217 table_does_not_exist ->
218 2 {table_does_not_exist, String};
219 _ ->
220
:-(
{cannot_dump, String}
221 end
222 end.
223
224 %% @doc Mnesia database restore
225 restore(Path) ->
226 10 mnesia:restore(Path, [{keep_tables, keep_tables()},
227 {default_op, skip_tables}]).
228
229 %% @doc This function returns a list of tables that should be kept from a
230 %% previous version backup.
231 %% Obsolete tables or tables created by modules which are no longer used are not
232 %% restored and are ignored.
233 -spec keep_tables() -> [atom()].
234 keep_tables() ->
235 10 lists:flatten([acl, passwd, disco_publish, keep_modules_tables()]).
236
237 %% @doc Returns the list of modules tables in use, according to the list of
238 %% actually loaded modules
239 -spec keep_modules_tables() -> [[atom()]]. % list of lists
240 keep_modules_tables() ->
241 10 lists:map(fun(Module) -> module_tables(Module) end,
242 gen_mod:loaded_modules()).
243
244 %% TODO: This mapping should probably be moved to a callback function in each module.
245 %% @doc Mapping between modules and their tables
246 -spec module_tables(_) -> [atom()].
247
:-(
module_tables(mod_announce) -> [motd, motd_users];
248
:-(
module_tables(mod_irc) -> [irc_custom];
249
:-(
module_tables(mod_last) -> [last_activity];
250
:-(
module_tables(mod_muc) -> [muc_room, muc_registered];
251
:-(
module_tables(mod_offline) -> [offline_msg];
252
:-(
module_tables(mod_privacy) -> [privacy];
253
:-(
module_tables(mod_private) -> [private_storage];
254
:-(
module_tables(mod_pubsub) -> [pubsub_node];
255 10 module_tables(mod_roster) -> [roster];
256
:-(
module_tables(mod_shared_roster) -> [sr_group, sr_user];
257 10 module_tables(mod_vcard) -> [vcard, vcard_search];
258 100 module_tables(_Other) -> [].
259
260 -spec get_local_tables() -> [any()].
261 get_local_tables() ->
262 6 Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
263 6 Tabs = lists:filter(
264 fun(T) ->
265 94 case mnesia:table_info(T, storage_type) of
266 20 disc_copies -> true;
267 2 disc_only_copies -> true;
268 72 _ -> false
269 end
270 end, Tabs1),
271 6 Tabs.
272
273 -spec dump_to_textfile(any(),
274 {error, atom()} | {ok, pid() | {file_descriptor, atom() | tuple(), _}}
275 ) -> ok | {error, atom()} | {file_error, atom()}.
276 dump_to_textfile(Tabs, {ok, F}) ->
277 8 case get_info_about_tables(Tabs, F) of
278 ok ->
279 6 lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
280 6 file:close(F);
281 {error, _} = Error ->
282 2 Error
283 end;
284 dump_to_textfile(_, {error, Reason}) ->
285 4 {file_error, Reason}.
286
287 -spec get_info_about_tables(any(), pid()) -> ok | {error, atom()}.
288 get_info_about_tables(Tabs, File) ->
289 8 try
290 8 Defs = lists:map(
291 20 fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
292 {attributes, mnesia:table_info(T, attributes)}]}
293 end,
294 Tabs),
295 6 io:format(File, "~p.~n", [{tables, Defs}])
296 catch _:_ ->
297 2 {error, table_does_not_exist}
298 end.
299
300 -spec dump_tab(pid(), atom()) -> ok.
301 dump_tab(F, T) ->
302 18 W = mnesia:table_info(T, wild_pattern),
303 18 {atomic, All} = mnesia:transaction(
304 18 fun() -> mnesia:match_object(T, W, read) end),
305 18 lists:foreach(
306 7 fun(Term) -> io:format(F, "~p.~n", [setelement(1, Term, T)]) end, All).
Line Hits Source