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