1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2018. 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-module(erl_distribution_SUITE). 21 22-include_lib("common_test/include/ct.hrl"). 23 24-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 25 init_per_group/2,end_per_group/2]). 26 27-export([tick/1, tick_change/1, 28 connect_node/1, 29 nodenames/1, hostnames/1, 30 illegal_nodenames/1, hidden_node/1, 31 setopts/1, 32 table_waste/1, net_setuptime/1, 33 inet_dist_options_options/1, 34 35 monitor_nodes_nodedown_reason/1, 36 monitor_nodes_complex_nodedown_reason/1, 37 monitor_nodes_node_type/1, 38 monitor_nodes_misc/1, 39 monitor_nodes_otp_6481/1, 40 monitor_nodes_errors/1, 41 monitor_nodes_combinations/1, 42 monitor_nodes_cleanup/1, 43 monitor_nodes_many/1]). 44 45%% Performs the test at another node. 46-export([get_socket_priorities/0, 47 tick_cli_test/1, tick_cli_test1/1, 48 tick_serv_test/2, tick_serv_test1/1, 49 run_remote_test/1, 50 setopts_do/2, 51 keep_conn/1, time_ping/1]). 52 53-export([init_per_testcase/2, end_per_testcase/2]). 54 55-export([start_node/2]). 56 57-export([pinger/1]). 58 59-define(DUMMY_NODE,dummy@test01). 60 61%%----------------------------------------------------------------- 62%% The distribution is mainly tested in the big old test_suite. 63%% This test only tests the net_ticktime configuration flag. 64%% Should be started in a CC view with: 65%% erl -sname master -rsh ctrsh 66%%----------------------------------------------------------------- 67 68suite() -> 69 [{ct_hooks,[ts_install_cth]}, 70 {timetrap,{minutes,4}}]. 71 72all() -> 73 [tick, tick_change, nodenames, hostnames, illegal_nodenames, 74 connect_node, 75 hidden_node, setopts, 76 table_waste, net_setuptime, inet_dist_options_options, 77 {group, monitor_nodes}]. 78 79groups() -> 80 [{monitor_nodes, [], 81 [monitor_nodes_nodedown_reason, 82 monitor_nodes_complex_nodedown_reason, 83 monitor_nodes_node_type, monitor_nodes_misc, 84 monitor_nodes_otp_6481, monitor_nodes_errors, 85 monitor_nodes_combinations, monitor_nodes_cleanup, 86 monitor_nodes_many]}]. 87 88init_per_suite(Config) -> 89 Config. 90 91end_per_suite(_Config) -> 92 [slave:stop(N) || N <- nodes()], 93 ok. 94 95init_per_group(_GroupName, Config) -> 96 Config. 97 98end_per_group(_GroupName, Config) -> 99 Config. 100 101init_per_testcase(TC, Config) when TC == hostnames; 102 TC == nodenames -> 103 file:make_dir("hostnames_nodedir"), 104 file:write_file("hostnames_nodedir/ignore_core_files",""), 105 Config; 106init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 107 Config. 108 109end_per_testcase(_Func, _Config) -> 110 ok. 111 112connect_node(Config) when is_list(Config) -> 113 Connected = nodes(connected), 114 true = net_kernel:connect_node(node()), 115 Connected = nodes(connected), 116 ok. 117 118tick(Config) when is_list(Config) -> 119 PaDir = filename:dirname(code:which(erl_distribution_SUITE)), 120 121 %% First check that the normal case is OK! 122 {ok, Node} = start_node(dist_test, "-pa " ++ PaDir), 123 rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]), 124 125 erlang:monitor_node(Node, true), 126 receive 127 {nodedown, Node} -> 128 ct:fail("nodedown from other node") 129 after 30000 -> 130 erlang:monitor_node(Node, false), 131 stop_node(Node) 132 end, 133 134 %% Now, set the net_ticktime for the other node to 12 secs. 135 %% After the sleep(2sec) and cast the other node shall destroy 136 %% the connection as it has not received anything on the connection. 137 %% The nodedown message should arrive within 8 < T < 16 secs. 138 139 %% We must have two slave nodes as the slave mechanism otherwise 140 %% halts the client node after tick timeout (the connection is down 141 %% and the slave node decides to halt !! 142 143 %% Set the ticktime on the server node to 100 secs so the server 144 %% node doesn't tick the client node within the interval ... 145 146 {ok, ServNode} = start_node(dist_test_server, 147 "-kernel net_ticktime 100 " 148 "-pa " ++ PaDir), 149 rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]), 150 151 {ok, _} = start_node(dist_test, 152 "-kernel net_ticktime 12 " 153 "-pa " ++ PaDir), 154 rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]), 155 156 spawn_link(erl_distribution_SUITE, keep_conn, [Node]), 157 158 {tick_serv, ServNode} ! {i_want_the_result, self()}, 159 160 monitor_node(ServNode, true), 161 monitor_node(Node, true), 162 163 receive 164 {tick_test, T} when is_integer(T) -> 165 stop_node(ServNode), 166 stop_node(Node), 167 T; 168 {tick_test, Error} -> 169 stop_node(ServNode), 170 stop_node(Node), 171 ct:fail(Error); 172 {nodedown, Node} -> 173 stop_node(ServNode), 174 ct:fail("client node died"); 175 {nodedown, ServNode} -> 176 stop_node(Node), 177 ct:fail("server node died") 178 end, 179 ok. 180 181%% Checks that pinging nonexistyent nodes does not waste space in distribution table. 182table_waste(Config) when is_list(Config) -> 183 {ok, HName} = inet:gethostname(), 184 F = fun(0,_F) -> []; 185 (N,F) -> 186 Name = list_to_atom("erl_distribution_"++integer_to_list(N)++ 187 "@"++HName), 188 pang = net_adm:ping(Name), 189 F(N-1,F) 190 end, 191 F(256,F), 192 {ok, N} = start_node(erl_distribution_300,""), 193 stop_node(N), 194 ok. 195 196%% Test that starting nodes with different legal name part works, and that illegal 197%% ones are filtered 198nodenames(Config) when is_list(Config) -> 199 legal("a1@b"), 200 legal("a-1@b"), 201 legal("a_1@b"), 202 203 illegal("cdé@a"), 204 illegal("te欢st@a"). 205 206%% Test that starting nodes with different legal host part works, and that illegal 207%% ones are filtered 208hostnames(Config) when is_list(Config) -> 209 Host = gethostname(), 210 legal([$a,$@|atom_to_list(Host)]), 211 legal("1@b1"), 212 legal("b@b1-c"), 213 legal("c@b1_c"), 214 legal("d@b1#c"), 215 legal("f@::1"), 216 legal("g@1:bc3:4e3f:f20:0:1"), 217 218 case file:native_name_encoding() of 219 latin1 -> ignore; 220 _ -> legal("e@b1é") 221 end, 222 long_hostnames(net_kernel:longnames()), 223 224 illegal("h@testالع"), 225 illegal("i@языtest"), 226 illegal("j@te欢st"). 227 228long_hostnames(true) -> 229 legal("k@b.b.c"), 230 legal("l@b.b-c.d"), 231 legal("m@b.b_c.d"), 232 legal("n@127.0.0.1"), 233 legal("o@207.123.456.789"); 234long_hostnames(false) -> 235 illegal("k@b.b.c"). 236 237legal(Name) -> 238 case test_node(Name) of 239 started -> 240 ok; 241 not_started -> 242 ct:fail("no ~p node started", [Name]) 243 end. 244 245illegal(Name) -> 246 case test_node(Name, true) of 247 not_started -> 248 ok; 249 started -> 250 ct:fail("~p node started with illegal name", [Name]) 251 end. 252 253test_node(Name) -> 254 test_node(Name, false). 255test_node(Name, Illigal) -> 256 ProgName = ct:get_progname(), 257 Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++ 258 " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++ 259 case Illigal of 260 true -> 261 " -eval \"timer:sleep(10000),init:stop().\""; 262 false -> 263 "" 264 end, 265 net_kernel:monitor_nodes(true), 266 BinCommand = unicode:characters_to_binary(Command, utf8), 267 Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]), 268 Node = list_to_atom(Name), 269 receive 270 {nodeup, Node} -> 271 net_kernel:monitor_nodes(false), 272 slave:stop(Node), 273 started 274 after 5000 -> 275 net_kernel:monitor_nodes(false), 276 not_started 277 end. 278 279long_or_short() -> 280 case net_kernel:longnames() of 281 true -> " -name "; 282 false -> " -sname " 283 end. 284 285% get the localhost's name, depending on the using name policy 286gethostname() -> 287 Hostname = case net_kernel:longnames() of 288 true-> 289 net_adm:localhost(); 290 _-> 291 {ok, Name}=inet:gethostname(), 292 Name 293 end, 294 list_to_atom(Hostname). 295 296%% Test that pinging an illegal nodename does not kill the node. 297illegal_nodenames(Config) when is_list(Config) -> 298 PaDir = filename:dirname(code:which(erl_distribution_SUITE)), 299 {ok, Node}=start_node(illegal_nodenames, "-pa " ++ PaDir), 300 monitor_node(Node, true), 301 RPid=rpc:call(Node, erlang, spawn, 302 [?MODULE, pinger, [self()]]), 303 receive 304 {RPid, pinged} -> 305 ok; 306 {nodedown, Node} -> 307 ct:fail("Remote node died.") 308 end, 309 stop_node(Node), 310 ok. 311 312pinger(Starter) -> 313 io:format("Starter:~p~n",[Starter]), 314 net_adm:ping(a@b@c), 315 Starter ! {self(), pinged}, 316 ok. 317 318 319%% Test that you can set the net_setuptime properly. 320net_setuptime(Config) when is_list(Config) -> 321 %% In this test case, we reluctantly accept shorter times than the given 322 %% setup time, because the connection attempt can end in a 323 %% "Host unreachable" error before the timeout fires. 324 325 Res0 = do_test_setuptime("2"), 326 io:format("Res0 = ~p", [Res0]), 327 true = (Res0 =< 4000), 328 Res1 = do_test_setuptime("0.3"), 329 io:format("Res1 = ~p", [Res1]), 330 true = (Res1 =< 500), 331 ok. 332 333do_test_setuptime(Setuptime) when is_list(Setuptime) -> 334 PaDir = filename:dirname(code:which(?MODULE)), 335 {ok, Node} = start_node(dist_setuptime_test, "-pa " ++ PaDir ++ 336 " -kernel net_setuptime " ++ Setuptime), 337 Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]), 338 stop_node(Node), 339 Res. 340 341time_ping(Node) -> 342 T0 = erlang:monotonic_time(), 343 pang = net_adm:ping(Node), 344 T1 = erlang:monotonic_time(), 345 erlang:convert_time_unit(T1 - T0, native, millisecond). 346 347%% Keep the connection with the client node up. 348%% This is necessary as the client node runs with much shorter 349%% tick time !! 350keep_conn(Node) -> 351 sleep(1), 352 rpc:cast(Node, erlang, time, []), 353 keep_conn(Node). 354 355tick_serv_test(Node, MasterNode) -> 356 spawn(erl_distribution_SUITE, keep_conn, [MasterNode]), 357 spawn(erl_distribution_SUITE, tick_serv_test1, [Node]). 358 359tick_serv_test1(Node) -> 360 register(tick_serv, self()), 361 TestServer = receive {i_want_the_result, TS} -> TS end, 362 monitor_node(Node, true), 363 receive 364 {nodedown, Node} -> 365 net_adm:ping(Node), %% Set up the connection again !! 366 367 {tick_test, Node} ! {whats_the_result, self()}, 368 receive 369 {tick_test, Res} -> 370 TestServer ! {tick_test, Res} 371 end 372 end. 373 374tick_cli_test(Node) -> 375 spawn(erl_distribution_SUITE, tick_cli_test1, [Node]). 376 377tick_cli_test1(Node) -> 378 register(tick_test, self()), 379 erlang:monitor_node(Node, true), 380 sleep(2), 381 rpc:call(Node, erlang, time, []), %% simulate action on the connection 382 T1 = erlang:monotonic_time(), 383 receive 384 {nodedown, Node} -> 385 T2 = erlang:monotonic_time(), 386 receive 387 {whats_the_result, From} -> 388 Diff = erlang:convert_time_unit(T2-T1, native, 389 millisecond), 390 case Diff of 391 T when T > 8000, T < 16000 -> 392 From ! {tick_test, T}; 393 T -> 394 From ! {tick_test, 395 {"T not in interval 8000 < T < 16000", 396 T}} 397 end 398 end 399 end. 400 401setopts(Config) when is_list(Config) -> 402 register(setopts_regname, self()), 403 [N1,N2,N3,N4] = get_nodenames(4, setopts), 404 405 {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test, 406 ["setopts_do", atom_to_list(node()), "1", "ping"]), 407 0 = wait_for_port_exit(Port1), 408 409 {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test, 410 ["setopts_do", atom_to_list(node()), "2", "ping"]), 411 0 = wait_for_port_exit(Port2), 412 413 {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]), 414 {ok, LTcpPort} = inet:port(LSock), 415 416 {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test, 417 ["setopts_do", atom_to_list(node()), 418 "1", integer_to_list(LTcpPort)]), 419 wait_and_connect(LSock, N3F, Port3), 420 0 = wait_for_port_exit(Port3), 421 422 {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test, 423 ["setopts_do", atom_to_list(node()), 424 "2", integer_to_list(LTcpPort)]), 425 wait_and_connect(LSock, N4F, Port4), 426 0 = wait_for_port_exit(Port4), 427 428 ok. 429 430wait_and_connect(LSock, NodeName, NodePort) -> 431 {ok, Sock} = gen_tcp:accept(LSock), 432 {ok, "Connect please"} = gen_tcp:recv(Sock, 0), 433 flush_from_port(NodePort), 434 pong = net_adm:ping(NodeName), 435 gen_tcp:send(Sock, "Connect done"), 436 gen_tcp:close(Sock). 437 438 439flush_from_port(Port) -> 440 flush_from_port(Port, 10). 441 442flush_from_port(Port, Timeout) -> 443 receive 444 {Port,{data,String}} -> 445 io:format("~p: ~s\n", [Port, String]), 446 flush_from_port(Port, Timeout) 447 after Timeout -> 448 timeout 449 end. 450 451wait_for_port_exit(Port) -> 452 case (receive M -> M end) of 453 {Port,{exit_status,Status}} -> 454 Status; 455 {Port,{data,String}} -> 456 io:format("~p: ~s\n", [Port, String]), 457 wait_for_port_exit(Port) 458 end. 459 460run_remote_test([FuncStr, TestNodeStr | Args]) -> 461 Status = try 462 io:format("Node ~p started~n", [node()]), 463 TestNode = list_to_atom(TestNodeStr), 464 io:format("Node ~p spawning function ~p~n", [node(), FuncStr]), 465 {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]), 466 io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]), 467 receive 468 {'DOWN', Ref, process, Pid, normal} -> 469 0; 470 Other -> 471 io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]), 472 1 473 end 474 catch 475 C:E:S -> 476 io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", 477 [node(), C, E, S]), 478 2 479 end, 480 io:format("Node ~p doing halt(~p).\n",[node(), Status]), 481 erlang:halt(Status). 482 483% Do the actual test on the remote node 484setopts_do(TestNode, [OptNr, ConnectData]) -> 485 [] = nodes(), 486 {Opt, Val} = opt_from_nr(OptNr), 487 ok = net_kernel:setopts(new, [{Opt, Val}]), 488 489 [] = nodes(), 490 {error, noconnection} = net_kernel:getopts(TestNode, [Opt]), 491 492 case ConnectData of 493 "ping" -> % We connect 494 net_adm:ping(TestNode); 495 TcpPort -> % Other connect 496 {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort), 497 [{active,false},{packet,2}]), 498 ok = gen_tcp:send(Sock, "Connect please"), 499 {ok, "Connect done"} = gen_tcp:recv(Sock, 0), 500 gen_tcp:close(Sock) 501 end, 502 [TestNode] = nodes(), 503 {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), 504 {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]), 505 506 NewVal = change_val(Val), 507 ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]), 508 {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]), 509 510 ok = net_kernel:setopts(TestNode, [{Opt, Val}]), 511 {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), 512 513 ok. 514 515opt_from_nr("1") -> {nodelay, true}; 516opt_from_nr("2") -> {nodelay, false}. 517 518change_val(true) -> false; 519change_val(false) -> true. 520 521start_node_unconnected(Name, Mod, Func, Args) -> 522 FullName = full_node_name(Name), 523 CmdLine = mk_node_cmdline(Name,Mod,Func,Args), 524 io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), 525 case open_port({spawn, CmdLine}, [exit_status]) of 526 Port when is_port(Port) -> 527 {FullName, Port}; 528 Error -> 529 exit({failed_to_start_node, FullName, Error}) 530 end. 531 532full_node_name(PreName) -> 533 HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, 534 atom_to_list(node())), 535 list_to_atom(atom_to_list(PreName) ++ HostSuffix). 536 537mk_node_cmdline(Name,Mod,Func,Args) -> 538 Static = "-noinput", 539 Pa = filename:dirname(code:which(?MODULE)), 540 Prog = case catch init:get_argument(progname) of 541 {ok,[[P]]} -> P; 542 _ -> exit(no_progname_argument_found) 543 end, 544 NameSw = case net_kernel:longnames() of 545 false -> "-sname "; 546 true -> "-name "; 547 _ -> exit(not_distributed_node) 548 end, 549 {ok, Pwd} = file:get_cwd(), 550 NameStr = atom_to_list(Name), 551 Prog ++ " " 552 ++ Static ++ " " 553 ++ NameSw ++ " " ++ NameStr 554 ++ " -pa " ++ Pa 555 ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr 556 ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) 557 ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func) 558 ++ " " ++ string:join(Args, " "). 559 560 561%% OTP-4255. 562tick_change(Config) when is_list(Config) -> 563 PaDir = filename:dirname(code:which(?MODULE)), 564 [BN, CN] = get_nodenames(2, tick_change), 565 DefaultTT = net_kernel:get_net_ticktime(), 566 unchanged = net_kernel:set_net_ticktime(DefaultTT, 60), 567 case DefaultTT of 568 I when is_integer(I) -> ok; 569 _ -> ct:fail(DefaultTT) 570 end, 571 572 %% In case other nodes are connected 573 case nodes(connected) of 574 [] -> net_kernel:set_net_ticktime(10, 0); 575 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 576 set_net_ticktime, [10, 5]) 577 end, 578 579 wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end), 580 {ok, B} = start_node(BN, "-kernel net_ticktime 10 -pa " ++ PaDir), 581 {ok, C} = start_node(CN, "-kernel net_ticktime 10 -hidden -pa " 582 ++ PaDir), 583 584 OTE = process_flag(trap_exit, true), 585 case catch begin 586 run_tick_change_test(B, C, 10, 1, PaDir), 587 run_tick_change_test(B, C, 1, 10, PaDir) 588 end of 589 {'EXIT', Reason} -> 590 stop_node(B), 591 stop_node(C), 592 %% In case other nodes are connected 593 case nodes(connected) of 594 [] -> net_kernel:set_net_ticktime(DefaultTT, 0); 595 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 596 set_net_ticktime, [DefaultTT, 10]) 597 end, 598 wait_until(fun () -> 599 DefaultTT == net_kernel:get_net_ticktime() 600 end), 601 process_flag(trap_exit, OTE), 602 ct:fail(Reason); 603 _ -> 604 ok 605 end, 606 process_flag(trap_exit, OTE), 607 stop_node(B), 608 stop_node(C), 609 610 %% In case other nodes are connected 611 case nodes(connected) of 612 [] -> net_kernel:set_net_ticktime(DefaultTT, 0); 613 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 614 set_net_ticktime, [DefaultTT, 5]) 615 end, 616 617 wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end), 618 ok. 619 620 621wait_for_nodedowns(Tester, Ref) -> 622 receive 623 {nodedown, Node} -> 624 io:format("~p~n", [{node(), {nodedown, Node}}]), 625 Tester ! {Ref, {node(), {nodedown, Node}}} 626 end, 627 wait_for_nodedowns(Tester, Ref). 628 629run_tick_change_test(B, C, PrevTT, TT, PaDir) -> 630 [DN, EN] = get_nodenames(2, tick_change), 631 632 Tester = self(), 633 Ref = make_ref(), 634 MonitorNodes = fun (Nodes) -> 635 lists:foreach( 636 fun (N) -> 637 monitor_node(N,true) 638 end, 639 Nodes), 640 wait_for_nodedowns(Tester, Ref) 641 end, 642 643 {ok, D} = start_node(DN, "-kernel net_ticktime " 644 ++ integer_to_list(PrevTT) ++ " -pa " ++ PaDir), 645 646 NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end), 647 NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end), 648 NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end), 649 650 MaxTT = case PrevTT > TT of 651 true -> PrevTT; 652 false -> TT 653 end, 654 655 CheckResult = make_ref(), 656 spawn_link(fun () -> 657 receive 658 after (25 + MaxTT)*1000 -> 659 Tester ! CheckResult 660 end 661 end), 662 663 %% In case other nodes than these are connected 664 case nodes(connected) -- [B, C, D] of 665 [] -> ok; 666 OtherNodes -> rpc:multicall(OtherNodes, net_kernel, 667 set_net_ticktime, [TT, 20]) 668 end, 669 670 change_initiated = net_kernel:set_net_ticktime(TT,20), 671 {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20), 672 sleep(3), 673 change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]), 674 sleep(7), 675 change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]), 676 677 {ok, E} = start_node(EN, "-kernel net_ticktime " 678 ++ integer_to_list(TT) ++ " -pa " ++ PaDir), 679 NME = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end), 680 NMA2 = spawn_link(fun () -> MonitorNodes([E]) end), 681 NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end), 682 NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end), 683 684 receive CheckResult -> ok end, 685 686 unlink(NMA), exit(NMA, kill), 687 unlink(NMB), exit(NMB, kill), 688 unlink(NMC), exit(NMC, kill), 689 unlink(NME), exit(NME, kill), 690 unlink(NMA2), exit(NMA2, kill), 691 unlink(NMB2), exit(NMB2, kill), 692 unlink(NMC2), exit(NMC2, kill), 693 694 %% The node not changing ticktime should have been disconnected from the 695 %% other nodes 696 receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ok 697 after 0 -> exit({?LINE, no_nodedown}) 698 end, 699 receive {Ref, {B, {nodedown, D}}} -> ok 700 after 0 -> exit({?LINE, no_nodedown}) 701 end, 702 receive {Ref, {C, {nodedown, D}}} -> ok 703 after 0 -> exit({?LINE, no_nodedown}) 704 end, 705 receive {Ref, {E, {nodedown, D}}} -> ok 706 after 0 -> exit({?LINE, no_nodedown}) 707 end, 708 709 %% No other connections should have been broken 710 receive 711 {Ref, Reason} -> 712 stop_node(E), 713 exit({?LINE, Reason}); 714 {'EXIT', Pid, Reason} when Pid == NMA; 715 Pid == NMB; 716 Pid == NMC; 717 Pid == NME; 718 Pid == NMA2; 719 Pid == NMB2; 720 Pid == NMC2 -> 721 stop_node(E), 722 723 exit({?LINE, {node(Pid), Reason}}) 724 after 0 -> 725 TT = net_kernel:get_net_ticktime(), 726 TT = rpc:call(B, net_kernel, get_net_ticktime, []), 727 TT = rpc:call(C, net_kernel, get_net_ticktime, []), 728 TT = rpc:call(E, net_kernel, get_net_ticktime, []), 729 stop_node(E), 730 ok 731 end. 732 733%% 734%% Basic tests of hidden node. 735%% 736%% Basic test of hidden node. 737hidden_node(Config) when is_list(Config) -> 738 PaDir = filename:dirname(code:which(?MODULE)), 739 VArgs = "-pa " ++ PaDir, 740 HArgs = "-hidden -pa " ++ PaDir, 741 {ok, V} = start_node(visible_node, VArgs), 742 VMN = start_monitor_nodes_proc(V), 743 {ok, H} = start_node(hidden_node, HArgs), 744 %% Connect visible_node -> hidden_node 745 connect_nodes(V, H), 746 test_nodes(V, H), 747 stop_node(H), 748 sleep(5), 749 check_monitor_nodes_res(VMN, H), 750 stop_node(V), 751 {ok, H} = start_node(hidden_node, HArgs), 752 HMN = start_monitor_nodes_proc(H), 753 {ok, V} = start_node(visible_node, VArgs), 754 %% Connect hidden_node -> visible_node 755 connect_nodes(H, V), 756 test_nodes(V, H), 757 stop_node(V), 758 sleep(5), 759 check_monitor_nodes_res(HMN, V), 760 stop_node(H), 761 ok. 762 763connect_nodes(A, B) -> 764 %% Check that they haven't already connected. 765 false = lists:member(A, rpc:call(B, erlang, nodes, [connected])), 766 false = lists:member(B, rpc:call(A, erlang, nodes, [connected])), 767 %% Connect them. 768 pong = rpc:call(A, net_adm, ping, [B]). 769 770 771test_nodes(V, H) -> 772 %% No nodes should be visible on hidden_node 773 [] = rpc:call(H, erlang, nodes, []), 774 %% visible_node should be hidden on hidden_node 775 true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])), 776 %% hidden_node node shouldn't be visible on visible_node 777 false = lists:member(H, rpc:call(V, erlang, nodes, [])), 778 %% hidden_node should be hidden on visible_node 779 true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])). 780 781mn_loop(MNs) -> 782 receive 783 {nodeup, N} -> 784 mn_loop([{nodeup, N}|MNs]); 785 {nodedown, N} -> 786 mn_loop([{nodedown, N}|MNs]); 787 {monitor_nodes_result, Ref, From} -> 788 From ! {Ref, MNs}; 789 _ -> 790 mn_loop(MNs) 791 end. 792 793start_monitor_nodes_proc(Node) -> 794 Ref = make_ref(), 795 Starter = self(), 796 Pid = spawn(Node, 797 fun() -> 798 net_kernel:monitor_nodes(true), 799 Starter ! Ref, 800 mn_loop([]) 801 end), 802 receive 803 Ref -> 804 ok 805 end, 806 Pid. 807 808 809check_monitor_nodes_res(Pid, Node) -> 810 Ref = make_ref(), 811 Pid ! {monitor_nodes_result, Ref, self()}, 812 receive 813 {Ref, MNs} -> 814 false = lists:keysearch(Node, 2, MNs) 815 end. 816 817 818 819%% Check the kernel inet_dist_{listen,connect}_options options. 820inet_dist_options_options(Config) when is_list(Config) -> 821 Prio = 1, 822 case gen_udp:open(0, [{priority,Prio}]) of 823 {ok,Socket} -> 824 case inet:getopts(Socket, [priority]) of 825 {ok,[{priority,Prio}]} -> 826 ok = gen_udp:close(Socket), 827 do_inet_dist_options_options(Prio); 828 _ -> 829 ok = gen_udp:close(Socket), 830 {skip, 831 "Can not set priority "++integer_to_list(Prio)++ 832 " on socket"} 833 end; 834 {error,_} -> 835 {skip, "Can not set priority on socket"} 836 end. 837 838do_inet_dist_options_options(Prio) -> 839 PriorityString0 = "[{priority,"++integer_to_list(Prio)++"}]", 840 PriorityString = 841 case os:cmd("echo [{a,1}]") of 842 "[{a,1}]"++_ -> 843 PriorityString0; 844 _ -> 845 %% Some shells need quoting of [{}] 846 "'"++PriorityString0++"'" 847 end, 848 InetDistOptions = 849 "-hidden " 850 "-kernel inet_dist_connect_options "++PriorityString++" " 851 "-kernel inet_dist_listen_options "++PriorityString, 852 {ok,Node1} = 853 start_node(inet_dist_options_1, InetDistOptions), 854 {ok,Node2} = 855 start_node(inet_dist_options_2, InetDistOptions), 856 %% 857 pong = 858 rpc:call(Node1, net_adm, ping, [Node2]), 859 PrioritiesNode1 = 860 rpc:call(Node1, ?MODULE, get_socket_priorities, []), 861 PrioritiesNode2 = 862 rpc:call(Node2, ?MODULE, get_socket_priorities, []), 863 io:format("PrioritiesNode1 = ~p", [PrioritiesNode1]), 864 io:format("PrioritiesNode2 = ~p", [PrioritiesNode2]), 865 Elevated = [P || P <- PrioritiesNode1, P =:= Prio], 866 Elevated = [P || P <- PrioritiesNode2, P =:= Prio], 867 [_|_] = Elevated, 868 %% 869 stop_node(Node2), 870 stop_node(Node1), 871 ok. 872 873get_socket_priorities() -> 874 [Priority || 875 {ok,[{priority,Priority}]} <- 876 [inet:getopts(Port, [priority]) || 877 Port <- erlang:ports(), 878 element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]]. 879 880 881 882%% 883%% Testcase: 884%% monitor_nodes_nodedown_reason 885%% 886 887monitor_nodes_nodedown_reason(Config) when is_list(Config) -> 888 MonNodeState = monitor_node_state(), 889 ok = net_kernel:monitor_nodes(true), 890 ok = net_kernel:monitor_nodes(true, [nodedown_reason]), 891 892 Names = get_numbered_nodenames(5, node), 893 [NN1, NN2, NN3, NN4, NN5] = Names, 894 895 {ok, N1} = start_node(NN1), 896 {ok, N2} = start_node(NN2), 897 {ok, N3} = start_node(NN3), 898 {ok, N4} = start_node(NN4, "-hidden"), 899 900 receive {nodeup, N1} -> ok end, 901 receive {nodeup, N2} -> ok end, 902 receive {nodeup, N3} -> ok end, 903 904 receive {nodeup, N1, []} -> ok end, 905 receive {nodeup, N2, []} -> ok end, 906 receive {nodeup, N3, []} -> ok end, 907 908 stop_node(N1), 909 stop_node(N4), 910 true = net_kernel:disconnect(N2), 911 TickTime = net_kernel:get_net_ticktime(), 912 SleepTime = TickTime + (TickTime div 2), 913 spawn(N3, fun () -> 914 block_emu(SleepTime*1000), 915 halt() 916 end), 917 918 receive {nodedown, N1} -> ok end, 919 receive {nodedown, N2} -> ok end, 920 receive {nodedown, N3} -> ok end, 921 922 receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end, 923 receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end, 924 receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end, 925 926 ok = net_kernel:monitor_nodes(false, [nodedown_reason]), 927 928 {ok, N5} = start_node(NN5), 929 stop_node(N5), 930 931 receive {nodeup, N5} -> ok end, 932 receive {nodedown, N5} -> ok end, 933 print_my_messages(), 934 ok = check_no_nodedown_nodeup(1000), 935 ok = net_kernel:monitor_nodes(false), 936 MonNodeState = monitor_node_state(), 937 ok. 938 939 940monitor_nodes_complex_nodedown_reason(Config) when is_list(Config) -> 941 MonNodeState = monitor_node_state(), 942 Me = self(), 943 ok = net_kernel:monitor_nodes(true, [nodedown_reason]), 944 [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason), 945 {ok, Node} = start_node(Name, ""), 946 Pid = spawn(Node, 947 fun() -> 948 Me ! {stuff, 949 self(), 950 [make_ref(), 951 {processes(), erlang:ports()}]} 952 end), 953 receive {nodeup, Node, []} -> ok end, 954 {ok, NodeInfo} = net_kernel:node_info(Node), 955 {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo), 956 ComplexTerm = receive {stuff, Pid, _} = Msg -> 957 {Msg, term_to_binary(Msg)} 958 end, 959 exit(Owner, ComplexTerm), 960 receive 961 {nodedown, Node, [{nodedown_reason, NodeDownReason}]} -> 962 ok 963 end, 964 %% If the complex nodedown_reason messed something up garbage collections 965 %% are likely to dump core 966 garbage_collect(), 967 garbage_collect(), 968 garbage_collect(), 969 ComplexTerm = NodeDownReason, 970 ok = net_kernel:monitor_nodes(false, [nodedown_reason]), 971 no_msgs(), 972 MonNodeState = monitor_node_state(), 973 ok. 974 975 976 977 978%% 979%% Testcase: 980%% monitor_nodes_node_type 981%% 982 983monitor_nodes_node_type(Config) when is_list(Config) -> 984 MonNodeState = monitor_node_state(), 985 ok = net_kernel:monitor_nodes(true), 986 ok = net_kernel:monitor_nodes(true, [{node_type, all}]), 987 Names = get_numbered_nodenames(9, node), 988 [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names, 989 990 {ok, N1} = start_node(NN1), 991 {ok, N2} = start_node(NN2), 992 {ok, N3} = start_node(NN3, "-hidden"), 993 {ok, N4} = start_node(NN4, "-hidden"), 994 995 receive {nodeup, N1} -> ok end, 996 receive {nodeup, N2} -> ok end, 997 998 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 999 receive {nodeup, N2, [{node_type, visible}]} -> ok end, 1000 receive {nodeup, N3, [{node_type, hidden}]} -> ok end, 1001 receive {nodeup, N4, [{node_type, hidden}]} -> ok end, 1002 1003 stop_node(N1), 1004 stop_node(N2), 1005 stop_node(N3), 1006 stop_node(N4), 1007 1008 receive {nodedown, N1} -> ok end, 1009 receive {nodedown, N2} -> ok end, 1010 1011 receive {nodedown, N1, [{node_type, visible}]} -> ok end, 1012 receive {nodedown, N2, [{node_type, visible}]} -> ok end, 1013 receive {nodedown, N3, [{node_type, hidden}]} -> ok end, 1014 receive {nodedown, N4, [{node_type, hidden}]} -> ok end, 1015 1016 ok = net_kernel:monitor_nodes(false, [{node_type, all}]), 1017 {ok, N5} = start_node(NN5), 1018 1019 receive {nodeup, N5} -> ok end, 1020 stop_node(N5), 1021 receive {nodedown, N5} -> ok end, 1022 1023 ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]), 1024 {ok, N6} = start_node(NN6), 1025 {ok, N7} = start_node(NN7, "-hidden"), 1026 1027 1028 receive {nodeup, N6} -> ok end, 1029 receive {nodeup, N7, [{node_type, hidden}]} -> ok end, 1030 stop_node(N6), 1031 stop_node(N7), 1032 1033 receive {nodedown, N6} -> ok end, 1034 receive {nodedown, N7, [{node_type, hidden}]} -> ok end, 1035 1036 ok = net_kernel:monitor_nodes(true, [{node_type, visible}]), 1037 ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]), 1038 ok = net_kernel:monitor_nodes(false), 1039 1040 {ok, N8} = start_node(NN8), 1041 {ok, N9} = start_node(NN9, "-hidden"), 1042 1043 receive {nodeup, N8, [{node_type, visible}]} -> ok end, 1044 stop_node(N8), 1045 stop_node(N9), 1046 1047 receive {nodedown, N8, [{node_type, visible}]} -> ok end, 1048 print_my_messages(), 1049 ok = check_no_nodedown_nodeup(1000), 1050 ok = net_kernel:monitor_nodes(false, [{node_type, visible}]), 1051 MonNodeState = monitor_node_state(), 1052 ok. 1053 1054 1055%% 1056%% Testcase: 1057%% monitor_nodes 1058%% 1059 1060monitor_nodes_misc(Config) when is_list(Config) -> 1061 MonNodeState = monitor_node_state(), 1062 ok = net_kernel:monitor_nodes(true), 1063 ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]), 1064 ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]), 1065 Names = get_numbered_nodenames(3, node), 1066 [NN1, NN2, NN3] = Names, 1067 1068 {ok, N1} = start_node(NN1), 1069 {ok, N2} = start_node(NN2, "-hidden"), 1070 1071 receive {nodeup, N1} -> ok end, 1072 1073 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 1074 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 1075 receive {nodeup, N2, [{node_type, hidden}]} -> ok end, 1076 receive {nodeup, N2, [{node_type, hidden}]} -> ok end, 1077 1078 stop_node(N1), 1079 stop_node(N2), 1080 1081 VisbleDownInfo = lists:sort([{node_type, visible}, 1082 {nodedown_reason, connection_closed}]), 1083 HiddenDownInfo = lists:sort([{node_type, hidden}, 1084 {nodedown_reason, connection_closed}]), 1085 1086 receive {nodedown, N1} -> ok end, 1087 1088 receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end, 1089 receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end, 1090 receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end, 1091 receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end, 1092 1093 ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]), 1094 1095 {ok, N3} = start_node(NN3), 1096 receive {nodeup, N3} -> ok end, 1097 stop_node(N3), 1098 receive {nodedown, N3} -> ok end, 1099 print_my_messages(), 1100 ok = check_no_nodedown_nodeup(1000), 1101 ok = net_kernel:monitor_nodes(false), 1102 MonNodeState = monitor_node_state(), 1103 ok. 1104 1105 1106%% Tests that {nodeup, Node} messages are received before 1107%% messages from Node and that {nodedown, Node} messages are 1108%% received after messages from Node. 1109monitor_nodes_otp_6481(Config) when is_list(Config) -> 1110 io:format("Testing nodedown...~n"), 1111 monitor_nodes_otp_6481_test(Config, nodedown), 1112 io:format("ok~n"), 1113 io:format("Testing nodeup...~n"), 1114 monitor_nodes_otp_6481_test(Config, nodeup), 1115 io:format("ok~n"), 1116 ok. 1117 1118monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> 1119 MonNodeState = monitor_node_state(), 1120 NodeMsg = make_ref(), 1121 Me = self(), 1122 [Name] = get_nodenames(1, monitor_nodes_otp_6481), 1123 case TestType of 1124 nodedown -> ok = net_kernel:monitor_nodes(true); 1125 nodeup -> ok 1126 end, 1127 Seq = lists:seq(1,10000), 1128 MN = spawn_link( 1129 fun () -> 1130 lists:foreach( 1131 fun (_) -> 1132 ok = net_kernel:monitor_nodes(true) 1133 end, 1134 Seq), 1135 Me ! {mon_set, self()}, 1136 receive after infinity -> ok end 1137 end), 1138 receive {mon_set, MN} -> ok end, 1139 case TestType of 1140 nodedown -> ok; 1141 nodeup -> ok = net_kernel:monitor_nodes(true) 1142 end, 1143 1144 %% Whitebox: 1145 %% nodedown test: Since this process was the first one monitoring 1146 %% nodes this process will be the first one notified 1147 %% on nodedown. 1148 %% nodeup test: Since this process was the last one monitoring 1149 %% nodes this process will be the last one notified 1150 %% on nodeup 1151 1152 %% Verify the monitor_nodes order expected 1153 TestMonNodeState = monitor_node_state(), 1154 %% io:format("~p~n", [TestMonNodeState]), 1155 TestMonNodeState = 1156 case TestType of 1157 nodedown -> []; 1158 nodeup -> [{self(), []}] 1159 end 1160 ++ lists:map(fun (_) -> {MN, []} end, Seq) 1161 ++ case TestType of 1162 nodedown -> [{self(), []}]; 1163 nodeup -> [] 1164 end 1165 ++ MonNodeState, 1166 1167 {ok, Node} = start_node(Name, "", this), 1168 receive {nodeup, Node} -> ok end, 1169 1170 RemotePid = spawn(Node, 1171 fun () -> 1172 receive after 1500 -> ok end, 1173 %% infinite loop of msgs 1174 %% we want an endless stream of messages and the kill 1175 %% the node mercilessly. 1176 %% We then want to ensure that the nodedown message arrives 1177 %% last ... without garbage after it. 1178 _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), 1179 receive {Me, kill_it} -> ok end, 1180 halt() 1181 end), 1182 1183 net_kernel:disconnect(Node), 1184 receive {nodedown, Node} -> ok end, 1185 1186 %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message 1187 %% bringing up the connection). 1188 {nodeup, Node} = receive Msg1 -> Msg1 end, 1189 {NodeMsg, N} = receive Msg2 -> Msg2 end, 1190 %% msg stream has begun, kill the node 1191 RemotePid ! {self(), kill_it}, 1192 1193 %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}' 1194 %% message. 1195 {nodedown, Node} = flush_node_msgs(NodeMsg, N+1), 1196 no_msgs(500), 1197 1198 Mon = erlang:monitor(process, MN), 1199 unlink(MN), 1200 exit(MN, bang), 1201 receive {'DOWN', Mon, process, MN, bang} -> ok end, 1202 ok = net_kernel:monitor_nodes(false), 1203 MonNodeState = monitor_node_state(), 1204 ok. 1205 1206flush_node_msgs(NodeMsg, No) -> 1207 case receive Msg -> Msg end of 1208 {NodeMsg, N} when N >= No -> 1209 flush_node_msgs(NodeMsg, N+1); 1210 OtherMsg -> 1211 OtherMsg 1212 end. 1213 1214node_loop_send(Pid, Msg, No) -> 1215 Pid ! {Msg, No}, 1216 node_loop_send(Pid, Msg, No + 1). 1217 1218monitor_nodes_errors(Config) when is_list(Config) -> 1219 MonNodeState = monitor_node_state(), 1220 error = net_kernel:monitor_nodes(asdf), 1221 {error, 1222 {unknown_options, 1223 [gurka]}} = net_kernel:monitor_nodes(true, 1224 [gurka]), 1225 {error, 1226 {options_not_a_list, 1227 gurka}} = net_kernel:monitor_nodes(true, 1228 gurka), 1229 {error, 1230 {option_value_mismatch, 1231 [{node_type,visible}, 1232 {node_type,hidden}]}} 1233 = net_kernel:monitor_nodes(true, 1234 [{node_type,hidden}, 1235 {node_type,visible}]), 1236 {error, 1237 {option_value_mismatch, 1238 [{node_type,visible}, 1239 {node_type,all}]}} 1240 = net_kernel:monitor_nodes(true, 1241 [{node_type,all}, 1242 {node_type,visible}]), 1243 {error, 1244 {bad_option_value, 1245 {node_type, 1246 blaha}}} 1247 = net_kernel:monitor_nodes(true, [{node_type, blaha}]), 1248 MonNodeState = monitor_node_state(), 1249 ok. 1250 1251monitor_nodes_combinations(Config) when is_list(Config) -> 1252 MonNodeState = monitor_node_state(), 1253 monitor_nodes_all_comb(true), 1254 [VisibleName, HiddenName] = get_nodenames(2, 1255 monitor_nodes_combinations), 1256 {ok, Visible} = start_node(VisibleName, ""), 1257 receive_all_comb_nodeup_msgs(visible, Visible), 1258 no_msgs(), 1259 stop_node(Visible), 1260 receive_all_comb_nodedown_msgs(visible, Visible, connection_closed), 1261 no_msgs(), 1262 {ok, Hidden} = start_node(HiddenName, "-hidden"), 1263 receive_all_comb_nodeup_msgs(hidden, Hidden), 1264 no_msgs(), 1265 stop_node(Hidden), 1266 receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed), 1267 no_msgs(), 1268 monitor_nodes_all_comb(false), 1269 MonNodeState = monitor_node_state(), 1270 no_msgs(), 1271 ok. 1272 1273monitor_nodes_all_comb(Flag) -> 1274 ok = net_kernel:monitor_nodes(Flag), 1275 ok = net_kernel:monitor_nodes(Flag, 1276 [nodedown_reason]), 1277 ok = net_kernel:monitor_nodes(Flag, 1278 [{node_type, hidden}]), 1279 ok = net_kernel:monitor_nodes(Flag, 1280 [{node_type, visible}]), 1281 ok = net_kernel:monitor_nodes(Flag, 1282 [{node_type, all}]), 1283 ok = net_kernel:monitor_nodes(Flag, 1284 [nodedown_reason, 1285 {node_type, hidden}]), 1286 ok = net_kernel:monitor_nodes(Flag, 1287 [nodedown_reason, 1288 {node_type, visible}]), 1289 ok = net_kernel:monitor_nodes(Flag, 1290 [nodedown_reason, 1291 {node_type, all}]), 1292 %% There currently are 8 different combinations 1293 8. 1294 1295 1296receive_all_comb_nodeup_msgs(visible, Node) -> 1297 io:format("Receive nodeup visible...~n"), 1298 Exp = [{nodeup, Node}, 1299 {nodeup, Node, []}] 1300 ++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node), 1301 receive_mn_msgs(Exp), 1302 io:format("ok~n"), 1303 ok; 1304receive_all_comb_nodeup_msgs(hidden, Node) -> 1305 io:format("Receive nodeup hidden...~n"), 1306 Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node), 1307 receive_mn_msgs(Exp), 1308 io:format("ok~n"), 1309 ok. 1310 1311mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) -> 1312 InfoNt = [{node_type, Type}], 1313 [{nodeup, Node, InfoNt}, 1314 {nodeup, Node, InfoNt}, 1315 {nodeup, Node, InfoNt}, 1316 {nodeup, Node, InfoNt}]. 1317 1318receive_all_comb_nodedown_msgs(visible, Node, Reason) -> 1319 io:format("Receive nodedown visible...~n"), 1320 Exp = [{nodedown, Node}, 1321 {nodedown, Node, [{nodedown_reason, Reason}]}] 1322 ++ mk_exp_mn_all_comb_nodedown_msgs_common(visible, 1323 Node, 1324 Reason), 1325 receive_mn_msgs(Exp), 1326 io:format("ok~n"), 1327 ok; 1328receive_all_comb_nodedown_msgs(hidden, Node, Reason) -> 1329 io:format("Receive nodedown hidden...~n"), 1330 Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason), 1331 receive_mn_msgs(Exp), 1332 io:format("ok~n"), 1333 ok. 1334 1335mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) -> 1336 InfoNt = [{node_type, Type}], 1337 InfoNdrNt = lists:sort([{nodedown_reason, Reason}]++InfoNt), 1338 [{nodedown, Node, InfoNt}, 1339 {nodedown, Node, InfoNt}, 1340 {nodedown, Node, InfoNdrNt}, 1341 {nodedown, Node, InfoNdrNt}]. 1342 1343receive_mn_msgs([]) -> 1344 ok; 1345receive_mn_msgs(Msgs) -> 1346 io:format("Expecting msgs: ~p~n", [Msgs]), 1347 receive 1348 {_Dir, _Node} = Msg -> 1349 io:format("received ~p~n", [Msg]), 1350 case lists:member(Msg, Msgs) of 1351 true -> receive_mn_msgs(lists:delete(Msg, Msgs)); 1352 false -> ct:fail({unexpected_message, Msg, 1353 expected_messages, Msgs}) 1354 end; 1355 {Dir, Node, Info} -> 1356 Msg = {Dir, Node, lists:sort(Info)}, 1357 io:format("received ~p~n", [Msg]), 1358 case lists:member(Msg, Msgs) of 1359 true -> receive_mn_msgs(lists:delete(Msg, Msgs)); 1360 false -> ct:fail({unexpected_message, Msg, 1361 expected_messages, Msgs}) 1362 end; 1363 Msg -> 1364 io:format("received ~p~n", [Msg]), 1365 ct:fail({unexpected_message, Msg, 1366 expected_messages, Msgs}) 1367 end. 1368 1369monitor_nodes_cleanup(Config) when is_list(Config) -> 1370 MonNodeState = monitor_node_state(), 1371 Me = self(), 1372 No = monitor_nodes_all_comb(true), 1373 Inf = spawn(fun () -> 1374 monitor_nodes_all_comb(true), 1375 Me ! {mons_set, self()}, 1376 receive after infinity -> ok end 1377 end), 1378 TO = spawn(fun () -> 1379 monitor_nodes_all_comb(true), 1380 Me ! {mons_set, self()}, 1381 receive after 500 -> ok end 1382 end), 1383 receive {mons_set, Inf} -> ok end, 1384 receive {mons_set, TO} -> ok end, 1385 MNLen = length(MonNodeState) + No*3, 1386 MNLen = length(monitor_node_state()), 1387 MonInf = erlang:monitor(process, Inf), 1388 MonTO = erlang:monitor(process, TO), 1389 exit(Inf, bang), 1390 No = monitor_nodes_all_comb(false), 1391 receive {'DOWN', MonInf, process, Inf, bang} -> ok end, 1392 receive {'DOWN', MonTO, process, TO, normal} -> ok end, 1393 MonNodeState = monitor_node_state(), 1394 no_msgs(), 1395 ok. 1396 1397monitor_nodes_many(Config) when is_list(Config) -> 1398 MonNodeState = monitor_node_state(), 1399 [Name] = get_nodenames(1, monitor_nodes_many), 1400 %% We want to perform more than 2^16 net_kernel:monitor_nodes 1401 %% since this will wrap an internal counter 1402 No = (1 bsl 16) + 17, 1403 repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No), 1404 No = length(monitor_node_state()) - length(MonNodeState), 1405 {ok, Node} = start_node(Name), 1406 repeat(fun () -> receive {nodeup, Node} -> ok end end, No), 1407 stop_node(Node), 1408 repeat(fun () -> receive {nodedown, Node} -> ok end end, No), 1409 ok = net_kernel:monitor_nodes(false), 1410 no_msgs(10), 1411 MonNodeState = monitor_node_state(), 1412 ok. 1413 1414%% Misc. functions 1415 1416monitor_node_state() -> 1417 erts_debug:set_internal_state(available_internal_state, true), 1418 MonitoringNodes = erts_debug:get_internal_state(monitoring_nodes), 1419 erts_debug:set_internal_state(available_internal_state, false), 1420 MonitoringNodes. 1421 1422 1423check_no_nodedown_nodeup(TimeOut) -> 1424 receive 1425 {nodeup, _, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); 1426 {nodeup, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); 1427 {nodedown, _, _} = Msg -> ct:fail({unexpected_nodedown, Msg}); 1428 {nodedown, _} = Msg -> ct:fail({unexpected_nodedown, Msg}) 1429 after TimeOut -> 1430 ok 1431 end. 1432 1433print_my_messages() -> 1434 {messages, Messages} = process_info(self(), messages), 1435 io:format("Messages: ~p~n", [Messages]), 1436 ok. 1437 1438 1439sleep(T) -> receive after T * 1000 -> ok end. 1440 1441start_node(Name, Param, this) -> 1442 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 1443 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); 1444start_node(Name, Param, "this") -> 1445 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 1446 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); 1447start_node(Name, Param, Rel) when is_atom(Rel) -> 1448 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 1449 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]); 1450start_node(Name, Param, Rel) when is_list(Rel) -> 1451 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 1452 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]). 1453 1454start_node(Name, Param) -> 1455 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 1456 test_server:start_node(Name, slave, [{args, NewParam}]). 1457 1458start_node(Name) -> 1459 start_node(Name, ""). 1460 1461stop_node(Node) -> 1462 test_server:stop_node(Node). 1463 1464get_nodenames(N, T) -> 1465 get_nodenames(N, T, []). 1466 1467get_nodenames(0, _, Acc) -> 1468 Acc; 1469get_nodenames(N, T, Acc) -> 1470 U = erlang:unique_integer([positive]), 1471 get_nodenames(N-1, T, [list_to_atom(atom_to_list(T) 1472 ++ "-" 1473 ++ ?MODULE_STRING 1474 ++ "-" 1475 ++ integer_to_list(U)) | Acc]). 1476 1477get_numbered_nodenames(N, T) -> 1478 get_numbered_nodenames(N, T, []). 1479 1480get_numbered_nodenames(0, _, Acc) -> 1481 Acc; 1482get_numbered_nodenames(N, T, Acc) -> 1483 U = erlang:unique_integer([positive]), 1484 NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N) 1485 ++ "-" 1486 ++ ?MODULE_STRING 1487 ++ "-" 1488 ++ integer_to_list(U)) | Acc], 1489 get_numbered_nodenames(N-1, T, NL). 1490 1491wait_until(Fun) -> 1492 case Fun() of 1493 true -> 1494 ok; 1495 _ -> 1496 receive 1497 after 100 -> 1498 wait_until(Fun) 1499 end 1500 end. 1501 1502repeat(Fun, 0) when is_function(Fun) -> 1503 ok; 1504repeat(Fun, N) when is_function(Fun), is_integer(N), N > 0 -> 1505 Fun(), 1506 repeat(Fun, N-1). 1507 1508no_msgs(Wait) -> 1509 receive after Wait -> no_msgs() end. 1510 1511no_msgs() -> 1512 {messages, []} = process_info(self(), messages). 1513 1514block_emu(Ms) -> 1515 erts_debug:set_internal_state(available_internal_state, true), 1516 Res = erts_debug:set_internal_state(block, Ms), 1517 erts_debug:set_internal_state(available_internal_state, false), 1518 Res. 1519