1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1998-2017. 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
21%%
22%%%----------------------------------------------------------------------
23%%% File    : mnesia_evil_backup.erl
24%%% Author  : Dan Gudmundsson <dgud@legolas>
25%%% Purpose : Evil backup tests
26%%% Created : 3 Jun 1998 by Dan Gudmundsson <dgud@erix.ericsson.se>
27%%%----------------------------------------------------------------------
28
29-module(mnesia_evil_backup).
30-author('dgud@erix.ericsson.se').
31-include("mnesia_test_lib.hrl").
32
33-export([init_per_testcase/2, end_per_testcase/2,
34         init_per_group/2, end_per_group/2,
35         all/0, groups/0]).
36
37-export([backup/1, bad_backup/1, global_backup_checkpoint/1,
38         traverse_backup/1,
39         selective_backup_checkpoint/1,
40         incremental_backup_checkpoint/1, install_fallback/1,
41         uninstall_fallback/1, local_fallback/1,
42         sops_with_checkpoint/1,
43         restore_errors/1, restore_clear/1, restore_keep/1,
44         restore_recreate/1, restore_clear_ram/1
45        ]).
46
47-export([check_tab/2]).
48
49init_per_testcase(Func, Conf) ->
50    mnesia_test_lib:init_per_testcase(Func, Conf).
51
52end_per_testcase(Func, Conf) ->
53    mnesia_test_lib:end_per_testcase(Func, Conf).
54
55%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56
57all() ->
58    [backup, bad_backup, global_backup_checkpoint,
59     {group, restore_tables}, traverse_backup,
60     selective_backup_checkpoint,
61     incremental_backup_checkpoint, install_fallback,
62     uninstall_fallback, local_fallback,
63     sops_with_checkpoint].
64
65groups() ->
66    [{restore_tables, [],
67      [restore_errors, restore_clear, restore_keep,
68       restore_recreate, restore_clear_ram]}].
69
70init_per_group(_GroupName, Config) ->
71    Config.
72
73end_per_group(_GroupName, Config) ->
74    Config.
75
76
77backup(doc) -> ["Checking the interface to the function backup",
78                "We don't check that the backups can be used here",
79                "That is checked in install_fallback and in restore"];
80backup(suite) -> [];
81backup(Config) when is_list(Config) ->
82    [Node1, Node2] = _Nodes = ?acquire_nodes(2, Config),
83    Tab = backup_tab,
84    Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
85    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
86    ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
87    File = "backup_test.BUP",
88    ?match(ok, mnesia:backup(File)),
89
90    File2 = "backup_test2.BUP",
91    Tab2 = backup_tab2,
92    Def2 = [{disc_only_copies, [Node2]}],
93    ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
94    ?match(ok, mnesia:backup(File2, mnesia_backup)),
95
96    File3 = "backup_test3.BUP",
97    mnesia_test_lib:kill_mnesia([Node2]),
98    ?match({error, _}, mnesia:backup(File3, mnesia_backup)),
99
100    ?match(ok, file:delete(File)),
101    ?match(ok, file:delete(File2)),
102    ?match({error, _}, file:delete(File3)),
103    ?verify_mnesia([Node1], [Node2]).
104
105
106bad_backup(suite) -> [];
107bad_backup(Config) when is_list(Config) ->
108    [Node1] = ?acquire_nodes(1, Config),
109    Tab = backup_tab,
110    Def = [{disc_copies, [Node1]}],
111    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
112    ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
113    File = "backup_test.BUP",
114    ?match(ok, mnesia:backup(File)),
115    file:write_file(File, "trash", [append]),
116    ?match(ok, mnesia:dirty_write({Tab, 1, test_bad})),
117    ?match({atomic,[Tab]}, mnesia:restore(File, [{clear_tables, [Tab]}])),
118    ?match([{Tab,1,test_ok}], mnesia:dirty_read(Tab, 1)),
119
120    ?match(ok, file:delete(File)),
121    ?verify_mnesia([Node1], []).
122
123
124
125global_backup_checkpoint(doc) ->
126    ["Checking the interface to the function backup_checkpoint",
127     "We don't check that the backups can be used here",
128     "That is checked in install_fallback and in restore"];
129global_backup_checkpoint(suite) -> [];
130global_backup_checkpoint(Config) when is_list(Config) ->
131    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
132    Tab = backup_cp,
133    Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
134    File = "backup_checkpoint.BUP",
135    File2 = "backup_checkpoint2.BUP",
136    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
137    ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
138    ?match({error, _}, mnesia:backup_checkpoint(cp_name, File)),
139    Spec = [{name, cp_name}, {max,  mnesia:system_info(tables)}],
140    ?match({ok, _Name, _Ns}, mnesia:activate_checkpoint(Spec)),
141    ?match(ok, mnesia:backup_checkpoint(cp_name, File)),
142    ?match({error, _}, mnesia:backup_checkpoint(cp_name_nonexist, File)),
143    ?match(ok, mnesia:backup_checkpoint(cp_name, File2, mnesia_backup)),
144    ?match({error, _}, file:delete(File)),
145    ?match(ok, file:delete(File2)),
146    ?verify_mnesia(Nodes, []).
147
148
149restore_errors(suite) -> [];
150restore_errors(Config) when is_list(Config) ->
151    [_Node] = ?acquire_nodes(1, Config),
152    ?match({aborted, enoent}, mnesia:restore(notAfile, [])),
153    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, not_a_list)),
154    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [test_badarg])),
155    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{test_badarg, xxx}])),
156    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{skip_tables, xxx}])),
157    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{recreate_tables, [schema]}])),
158    ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{default_op, asdklasd}])),
159    MnesiaDir = mnesia_lib:dir(),
160    ?match({aborted, {not_a_log_file, _}}, mnesia:restore(filename:join(MnesiaDir, "schema.DAT"), [])),
161    ?match({aborted, _}, mnesia:restore(filename:join(MnesiaDir, "LATEST.LOG"), [])),
162    ok.
163
164restore_clear(suite) -> [];
165restore_clear(Config) when is_list(Config) ->
166    restore(Config, clear_tables).
167
168restore_keep(suite) -> [];
169restore_keep(Config) when is_list(Config) ->
170    restore(Config, keep_tables).
171
172restore_recreate(suite) -> [];
173restore_recreate(Config) when is_list(Config) ->
174    restore(Config, recreate_tables).
175
176check_tab(Records, Line) ->
177    Verify = fun({Table, Key, Val}) ->
178		     case catch mnesia:dirty_read({Table, Key}) of
179			 [{Table, Key, Val}] -> ok;
180			 Else ->
181			     mnesia_test_lib:error("Not matching on Node ~p ~n"
182						   " Expected ~p~n Actual  ~p~n",
183						   [node(), {Table, Key, Val}, Else],
184						   ?MODULE, Line),
185			     exit(error)
186		     end;
187		(Recs) ->
188		     [{Tab, Key, _}, _] = Recs,
189		     SRecs = lists:sort(Recs),
190		     R_Recs = lists:sort(catch mnesia:dirty_read({Tab, Key})),
191		     case R_Recs of
192			 SRecs -> ok;
193			 Else ->
194			     mnesia_test_lib:error("Not matching on Node ~p ~n"
195						   " Expected ~p~n Actual  ~p~n",
196						   [node(), SRecs, Else],
197						   ?MODULE, Line),
198			     exit(error)
199		     end
200	     end,
201    lists:foreach(Verify, Records).
202
203restore(Config, Op)  ->
204    [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
205
206    Tab1 = ram_snmp,
207    Def1 = [{snmp, [{key, integer}]}, {ram_copies, [Node1]}],
208    Tab2 = disc_index,
209    Def2 = [{index, [val]}, {disc_copies, [Node1, Node2]}],
210    Tab3 = dionly_bag,
211    Def3 = [{type, bag}, {disc_only_copies, Nodes}],
212    ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
213    ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
214    ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
215
216    File1 = "restore1.BUP",
217    File2 = "restore2.BUP",
218
219    Restore = fun(O, A) ->
220		      case mnesia:restore(O, A) of
221			  {atomic, Tabs} when is_list(Tabs) -> {atomic, lists:sort(Tabs)};
222			  Other -> Other
223		      end
224	      end,
225    Tabs = lists:sort([Tab1, Tab2, Tab3]),
226
227    [mnesia:dirty_write({Tab1, N, N+42}) || N <- lists:seq(1, 10)],
228    [mnesia:dirty_write({Tab2, N, N+43}) || N <- lists:seq(1, 10)],
229    [mnesia:dirty_write({Tab3, N, N+44}) || N <- lists:seq(1, 10)],
230
231    Res1 = [{Tab1, N, N+42} || N <- lists:seq(1, 10)],
232    Res2 = [{Tab2, N, N+43} || N <- lists:seq(1, 10)],
233    Res3 = [{Tab3, N, N+44} || N <- lists:seq(1, 10)],
234
235    {ok, Name, _} = mnesia:activate_checkpoint([{min, Tabs}, {ram_overrides_dump, true}]),
236    file:delete(File1),
237
238    %% Test standard Restore on one table on one node
239    ?match(ok, mnesia:backup_checkpoint(Name, File1)),
240    ?match(ok, mnesia:deactivate_checkpoint(Name)),
241    ?match(ok, mnesia:backup(File2)),
242    [mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
243    [mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
244    [mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
245
246    Res21 = [{Tab2, N, N+1} || N <- lists:seq(1, 11)],
247    Res31 = [[{Tab3, N, N+1}, {Tab3, N, N+44}] || N <- lists:seq(1, 10)],
248    Check = fun() ->
249		    [disk_log:pid2name(X) ||
250			X <- processes(), Data <- [process_info(X, [current_function])],
251			Data =/= undefined,
252			element(1, element(2, lists:keyfind(current_function, 1, Data)))=:= disk_log]
253	    end,
254    Before = Check(),
255    ?match({atomic, [Tab1]}, Restore(File1, [{Op, [Tab1]},
256					     {skip_tables, Tabs -- [Tab1]}])),
257    case Op of
258	keep_tables ->
259	    ?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11}));
260	clear_tables ->
261	    ?match([], mnesia:dirty_read({Tab1, 11}));
262	recreate_tables ->
263	    ?match([], mnesia:dirty_read({Tab1, 11}))
264    end,
265    [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
266    [rpc:call(Node, ?MODULE, check_tab, [Res21, ?LINE]) || Node <- Nodes],
267    [rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
268
269    %% Restore all tables on it's nodes
270    mnesia:clear_table(Tab1),
271    mnesia:clear_table(Tab2),
272    mnesia:clear_table(Tab3),
273    [mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
274    [mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
275    [mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
276
277    ?match({atomic, ok}, mnesia:del_table_copy(Tab2, Node1)),
278
279    ?match({ok, Node1}, mnesia:subscribe({table, Tab1})),
280
281    ?match({atomic, Tabs}, Restore(File1, [{default_op, Op},
282					   {module, mnesia_backup}])),
283    case Op of
284	clear_tables ->
285	    ?match_receive({mnesia_table_event, {delete, {schema, Tab1}, _}}),
286	    ?match_receive({mnesia_table_event, {write, {schema, Tab1, _}, _}}),
287	    check_subscr(Tab1),
288	    [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
289	    [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
290	    [rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
291	    ?match([], mnesia:dirty_read({Tab1, 11})),
292	    ?match([], mnesia:dirty_read({Tab2, 11})),
293	    ?match([], mnesia:dirty_read({Tab3, 11})),
294	    %% Check Index
295	    ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
296	    ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
297	    %% Check Snmp
298	    ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
299	    ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
300	    ?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
301	    %% Check schema info
302	    ?match([Node2], mnesia:table_info(Tab2, where_to_write));
303	keep_tables ->
304	    check_subscr(Tab1),
305	    [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
306	    [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
307	    [rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
308	    ?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11})),
309	    ?match([{Tab2, 11, 12}], mnesia:dirty_read({Tab2, 11})),
310	    ?match([{Tab3, 11, 12}], mnesia:dirty_read({Tab3, 11})),
311	    ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
312	    %% Check Index
313	    ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
314	    ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
315	    %% Check Snmp
316	    ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
317	    ?match({ok, {Tab1, 11, 12}}, mnesia:snmp_get_row(Tab1, [11])),
318	    %% Check schema info
319	    ?match([Node2], mnesia:table_info(Tab2, where_to_write));
320	recreate_tables ->
321	    check_subscr(Tab1, 0),
322	    [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
323	    [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
324	    [rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
325	    ?match([], mnesia:dirty_read({Tab1, 11})),
326	    ?match([], mnesia:dirty_read({Tab2, 11})),
327	    ?match([], mnesia:dirty_read({Tab3, 11})),
328	    %% Check Index
329	    ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
330	    ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
331	    %% Check Snmp
332	    ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
333	    ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
334	    ?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
335	    %% Check schema info
336	    Ns = lists:sort([Node1, Node2]),
337	    ?match(Ns, lists:sort(mnesia:table_info(Tab2, where_to_write)))
338    end,
339    ?match(ok, file:delete(File1)),
340    ?match(ok, file:delete(File2)),
341    ?match([], Check() -- (Before ++ [{ok, latest_log}, {ok, previous_log}])),
342
343    ?verify_mnesia(Nodes, []).
344
345
346check_subscr(Tab) ->
347    check_subscr(Tab, 10).
348
349check_subscr(_Tab, 0) ->
350    receive
351	Msg ->
352	    ?error("Too many msgs ~p~n", [Msg])
353    after 500 ->
354	    ok
355    end;
356check_subscr(Tab, N) ->
357    V = N +42,
358    receive
359	{mnesia_table_event, {write, {Tab, N, V}, _}} ->
360	    check_subscr(Tab, N-1)
361    after 500 ->
362	    ?error("Missing ~p~n", [{Tab, N, V}])
363    end.
364
365restore_clear_ram(suite) -> [];
366restore_clear_ram(Config) when is_list(Config) ->
367    Nodes = ?acquire_nodes(3, [{diskless, true}|Config]),
368
369    ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
370
371    Write = fun(What) ->
372		    mnesia:write({a,1,What}),
373		    mnesia:write({a,2,What}),
374		    mnesia:write({a,3,What})
375	    end,
376    Bup = "restore_clear_ram.BUP",
377
378    ?match({atomic, ok}, mnesia:transaction(Write, [initial])),
379    ?match({ok, _, _}, mnesia:activate_checkpoint([{name,test},
380						   {min, [schema, a]},
381						   {ram_overrides_dump, true}])),
382    ?match(ok, mnesia:backup_checkpoint(test, Bup)),
383
384    ?match({atomic, ok}, mnesia:transaction(Write, [data])),
385    ?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
386
387    restore_clear_ram_loop(100, Nodes, Bup),
388
389    ok.
390
391restore_clear_ram_loop(N, Nodes = [N1,N2,N3], Bup) when N > 0 ->
392    ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
393    ?match({_, []}, rpc:multicall([N1,N2], mnesia, start, [[{extra_db_nodes, Nodes}]])),
394    Key = rpc:async_call(N3, mnesia, start, [[{extra_db_nodes, Nodes}]]),
395    ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
396    ?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
397    ?match(ok, rpc:yield(Key)),
398    ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[a], 3000])),
399    case rpc:multicall(Nodes, mnesia, table_info, [a,size]) of
400	{[3,3,3], []} ->
401	    restore_clear_ram_loop(N-1, Nodes, Bup);
402	Error ->
403	    ?match(3, Error)
404    end;
405restore_clear_ram_loop(_,_,_) ->
406    ok.
407
408traverse_backup(doc) ->
409    ["Testing the traverse_backup interface, the resulting file is not tested though",
410     "See install_fallback for result using the output file from traverse_backup",
411     "A side effect is that the backup file contents are tested"];
412traverse_backup(suite) -> [];
413traverse_backup(Config) when is_list(Config) ->
414    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
415    Tab = backup_tab,
416    Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
417    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
418    ?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
419    ?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
420    ?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
421    ?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
422    ?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
423    File = "_treverse_backup.BUP",
424    File2 = "traverse_backup2.BUP",
425    File3 = "traverse_backup3.BUP",
426    ?match(ok, mnesia:backup(File)),
427
428    Fun = fun({backup_tab, N, _}, Acc) -> {[{backup_tab, N, test_ok}], Acc+1};
429             (Other, Acc) -> {[Other], Acc}
430          end,
431
432    ?match({ok, 5}, mnesia:traverse_backup(File, read_only, Fun, 0)),
433    ?match(ok, file:delete(read_only)),
434
435    ?match({ok, 5}, mnesia:traverse_backup(File, mnesia_backup,
436                                           dummy, read_only, Fun, 0)),
437
438    ?match({ok, 5}, mnesia:traverse_backup(File, File2, Fun, 0)),
439    ?match({ok, 5}, mnesia:traverse_backup(File2, mnesia_backup,
440                                           File3, mnesia_backup, Fun, 0)),
441
442    BadFun = fun({bad_tab, _N, asd}, Acc) -> {{error, error}, Acc} end,
443    ?match({error, _}, mnesia:traverse_backup(File, read_only, BadFun, 0)),
444    ?match({error, _}, file:delete(read_only)),
445    ?match(ok, file:delete(File)),
446    ?match(ok, file:delete(File2)),
447    ?match(ok, file:delete(File3)),
448    ?verify_mnesia(Nodes, []).
449
450
451install_fallback(doc) ->
452    ["This tests the install_fallback intf.",
453     "It also verifies that the output from backup_checkpoint and traverse_backup",
454     "is valid"];
455install_fallback(suite) -> [];
456install_fallback(Config) when is_list(Config) ->
457    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
458    Tab = fallbacks_test,
459    Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
460    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
461    ?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
462    ?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
463    ?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
464    ?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
465    ?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
466
467    Tab2 = fallbacks_test2,
468    Def2 = [{disc_copies, [node()]}],
469    ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
470    Tab3 = fallbacks_test3,
471    ?match({atomic, ok}, mnesia:create_table(Tab3, Def2)),
472    Fun2 = fun(Key) ->
473		  Rec = {Tab2, Key, test_ok},
474		  mnesia:dirty_write(Rec),
475		  [Rec]
476	  end,
477    TabSize3 = 1000,
478    OldRecs2 = [Fun2(K) || K <- lists:seq(1, TabSize3)],
479
480    Spec =[{name, cp_name}, {max,  mnesia:system_info(tables)}],
481    ?match({ok, _Name, Nodes}, mnesia:activate_checkpoint(Spec)),
482    ?match(ok, mnesia:dirty_write({Tab, 6, test_nok})),
483    [mnesia:dirty_write({Tab2, K, test_nok}) || K <- lists:seq(1, TabSize3 + 10)],
484    File = "install_fallback.BUP",
485    File2 = "install_fallback2.BUP",
486    File3 = "install_fallback3.BUP",
487    ?match(ok, mnesia:backup_checkpoint(cp_name, File)),
488
489    Fun = fun({T, N, _}, Acc) when T == Tab ->
490		  case N rem 2 of
491		      0 ->
492			  io:format("write ~p -> ~p~n", [N, T]),
493			  {[{T, N, test_ok}], Acc + 1};
494		      1 ->
495			  io:format("write ~p -> ~p~n", [N, Tab3]),
496			  {[{Tab3, N, test_ok}], Acc + 1}
497		  end;
498             ({T, N}, Acc) when T == Tab ->
499		  case N rem 2 of
500		      0 ->
501			  io:format("delete ~p -> ~p~n", [N, T]),
502			  {[{T, N}], Acc + 1};
503		      1 ->
504			  io:format("delete ~p -> ~p~n", [N, Tab3]),
505			  {[{Tab3, N}], Acc + 1}
506		  end;
507             (Other, Acc) ->
508		  {[Other], Acc}
509          end,
510    ?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
511    ?match(ok, mnesia:install_fallback(File2)),
512
513    mnesia_test_lib:kill_mnesia([Node1, Node2]),
514    timer:sleep(timer:seconds(1)), % Let it die!
515
516    ok = mnesia:start([{ignore_fallback_at_startup, true}]),
517    ok = mnesia:wait_for_tables([Tab, Tab2, Tab3], 10000),
518    ?match([{Tab, 6, test_nok}], mnesia:dirty_read({Tab, 6})),
519    mnesia_test_lib:kill_mnesia([Node1]),
520    application:set_env(mnesia, ignore_fallback_at_startup, false),
521
522    timer:sleep(timer:seconds(1)), % Let it die!
523
524    ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab, Tab2, Tab3])),
525
526    % Verify
527    ?match([], mnesia:dirty_read({Tab, 1})),
528    ?match([{Tab3, 1, test_ok}], mnesia:dirty_read({Tab3, 1})),
529    ?match([{Tab, 2, test_ok}], mnesia:dirty_read({Tab, 2})),
530    ?match([], mnesia:dirty_read({Tab3, 2})),
531    ?match([], mnesia:dirty_read({Tab, 3})),
532    ?match([{Tab3, 3, test_ok}], mnesia:dirty_read({Tab3, 3})),
533    ?match([{Tab, 4, test_ok}], mnesia:dirty_read({Tab, 4})),
534    ?match([], mnesia:dirty_read({Tab3, 4})),
535    ?match([], mnesia:dirty_read({Tab, 5})),
536    ?match([{Tab3, 5, test_ok}], mnesia:dirty_read({Tab3, 5})),
537    ?match([], mnesia:dirty_read({Tab, 6})),
538    ?match([], mnesia:dirty_read({Tab3, 6})),
539    ?match([], [mnesia:dirty_read({Tab2, K}) || K <- lists:seq(1, TabSize3)] -- OldRecs2),
540    ?match(TabSize3, mnesia:table_info(Tab2, size)),
541
542    % Check the interface
543    file:delete(File3),
544    ?match({error, _}, mnesia:install_fallback(File3)),
545    ?match({error, _}, mnesia:install_fallback(File2, mnesia_badmod)),
546    ?match({error, _}, mnesia:install_fallback(File2, {foo, foo})),
547    ?match({error, _}, mnesia:install_fallback(File2, [{foo, foo}])),
548    ?match({error, {badarg, {skip_tables, _}}},
549	   mnesia:install_fallback(File2, [{default_op, skip_tables},
550					   {default_op, keep_tables},
551					   {keep_tables, [Tab, Tab2, Tab3]},
552					   {skip_tables, [foo,{asd}]}])),
553    ?match(ok, mnesia:install_fallback(File2, mnesia_backup)),
554    ?match(ok, file:delete(File)),
555    ?match(ok, file:delete(File2)),
556    ?match({error, _}, file:delete(File3)),
557    ?verify_mnesia(Nodes, []).
558
559uninstall_fallback(suite) -> [];
560uninstall_fallback(Config) when is_list(Config) ->
561    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
562    Tab = uinst_fallbacks_test,
563    File = "uinst_fallback.BUP",
564    File2 = "uinst_fallback2.BUP",
565    Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
566    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
567    ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
568    ?match(ok, mnesia:backup(File)),
569    Fun = fun({T, N, _}, Acc) when T == Tab ->
570                  {[{T, N, test_nok}], Acc+1};
571             (Other, Acc) -> {[Other], Acc}
572          end,
573    ?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
574    ?match({error, enoent}, mnesia:uninstall_fallback()),
575    ?match(ok, mnesia:install_fallback(File2)),
576    ?match(ok, file:delete(File)),
577    ?match(ok, file:delete(File2)),
578    ?match({error, _}, mnesia:uninstall_fallback([foobar])),
579    ?match(ok, mnesia:uninstall_fallback()),
580
581    mnesia_test_lib:kill_mnesia([Node1, Node2]),
582    timer:sleep(timer:seconds(1)), % Let it die!
583    ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab])),
584    ?match([{Tab, 1, test_ok}], mnesia:dirty_read({Tab, 1})),
585    ?verify_mnesia(Nodes, []).
586
587local_fallback(suite) -> [];
588local_fallback(Config) when is_list(Config) ->
589    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
590    Tab = local_fallback,
591    File = "local_fallback.BUP",
592    Def = [{disc_copies, Nodes}],
593    Key = foo,
594    Pre =  {Tab, Key, pre},
595    Post =  {Tab, Key, post},
596    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
597    ?match(ok, mnesia:dirty_write(Pre)),
598    ?match(ok, mnesia:backup(File)),
599    ?match(ok, mnesia:dirty_write(Post)),
600    Local = [{scope, local}],
601    ?match({error, enoent}, mnesia:uninstall_fallback(Local)),
602    ?match(ok, mnesia:install_fallback(File, Local)),
603    ?match(true, mnesia:system_info(fallback_activated)),
604    ?match(ok, mnesia:uninstall_fallback(Local)),
605    ?match(false, mnesia:system_info(fallback_activated)),
606    ?match(ok, mnesia:install_fallback(File, Local)),
607    ?match(true, mnesia:system_info(fallback_activated)),
608
609    ?match(false, rpc:call(Node2, mnesia, system_info , [fallback_activated])),
610    ?match(ok, rpc:call(Node2, mnesia, install_fallback , [File, Local])),
611    ?match([Post], mnesia:dirty_read({Tab, Key})),
612    ?match([Post], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
613
614    ?match([], mnesia_test_lib:kill_mnesia(Nodes)),
615    ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
616    ?match([Pre], mnesia:dirty_read({Tab, Key})),
617    ?match([Pre], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
618    Dir = rpc:call(Node2, mnesia, system_info , [directory]),
619
620    ?match(ok, mnesia:dirty_write(Post)),
621    ?match([Post], mnesia:dirty_read({Tab, Key})),
622    ?match([], mnesia_test_lib:kill_mnesia([Node2])),
623    ?match(ok, mnesia:install_fallback(File, Local ++ [{mnesia_dir, Dir}])),
624    ?match([], mnesia_test_lib:kill_mnesia([Node1])),
625
626    ?match([], mnesia_test_lib:start_mnesia([Node2], [])),
627    ?match(yes, rpc:call(Node2, mnesia, force_load_table, [Tab])),
628    ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
629    ?match([Pre], mnesia:dirty_read({Tab, Key})),
630
631    ?match(ok, file:delete(File)),
632    ?verify_mnesia(Nodes, []).
633
634selective_backup_checkpoint(doc) ->
635    ["Perform a selective backup of a checkpoint"];
636selective_backup_checkpoint(suite) -> [];
637selective_backup_checkpoint(Config) when is_list(Config) ->
638    [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
639    Tab = sel_backup,
640    OmitTab = sel_backup_omit,
641    CpName = sel_cp,
642    Def = [{disc_copies, [Node1, Node2]}],
643    File = "selective_backup_checkpoint.BUP",
644    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
645    ?match({atomic, ok}, mnesia:create_table(OmitTab, Def)),
646    ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
647    ?match(ok, mnesia:dirty_write({OmitTab, 1, test_ok})),
648    CpSpec = [{name, CpName}, {max,  mnesia:system_info(tables)}],
649    ?match({ok, CpName, _Ns}, mnesia:activate_checkpoint(CpSpec)),
650
651    BupSpec = [{tables, [Tab]}],
652    ?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec)),
653
654    ?match([schema, sel_backup], bup_tables(File, mnesia_backup)),
655    ?match(ok, file:delete(File)),
656
657    BupSpec2 = [{tables, [Tab, OmitTab]}],
658    ?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec2)),
659
660    ?match([schema, sel_backup, sel_backup_omit],
661	   bup_tables(File, mnesia_backup)),
662    ?match(ok, file:delete(File)),
663    ?verify_mnesia(Nodes, []).
664
665bup_tables(File, Mod) ->
666    Fun = fun(Rec, Tabs) ->
667		  Tab = element(1, Rec),
668		  Tabs2 = [Tab | lists:delete(Tab, Tabs)],
669		  {[Rec], Tabs2}
670	  end,
671    case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
672	{ok, Tabs} ->
673	    lists:sort(Tabs);
674	{error, Reason} ->
675	    exit(Reason)
676    end.
677
678incremental_backup_checkpoint(doc) ->
679    ["Perform a incremental backup of a checkpoint"];
680incremental_backup_checkpoint(suite) -> [];
681incremental_backup_checkpoint(Config) when is_list(Config) ->
682    [Node1] = Nodes = ?acquire_nodes(1, Config),
683    Tab = incr_backup,
684    Def = [{disc_copies, [Node1]}],
685    ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
686    OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
687    ?match([ok|_], [mnesia:dirty_write(R) || R <- OldRecs]),
688    OldCpName = old_cp,
689    OldCpSpec = [{name, OldCpName}, {min,  [Tab]}],
690    ?match({ok, OldCpName, _Ns}, mnesia:activate_checkpoint(OldCpSpec)),
691
692    BupSpec = [{tables, [Tab]}],
693    OldFile = "old_full_backup.BUP",
694    ?match(ok, mnesia:backup_checkpoint(OldCpName, OldFile, BupSpec)),
695    ?match(OldRecs, bup_records(OldFile, mnesia_backup)),
696    ?match(ok, mnesia:dirty_delete({Tab, 1})),
697    ?match(ok, mnesia:dirty_write({Tab, 2, 2})),
698    ?match(ok, mnesia:dirty_write({Tab, 3, -3})),
699
700    NewCpName = new_cp,
701    NewCpSpec = [{name, NewCpName}, {min,  [Tab]}],
702    ?match({ok, NewCpName, _Ns}, mnesia:activate_checkpoint(NewCpSpec)),
703    ?match(ok, mnesia:dirty_write({Tab, 4, 4})),
704
705    NewFile = "new_full_backup.BUP",
706    ?match(ok, mnesia:backup_checkpoint(NewCpName, NewFile, BupSpec)),
707    NewRecs = [{Tab, 2, 2}, {Tab, 3, -3},
708	       {Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}, {Tab, 5, -5}],
709    ?match(NewRecs, bup_records(NewFile, mnesia_backup)),
710
711    DiffFile = "diff_backup.BUP",
712    DiffBupSpec = [{tables, [Tab]}, {incremental, OldCpName}],
713    ?match(ok, mnesia:backup_checkpoint(NewCpName, DiffFile, DiffBupSpec)),
714    DiffRecs = [{Tab, 1}, {Tab, 2}, {Tab, 2, 2}, {Tab, 3}, {Tab, 3, -3},
715		{Tab, 4}, {Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}],
716    ?match(DiffRecs, bup_records(DiffFile, mnesia_backup)),
717
718    ?match(ok, mnesia:deactivate_checkpoint(OldCpName)),
719    ?match(ok, mnesia:deactivate_checkpoint(NewCpName)),
720    ?match(ok, file:delete(OldFile)),
721    ?match(ok, file:delete(NewFile)),
722    ?match(ok, file:delete(DiffFile)),
723
724    ?verify_mnesia(Nodes, []).
725
726bup_records(File, Mod) ->
727    Fun = fun(Rec, Recs) when element(1, Rec) == schema ->
728		  {[Rec], Recs};
729	     (Rec, Recs) ->
730		  {[Rec], [Rec | Recs]}
731	  end,
732    case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
733	{ok, Recs} ->
734	    lists:keysort(1, lists:keysort(2, lists:reverse(Recs)));
735	{error, Reason} ->
736	    exit(Reason)
737    end.
738
739sops_with_checkpoint(doc) ->
740    ["Test schema operations during a checkpoint"];
741sops_with_checkpoint(suite) -> [];
742sops_with_checkpoint(Config) when is_list(Config) ->
743    Ns = [N1,N2] = ?acquire_nodes(2, Config),
744
745    ?match({ok, cp1, Ns}, mnesia:activate_checkpoint([{name, cp1},{max,mnesia:system_info(tables)}])),
746    Tab = tab,
747    ?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies,Ns}])),
748    OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
749    [mnesia:dirty_write(R) || R <- OldRecs],
750
751    ?match({ok, cp2, Ns}, mnesia:activate_checkpoint([{name, cp2},{max,mnesia:system_info(tables)}])),
752    File1 = "cp1_delete_me.BUP",
753    ?match(ok, mnesia:dirty_write({Tab,6,-6})),
754    ?match(ok, mnesia:backup_checkpoint(cp1, File1)),
755    ?match(ok, mnesia:dirty_write({Tab,7,-7})),
756    File2 = "cp2_delete_me.BUP",
757    ?match(ok, mnesia:backup_checkpoint(cp2, File2)),
758
759    ?match(ok, mnesia:deactivate_checkpoint(cp1)),
760    ?match(ok, mnesia:backup_checkpoint(cp2, File1)),
761    ?match(ok, mnesia:dirty_write({Tab,8,-8})),
762
763    ?match({atomic,ok}, mnesia:delete_table(Tab)),
764    ?match({error,_}, mnesia:backup_checkpoint(cp2, File2)),
765    ?match({'EXIT',_}, mnesia:dirty_write({Tab,9,-9})),
766
767    ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])),
768    Test = fun(N) when N > 5 -> ?error("To many records in backup ~p ~n", [N]);
769	      (N) -> case mnesia:dirty_read(Tab,N) of
770			 [{Tab,N,B}] when -B =:= N -> ok;
771			 Other -> ?error("Not matching ~p ~p~n", [N,Other])
772		     end
773	   end,
774    [Test(N) || N <- mnesia:dirty_all_keys(Tab)],
775    ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])),
776
777    %% Mnesia crashes when deleting a table during backup
778    ?match([], mnesia_test_lib:stop_mnesia([N2])),
779    Tab2 = ram,
780    ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies,[N1]}])),
781    ?match({ok, cp3, _}, mnesia:activate_checkpoint([{name, cp3},
782                                                     {ram_overrides_dump,true},
783                                                     {min,[Tab2]}])),
784    Write = fun Loop (N) ->
785                   case N > 0 of
786                       true ->
787                           mnesia:dirty_write({Tab2, N+100, N+100}),
788                           Loop(N-1);
789                       false ->
790                           ok
791                   end
792           end,
793    ok = Write(100000),
794    spawn_link(fun() -> ?match({atomic, ok},mnesia:delete_table(Tab2)) end),
795
796    %% We don't check result here, depends on timing of above call
797    mnesia:backup_checkpoint(cp3, File2),
798    file:delete(File1), file:delete(File2),
799
800    ?verify_mnesia([N1], [N2]).
801