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