1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2021. 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-include_lib("kernel/include/dist.hrl"). 24 25-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 26 init_per_group/2,end_per_group/2]). 27 28-export([tick/1, tick_change/1, 29 connect_node/1, 30 nodenames/1, hostnames/1, 31 illegal_nodenames/1, hidden_node/1, 32 dyn_node_name/1, 33 epmd_reconnect/1, 34 setopts/1, 35 table_waste/1, net_setuptime/1, 36 inet_dist_options_options/1, 37 38 monitor_nodes_nodedown_reason/1, 39 monitor_nodes_complex_nodedown_reason/1, 40 monitor_nodes_node_type/1, 41 monitor_nodes_misc/1, 42 monitor_nodes_otp_6481/1, 43 monitor_nodes_errors/1, 44 monitor_nodes_combinations/1, 45 monitor_nodes_cleanup/1, 46 monitor_nodes_many/1, 47 monitor_nodes_down_up/1, 48 dist_ctrl_proc_smoke/1, 49 dist_ctrl_proc_reject/1, 50 erl_uds_dist_smoke_test/1, 51 erl_1424/1, differing_cookies/1, 52 cmdline_setcookie_2/1, connection_cookie/1, 53 dyn_differing_cookies/1]). 54 55%% Performs the test at another node. 56-export([get_socket_priorities/0, 57 tick_cli_test/1, tick_cli_test1/1, 58 tick_serv_test/2, tick_serv_test1/1, 59 run_remote_test/1, 60 dyn_node_name_do/2, 61 epmd_reconnect_do/2, 62 setopts_do/2, 63 keep_conn/1, time_ping/1, 64 dyn_differing_cookies/2]). 65 66-export([init_per_testcase/2, end_per_testcase/2]). 67 68-export([dist_cntrlr_output_test_size/2]). 69 70-export([pinger/1]). 71 72-export([start_uds_rpc_server/1]). 73 74-define(DUMMY_NODE,dummy@test01). 75-define(ALT_EPMD_PORT, "12321"). 76-define(ALT_EPMD_CMD, "epmd -port "++?ALT_EPMD_PORT). 77 78%%----------------------------------------------------------------- 79%% The distribution is mainly tested in the big old test_suite. 80%% This test only tests the net_ticktime configuration flag. 81%% Should be started in a CC view with: 82%% erl -sname master -rsh ctrsh 83%%----------------------------------------------------------------- 84 85suite() -> 86 [{ct_hooks,[ts_install_cth]}, 87 {timetrap,{minutes,12}}]. 88 89all() -> 90 [dist_ctrl_proc_smoke, 91 dist_ctrl_proc_reject, 92 tick, tick_change, nodenames, hostnames, illegal_nodenames, 93 connect_node, 94 dyn_node_name, 95 epmd_reconnect, 96 hidden_node, setopts, 97 table_waste, net_setuptime, inet_dist_options_options, 98 {group, monitor_nodes}, 99 erl_uds_dist_smoke_test, 100 erl_1424, 101 {group, differing_cookies}]. 102 103groups() -> 104 [{monitor_nodes, [], 105 [monitor_nodes_nodedown_reason, 106 monitor_nodes_complex_nodedown_reason, 107 monitor_nodes_node_type, monitor_nodes_misc, 108 monitor_nodes_otp_6481, monitor_nodes_errors, 109 monitor_nodes_combinations, monitor_nodes_cleanup, 110 monitor_nodes_many, 111 monitor_nodes_down_up]}, 112 {differing_cookies, [], 113 [differing_cookies, 114 cmdline_setcookie_2, 115 connection_cookie, 116 dyn_differing_cookies]}]. 117 118init_per_suite(Config) -> 119 start_gen_tcp_dist_test_type_server(), 120 Config. 121 122end_per_suite(_Config) -> 123 [slave:stop(N) || N <- nodes()], 124 kill_gen_tcp_dist_test_type_server(), 125 ok. 126 127init_per_group(_GroupName, Config) -> 128 Config. 129 130end_per_group(_GroupName, Config) -> 131 Config. 132 133init_per_testcase(TC, Config) when TC == hostnames; 134 TC == nodenames -> 135 file:make_dir("hostnames_nodedir"), 136 file:write_file("hostnames_nodedir/ignore_core_files",""), 137 Config; 138init_per_testcase(epmd_reconnect, Config) -> 139 [] = os:cmd(?ALT_EPMD_CMD++" -relaxed_command_check -daemon"), 140 Config; 141init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 142 Config. 143 144end_per_testcase(epmd_reconnect, _Config) -> 145 os:cmd(?ALT_EPMD_CMD++" -kill"), 146 ok; 147end_per_testcase(_Func, _Config) -> 148 ok. 149 150connect_node(Config) when is_list(Config) -> 151 Connected = nodes(connected), 152 true = net_kernel:connect_node(node()), 153 Connected = nodes(connected), 154 ok. 155 156tick(Config) when is_list(Config) -> 157 run_dist_configs(fun tick/2, Config). 158 159tick(DCfg, _Config) -> 160 %% First check that the normal case is OK! 161 [Name1, Name2] = get_nodenames(2, dist_test), 162 {ok, Node} = start_node(DCfg, Name1), 163 rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]), 164 165 erlang:monitor_node(Node, true), 166 receive 167 {nodedown, Node} -> 168 ct:fail("nodedown from other node") 169 after 30000 -> 170 erlang:monitor_node(Node, false), 171 stop_node(Node) 172 end, 173 174 %% Now, set the net_ticktime for the other node to 12 secs. 175 %% After the sleep(2sec) and cast the other node shall destroy 176 %% the connection as it has not received anything on the connection. 177 %% The nodedown message should arrive within 8 < T < 16 secs. 178 179 %% We must have two slave nodes as the slave mechanism otherwise 180 %% halts the client node after tick timeout (the connection is down 181 %% and the slave node decides to halt !! 182 183 %% Set the ticktime on the server node to 100 secs so the server 184 %% node doesn't tick the client node within the interval ... 185 186 {ok, ServNode} = start_node(DCfg, Name2, 187 "-kernel net_ticktime 100"), 188 rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]), 189 190 {ok, Node} = start_node(DCfg, Name1, 191 "-kernel net_ticktime 12"), 192 rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]), 193 194 spawn_link(erl_distribution_SUITE, keep_conn, [Node]), 195 196 {tick_serv, ServNode} ! {i_want_the_result, self()}, 197 198 monitor_node(ServNode, true), 199 monitor_node(Node, true), 200 201 receive 202 {tick_test, T} when is_integer(T) -> 203 stop_node(ServNode), 204 stop_node(Node), 205 T; 206 {tick_test, Error} -> 207 stop_node(ServNode), 208 stop_node(Node), 209 ct:fail(Error); 210 {nodedown, Node} -> 211 stop_node(ServNode), 212 ct:fail("client node died"); 213 {nodedown, ServNode} -> 214 stop_node(Node), 215 ct:fail("server node died") 216 end, 217 ok. 218 219%% Checks that pinging nonexistyent nodes does not waste space in distribution table. 220table_waste(Config) when is_list(Config) -> 221 run_dist_configs(fun table_waste/2, Config). 222 223table_waste(DCfg, _Config) -> 224 {ok, HName} = inet:gethostname(), 225 F = fun(0,_F) -> []; 226 (N,F) -> 227 Name = list_to_atom("erl_distribution_"++integer_to_list(N)++ 228 "@"++HName), 229 pang = net_adm:ping(Name), 230 F(N-1,F) 231 end, 232 F(256,F), 233 {ok, N} = start_node(DCfg, erl_distribution_300), 234 stop_node(N), 235 ok. 236 237%% Test that starting nodes with different legal name part works, and that illegal 238%% ones are filtered 239nodenames(Config) when is_list(Config) -> 240 legal("a1@b"), 241 legal("a-1@b"), 242 legal("a_1@b"), 243 244 %% Test that giving two -sname works as it should 245 started = test_node("a_1@b", false, long_or_short() ++ "a_0@b"), 246 247 illegal("cdé@a"), 248 illegal("te欢st@a"). 249 250%% Test that starting nodes with different legal host part works, and that illegal 251%% ones are filtered 252hostnames(Config) when is_list(Config) -> 253 Host = gethostname(), 254 legal([$a,$@|atom_to_list(Host)]), 255 legal("1@b1"), 256 legal("b@b1-c"), 257 legal("c@b1_c"), 258 legal("d@b1#c"), 259 legal("f@::1"), 260 legal("g@1:bc3:4e3f:f20:0:1"), 261 262 case file:native_name_encoding() of 263 latin1 -> ignore; 264 _ -> legal("e@b1é") 265 end, 266 long_hostnames(net_kernel:longnames()), 267 268 illegal("h@testالع"), 269 illegal("i@языtest"), 270 illegal("j@te欢st"). 271 272long_hostnames(true) -> 273 legal("k@b.b.c"), 274 legal("l@b.b-c.d"), 275 legal("m@b.b_c.d"), 276 legal("n@127.0.0.1"), 277 legal("o@207.123.456.789"); 278long_hostnames(false) -> 279 illegal("k@b.b.c"). 280 281legal(Name) -> 282 case test_node(Name) of 283 started -> 284 ok; 285 not_started -> 286 ct:fail("no ~p node started", [Name]) 287 end. 288 289illegal(Name) -> 290 case test_node(Name, true) of 291 not_started -> 292 ok; 293 started -> 294 ct:fail("~p node started with illegal name", [Name]) 295 end. 296 297test_node(Name) -> 298 test_node(Name, false). 299test_node(Name, Illigal) -> 300 test_node(Name, Illigal, ""). 301test_node(Name, Illigal, ExtraArgs) -> 302 ProgName = ct:get_progname(), 303 Command = ProgName ++ " -noinput " ++ 304 long_or_short() ++ Name ++ ExtraArgs ++ 305 " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++ 306 case Illigal of 307 true -> 308 " -eval \"timer:sleep(10000),init:stop().\""; 309 false -> 310 "" 311 end, 312 net_kernel:monitor_nodes(true), 313 BinCommand = unicode:characters_to_binary(Command, utf8), 314 _Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]), 315 Node = list_to_atom(Name), 316 receive 317 {nodeup, Node} -> 318 net_kernel:monitor_nodes(false), 319 slave:stop(Node), 320 started 321 after 5000 -> 322 net_kernel:monitor_nodes(false), 323 not_started 324 end. 325 326long_or_short() -> 327 case net_kernel:longnames() of 328 true -> " -name "; 329 false -> " -sname " 330 end. 331 332% get the localhost's name, depending on the using name policy 333gethostname() -> 334 Hostname = case net_kernel:longnames() of 335 true-> 336 net_adm:localhost(); 337 _-> 338 {ok, Name}=inet:gethostname(), 339 Name 340 end, 341 list_to_atom(Hostname). 342 343%% Test that pinging an illegal nodename does not kill the node. 344illegal_nodenames(Config) when is_list(Config) -> 345 run_dist_configs(fun illegal_nodenames/2, Config). 346 347illegal_nodenames(DCfg, _Config) -> 348 {ok, Node}=start_node(DCfg, illegal_nodenames), 349 monitor_node(Node, true), 350 RPid=rpc:call(Node, erlang, spawn, 351 [?MODULE, pinger, [self()]]), 352 receive 353 {RPid, pinged} -> 354 monitor_node(Node, false), 355 ok; 356 {nodedown, Node} -> 357 ct:fail("Remote node died.") 358 end, 359 stop_node(Node), 360 ok. 361 362pinger(Starter) -> 363 io:format("Starter:~p~n",[Starter]), 364 net_adm:ping(a@b@c), 365 Starter ! {self(), pinged}, 366 ok. 367 368 369%% Test that you can set the net_setuptime properly. 370net_setuptime(Config) when is_list(Config) -> 371 run_dist_configs(fun net_setuptime/2, Config). 372 373net_setuptime(DCfg, _Config) -> 374 375 %% In this test case, we reluctantly accept shorter times than the given 376 %% setup time, because the connection attempt can end in a 377 %% "Host unreachable" error before the timeout fires. 378 379 Res0 = do_test_setuptime(DCfg, "2"), 380 io:format("Res0 = ~p", [Res0]), 381 true = (Res0 =< 4000), 382 Res1 = do_test_setuptime(DCfg, "0.3"), 383 io:format("Res1 = ~p", [Res1]), 384 true = (Res1 =< 500), 385 ok. 386 387do_test_setuptime(DCfg, Setuptime) when is_list(Setuptime) -> 388 {ok, Node} = start_node(DCfg, dist_setuptime_test, 389 "-kernel net_setuptime " ++ Setuptime), 390 Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]), 391 stop_node(Node), 392 Res. 393 394time_ping(Node) -> 395 T0 = erlang:monotonic_time(), 396 pang = net_adm:ping(Node), 397 T1 = erlang:monotonic_time(), 398 erlang:convert_time_unit(T1 - T0, native, millisecond). 399 400%% Keep the connection with the client node up. 401%% This is necessary as the client node runs with much shorter 402%% tick time !! 403keep_conn(Node) -> 404 sleep(1), 405 rpc:cast(Node, erlang, time, []), 406 keep_conn(Node). 407 408tick_serv_test(Node, MasterNode) -> 409 spawn(erl_distribution_SUITE, keep_conn, [MasterNode]), 410 spawn(erl_distribution_SUITE, tick_serv_test1, [Node]). 411 412tick_serv_test1(Node) -> 413 register(tick_serv, self()), 414 TestServer = receive {i_want_the_result, TS} -> TS end, 415 monitor_node(Node, true), 416 receive 417 {nodedown, Node} -> 418 net_adm:ping(Node), %% Set up the connection again !! 419 420 {tick_test, Node} ! {whats_the_result, self()}, 421 receive 422 {tick_test, Res} -> 423 TestServer ! {tick_test, Res} 424 end 425 end. 426 427tick_cli_test(Node) -> 428 spawn(erl_distribution_SUITE, tick_cli_test1, [Node]). 429 430tick_cli_test1(Node) -> 431 register(tick_test, self()), 432 erlang:monitor_node(Node, true), 433 sleep(2), 434 rpc:call(Node, erlang, time, []), %% simulate action on the connection 435 T1 = erlang:monotonic_time(), 436 receive 437 {nodedown, Node} -> 438 T2 = erlang:monotonic_time(), 439 receive 440 {whats_the_result, From} -> 441 Diff = erlang:convert_time_unit(T2-T1, native, 442 millisecond), 443 case Diff of 444 T when T > 8000, T < 16000 -> 445 From ! {tick_test, T}; 446 T -> 447 From ! {tick_test, 448 {"T not in interval 8000 < T < 16000", 449 T}} 450 end 451 end 452 end. 453 454epmd_reconnect(Config) when is_list(Config) -> 455 NodeNames = [N1,N2,N3] = get_nodenames(3, ?FUNCTION_NAME), 456 Nodes = [atom_to_list(full_node_name(NN)) || NN <- NodeNames], 457 458 DCfg = "-epmd_port "++?ALT_EPMD_PORT, 459 460 {_N1F,Port1} = start_node_unconnected(DCfg, N1, ?MODULE, run_remote_test, 461 ["epmd_reconnect_do", atom_to_list(node()), "1" | Nodes]), 462 {_N2F,Port2} = start_node_unconnected(DCfg, N2, ?MODULE, run_remote_test, 463 ["epmd_reconnect_do", atom_to_list(node()), "2" | Nodes]), 464 {_N3F,Port3} = start_node_unconnected(DCfg, N3, ?MODULE, run_remote_test, 465 ["epmd_reconnect_do", atom_to_list(node()), "3" | Nodes]), 466 Ports = [Port1, Port2, Port3], 467 468 ok = reap_ports(Ports), 469 470 ok. 471 472reap_ports([]) -> 473 ok; 474reap_ports(Ports) -> 475 case (receive M -> M end) of 476 {Port, Message} -> 477 case lists:member(Port, Ports) andalso Message of 478 {data,String} -> 479 io:format("~p: ~s\n", [Port, String]), 480 reap_ports(Ports); 481 {exit_status,0} -> 482 reap_ports(Ports -- [Port]) 483 end 484 end. 485 486epmd_reconnect_do(_Node, ["1", Node1, Node2, Node3]) -> 487 Names = [Name || Name <- [hd(string:tokens(Node, "@")) || Node <- [Node1, Node2, Node3]]], 488 %% wait until all nodes are registered 489 ok = wait_for_names(Names), 490 "Killed" ++_ = os:cmd(?ALT_EPMD_CMD++" -kill"), 491 open_port({spawn, ?ALT_EPMD_CMD}, []), 492 %% check that all nodes reregister with epmd 493 ok = wait_for_names(Names), 494 lists:foreach(fun(Node) -> 495 ANode = list_to_atom(Node), 496 pong = net_adm:ping(ANode), 497 {epmd_reconnect_do, ANode} ! {stop, Node1, Node} 498 end, [Node2, Node3]), 499 ok; 500epmd_reconnect_do(_Node, ["2", Node1, Node2, _Node3]) -> 501 register(epmd_reconnect_do, self()), 502 receive {stop, Node1, Node2} -> 503 ok 504 after 7000 -> 505 exit(timeout) 506 end; 507epmd_reconnect_do(_Node, ["3", Node1, _Node2, Node3]) -> 508 register(epmd_reconnect_do, self()), 509 receive {stop, Node1, Node3} -> 510 ok 511 after 7000 -> 512 exit(timeout) 513 end. 514 515wait_for_names(Names) -> 516 %% wait for up to 3 seconds (the current retry timer in erl_epmd is 2s) 517 wait_for_names(lists:sort(Names), 30, 100). 518 519wait_for_names(Names, N, Wait) when N > 0 -> 520 try 521 {ok, Info} = erl_epmd:names(), 522 Names = lists:sort([Name || {Name, _Port} <- Info]), 523 ok 524 catch 525 error:{badmatch, _} -> 526 timer:sleep(Wait), 527 wait_for_names(Names, N-1, Wait) 528 end. 529 530 531dyn_node_name(Config) when is_list(Config) -> 532 %%run_dist_configs(fun dyn_node_name/2, Config). 533 dyn_node_name("", Config). 534 535dyn_node_name(DCfg, _Config) -> 536 {_N1F,Port1} = start_node_unconnected(DCfg ++ " -dist_listen false", 537 undefined, ?MODULE, run_remote_test, 538 ["dyn_node_name_do", atom_to_list(node())]), 539 0 = wait_for_port_exit(Port1), 540 ok. 541 542dyn_node_name_do(TestNode, _Args) -> 543 nonode@nohost = node(), 544 [] = nodes(), 545 [] = nodes(hidden), 546 net_kernel:monitor_nodes(true, [{node_type,all}]), 547 net_kernel:connect_node(TestNode), 548 [] = nodes(), 549 [TestNode] = nodes(hidden), 550 MyName = node(), 551 check([MyName], rpc:call(TestNode, erlang, nodes, [hidden])), 552 553 {nodeup, MyName, [{node_type, visible}]} = receive_any(0), 554 {nodeup, TestNode, [{node_type, hidden}]} = receive_any(0), 555 556 true = net_kernel:disconnect(TestNode), 557 558 {nodedown, TestNode, [{node_type, hidden}]} = receive_any(0), 559 [] = nodes(hidden), 560 {nodedown, MyName, [{node_type, visible}]} = receive_any(1000), 561 562 nonode@nohost = node(), 563 564 net_kernel:connect_node(TestNode), 565 [] = nodes(), 566 [TestNode] = nodes(hidden), 567 MyName = node(), 568 check([MyName], rpc:call(TestNode, erlang, nodes, [hidden])), 569 570 {nodeup, MyName, [{node_type, visible}]} = receive_any(0), 571 {nodeup, TestNode, [{node_type, hidden}]} = receive_any(0), 572 573 true = rpc:cast(TestNode, net_kernel, disconnect, [MyName]), 574 575 {nodedown, TestNode, [{node_type, hidden}]} = receive_any(1000), 576 [] = nodes(hidden), 577 {nodedown, MyName, [{node_type, visible}]} = receive_any(1000), 578 nonode@nohost = node(), 579 580 ok. 581 582check(X, X) -> ok. 583 584setopts(Config) when is_list(Config) -> 585 run_dist_configs(fun setopts/2, Config). 586 587setopts(DCfg, _Config) -> 588 register(setopts_regname, self()), 589 [N1,N2,N3,N4] = get_nodenames(4, setopts), 590 591 {_N1F,Port1} = start_node_unconnected(DCfg, N1, ?MODULE, run_remote_test, 592 ["setopts_do", atom_to_list(node()), "1", "ping"]), 593 0 = wait_for_port_exit(Port1), 594 595 {_N2F,Port2} = start_node_unconnected(DCfg, N2, ?MODULE, run_remote_test, 596 ["setopts_do", atom_to_list(node()), "2", "ping"]), 597 0 = wait_for_port_exit(Port2), 598 599 {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]), 600 {ok, LTcpPort} = inet:port(LSock), 601 602 {N3F,Port3} = start_node_unconnected(DCfg, N3, ?MODULE, run_remote_test, 603 ["setopts_do", atom_to_list(node()), 604 "1", integer_to_list(LTcpPort)]), 605 wait_and_connect(LSock, N3F, Port3), 606 0 = wait_for_port_exit(Port3), 607 608 {N4F,Port4} = start_node_unconnected(DCfg, N4, ?MODULE, run_remote_test, 609 ["setopts_do", atom_to_list(node()), 610 "2", integer_to_list(LTcpPort)]), 611 wait_and_connect(LSock, N4F, Port4), 612 0 = wait_for_port_exit(Port4), 613 614 unregister(setopts_regname), 615 ok. 616 617wait_and_connect(LSock, NodeName, NodePort) -> 618 {ok, Sock} = gen_tcp:accept(LSock), 619 {ok, "Connect please"} = gen_tcp:recv(Sock, 0), 620 flush_from_port(NodePort), 621 pong = net_adm:ping(NodeName), 622 gen_tcp:send(Sock, "Connect done"), 623 gen_tcp:close(Sock). 624 625 626flush_from_port(Port) -> 627 flush_from_port(Port, 10). 628 629flush_from_port(Port, Timeout) -> 630 receive 631 {Port,{data,String}} -> 632 io:format("~p: ~s\n", [Port, String]), 633 flush_from_port(Port, Timeout) 634 after Timeout -> 635 timeout 636 end. 637 638wait_for_port_exit(Port) -> 639 case (receive M -> M end) of 640 {Port,{exit_status,Status}} -> 641 Status; 642 {Port,{data,String}} -> 643 io:format("~p: ~s\n", [Port, String]), 644 wait_for_port_exit(Port) 645 end. 646 647run_remote_test([FuncStr, TestNodeStr | Args]) -> 648 Status = try 649 io:format("Node ~p started~n", [node()]), 650 TestNode = list_to_atom(TestNodeStr), 651 io:format("Node ~p spawning function ~p~n", [node(), FuncStr]), 652 {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]), 653 io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]), 654 receive 655 {'DOWN', Ref, process, Pid, normal} -> 656 0; 657 Other -> 658 io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]), 659 1 660 end 661 catch 662 C:E:S -> 663 io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", 664 [node(), C, E, S]), 665 2 666 end, 667 io:format("Node ~p doing halt(~p).\n",[node(), Status]), 668 erlang:halt(Status). 669 670% Do the actual test on the remote node 671setopts_do(TestNode, [OptNr, ConnectData]) -> 672 [] = nodes(), 673 {Opt, Val} = opt_from_nr(OptNr), 674 ok = net_kernel:setopts(new, [{Opt, Val}]), 675 676 [] = nodes(), 677 {error, noconnection} = net_kernel:getopts(TestNode, [Opt]), 678 679 case ConnectData of 680 "ping" -> % We connect 681 net_adm:ping(TestNode); 682 TcpPort -> % Other connect 683 {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort), 684 [{active,false},{packet,2}]), 685 ok = gen_tcp:send(Sock, "Connect please"), 686 {ok, "Connect done"} = gen_tcp:recv(Sock, 0), 687 gen_tcp:close(Sock) 688 end, 689 [TestNode] = nodes(), 690 {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), 691 {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]), 692 693 NewVal = change_val(Val), 694 ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]), 695 {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]), 696 697 ok = net_kernel:setopts(TestNode, [{Opt, Val}]), 698 {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), 699 700 ok. 701 702opt_from_nr("1") -> {nodelay, true}; 703opt_from_nr("2") -> {nodelay, false}. 704 705change_val(true) -> false; 706change_val(false) -> true. 707 708 709start_node_unconnected(DCfg, Name, Mod, Func, Args) -> 710 start_node_unconnected(DCfg, Name, erlang:get_cookie(), Mod, Func, Args). 711 712start_node_unconnected(DCfg, Name, Cookie, Mod, Func, Args) -> 713 FullName = full_node_name(Name), 714 CmdLine = 715 mk_node_cmdline(DCfg, Name, Cookie, Mod, Func, Args), 716 io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), 717 case open_port({spawn, CmdLine}, [exit_status]) of 718 Port when is_port(Port) -> 719 {FullName, Port}; 720 Error -> 721 exit({failed_to_start_node, FullName, Error}) 722 end. 723 724full_node_name(PreName) when is_atom(PreName) -> 725 full_node_name(atom_to_list(PreName)); 726full_node_name(PreNameL) when is_list(PreNameL) -> 727 HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, 728 atom_to_list(node())), 729 list_to_atom(PreNameL ++ HostSuffix). 730 731mk_node_cmdline(DCfg, Name, Cookie, Mod, Func, Args) -> 732 Static = "-noinput", 733 Pa = filename:dirname(code:which(?MODULE)), 734 Prog = case catch init:get_argument(progname) of 735 {ok,[[P]]} -> P; 736 _ -> exit(no_progname_argument_found) 737 end, 738 NameSw = case net_kernel:longnames() of 739 false -> "-sname "; 740 true -> "-name "; 741 _ -> exit(not_distributed_node) 742 end, 743 {ok, Pwd} = file:get_cwd(), 744 NameStr = atom_to_list(Name), 745 Prog ++ " " 746 ++ Static ++ " " 747 ++ NameSw ++ " " ++ NameStr 748 ++ " " ++ DCfg 749 ++ " -pa " ++ Pa 750 ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr 751 ++ " -setcookie " ++ atom_to_list(Cookie) 752 ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func) 753 ++ " " ++ string:join(Args, " "). 754 755 756%% OTP-4255. 757tick_change(Config) when is_list(Config) -> 758 run_dist_configs(fun tick_change/2, Config). 759 760tick_change(DCfg, _Config) -> 761 [BN, CN] = get_nodenames(2, tick_change), 762 DefaultTT = net_kernel:get_net_ticktime(), 763 unchanged = net_kernel:set_net_ticktime(DefaultTT, 60), 764 case DefaultTT of 765 I when is_integer(I) -> ok; 766 _ -> ct:fail(DefaultTT) 767 end, 768 769 %% In case other nodes are connected 770 case nodes(connected) of 771 [] -> net_kernel:set_net_ticktime(10, 0); 772 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 773 set_net_ticktime, [10, 5]) 774 end, 775 776 wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end), 777 {ok, B} = start_node(DCfg, BN, "-kernel net_ticktime 10"), 778 {ok, C} = start_node(DCfg, CN, "-kernel net_ticktime 10 -hidden"), 779 780 OTE = process_flag(trap_exit, true), 781 case catch begin 782 run_tick_change_test(DCfg, B, C, 10, 1), 783 run_tick_change_test(DCfg, B, C, 1, 10) 784 end of 785 {'EXIT', Reason} -> 786 stop_node(B), 787 stop_node(C), 788 %% In case other nodes are connected 789 case nodes(connected) of 790 [] -> net_kernel:set_net_ticktime(DefaultTT, 0); 791 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 792 set_net_ticktime, [DefaultTT, 10]) 793 end, 794 wait_until(fun () -> 795 DefaultTT == net_kernel:get_net_ticktime() 796 end), 797 process_flag(trap_exit, OTE), 798 ct:fail(Reason); 799 _ -> 800 ok 801 end, 802 process_flag(trap_exit, OTE), 803 stop_node(B), 804 stop_node(C), 805 806 %% In case other nodes are connected 807 case nodes(connected) of 808 [] -> net_kernel:set_net_ticktime(DefaultTT, 0); 809 _ -> rpc:multicall(nodes([this, connected]), net_kernel, 810 set_net_ticktime, [DefaultTT, 5]) 811 end, 812 813 wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end), 814 ok. 815 816 817wait_for_nodedowns(Tester, Ref) -> 818 receive 819 {nodedown, Node} -> 820 io:format("~p~n", [{node(), {nodedown, Node}}]), 821 Tester ! {Ref, {node(), {nodedown, Node}}} 822 end, 823 wait_for_nodedowns(Tester, Ref). 824 825run_tick_change_test(DCfg, B, C, PrevTT, TT) -> 826 [DN, EN] = get_nodenames(2, tick_change), 827 828 Tester = self(), 829 Ref = make_ref(), 830 MonitorNodes = fun (Nodes) -> 831 lists:foreach( 832 fun (N) -> 833 monitor_node(N,true) 834 end, 835 Nodes), 836 wait_for_nodedowns(Tester, Ref) 837 end, 838 839 {ok, D} = start_node(DCfg, DN, "-kernel net_ticktime " 840 ++ integer_to_list(PrevTT)), 841 842 NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end), 843 NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end), 844 NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end), 845 846 MaxTT = case PrevTT > TT of 847 true -> PrevTT; 848 false -> TT 849 end, 850 851 CheckResult = make_ref(), 852 spawn_link(fun () -> 853 receive 854 after (25 + MaxTT)*1000 -> 855 Tester ! CheckResult 856 end 857 end), 858 859 %% In case other nodes than these are connected 860 case nodes(connected) -- [B, C, D] of 861 [] -> ok; 862 OtherNodes -> rpc:multicall(OtherNodes, net_kernel, 863 set_net_ticktime, [TT, 20]) 864 end, 865 866 change_initiated = net_kernel:set_net_ticktime(TT,20), 867 {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20), 868 sleep(3), 869 change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]), 870 sleep(7), 871 change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]), 872 873 {ok, E} = start_node(DCfg, EN, "-kernel net_ticktime " 874 ++ integer_to_list(TT)), 875 NME = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end), 876 NMA2 = spawn_link(fun () -> MonitorNodes([E]) end), 877 NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end), 878 NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end), 879 880 receive CheckResult -> ok end, 881 882 unlink(NMA), exit(NMA, kill), 883 unlink(NMB), exit(NMB, kill), 884 unlink(NMC), exit(NMC, kill), 885 unlink(NME), exit(NME, kill), 886 unlink(NMA2), exit(NMA2, kill), 887 unlink(NMB2), exit(NMB2, kill), 888 unlink(NMC2), exit(NMC2, kill), 889 890 %% The node not changing ticktime should have been disconnected from the 891 %% other nodes 892 receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ok 893 after 0 -> exit({?LINE, no_nodedown}) 894 end, 895 receive {Ref, {B, {nodedown, D}}} -> ok 896 after 0 -> exit({?LINE, no_nodedown}) 897 end, 898 receive {Ref, {C, {nodedown, D}}} -> ok 899 after 0 -> exit({?LINE, no_nodedown}) 900 end, 901 receive {Ref, {E, {nodedown, D}}} -> ok 902 after 0 -> exit({?LINE, no_nodedown}) 903 end, 904 905 %% No other connections should have been broken 906 receive 907 {Ref, Reason} -> 908 stop_node(E), 909 exit({?LINE, Reason}); 910 {'EXIT', Pid, Reason} when Pid == NMA; 911 Pid == NMB; 912 Pid == NMC; 913 Pid == NME; 914 Pid == NMA2; 915 Pid == NMB2; 916 Pid == NMC2 -> 917 stop_node(E), 918 919 exit({?LINE, {node(Pid), Reason}}) 920 after 0 -> 921 TT = net_kernel:get_net_ticktime(), 922 TT = rpc:call(B, net_kernel, get_net_ticktime, []), 923 TT = rpc:call(C, net_kernel, get_net_ticktime, []), 924 TT = rpc:call(E, net_kernel, get_net_ticktime, []), 925 stop_node(E), 926 ok 927 end. 928 929%% 930%% Basic tests of hidden node. 931%% 932%% Basic test of hidden node. 933hidden_node(Config) when is_list(Config) -> 934 run_dist_configs(fun hidden_node/2, Config). 935 936hidden_node(DCfg, _Config) -> 937 HArgs = "-hidden", 938 {ok, V} = start_node(DCfg, visible_node), 939 VMN = start_monitor_nodes_proc(V), 940 {ok, H} = start_node(DCfg, hidden_node, HArgs), 941 %% Connect visible_node -> hidden_node 942 connect_nodes(V, H), 943 test_nodes(V, H), 944 stop_node(H), 945 sleep(5), 946 check_monitor_nodes_res(VMN, H), 947 stop_node(V), 948 {ok, H} = start_node(DCfg, hidden_node, HArgs), 949 HMN = start_monitor_nodes_proc(H), 950 {ok, V} = start_node(DCfg, visible_node), 951 %% Connect hidden_node -> visible_node 952 connect_nodes(H, V), 953 test_nodes(V, H), 954 stop_node(V), 955 sleep(5), 956 check_monitor_nodes_res(HMN, V), 957 stop_node(H), 958 ok. 959 960connect_nodes(A, B) -> 961 %% Check that they haven't already connected. 962 false = lists:member(A, rpc:call(B, erlang, nodes, [connected])), 963 false = lists:member(B, rpc:call(A, erlang, nodes, [connected])), 964 %% Connect them. 965 pong = rpc:call(A, net_adm, ping, [B]). 966 967 968test_nodes(V, H) -> 969 %% No nodes should be visible on hidden_node 970 [] = rpc:call(H, erlang, nodes, []), 971 %% visible_node should be hidden on hidden_node 972 true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])), 973 %% hidden_node node shouldn't be visible on visible_node 974 false = lists:member(H, rpc:call(V, erlang, nodes, [])), 975 %% hidden_node should be hidden on visible_node 976 true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])). 977 978mn_loop(MNs) -> 979 receive 980 {nodeup, N} -> 981 mn_loop([{nodeup, N}|MNs]); 982 {nodedown, N} -> 983 mn_loop([{nodedown, N}|MNs]); 984 {monitor_nodes_result, Ref, From} -> 985 From ! {Ref, MNs}; 986 _ -> 987 mn_loop(MNs) 988 end. 989 990start_monitor_nodes_proc(Node) -> 991 Ref = make_ref(), 992 Starter = self(), 993 Pid = spawn(Node, 994 fun() -> 995 net_kernel:monitor_nodes(true), 996 Starter ! Ref, 997 mn_loop([]) 998 end), 999 receive 1000 Ref -> 1001 ok 1002 end, 1003 Pid. 1004 1005 1006check_monitor_nodes_res(Pid, Node) -> 1007 Ref = make_ref(), 1008 Pid ! {monitor_nodes_result, Ref, self()}, 1009 receive 1010 {Ref, MNs} -> 1011 false = lists:keysearch(Node, 2, MNs) 1012 end. 1013 1014 1015 1016%% Check the kernel inet_dist_{listen,connect}_options options. 1017inet_dist_options_options(Config) when is_list(Config) -> 1018 Prio = 1, 1019 case gen_udp:open(0, [{priority,Prio}]) of 1020 {ok,Socket} -> 1021 case inet:getopts(Socket, [priority]) of 1022 {ok,[{priority,Prio}]} -> 1023 ok = gen_udp:close(Socket), 1024 do_inet_dist_options_options(Prio); 1025 _ -> 1026 ok = gen_udp:close(Socket), 1027 {skip, 1028 "Can not set priority "++integer_to_list(Prio)++ 1029 " on socket"} 1030 end; 1031 {error,_} -> 1032 {skip, "Can not set priority on socket"} 1033 end. 1034 1035do_inet_dist_options_options(Prio) -> 1036 PriorityString0 = "[{priority,"++integer_to_list(Prio)++"}]", 1037 PriorityString = 1038 case os:cmd("echo [{a,1}]") of 1039 "[{a,1}]"++_ -> 1040 PriorityString0; 1041 _ -> 1042 %% Some shells need quoting of [{}] 1043 "'"++PriorityString0++"'" 1044 end, 1045 InetDistOptions = 1046 "-hidden " 1047 "-kernel inet_dist_connect_options "++PriorityString++" " 1048 "-kernel inet_dist_listen_options "++PriorityString, 1049 {ok,Node1} = 1050 start_node("", inet_dist_options_1, InetDistOptions), 1051 {ok,Node2} = 1052 start_node("", inet_dist_options_2, InetDistOptions), 1053 %% 1054 pong = 1055 rpc:call(Node1, net_adm, ping, [Node2]), 1056 PrioritiesNode1 = 1057 rpc:call(Node1, ?MODULE, get_socket_priorities, []), 1058 PrioritiesNode2 = 1059 rpc:call(Node2, ?MODULE, get_socket_priorities, []), 1060 io:format("PrioritiesNode1 = ~p", [PrioritiesNode1]), 1061 io:format("PrioritiesNode2 = ~p", [PrioritiesNode2]), 1062 Elevated = [P || P <- PrioritiesNode1, P =:= Prio], 1063 Elevated = [P || P <- PrioritiesNode2, P =:= Prio], 1064 [_|_] = Elevated, 1065 %% 1066 stop_node(Node2), 1067 stop_node(Node1), 1068 ok. 1069 1070get_socket_priorities() -> 1071 [Priority || 1072 {ok,[{priority,Priority}]} <- 1073 [inet:getopts(Port, [priority]) || 1074 Port <- erlang:ports(), 1075 element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]]. 1076 1077 1078 1079%% 1080%% Testcase: 1081%% monitor_nodes_nodedown_reason 1082%% 1083 1084monitor_nodes_nodedown_reason(Config) when is_list(Config) -> 1085 run_dist_configs(fun monitor_nodes_nodedown_reason/2, Config). 1086 1087monitor_nodes_nodedown_reason(DCfg, _Config) -> 1088 MonNodeState = monitor_node_state(), 1089 ok = net_kernel:monitor_nodes(true), 1090 ok = net_kernel:monitor_nodes(true, [nodedown_reason]), 1091 1092 Names = get_numbered_nodenames(5, node), 1093 [NN1, NN2, NN3, NN4, NN5] = Names, 1094 1095 {ok, N1} = start_node(DCfg, NN1), 1096 {ok, N2} = start_node(DCfg, NN2), 1097 {ok, N3} = start_node(DCfg, NN3), 1098 {ok, N4} = start_node(DCfg, NN4, "-hidden"), 1099 1100 receive {nodeup, N1} -> ok end, 1101 receive {nodeup, N2} -> ok end, 1102 receive {nodeup, N3} -> ok end, 1103 1104 receive {nodeup, N1, []} -> ok end, 1105 receive {nodeup, N2, []} -> ok end, 1106 receive {nodeup, N3, []} -> ok end, 1107 1108 stop_node(N1), 1109 stop_node(N4), 1110 true = net_kernel:disconnect(N2), 1111 TickTime = net_kernel:get_net_ticktime(), 1112 SleepTime = TickTime + (TickTime div 2), 1113 spawn(N3, fun () -> 1114 block_emu(SleepTime*1000), 1115 halt() 1116 end), 1117 1118 receive {nodedown, N1} -> ok end, 1119 receive {nodedown, N2} -> ok end, 1120 receive {nodedown, N3} -> ok end, 1121 1122 receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end, 1123 receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end, 1124 receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end, 1125 1126 ok = net_kernel:monitor_nodes(false, [nodedown_reason]), 1127 1128 {ok, N5} = start_node(DCfg, NN5), 1129 stop_node(N5), 1130 1131 receive {nodeup, N5} -> ok end, 1132 receive {nodedown, N5} -> ok end, 1133 print_my_messages(), 1134 ok = check_no_nodedown_nodeup(1000), 1135 ok = net_kernel:monitor_nodes(false), 1136 MonNodeState = monitor_node_state(), 1137 ok. 1138 1139 1140monitor_nodes_complex_nodedown_reason(Config) when is_list(Config) -> 1141 run_dist_configs(fun monitor_nodes_complex_nodedown_reason/2, Config). 1142 1143monitor_nodes_complex_nodedown_reason(DCfg, _Config) -> 1144 MonNodeState = monitor_node_state(), 1145 Me = self(), 1146 ok = net_kernel:monitor_nodes(true, [nodedown_reason]), 1147 [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason), 1148 {ok, Node} = start_node(DCfg, Name, ""), 1149 Pid = spawn(Node, 1150 fun() -> 1151 Me ! {stuff, 1152 self(), 1153 [make_ref(), 1154 {processes(), erlang:ports()}]} 1155 end), 1156 receive {nodeup, Node, []} -> ok end, 1157 {ok, NodeInfo} = net_kernel:node_info(Node), 1158 {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo), 1159 ComplexTerm = receive {stuff, Pid, _} = Msg -> 1160 {Msg, term_to_binary(Msg)} 1161 end, 1162 exit(Owner, ComplexTerm), 1163 receive 1164 {nodedown, Node, [{nodedown_reason, NodeDownReason}]} -> 1165 ok 1166 end, 1167 %% If the complex nodedown_reason messed something up garbage collections 1168 %% are likely to dump core 1169 garbage_collect(), 1170 garbage_collect(), 1171 garbage_collect(), 1172 ComplexTerm = NodeDownReason, 1173 ok = net_kernel:monitor_nodes(false, [nodedown_reason]), 1174 no_msgs(), 1175 MonNodeState = monitor_node_state(), 1176 ok. 1177 1178 1179 1180 1181%% 1182%% Testcase: 1183%% monitor_nodes_node_type 1184%% 1185 1186monitor_nodes_node_type(Config) when is_list(Config) -> 1187 run_dist_configs(fun monitor_nodes_node_type/2, Config). 1188 1189monitor_nodes_node_type(DCfg, _Config) -> 1190 MonNodeState = monitor_node_state(), 1191 ok = net_kernel:monitor_nodes(true), 1192 ok = net_kernel:monitor_nodes(true, [{node_type, all}]), 1193 Names = get_numbered_nodenames(9, node), 1194 [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names, 1195 1196 {ok, N1} = start_node(DCfg, NN1), 1197 {ok, N2} = start_node(DCfg, NN2), 1198 {ok, N3} = start_node(DCfg, NN3, "-hidden"), 1199 {ok, N4} = start_node(DCfg, NN4, "-hidden"), 1200 1201 receive {nodeup, N1} -> ok end, 1202 receive {nodeup, N2} -> ok end, 1203 1204 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 1205 receive {nodeup, N2, [{node_type, visible}]} -> ok end, 1206 receive {nodeup, N3, [{node_type, hidden}]} -> ok end, 1207 receive {nodeup, N4, [{node_type, hidden}]} -> ok end, 1208 1209 stop_node(N1), 1210 stop_node(N2), 1211 stop_node(N3), 1212 stop_node(N4), 1213 1214 receive {nodedown, N1} -> ok end, 1215 receive {nodedown, N2} -> ok end, 1216 1217 receive {nodedown, N1, [{node_type, visible}]} -> ok end, 1218 receive {nodedown, N2, [{node_type, visible}]} -> ok end, 1219 receive {nodedown, N3, [{node_type, hidden}]} -> ok end, 1220 receive {nodedown, N4, [{node_type, hidden}]} -> ok end, 1221 1222 ok = net_kernel:monitor_nodes(false, [{node_type, all}]), 1223 {ok, N5} = start_node(DCfg, NN5), 1224 1225 receive {nodeup, N5} -> ok end, 1226 stop_node(N5), 1227 receive {nodedown, N5} -> ok end, 1228 1229 ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]), 1230 {ok, N6} = start_node(DCfg, NN6), 1231 {ok, N7} = start_node(DCfg, NN7, "-hidden"), 1232 1233 1234 receive {nodeup, N6} -> ok end, 1235 receive {nodeup, N7, [{node_type, hidden}]} -> ok end, 1236 stop_node(N6), 1237 stop_node(N7), 1238 1239 receive {nodedown, N6} -> ok end, 1240 receive {nodedown, N7, [{node_type, hidden}]} -> ok end, 1241 1242 ok = net_kernel:monitor_nodes(true, [{node_type, visible}]), 1243 ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]), 1244 ok = net_kernel:monitor_nodes(false), 1245 1246 {ok, N8} = start_node(DCfg, NN8), 1247 {ok, N9} = start_node(DCfg, NN9, "-hidden"), 1248 1249 receive {nodeup, N8, [{node_type, visible}]} -> ok end, 1250 stop_node(N8), 1251 stop_node(N9), 1252 1253 receive {nodedown, N8, [{node_type, visible}]} -> ok end, 1254 print_my_messages(), 1255 ok = check_no_nodedown_nodeup(1000), 1256 ok = net_kernel:monitor_nodes(false, [{node_type, visible}]), 1257 MonNodeState = monitor_node_state(), 1258 ok. 1259 1260 1261%% 1262%% Testcase: 1263%% monitor_nodes 1264%% 1265 1266monitor_nodes_misc(Config) when is_list(Config) -> 1267 run_dist_configs(fun monitor_nodes_misc/2, Config). 1268 1269monitor_nodes_misc(DCfg, _Config) -> 1270 MonNodeState = monitor_node_state(), 1271 ok = net_kernel:monitor_nodes(true), 1272 ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]), 1273 ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]), 1274 Names = get_numbered_nodenames(3, node), 1275 [NN1, NN2, NN3] = Names, 1276 1277 {ok, N1} = start_node(DCfg, NN1), 1278 {ok, N2} = start_node(DCfg, NN2, "-hidden"), 1279 1280 receive {nodeup, N1} -> ok end, 1281 1282 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 1283 receive {nodeup, N1, [{node_type, visible}]} -> ok end, 1284 receive {nodeup, N2, [{node_type, hidden}]} -> ok end, 1285 receive {nodeup, N2, [{node_type, hidden}]} -> ok end, 1286 1287 stop_node(N1), 1288 stop_node(N2), 1289 1290 VisbleDownInfo = lists:sort([{node_type, visible}, 1291 {nodedown_reason, connection_closed}]), 1292 HiddenDownInfo = lists:sort([{node_type, hidden}, 1293 {nodedown_reason, connection_closed}]), 1294 1295 receive {nodedown, N1} -> ok end, 1296 1297 receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end, 1298 receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end, 1299 receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end, 1300 receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end, 1301 1302 ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]), 1303 1304 {ok, N3} = start_node(DCfg, NN3), 1305 receive {nodeup, N3} -> ok end, 1306 stop_node(N3), 1307 receive {nodedown, N3} -> ok end, 1308 print_my_messages(), 1309 ok = check_no_nodedown_nodeup(1000), 1310 ok = net_kernel:monitor_nodes(false), 1311 MonNodeState = monitor_node_state(), 1312 ok. 1313 1314 1315%% Tests that {nodeup, Node} messages are received before 1316%% messages from Node and that {nodedown, Node} messages are 1317%% received after messages from Node. 1318monitor_nodes_otp_6481(Config) when is_list(Config) -> 1319 run_dist_configs(fun monitor_nodes_otp_6481/2, Config). 1320 1321monitor_nodes_otp_6481(DCfg, Config) -> 1322 io:format("Testing nodedown...~n"), 1323 monitor_nodes_otp_6481_test(DCfg, Config, nodedown), 1324 io:format("ok~n"), 1325 io:format("Testing nodeup...~n"), 1326 monitor_nodes_otp_6481_test(DCfg, Config, nodeup), 1327 io:format("ok~n"), 1328 ok. 1329 1330monitor_nodes_otp_6481_test(DCfg, Config, TestType) when is_list(Config) -> 1331 MonNodeState = monitor_node_state(), 1332 NodeMsg = make_ref(), 1333 Me = self(), 1334 [Name] = get_nodenames(1, monitor_nodes_otp_6481), 1335 case TestType of 1336 nodedown -> ok = net_kernel:monitor_nodes(true); 1337 nodeup -> ok 1338 end, 1339 Seq = lists:seq(1,10000), 1340 MN = spawn_link( 1341 fun () -> 1342 lists:foreach( 1343 fun (_) -> 1344 ok = net_kernel:monitor_nodes(true) 1345 end, 1346 Seq), 1347 Me ! {mon_set, self()}, 1348 receive after infinity -> ok end 1349 end), 1350 receive {mon_set, MN} -> ok end, 1351 case TestType of 1352 nodedown -> ok; 1353 nodeup -> ok = net_kernel:monitor_nodes(true) 1354 end, 1355 1356 %% Whitebox: 1357 %% nodedown test: Since this process was the first one monitoring 1358 %% nodes this process will be the first one notified 1359 %% on nodedown. 1360 %% nodeup test: Since this process was the last one monitoring 1361 %% nodes this process will be the last one notified 1362 %% on nodeup 1363 1364 %% Verify the monitor_nodes order expected 1365 TestMonNodeState = monitor_node_state(), 1366 %% io:format("~p~n", [TestMonNodeState]), 1367 TestMonNodeState = 1368 case TestType of 1369 nodedown -> []; 1370 nodeup -> [{self(), []}] 1371 end 1372 ++ lists:map(fun (_) -> {MN, []} end, Seq) 1373 ++ case TestType of 1374 nodedown -> [{self(), []}]; 1375 nodeup -> [] 1376 end 1377 ++ MonNodeState, 1378 1379 {ok, Node} = start_node(DCfg, Name, "", this), 1380 receive {nodeup, Node} -> ok end, 1381 1382 RemotePid = spawn(Node, 1383 fun () -> 1384 receive after 1500 -> ok end, 1385 %% infinite loop of msgs 1386 %% we want an endless stream of messages and the kill 1387 %% the node mercilessly. 1388 %% We then want to ensure that the nodedown message arrives 1389 %% last ... without garbage after it. 1390 _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), 1391 receive {Me, kill_it} -> ok end, 1392 halt() 1393 end), 1394 1395 net_kernel:disconnect(Node), 1396 receive {nodedown, Node} -> ok end, 1397 1398 %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message 1399 %% bringing up the connection). 1400 {nodeup, Node} = receive Msg1 -> Msg1 end, 1401 {NodeMsg, N} = receive Msg2 -> Msg2 end, 1402 %% msg stream has begun, kill the node 1403 RemotePid ! {self(), kill_it}, 1404 1405 %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}' 1406 %% message. 1407 {nodedown, Node} = flush_node_msgs(NodeMsg, N+1), 1408 no_msgs(500), 1409 1410 Mon = erlang:monitor(process, MN), 1411 unlink(MN), 1412 exit(MN, bang), 1413 receive {'DOWN', Mon, process, MN, bang} -> ok end, 1414 ok = net_kernel:monitor_nodes(false), 1415 MonNodeState = monitor_node_state(), 1416 ok. 1417 1418flush_node_msgs(NodeMsg, No) -> 1419 case receive Msg -> Msg end of 1420 {NodeMsg, N} when N >= No -> 1421 flush_node_msgs(NodeMsg, N+1); 1422 OtherMsg -> 1423 OtherMsg 1424 end. 1425 1426node_loop_send(Pid, Msg, No) -> 1427 Pid ! {Msg, No}, 1428 node_loop_send(Pid, Msg, No + 1). 1429 1430monitor_nodes_errors(Config) when is_list(Config) -> 1431 MonNodeState = monitor_node_state(), 1432 error = net_kernel:monitor_nodes(asdf), 1433 {error, 1434 {unknown_options, 1435 [gurka]}} = net_kernel:monitor_nodes(true, 1436 [gurka]), 1437 {error, 1438 {options_not_a_list, 1439 gurka}} = net_kernel:monitor_nodes(true, 1440 gurka), 1441 {error, 1442 {option_value_mismatch, 1443 [{node_type,visible}, 1444 {node_type,hidden}]}} 1445 = net_kernel:monitor_nodes(true, 1446 [{node_type,hidden}, 1447 {node_type,visible}]), 1448 {error, 1449 {option_value_mismatch, 1450 [{node_type,visible}, 1451 {node_type,all}]}} 1452 = net_kernel:monitor_nodes(true, 1453 [{node_type,all}, 1454 {node_type,visible}]), 1455 {error, 1456 {bad_option_value, 1457 {node_type, 1458 blaha}}} 1459 = net_kernel:monitor_nodes(true, [{node_type, blaha}]), 1460 MonNodeState = monitor_node_state(), 1461 ok. 1462 1463monitor_nodes_combinations(Config) when is_list(Config) -> 1464 run_dist_configs(fun monitor_nodes_combinations/2, Config). 1465 1466monitor_nodes_combinations(DCfg, _Config) -> 1467 MonNodeState = monitor_node_state(), 1468 monitor_nodes_all_comb(true), 1469 [VisibleName, HiddenName] = get_nodenames(2, 1470 monitor_nodes_combinations), 1471 {ok, Visible} = start_node(DCfg, VisibleName, ""), 1472 receive_all_comb_nodeup_msgs(visible, Visible), 1473 no_msgs(), 1474 stop_node(Visible), 1475 receive_all_comb_nodedown_msgs(visible, Visible, connection_closed), 1476 no_msgs(), 1477 {ok, Hidden} = start_node(DCfg, HiddenName, "-hidden"), 1478 receive_all_comb_nodeup_msgs(hidden, Hidden), 1479 no_msgs(), 1480 stop_node(Hidden), 1481 receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed), 1482 no_msgs(), 1483 monitor_nodes_all_comb(false), 1484 MonNodeState = monitor_node_state(), 1485 no_msgs(), 1486 ok. 1487 1488monitor_nodes_all_comb(Flag) -> 1489 ok = net_kernel:monitor_nodes(Flag), 1490 ok = net_kernel:monitor_nodes(Flag, 1491 [nodedown_reason]), 1492 ok = net_kernel:monitor_nodes(Flag, 1493 [{node_type, hidden}]), 1494 ok = net_kernel:monitor_nodes(Flag, 1495 [{node_type, visible}]), 1496 ok = net_kernel:monitor_nodes(Flag, 1497 [{node_type, all}]), 1498 ok = net_kernel:monitor_nodes(Flag, 1499 [nodedown_reason, 1500 {node_type, hidden}]), 1501 ok = net_kernel:monitor_nodes(Flag, 1502 [nodedown_reason, 1503 {node_type, visible}]), 1504 ok = net_kernel:monitor_nodes(Flag, 1505 [nodedown_reason, 1506 {node_type, all}]), 1507 %% There currently are 8 different combinations 1508 8. 1509 1510 1511receive_all_comb_nodeup_msgs(visible, Node) -> 1512 io:format("Receive nodeup visible...~n"), 1513 Exp = [{nodeup, Node}, 1514 {nodeup, Node, []}] 1515 ++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node), 1516 receive_mn_msgs(Exp), 1517 io:format("ok~n"), 1518 ok; 1519receive_all_comb_nodeup_msgs(hidden, Node) -> 1520 io:format("Receive nodeup hidden...~n"), 1521 Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node), 1522 receive_mn_msgs(Exp), 1523 io:format("ok~n"), 1524 ok. 1525 1526mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) -> 1527 InfoNt = [{node_type, Type}], 1528 [{nodeup, Node, InfoNt}, 1529 {nodeup, Node, InfoNt}, 1530 {nodeup, Node, InfoNt}, 1531 {nodeup, Node, InfoNt}]. 1532 1533receive_all_comb_nodedown_msgs(visible, Node, Reason) -> 1534 io:format("Receive nodedown visible...~n"), 1535 Exp = [{nodedown, Node}, 1536 {nodedown, Node, [{nodedown_reason, Reason}]}] 1537 ++ mk_exp_mn_all_comb_nodedown_msgs_common(visible, 1538 Node, 1539 Reason), 1540 receive_mn_msgs(Exp), 1541 io:format("ok~n"), 1542 ok; 1543receive_all_comb_nodedown_msgs(hidden, Node, Reason) -> 1544 io:format("Receive nodedown hidden...~n"), 1545 Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason), 1546 receive_mn_msgs(Exp), 1547 io:format("ok~n"), 1548 ok. 1549 1550mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) -> 1551 InfoNt = [{node_type, Type}], 1552 InfoNdrNt = lists:sort([{nodedown_reason, Reason}]++InfoNt), 1553 [{nodedown, Node, InfoNt}, 1554 {nodedown, Node, InfoNt}, 1555 {nodedown, Node, InfoNdrNt}, 1556 {nodedown, Node, InfoNdrNt}]. 1557 1558receive_mn_msgs([]) -> 1559 ok; 1560receive_mn_msgs(Msgs) -> 1561 io:format("Expecting msgs: ~p~n", [Msgs]), 1562 receive 1563 {_Dir, _Node} = Msg -> 1564 io:format("received ~p~n", [Msg]), 1565 case lists:member(Msg, Msgs) of 1566 true -> receive_mn_msgs(lists:delete(Msg, Msgs)); 1567 false -> ct:fail({unexpected_message, Msg, 1568 expected_messages, Msgs}) 1569 end; 1570 {Dir, Node, Info} -> 1571 Msg = {Dir, Node, lists:sort(Info)}, 1572 io:format("received ~p~n", [Msg]), 1573 case lists:member(Msg, Msgs) of 1574 true -> receive_mn_msgs(lists:delete(Msg, Msgs)); 1575 false -> ct:fail({unexpected_message, Msg, 1576 expected_messages, Msgs}) 1577 end; 1578 Msg -> 1579 io:format("received ~p~n", [Msg]), 1580 ct:fail({unexpected_message, Msg, 1581 expected_messages, Msgs}) 1582 end. 1583 1584monitor_nodes_cleanup(Config) when is_list(Config) -> 1585 MonNodeState = monitor_node_state(), 1586 Me = self(), 1587 No = monitor_nodes_all_comb(true), 1588 Inf = spawn(fun () -> 1589 monitor_nodes_all_comb(true), 1590 Me ! {mons_set, self()}, 1591 receive after infinity -> ok end 1592 end), 1593 TO = spawn(fun () -> 1594 monitor_nodes_all_comb(true), 1595 Me ! {mons_set, self()}, 1596 receive after 500 -> ok end 1597 end), 1598 receive {mons_set, Inf} -> ok end, 1599 receive {mons_set, TO} -> ok end, 1600 MNLen = length(MonNodeState) + No*3, 1601 MNLen = length(monitor_node_state()), 1602 MonInf = erlang:monitor(process, Inf), 1603 MonTO = erlang:monitor(process, TO), 1604 exit(Inf, bang), 1605 No = monitor_nodes_all_comb(false), 1606 receive {'DOWN', MonInf, process, Inf, bang} -> ok end, 1607 receive {'DOWN', MonTO, process, TO, normal} -> ok end, 1608 MonNodeState = monitor_node_state(), 1609 no_msgs(), 1610 ok. 1611 1612monitor_nodes_many(Config) when is_list(Config) -> 1613 run_dist_configs(fun monitor_nodes_many/2, Config). 1614 1615monitor_nodes_many(DCfg, _Config) -> 1616 MonNodeState = monitor_node_state(), 1617 [Name] = get_nodenames(1, monitor_nodes_many), 1618 %% We want to perform more than 2^16 net_kernel:monitor_nodes 1619 %% since this will wrap an internal counter 1620 No = (1 bsl 16) + 17, 1621 repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No), 1622 No = length(monitor_node_state()) - length(MonNodeState), 1623 {ok, Node} = start_node(DCfg, Name), 1624 repeat(fun () -> receive {nodeup, Node} -> ok end end, No), 1625 stop_node(Node), 1626 repeat(fun () -> receive {nodedown, Node} -> ok end end, No), 1627 ok = net_kernel:monitor_nodes(false), 1628 no_msgs(10), 1629 MonNodeState = monitor_node_state(), 1630 ok. 1631 1632%% Test order of messages nodedown and nodeup. 1633monitor_nodes_down_up(Config) when is_list(Config) -> 1634 [An] = get_nodenames(1, monitor_nodeup), 1635 {ok, A} = ct_slave:start(An), 1636 1637 try 1638 monitor_nodes_yoyo(A) 1639 after 1640 catch ct_slave:stop(A) 1641 end. 1642 1643monitor_nodes_yoyo(A) -> 1644 net_kernel:monitor_nodes(true), 1645 Papa = self(), 1646 1647 %% Spawn lots of processes doing one erlang:monitor_node(A,true) each 1648 %% just to get lots of other monitors to fire when connection goes down 1649 %% and thereby give time for {nodeup,A} to race before {nodedown,A}. 1650 NodeMonCnt = 10000, 1651 NodeMons = [my_spawn_opt(fun F() -> 1652 monitor_node = receive_any(), 1653 monitor_node(A, true), 1654 Papa ! ready, 1655 {nodedown, A} = receive_any(), 1656 F() 1657 end, 1658 [link, monitor, {priority, low}]) 1659 || 1660 _ <- lists:seq(1, NodeMonCnt)], 1661 1662 %% Spawn message spamming process to trigger new connection setups 1663 %% as quick as possible. 1664 Spammer = my_spawn_opt(fun F() -> 1665 {dummy, A} ! trigger_auto_connect, 1666 F() 1667 end, 1668 [link, monitor]), 1669 1670 %% Now bring connection down and verify we get {nodedown,A} before {nodeup,A}. 1671 Yoyos = 20, 1672 [begin 1673 [P ! monitor_node || P <- NodeMons], 1674 [receive ready -> ok end || _ <- NodeMons], 1675 1676 Owner = get_conn_owner(A), 1677 exit(Owner, kill), 1678 1679 {nodedown, A} = receive_any(), 1680 {nodeup, A} = receive_any() 1681 end 1682 || _ <- lists:seq(1,Yoyos)], 1683 1684 unlink(Spammer), 1685 exit(Spammer, die), 1686 receive {'DOWN',_,process,Spammer,_} -> ok end, 1687 1688 [begin unlink(P), exit(P, die) end || P <- NodeMons], 1689 [receive {'DOWN',_,process,P,_} -> ok end || P <- NodeMons], 1690 1691 net_kernel:monitor_nodes(false), 1692 ok. 1693 1694receive_any() -> 1695 receive_any(infinity). 1696 1697receive_any(Timeout) -> 1698 receive 1699 M -> M 1700 after 1701 Timeout -> timeout 1702 end. 1703 1704my_spawn_opt(Fun, Opts) -> 1705 case spawn_opt(Fun, Opts) of 1706 {Pid, _Mref} -> Pid; 1707 Pid -> Pid 1708 end. 1709 1710get_conn_owner(Node) -> 1711 {ok, NodeInfo} = net_kernel:node_info(Node), 1712 {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo), 1713 Owner. 1714 1715dist_ctrl_proc_smoke(Config) when is_list(Config) -> 1716 dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME)). 1717 1718dist_ctrl_proc_reject(Config) when is_list(Config) -> 1719 ToReject = combinations(dist_util:rejectable_flags()), 1720 lists:map(fun(Flags) -> 1721 ct:log("Try to reject ~p",[Flags]), 1722 dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME), 1723 "-gen_tcp_dist_reject_flags " ++ integer_to_list(Flags)) 1724 end, ToReject). 1725 1726combinations([H | T]) -> 1727 lists:flatten([[(1 bsl H) bor C || C <- combinations(T)] | combinations(T)]); 1728combinations([]) -> 1729 [0]; 1730combinations(BitField) -> 1731 lists:sort(combinations(bits(BitField, 0))). 1732 1733bits(0, _) -> 1734 []; 1735bits(BitField, Cnt) when BitField band 1 == 1 -> 1736 [Cnt | bits(BitField bsr 1, Cnt + 1)]; 1737bits(BitField, Cnt) -> 1738 bits(BitField bsr 1, Cnt + 1). 1739 1740dist_ctrl_proc_test(Nodes) -> 1741 dist_ctrl_proc_test(Nodes,""). 1742 1743dist_ctrl_proc_test([Name1,Name2], Extra) -> 1744 ThisNode = node(), 1745 GenTcpOptProlog = "-proto_dist gen_tcp " 1746 "-gen_tcp_dist_output_loop " ++ atom_to_list(?MODULE) ++ " " ++ 1747 "dist_cntrlr_output_test_size " ++ Extra, 1748 {ok, Node1} = start_node("", Name1, "-proto_dist gen_tcp"), 1749 {ok, Node2} = start_node("", Name2, GenTcpOptProlog), 1750 NL = lists:sort([ThisNode, Node1, Node2]), 1751 wait_until(fun () -> 1752 NL == lists:sort([node()|nodes()]) 1753 end), 1754 wait_until(fun () -> 1755 NL == lists:sort([rpc:call(Node1,erlang, node, []) 1756 | rpc:call(Node1, erlang, nodes, [])]) 1757 end), 1758 wait_until(fun () -> 1759 NL == lists:sort([rpc:call(Node2,erlang, node, []) 1760 | rpc:call(Node2, erlang, nodes, [])]) 1761 end), 1762 1763 smoke_communicate(Node1, gen_tcp_dist, dist_cntrlr_output_loop), 1764 smoke_communicate(Node2, erl_distribution_SUITE, dist_cntrlr_output_loop_size), 1765 1766 stop_node(Node1), 1767 stop_node(Node2), 1768 ok. 1769 1770smoke_communicate(Node, OLoopMod, OLoopFun) -> 1771 %% Verify that we actually are executing the distribution 1772 %% module we expect and also massage message passing over 1773 %% the connection a bit... 1774 Ps = rpc:call(Node, erlang, processes, []), 1775 try 1776 lists:foreach( 1777 fun (P) -> 1778 case rpc:call(Node, erlang, process_info, [P, current_stacktrace]) of 1779 undefined -> 1780 ok; 1781 {current_stacktrace, StkTrace} -> 1782 lists:foreach(fun ({Mod, Fun, 2, _}) when Mod == OLoopMod, 1783 Fun == OLoopFun -> 1784 io:format("~p ~p~n", [P, StkTrace]), 1785 throw(found_it); 1786 (_) -> 1787 ok 1788 end, StkTrace) 1789 end 1790 end, Ps), 1791 exit({missing, {OLoopMod, OLoopFun}}) 1792 catch 1793 throw:found_it -> ok 1794 end, 1795 Bin = list_to_binary(lists:duplicate(1000,100)), 1796 BitStr = <<0:7999>>, 1797 List = [[Bin], atom, [BitStr|Bin], make_ref(), [[[BitStr|"hopp"]]], 1798 4711, 111122222211111111111111,"hej", fun () -> ok end, BitStr, 1799 self(), fun erlang:node/1], 1800 Pid = spawn_link(Node, fun () -> receive {From1, Msg1} -> From1 ! Msg1 end, 1801 receive {From2, Msg2} -> From2 ! Msg2 end 1802 end), 1803 R = make_ref(), 1804 Pid ! {self(), [R, List]}, 1805 receive [R, L1] -> List = L1 end, 1806 1807 %% Send a huge message in order to trigger message fragmentation if enabled 1808 FragBin = <<0:(2*(1024*64*8))>>, 1809 Pid ! {self(), [R, List, FragBin]}, 1810 receive [R, L2, B] -> List = L2, FragBin = B end, 1811 1812 unlink(Pid), 1813 exit(Pid, kill), 1814 ok. 1815 1816 1817erl_uds_dist_smoke_test(Config) when is_list(Config) -> 1818 case os:type() of 1819 {win32,_} -> 1820 {skipped, "Not on Windows"}; 1821 _ -> 1822 do_erl_uds_dist_smoke_test() 1823 end. 1824 1825do_erl_uds_dist_smoke_test() -> 1826 [Node1, Node2] = lists:map(fun (Name) -> 1827 list_to_atom(atom_to_list(Name) ++ "@localhost") 1828 end, 1829 get_nodenames(2, erl_uds_dist_smoke_test)), 1830 {LPort, Acceptor} = uds_listen(), 1831 start_uds_node(Node1, LPort), 1832 start_uds_node(Node2, LPort), 1833 receive 1834 {uds_nodeup, N1} -> 1835 io:format("~p is up~n", [N1]) 1836 end, 1837 receive 1838 {uds_nodeup, N2} -> 1839 io:format("~p is up~n", [N2]) 1840 end, 1841 1842 io:format("Testing ping net_adm:ping(~p) on ~p~n", [Node2, Node1]), 1843 Node1 ! {self(), {net_adm, ping, [Node2]}}, 1844 receive 1845 {Node1, PingRes} -> 1846 io:format("~p~n", [PingRes]), 1847 pong = PingRes 1848 end, 1849 1850 io:format("Testing nodes() on ~p~n", [Node1]), 1851 Node1 ! {self(), {erlang, nodes, []}}, 1852 receive 1853 {Node1, N1List} -> 1854 io:format("~p~n", [N1List]), 1855 [Node2] = N1List 1856 end, 1857 1858 io:format("Testing nodes() on ~p~n", [Node2]), 1859 Node2 ! {self(), {erlang, nodes, []}}, 1860 receive 1861 {Node2, N2List} -> 1862 io:format("~p~n", [N2List]), 1863 [Node1] = N2List 1864 end, 1865 1866 io:format("Shutting down~n", []), 1867 1868 Node1 ! {self(), close}, 1869 Node2 ! {self(), close}, 1870 1871 receive {Node1, C1} -> ok = C1 end, 1872 receive {Node2, C2} -> ok = C2 end, 1873 1874 unlink(Acceptor), 1875 exit(Acceptor, kill), 1876 1877 io:format("ok~n", []), 1878 1879 ok. 1880 1881%% Helpers for testing the erl_uds_dist example 1882 1883uds_listen() -> 1884 Me = self(), 1885 {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]), 1886 {ok, LPort} = inet:port(LSock), 1887 {LPort, spawn_link(fun () -> 1888 uds_accept_loop(LSock, Me) 1889 end)}. 1890 1891uds_accept_loop(LSock, TestProc) -> 1892 {ok, Sock} = gen_tcp:accept(LSock), 1893 _ = spawn_link(fun () -> 1894 uds_rpc_client_init(Sock, TestProc) 1895 end), 1896 uds_accept_loop(LSock, TestProc). 1897 1898uds_rpc(Sock, MFA) -> 1899 ok = gen_tcp:send(Sock, term_to_binary(MFA)), 1900 case gen_tcp:recv(Sock, 0) of 1901 {error, Reason} -> 1902 error({recv_failed, Reason}); 1903 {ok, Packet} -> 1904 binary_to_term(Packet) 1905 end. 1906 1907uds_rpc_client_init(Sock, TestProc) -> 1908 case uds_rpc(Sock, {erlang, node, []}) of 1909 nonode@nohost -> 1910 %% Wait for distribution to come up... 1911 receive after 100 -> ok end, 1912 uds_rpc_client_init(Sock, TestProc); 1913 Node when is_atom(Node) -> 1914 register(Node, self()), 1915 TestProc ! {uds_nodeup, Node}, 1916 uds_rpc_client_loop(Sock, Node) 1917 end. 1918 1919uds_rpc_client_loop(Sock, Node) -> 1920 receive 1921 {From, close} -> 1922 ok = gen_tcp:send(Sock, term_to_binary(close)), 1923 From ! {Node, gen_tcp:close(Sock)}, 1924 exit(normal); 1925 {From, ApplyData} -> 1926 From ! {Node, uds_rpc(Sock, ApplyData)}, 1927 uds_rpc_client_loop(Sock, Node) 1928 end. 1929 1930uds_rpc_server_loop(Sock) -> 1931 case gen_tcp:recv(Sock, 0) of 1932 {error, Reason} -> 1933 error({recv_failed, Reason}); 1934 {ok, Packet} -> 1935 case binary_to_term(Packet) of 1936 {M, F, A} when is_atom(M), is_atom(F), is_list(A) -> 1937 ok = gen_tcp:send(Sock, term_to_binary(apply(M, F, A))); 1938 {F, A} when is_function(F), is_list(A) -> 1939 ok = gen_tcp:send(Sock, term_to_binary(apply(F, A))); 1940 close -> 1941 ok = gen_tcp:close(Sock), 1942 exit(normal); 1943 Other -> 1944 error({unexpected_data, Other}) 1945 end 1946 end, 1947 uds_rpc_server_loop(Sock). 1948 1949start_uds_rpc_server([PortString]) -> 1950 Port = list_to_integer(PortString), 1951 {Pid, Mon} = spawn_monitor(fun () -> 1952 {ok, Sock} = gen_tcp:connect({127,0,0,1}, Port, 1953 [binary, {packet, 4}, 1954 {active, false}]), 1955 uds_rpc_server_loop(Sock) 1956 end), 1957 receive 1958 {'DOWN', Mon, process, Pid, Reason} -> 1959 if Reason == normal -> 1960 halt(); 1961 true -> 1962 EStr = lists:flatten(io_lib:format("uds rpc server crashed: ~p", [Reason])), 1963 (catch file:write_file("uds_rpc_server_crash."++os:getpid(), EStr)), 1964 halt(EStr) 1965 end 1966 end. 1967 1968start_uds_node(NodeName, LPort) -> 1969 Static = "-detached -noinput -proto_dist erl_uds", 1970 Pa = filename:dirname(code:which(?MODULE)), 1971 Prog = case catch init:get_argument(progname) of 1972 {ok,[[P]]} -> P; 1973 _ -> error(no_progname_argument_found) 1974 end, 1975 {ok, Pwd} = file:get_cwd(), 1976 NameStr = atom_to_list(NodeName), 1977 CmdLine = Prog ++ " " 1978 ++ Static 1979 ++ " -sname " ++ NameStr 1980 ++ " -pa " ++ Pa 1981 ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr 1982 ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) 1983 ++ " -run " ++ atom_to_list(?MODULE) ++ " start_uds_rpc_server " 1984 ++ integer_to_list(LPort), 1985 io:format("Starting: ~p~n", [CmdLine]), 1986 case open_port({spawn, CmdLine}, []) of 1987 Port when is_port(Port) -> 1988 unlink(Port), 1989 erlang:port_close(Port); 1990 Error -> 1991 error({open_port_failed, Error}) 1992 end, 1993 ok. 1994 1995erl_1424(Config) when is_list(Config) -> 1996 {error, Reason} = erl_epmd:names("."), 1997 {comment, lists:flatten(io_lib:format("Reason: ~p", [Reason]))}. 1998 1999differing_cookies(Config) when is_list(Config) -> 2000 test_server:timetrap({minutes, 1}), 2001 Node = node(), 2002 true = Node =/= nonode@nohost, 2003 [] = nodes(), 2004 BaseName = atom_to_list(?FUNCTION_NAME), 2005 2006 %% Use -hidden nodes to avoid global connecting all nodes 2007 2008 %% Start node A with different cookie 2009 NodeAName = BaseName++"_nodeA", 2010 NodeA = full_node_name(NodeAName), 2011 NodeACookieL = BaseName++"_cookieA", 2012 NodeACookie = list_to_atom(NodeACookieL), 2013 true = erlang:set_cookie( NodeA, NodeACookie ), 2014 { ok, NodeA } = 2015 start_node( "-hidden", NodeAName, "-setcookie "++NodeACookieL ), 2016 try 2017 2018 %% Verify the cluster 2019 [ NodeA ] = nodes(hidden), 2020 [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ), 2021 2022 %% Start node B with another different cookie 2023 NodeBName = BaseName++"_nodeB", 2024 NodeB = full_node_name(NodeBName), 2025 NodeBCookieL = BaseName++"_cookieB", 2026 NodeBCookie = list_to_atom(NodeBCookieL), 2027 true = erlang:set_cookie( NodeB, NodeBCookie ), 2028 { ok, NodeB } = 2029 start_node( "-hidden", NodeBName, "-setcookie "++NodeBCookieL ), 2030 try 2031 2032 %% Verify the cluster 2033 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2034 [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ), 2035 [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ), 2036 2037 %% Verify that the nodes can not connect 2038 %% before correcting the cookie configuration 2039 pang = rpc:call( NodeA, net_adm, ping, [NodeB] ), 2040 pang = rpc:call( NodeB, net_adm, ping, [NodeA] ), 2041 2042 %% Configure cookie and connect node A -> B 2043 true = rpc:call( NodeA, erlang, set_cookie, [NodeB, NodeBCookie] ), 2044 pong = rpc:call( NodeA, net_adm, ping, [NodeB] ), 2045 2046 %% Verify the cluster 2047 NodeACookie = rpc:call( NodeA, erlang, get_cookie, []), 2048 NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []), 2049 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2050 equal_sets( [Node, NodeB], 2051 rpc:call( NodeA, erlang, nodes, [hidden] )), 2052 equal_sets( [Node, NodeA], 2053 rpc:call( NodeB, erlang, nodes, [hidden] )), 2054 2055 %% Disconnect node A from B 2056 true = rpc:call( NodeB, net_kernel, disconnect, [NodeA] ), 2057 2058 %% Verify the cluster 2059 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2060 [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ), 2061 [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ), 2062 2063 %% Reconnect, now node B -> A 2064 pong = rpc:call( NodeB, net_adm, ping, [NodeA] ), 2065 2066 %% Verify the cluster 2067 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2068 equal_sets( [Node, NodeB], 2069 rpc:call( NodeA, erlang, nodes, [hidden] )), 2070 equal_sets( [Node, NodeA], 2071 rpc:call( NodeB, erlang, nodes, [hidden] )) 2072 2073 after 2074 _ = stop_node(NodeB) 2075 end 2076 after 2077 _ = stop_node(NodeA) 2078 end, 2079 [] = nodes(hidden), 2080 ok. 2081 2082cmdline_setcookie_2(Config) when is_list(Config) -> 2083 test_server:timetrap({minutes, 1}), 2084 Node = node(), 2085 true = Node =/= nonode@nohost, 2086 [] = nodes(), 2087 NodeL = atom_to_list(Node), 2088 BaseName = atom_to_list(?FUNCTION_NAME), 2089 NodeCookie = erlang:get_cookie(), 2090 NodeCookieL = atom_to_list(NodeCookie), 2091 2092 %% Use -hidden nodes to avoid global connecting all nodes 2093 2094 %% Start node A with different cookie 2095 %% and cookie configuration of mother node 2096 NodeAName = BaseName++"_nodeA", 2097 NodeA = full_node_name(NodeAName), 2098 NodeACookieL = BaseName++"_cookieA", 2099 NodeACookie = list_to_atom(NodeACookieL), 2100 { ok, NodeA } = 2101 start_node( 2102 "-hidden", NodeAName, 2103 "-setcookie "++NodeL++" "++NodeCookieL ), 2104 2105 try 2106 2107 %% Verify the cluster 2108 [ NodeA ] = nodes(hidden), 2109 [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ), 2110 NodeCookie = rpc:call( NodeA, erlang, get_cookie, []), 2111 2112 true = rpc:call( NodeA, erlang, set_cookie, [NodeACookie] ), 2113 2114 %% Start node B with different cookie 2115 %% and cookie configuration of mother node and node A 2116 NodeBName = BaseName++"_nodeB", 2117 NodeB = full_node_name(NodeBName), 2118 NodeBCookieL = BaseName++"_cookieB", 2119 NodeBCookie = list_to_atom(NodeBCookieL), 2120 { ok, NodeB } = 2121 start_node( 2122 "-hidden", NodeBName, 2123 "-setcookie "++NodeBCookieL++" " 2124 "-setcookie "++NodeL++" "++NodeCookieL++" " 2125 "-setcookie "++atom_to_list(NodeA)++" "++NodeACookieL ), 2126 2127 try 2128 2129 %% Verify the cluster 2130 NodeACookie = rpc:call( NodeA, erlang, get_cookie, []), 2131 NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []), 2132 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2133 [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ), 2134 [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ), 2135 2136 %% Connect the nodes 2137 pong = rpc:call( NodeA, net_adm, ping, [NodeB] ), 2138 2139 %% Verify the cluster 2140 NodeACookie = rpc:call( NodeA, erlang, get_cookie, []), 2141 NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []), 2142 equal_sets( [NodeA, NodeB], nodes(hidden) ), 2143 equal_sets( [Node, NodeB], 2144 rpc:call( NodeA, erlang, nodes, [hidden] )), 2145 equal_sets( [Node, NodeA], 2146 rpc:call( NodeB, erlang, nodes, [hidden] )) 2147 2148 after 2149 _ = stop_node(NodeB) 2150 end 2151 after 2152 _ = stop_node(NodeA) 2153 end, 2154 [] = nodes(hidden), 2155 ok. 2156 2157connection_cookie(Config) when is_list(Config) -> 2158 test_server:timetrap({minutes, 1}), 2159 Node = node(), 2160 true = Node =/= nonode@nohost, 2161 [] = nodes(), 2162 NodeL = atom_to_list(Node), 2163 BaseName = atom_to_list(?FUNCTION_NAME), 2164 2165 %% Start node A with dedicated connection cookie 2166 NodeAName = BaseName++"_nodeA", 2167 NodeA = full_node_name(NodeAName), 2168 NodeACookieL = BaseName++"_cookieA", 2169 NodeACookie = list_to_atom(NodeACookieL), 2170 true = NodeACookie =/= erlang:get_cookie(), 2171 ConnectionCookieL = BaseName++"_connectionCookie", 2172 ConnectionCookie = list_to_atom(ConnectionCookieL), 2173 true = erlang:set_cookie( NodeA, ConnectionCookie ), 2174 { ok, NodeA } = 2175 start_node( 2176 "", NodeAName, 2177 "-setcookie "++NodeACookieL++" " 2178 "-setcookie "++NodeL++" "++ConnectionCookieL ), 2179 2180 try 2181 2182 %% Verify the cluster 2183 [ NodeA ] = nodes(), 2184 [ Node ] = rpc:call( NodeA, erlang, nodes, [] ), 2185 NodeACookie = rpc:call( NodeA, erlang, get_cookie, []), 2186 ConnectionCookie = rpc:call( NodeA, auth, get_cookie, [Node]), 2187 ConnectionCookie = erlang:get_cookie( NodeA ) 2188 2189 after 2190 _ = stop_node(NodeA) 2191 end, 2192 [] = nodes(), 2193 ok. 2194 2195dyn_differing_cookies(Config) when is_list(Config) -> 2196 test_server:timetrap({minutes, 1}), 2197 MotherNode = node(), 2198 true = MotherNode =/= nonode@nohost, 2199 [] = nodes(hidden), 2200 MotherNodeL = atom_to_list(MotherNode), 2201 BaseName = atom_to_list(?FUNCTION_NAME), 2202 MotherNodeCookie = erlang:get_cookie(), 2203 MotherNodeCookieL = atom_to_list(MotherNodeCookie), 2204 register(?FUNCTION_NAME, self()), 2205 2206 %% Start node A with different cookie 2207 %% and cookie configuration of mother node 2208 DynNodeCookieL = BaseName++"_cookieA", 2209 DynNodeCookie = list_to_atom(DynNodeCookieL), 2210 {_NF, Port} = 2211 start_node_unconnected( 2212 "-setcookie "++MotherNodeL++" "++MotherNodeCookieL, 2213 undefined, DynNodeCookie, 2214 ?MODULE, run_remote_test, 2215 [atom_to_list(?FUNCTION_NAME), MotherNodeL] ), 2216 2217 dyn_differing_cookies(MotherNode, MotherNodeCookie, DynNodeCookie, Port). 2218 2219dyn_differing_cookies(MotherNode, MotherNodeCookie, DynNodeCookie, Port) -> 2220 receive 2221 { MotherNode, MotherNodeCookie, DynNodeCookie, DynNode } -> 2222 [ DynNode ] = nodes(hidden), 2223 [ MotherNode ] = rpc:call( DynNode, erlang, nodes, [hidden] ), 2224 DynNodeCookie = rpc:call( DynNode, erlang, get_cookie, [] ), 2225 MotherNodeCookie = 2226 rpc:call( DynNode, erlang, get_cookie, [MotherNode] ), 2227 {?FUNCTION_NAME, DynNode} ! 2228 {MotherNode, MotherNodeCookie, DynNode}, 2229 2230 0 = wait_for_port_exit(Port), 2231 2232 [] = nodes(hidden), 2233 ok; 2234 {Port, {data, Data}} -> 2235 io:format("~p: ~s", [Port, Data]), 2236 dyn_differing_cookies( 2237 MotherNode, MotherNodeCookie, DynNodeCookie, Port); 2238 Other -> 2239 error({unexpected, Other}) 2240 end. 2241 2242dyn_differing_cookies(MotherNode, _Args) -> 2243 nonode@nohost = node(), 2244 [] = nodes(hidden), 2245 true = net_kernel:connect_node( MotherNode ), 2246 [ MotherNode ] = nodes(hidden), 2247 DynNode = node(), 2248 [ DynNode ] = rpc:call( MotherNode, erlang, nodes, [hidden] ), 2249 MotherNodeCookie = erlang:get_cookie( MotherNode ), 2250 MotherNodeCookie = rpc:call( MotherNode, erlang, get_cookie, [] ), 2251 %% Here we get the mother node's default cookie 2252 MotherNodeCookie = rpc:call( MotherNode, erlang, get_cookie, [DynNode] ), 2253 DynNodeCookie = erlang:get_cookie(), 2254 register( ?FUNCTION_NAME, self() ), 2255 {?FUNCTION_NAME, MotherNode} ! 2256 {MotherNode, MotherNodeCookie, DynNodeCookie, DynNode}, 2257 receive 2258 { MotherNode, MotherNodeCookie, DynNode } -> 2259 true = disconnect_node( MotherNode ), 2260 [] = nodes(hidden), 2261 ok; 2262 Other -> 2263 error({unexpected, Other}) 2264 end. 2265 2266 2267%% Misc. functions 2268 2269equal_sets(A, B) -> 2270 S = lists:sort(A), 2271 case lists:sort(B) of 2272 S -> 2273 ok; 2274 _ -> 2275 erlang:error({not_equal_sets, A, B}) 2276 end. 2277 2278run_dist_configs(Func, Config) -> 2279 GetOptProlog = "-proto_dist gen_tcp -gen_tcp_dist_output_loop " 2280 ++ atom_to_list(?MODULE) ++ " ", 2281 GenTcpDistTest = case get_gen_tcp_dist_test_type() of 2282 default -> 2283 {"gen_tcp_dist", "-proto_dist gen_tcp"}; 2284 size -> 2285 {"gen_tcp_dist (get_size)", 2286 GetOptProlog ++ "dist_cntrlr_output_test_size"} 2287 end, 2288 lists:map(fun ({DCfgName, DCfg}) -> 2289 io:format("~n~n=== Running ~s configuration ===~n~n", 2290 [DCfgName]), 2291 Func(DCfg, Config) 2292 end, 2293 [{"default", ""}, GenTcpDistTest]). 2294 2295start_gen_tcp_dist_test_type_server() -> 2296 Me = self(), 2297 Go = make_ref(), 2298 io:format("STARTING: gen_tcp_dist_test_type_server~n",[]), 2299 {P, M} = spawn_monitor(fun () -> 2300 register(gen_tcp_dist_test_type_server, self()), 2301 Me ! Go, 2302 gen_tcp_dist_test_type_server() 2303 end), 2304 receive 2305 Go -> 2306 ok; 2307 {'DOWN', M, process, P, _} -> 2308 start_gen_tcp_dist_test_type_server() 2309 end. 2310 2311kill_gen_tcp_dist_test_type_server() -> 2312 case whereis(gen_tcp_dist_test_type_server) of 2313 undefined -> 2314 ok; 2315 Pid -> 2316 exit(Pid,kill), 2317 %% Sync death, before continuing... 2318 false = erlang:is_process_alive(Pid) 2319 end. 2320 2321gen_tcp_dist_test_type_server() -> 2322 Type = case abs(erlang:monotonic_time(second)) rem 2 of 2323 0 -> default; 2324 1 -> size 2325 end, 2326 gen_tcp_dist_test_type_server(Type). 2327 2328gen_tcp_dist_test_type_server(Type) -> 2329 receive 2330 {From, Ref} -> 2331 From ! {Ref, Type}, 2332 NewType = case Type of 2333 default -> size; 2334 size -> default 2335 end, 2336 gen_tcp_dist_test_type_server(NewType) 2337 end. 2338 2339get_gen_tcp_dist_test_type() -> 2340 Ref = make_ref(), 2341 try 2342 gen_tcp_dist_test_type_server ! {self(), Ref}, 2343 receive 2344 {Ref, Type} -> 2345 Type 2346 end 2347 catch 2348 error:badarg -> 2349 start_gen_tcp_dist_test_type_server(), 2350 get_gen_tcp_dist_test_type() 2351 end. 2352 2353dist_cntrlr_output_test_size(DHandle, Socket) -> 2354 false = erlang:dist_ctrl_get_opt(DHandle, get_size), 2355 false = erlang:dist_ctrl_set_opt(DHandle, get_size, true), 2356 true = erlang:dist_ctrl_get_opt(DHandle, get_size), 2357 true = erlang:dist_ctrl_set_opt(DHandle, get_size, false), 2358 false = erlang:dist_ctrl_get_opt(DHandle, get_size), 2359 false = erlang:dist_ctrl_set_opt(DHandle, get_size, true), 2360 true = erlang:dist_ctrl_get_opt(DHandle, get_size), 2361 dist_cntrlr_output_loop_size(DHandle, Socket). 2362 2363dist_cntrlr_output_loop_size(DHandle, Socket) -> 2364 receive 2365 dist_data -> 2366 %% Outgoing data from this node... 2367 dist_cntrlr_send_data_size(DHandle, Socket); 2368 _ -> 2369 ok %% Drop garbage message... 2370 end, 2371 dist_cntrlr_output_loop_size(DHandle, Socket). 2372 2373dist_cntrlr_send_data_size(DHandle, Socket) -> 2374 case erlang:dist_ctrl_get_data(DHandle) of 2375 none -> 2376 erlang:dist_ctrl_get_data_notification(DHandle); 2377 {Size, Data} -> 2378 ok = ensure_iovec(Data), 2379 Size = erlang:iolist_size(Data), 2380 ok = gen_tcp:send(Socket, Data), 2381 dist_cntrlr_send_data_size(DHandle, Socket) 2382 end. 2383 2384ensure_iovec([]) -> 2385 ok; 2386ensure_iovec([X|Y]) when is_binary(X) -> 2387 ensure_iovec(Y). 2388 2389monitor_node_state() -> 2390 erts_debug:set_internal_state(available_internal_state, true), 2391 MonitoringNodes = erts_debug:get_internal_state(monitoring_nodes), 2392 erts_debug:set_internal_state(available_internal_state, false), 2393 MonitoringNodes. 2394 2395 2396check_no_nodedown_nodeup(TimeOut) -> 2397 receive 2398 {nodeup, _, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); 2399 {nodeup, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); 2400 {nodedown, _, _} = Msg -> ct:fail({unexpected_nodedown, Msg}); 2401 {nodedown, _} = Msg -> ct:fail({unexpected_nodedown, Msg}) 2402 after TimeOut -> 2403 ok 2404 end. 2405 2406print_my_messages() -> 2407 {messages, Messages} = process_info(self(), messages), 2408 io:format("Messages: ~p~n", [Messages]), 2409 ok. 2410 2411 2412sleep(T) -> receive after T * 1000 -> ok end. 2413 2414start_node(_DCfg, Name, Param, this) -> 2415 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), 2416 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); 2417start_node(DCfg, Name, Param, "this") -> 2418 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg, 2419 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); 2420start_node(DCfg, Name, Param, Rel) when is_atom(Rel) -> 2421 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg, 2422 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]); 2423start_node(DCfg, Name, Param, Rel) when is_list(Rel) -> 2424 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg, 2425 test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]). 2426 2427start_node(DCfg, Name, Param) -> 2428 NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg, 2429 test_server:start_node(Name, slave, [{args, NewParam}]). 2430 2431start_node(DCfg, Name) -> 2432 start_node(DCfg, Name, ""). 2433 2434stop_node(Node) -> 2435 test_server:stop_node(Node). 2436 2437get_nodenames(N, T) -> 2438 get_nodenames(N, T, []). 2439 2440get_nodenames(0, _, Acc) -> 2441 Acc; 2442get_nodenames(N, T, Acc) -> 2443 U = erlang:unique_integer([positive]), 2444 get_nodenames(N-1, T, [list_to_atom(atom_to_list(T) 2445 ++ "-" 2446 ++ ?MODULE_STRING 2447 ++ "-" 2448 ++ integer_to_list(U)) | Acc]). 2449 2450get_numbered_nodenames(N, T) -> 2451 get_numbered_nodenames(N, T, []). 2452 2453get_numbered_nodenames(0, _, Acc) -> 2454 Acc; 2455get_numbered_nodenames(N, T, Acc) -> 2456 U = erlang:unique_integer([positive]), 2457 NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N) 2458 ++ "-" 2459 ++ ?MODULE_STRING 2460 ++ "-" 2461 ++ integer_to_list(U)) | Acc], 2462 get_numbered_nodenames(N-1, T, NL). 2463 2464wait_until(Fun) -> 2465 case Fun() of 2466 true -> 2467 ok; 2468 _ -> 2469 receive 2470 after 100 -> 2471 wait_until(Fun) 2472 end 2473 end. 2474 2475repeat(Fun, 0) when is_function(Fun) -> 2476 ok; 2477repeat(Fun, N) when is_function(Fun), is_integer(N), N > 0 -> 2478 Fun(), 2479 repeat(Fun, N-1). 2480 2481no_msgs(Wait) -> 2482 receive after Wait -> no_msgs() end. 2483 2484no_msgs() -> 2485 {messages, []} = process_info(self(), messages). 2486 2487block_emu(Ms) -> 2488 erts_debug:set_internal_state(available_internal_state, true), 2489 Res = erts_debug:set_internal_state(block, Ms), 2490 erts_debug:set_internal_state(available_internal_state, false), 2491 Res. 2492 2493 2494