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, []).