1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: Lightweight test server 24%%---------------------------------------------------------------------- 25%% 26 27-module(megaco_test_lib). 28 29%% -compile(export_all). 30 31-export([ 32 log/4, 33 error/3, 34 35 sleep/1, 36 hours/1, minutes/1, seconds/1, 37 formated_timestamp/0, format_timestamp/1, 38 39 skip/3, 40 non_pc_tc_maybe_skip/4, 41 os_based_skip/1, 42 43 flush/0, 44 still_alive/1, 45 46 display_alloc_info/0, 47 display_system_info/1, display_system_info/2, display_system_info/3, 48 49 try_tc/6, 50 51 prepare_test_case/5, 52 53 proxy_start/1, proxy_start/2, 54 55 mk_nodes/1, 56 start_nodes/3, start_nodes/4, 57 start_node/3, start_node/4, 58 59 stop_nodes/3, 60 stop_node/3 61 62 ]). 63-export([init_per_suite/1, end_per_suite/1, 64 init_per_testcase/2, end_per_testcase/2]). 65 66-export([proxy_init/2]). 67 68-include("megaco_test_lib.hrl"). 69 70-record('REASON', {mod, line, desc}). 71 72 73%% ---------------------------------------------------------------- 74%% Time related function 75%% 76 77sleep(infinity) -> 78 receive 79 after infinity -> 80 ok 81 end; 82sleep(MSecs) -> 83 receive 84 after trunc(MSecs) -> 85 ok 86 end, 87 ok. 88 89 90hours(N) -> trunc(N * 1000 * 60 * 60). 91minutes(N) -> trunc(N * 1000 * 60). 92seconds(N) -> trunc(N * 1000). 93 94 95formated_timestamp() -> 96 format_timestamp(os:timestamp()). 97 98format_timestamp(TS) -> 99 megaco:format_timestamp(TS). 100 101 102%% ---------------------------------------------------------------- 103%% Conditional skip of testcases 104%% 105 106non_pc_tc_maybe_skip(Config, Condition, File, Line) 107 when is_list(Config) andalso is_function(Condition) -> 108 %% Check if we shall skip the skip 109 case os:getenv("TS_OS_BASED_SKIP") of 110 "false" -> 111 ok; 112 _ -> 113 case lists:keysearch(ts, 1, Config) of 114 {value, {ts, megaco}} -> 115 %% Always run the testcase if we are using our own 116 %% test-server... 117 ok; 118 _ -> 119 case (catch Condition()) of 120 true -> 121 skip(non_pc_testcase, File, Line); 122 _ -> 123 ok 124 end 125 end 126 end. 127 128 129%% The type and spec'ing is just to increase readability 130-type os_family() :: win32 | unix. 131-type os_name() :: atom(). 132-type os_version() :: string() | {non_neg_integer(), 133 non_neg_integer(), 134 non_neg_integer()}. 135-type os_skip_check() :: fun(() -> boolean()) | 136 fun((os_version()) -> boolean()). 137-type skippable() :: any | [os_family() | 138 {os_family(), os_name() | 139 [os_name() | {os_name(), 140 os_skip_check()}]}]. 141 142-spec os_based_skip(skippable()) -> boolean(). 143 144os_based_skip(any) -> 145 true; 146os_based_skip(Skippable) when is_list(Skippable) -> 147 os_base_skip(Skippable, os:type()); 148os_based_skip(_Crap) -> 149 false. 150 151os_base_skip(Skippable, {OsFam, OsName}) -> 152 os_base_skip(Skippable, OsFam, OsName); 153os_base_skip(Skippable, OsFam) -> 154 os_base_skip(Skippable, OsFam, undefined). 155 156os_base_skip(Skippable, OsFam, OsName) -> 157 %% Check if the entire family is to be skipped 158 %% Example: [win32, unix] 159 case lists:member(OsFam, Skippable) of 160 true -> 161 true; 162 false -> 163 %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}] 164 case lists:keysearch(OsFam, 1, Skippable) of 165 {value, {OsFam, OsName}} -> 166 true; 167 {value, {OsFam, OsNames}} when is_list(OsNames) -> 168 %% OsNames is a list of: 169 %% [atom()|{atom(), function/0 | function/1}] 170 case lists:member(OsName, OsNames) of 171 true -> 172 true; 173 false -> 174 os_based_skip_check(OsName, OsNames) 175 end; 176 _ -> 177 false 178 end 179 end. 180 181 182 183%% Performs a check via a provided fun with arity 0 or 1. 184%% The argument is the result of os:version(). 185os_based_skip_check(OsName, OsNames) -> 186 case lists:keysearch(OsName, 1, OsNames) of 187 {value, {OsName, Check}} when is_function(Check, 0) -> 188 Check(); 189 {value, {OsName, Check}} when is_function(Check, 1) -> 190 Check(os:version()); 191 _ -> 192 false 193 end. 194 195 196 197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 198%% Evaluates a test case or test suite 199%% Returns a list of failing test cases: 200%% 201%% {Mod, Fun, ExpectedRes, ActualRes} 202%%---------------------------------------------------------------------- 203 204display_alloc_info() -> 205 io:format("Allocator memory information:~n", []), 206 AllocInfo = alloc_info(), 207 display_alloc_info(AllocInfo). 208 209display_alloc_info([]) -> 210 ok; 211display_alloc_info([{Alloc, Mem}|AllocInfo]) -> 212 io:format(" ~15w: ~10w~n", [Alloc, Mem]), 213 display_alloc_info(AllocInfo). 214 215alloc_info() -> 216 case erlang:system_info(allocator) of 217 {_Allocator, _Version, Features, _Settings} -> 218 alloc_info(Features); 219 _ -> 220 [] 221 end. 222 223alloc_info(Allocators) -> 224 Allocs = [temp_alloc, sl_alloc, std_alloc, ll_alloc, eheap_alloc, 225 ets_alloc, binary_alloc, driver_alloc], 226 alloc_info(Allocators, Allocs, []). 227 228alloc_info([], _, Acc) -> 229 lists:reverse(Acc); 230alloc_info([Allocator | Allocators], Allocs, Acc) -> 231 case lists:member(Allocator, Allocs) of 232 true -> 233 Instances0 = erlang:system_info({allocator, Allocator}), 234 Instances = 235 if 236 is_list(Instances0) -> 237 [Instance || Instance <- Instances0, 238 element(1, Instance) =:= instance]; 239 true -> 240 [] 241 end, 242 AllocatorMem = alloc_mem_info(Instances), 243 alloc_info(Allocators, Allocs, [{Allocator, AllocatorMem} | Acc]); 244 245 false -> 246 alloc_info(Allocators, Allocs, Acc) 247 end. 248 249alloc_mem_info(Instances) -> 250 alloc_mem_info(Instances, []). 251 252alloc_mem_info([], Acc) -> 253 lists:sum([Mem || {instance, _, Mem} <- Acc]); 254alloc_mem_info([{instance, N, Info}|Instances], Acc) -> 255 InstanceMemInfo = alloc_instance_mem_info(Info), 256 alloc_mem_info(Instances, [{instance, N, InstanceMemInfo} | Acc]). 257 258alloc_instance_mem_info(InstanceInfo) -> 259 MBCS = alloc_instance_mem_info(mbcs, InstanceInfo), 260 SBCS = alloc_instance_mem_info(sbcs, InstanceInfo), 261 MBCS + SBCS. 262 263alloc_instance_mem_info(Key, InstanceInfo) -> 264 case lists:keysearch(Key, 1, InstanceInfo) of 265 {value, {Key, Info}} -> 266 case lists:keysearch(blocks_size, 1, Info) of 267 {value, {blocks_size, Mem, _, _}} -> 268 Mem; 269 _ -> 270 0 271 end; 272 _ -> 273 0 274 end. 275 276 277display_system_info(WhenStr) -> 278 display_system_info(WhenStr, undefined, undefined). 279 280display_system_info(WhenStr, undefined, undefined) -> 281 display_system_info(WhenStr, ""); 282display_system_info(WhenStr, Mod, Func) -> 283 ModFuncStr = lists:flatten(io_lib:format(" ~w:~w", [Mod, Func])), 284 display_system_info(WhenStr, ModFuncStr). 285 286display_system_info(WhenStr, ModFuncStr) -> 287 Fun = fun(F) -> case (catch F()) of 288 {'EXIT', _} -> 289 undefined; 290 Res -> 291 Res 292 end 293 end, 294 ProcCount = Fun(fun() -> erlang:system_info(process_count) end), 295 ProcLimit = Fun(fun() -> erlang:system_info(process_limit) end), 296 ProcMemAlloc = Fun(fun() -> erlang:memory(processes) end), 297 ProcMemUsed = Fun(fun() -> erlang:memory(processes_used) end), 298 ProcMemBin = Fun(fun() -> erlang:memory(binary) end), 299 ProcMemTot = Fun(fun() -> erlang:memory(total) end), 300 %% error_logger:info_msg( 301 io:format("~n" 302 "~n*********************************************" 303 "~n" 304 "System info ~s~s => " 305 "~n Process count: ~w" 306 "~n Process limit: ~w" 307 "~n Process memory alloc: ~w" 308 "~n Process memory used: ~w" 309 "~n Memory for binaries: ~w" 310 "~n Memory total: ~w" 311 "~n" 312 "~n*********************************************" 313 "~n" 314 "~n", [WhenStr, ModFuncStr, 315 ProcCount, ProcLimit, ProcMemAlloc, ProcMemUsed, 316 ProcMemBin, ProcMemTot]), 317 ok. 318 319 320 321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 322%% Verify that the actual result of a test case matches the exected one 323%% Returns the actual result 324%% Stores the result in the process dictionary if mismatch 325 326error(Actual, Mod, Line) -> 327 global:send(megaco_global_logger, {failed, Mod, Line}), 328 log("<ERROR> Bad result: ~p~n", [Actual], Mod, Line), 329 Label = lists:concat([Mod, "(", Line, ") unexpected result"]), 330 megaco:report_event(60, Mod, Mod, Label, 331 [{line, Mod, Line}, {error, Actual}]), 332 case global:whereis_name(megaco_test_case_sup) of 333 undefined -> 334 ignore; 335 Pid -> 336 Fail = #'REASON'{mod = Mod, line = Line, desc = Actual}, 337 Pid ! {fail, self(), Fail} 338 end, 339 Actual. 340 341log(Format, Args, Mod, Line) -> 342 case global:whereis_name(megaco_global_logger) of 343 undefined -> 344 io:format(user, "~p~p(~p): " ++ Format, 345 [self(), Mod, Line] ++ Args); 346 Pid -> 347 io:format(Pid, "~p~p(~p): " ++ Format, 348 [self(), Mod, Line] ++ Args) 349 end. 350 351skip(Reason) -> 352 exit({skip, Reason}). 353 354skip(Actual, File, Line) -> 355 log("Skipping test case: ~p~n", [Actual], File, Line), 356 String = f("~p(~p): ~p~n", [File, Line, Actual]), 357 skip(String). 358 359fatal_skip(Actual, File, Line) -> 360 error(Actual, File, Line), 361 skip({fatal, Actual, File, Line}). 362 363 364 365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 366%% Flush the message queue and return its messages 367 368flush() -> 369 receive 370 Msg -> 371 [Msg | flush()] 372 after 1000 -> 373 [] 374 end. 375 376 377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 378%% Check if process is alive and kicking 379 380still_alive(Pid) -> 381 erlang:is_process_alive(Pid). 382 383 384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 385%% The proxy process 386 387proxy_start(ProxyId) -> 388 spawn_link(?MODULE, proxy_init, [ProxyId, self()]). 389 390proxy_start(Node, ProxyId) -> 391 spawn_link(Node, ?MODULE, proxy_init, [ProxyId, self()]). 392 393proxy_init(ProxyId, Controller) -> 394 process_flag(trap_exit, true), 395 IdStr = proxyid2string(ProxyId), 396 put(id, IdStr), 397 ?LOG("[~s] proxy started by ~p~n", [IdStr, Controller]), 398 proxy_loop(ProxyId, Controller). 399 400proxy_loop(OwnId, Controller) -> 401 receive 402 {'EXIT', Controller, Reason} -> 403 pprint("proxy_loop -> received exit from controller" 404 "~n Reason: ~p", [Reason]), 405 exit(Reason); 406 {stop, Controller, Reason} -> 407 p("proxy_loop -> received stop from controller" 408 "~n Reason: ~p" 409 "~n", [Reason]), 410 exit(Reason); 411 412 {apply, Fun} -> 413 pprint("proxy_loop -> received apply request"), 414 Res = Fun(), 415 pprint("proxy_loop -> apply result: " 416 "~n ~p", [Res]), 417 Controller ! {res, OwnId, Res}, 418 proxy_loop(OwnId, Controller); 419 OtherMsg -> 420 pprint("proxy_loop -> received unknown message: " 421 "~n ~p", [OtherMsg]), 422 Controller ! {msg, OwnId, OtherMsg}, 423 proxy_loop(OwnId, Controller) 424 end. 425 426proxyid2string(Id) when is_list(Id) -> 427 Id; 428proxyid2string(Id) when is_atom(Id) -> 429 atom_to_list(Id); 430proxyid2string(Id) -> 431 f("~p", [Id]). 432 433pprint(F) -> 434 pprint(F, []). 435 436pprint(F, A) -> 437 io:format("[~s] ~p ~s " ++ F ++ "~n", 438 [get(id), self(), formated_timestamp() | A]). 439 440 441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 442%% Test server callbacks 443 444init_per_suite(Config) -> 445 446 %% We have some crap machines that causes random test case failures 447 %% for no obvious reason. So, attempt to identify those without actually 448 %% checking for the host name... 449 %% We have two "machines" we are checking for. Both are old installations 450 %% running on really slow VMs (the host machines are old and tired). 451 LinuxVersionVerify = 452 fun(V) when (V > {3,6,11}) -> 453 false; % OK - No skip 454 (V) when (V =:= {3,6,11}) -> 455 case string:trim(os:cmd("cat /etc/issue")) of 456 "Fedora release 16 " ++ _ -> % Stone age Fedora => Skip 457 true; 458 _ -> 459 false 460 end; 461 (V) when (V > {2,6,24}) -> 462 false; % OK - No skip 463 (_) -> 464 %% We are specifically checking for 465 %% a *really* old gento... 466 case string:find(string:strip(os:cmd("uname -a")), "gentoo") of 467 nomatch -> 468 false; 469 _ -> % Stone age gentoo => Skip 470 true 471 end 472 end, 473 DarwinVersionVerify = 474 fun(V) when (V > {9, 8, 0}) -> 475 %% This version is OK: No Skip 476 false; 477 (_V) -> 478 %% This version is *not* ok: Skip 479 true 480 end, 481 %% We are "looking" for a specific machine (a VM) 482 %% which are *old and crappy" and slow, because it 483 %% causes a bunch of test cases to fail randomly. 484 %% But we don not want to test for the host name... 485 WinVersionVerify = 486 fun(V) when (V =:= {6,2,9200}) -> 487 try erlang:system_info(schedulers) of 488 2 -> 489 true; 490 _ -> 491 false 492 catch 493 _:_:_ -> 494 true 495 end; 496 (_) -> 497 false 498 end, 499 COND = [ 500 {unix, [{linux, LinuxVersionVerify}, 501 {darwin, DarwinVersionVerify}]}, 502 {win32, [{nt, WinVersionVerify}]} 503 ], 504 case os_based_skip(COND) of 505 true -> 506 {skip, "Unstable host and/or os (or combo thererof)"}; 507 false -> 508 Factor = analyze_and_print_host_info(), 509 maybe_start_global_sys_monitor(Config), 510 [{megaco_factor, Factor} | Config] 511 end. 512 513%% We start the global system monitor unless explicitly disabled 514maybe_start_global_sys_monitor(Config) -> 515 case lists:keysearch(sysmon, 1, Config) of 516 {value, {sysmon, false}} -> 517 ok; 518 _ -> 519 megaco_test_global_sys_monitor:start() 520 end. 521 522end_per_suite(Config) when is_list(Config) -> 523 524 case lists:keysearch(sysmon, 1, Config) of 525 {value, {sysmon, false}} -> 526 ok; 527 _ -> 528 megaco_test_global_sys_monitor:stop() 529 end, 530 531 Config. 532 533 534init_per_testcase(_Case, Config) -> 535 Pid = group_leader(), 536 Name = megaco_global_logger, 537 case global:whereis_name(Name) of 538 undefined -> 539 global:register_name(megaco_global_logger, Pid); 540 Pid -> 541 io:format("~w:init_per_testcase -> " 542 "already registered to ~p~n", [?MODULE, Pid]), 543 ok; 544 OtherPid when is_pid(OtherPid) -> 545 io:format("~w:init_per_testcase -> " 546 "already registered to other ~p (~p)~n", 547 [?MODULE, OtherPid, Pid]), 548 exit({already_registered, {megaco_global_logger, OtherPid, Pid}}) 549 end, 550 set_kill_timer(Config). 551 552end_per_testcase(_Case, Config) -> 553 Name = megaco_global_logger, 554 case global:whereis_name(Name) of 555 undefined -> 556 io:format("~w:end_per_testcase -> already un-registered~n", 557 [?MODULE]), 558 ok; 559 Pid when is_pid(Pid) -> 560 global:unregister_name(megaco_global_logger), 561 ok 562 end, 563 reset_kill_timer(Config). 564 565 566%% This function prints various host info, which might be usefull 567%% when analyzing the test suite (results). 568%% It also returns a "factor" that can be used when deciding 569%% the load for some test cases. Such as run time or number of 570%% iteraions. This only works for some OSes. 571%% 572%% We make some calculations on Linux, OpenBSD and FreeBSD. 573%% On SunOS we always set the factor to 2 (just to be on the safe side) 574%% On all other os:es (mostly windows) we check the number of schedulers, 575%% but at least the factor will be 2. 576analyze_and_print_host_info() -> 577 {OsFam, OsName} = os:type(), 578 Version = 579 case os:version() of 580 {Maj, Min, Rel} -> 581 f("~w.~w.~w", [Maj, Min, Rel]); 582 VStr -> 583 VStr 584 end, 585 case {OsFam, OsName} of 586 {unix, linux} -> 587 analyze_and_print_linux_host_info(Version); 588 {unix, openbsd} -> 589 analyze_and_print_openbsd_host_info(Version); 590 {unix, freebsd} -> 591 analyze_and_print_freebsd_host_info(Version); 592 {unix, sunos} -> 593 analyze_and_print_solaris_host_info(Version); 594 {win32, nt} -> 595 analyze_and_print_win_host_info(Version); 596 _ -> 597 io:format("OS Family: ~p" 598 "~n OS Type: ~p" 599 "~n Version: ~p" 600 "~n Num Schedulers: ~s" 601 "~n", [OsFam, OsName, Version, str_num_schedulers()]), 602 try erlang:system_info(schedulers) of 603 1 -> 604 10; 605 2 -> 606 5; 607 N when (N =< 6) -> 608 2; 609 _ -> 610 1 611 catch 612 _:_:_ -> 613 10 614 end 615 end. 616 617str_num_schedulers() -> 618 try erlang:system_info(schedulers) of 619 N -> f("~w", [N]) 620 catch 621 _:_:_ -> "-" 622 end. 623 624 625analyze_and_print_linux_host_info(Version) -> 626 case file:read_file_info("/etc/issue") of 627 {ok, _} -> 628 io:format("Linux: ~s" 629 "~n ~s" 630 "~n", 631 [Version, string:trim(os:cmd("cat /etc/issue"))]); 632 _ -> 633 io:format("Linux: ~s" 634 "~n", [Version]) 635 end, 636 Factor = 637 case (catch linux_which_cpuinfo()) of 638 {ok, {CPU, BogoMIPS}} -> 639 io:format("CPU: " 640 "~n Model: ~s" 641 "~n BogoMIPS: ~s" 642 "~n Num Schedulers: ~s" 643 "~n", [CPU, BogoMIPS, str_num_schedulers()]), 644 %% We first assume its a float, and if not try integer 645 try list_to_float(string:trim(BogoMIPS)) of 646 F when F > 4000 -> 647 1; 648 F when F > 1000 -> 649 2; 650 F when F > 500 -> 651 3; 652 _ -> 653 5 654 catch 655 _:_:_ -> 656 try list_to_integer(string:trim(BogoMIPS)) of 657 I when I > 4000 -> 658 1; 659 I when I > 1000 -> 660 2; 661 I when I > 500 -> 662 3; 663 _ -> 664 5 665 catch 666 _:_:_ -> 667 5 % Be a "bit" conservative... 668 end 669 end; 670 {ok, CPU} -> 671 io:format("CPU: " 672 "~n Model: ~s" 673 "~n Num Schedulers: ~s" 674 "~n", [CPU, str_num_schedulers()]), 675 2; % Be a "bit" conservative... 676 _ -> 677 5 % Be a "bit" (more) conservative... 678 end, 679 %% Check if we need to adjust the factor because of the memory 680 try linux_which_meminfo() of 681 AddFactor -> 682 Factor + AddFactor 683 catch 684 _:_:_ -> 685 Factor 686 end. 687 688linux_which_cpuinfo() -> 689 %% Check for x86 (Intel or AMD) 690 CPU = 691 try [string:trim(S) || S <- string:tokens(os:cmd("grep \"model name\" /proc/cpuinfo"), [$:,$\n])] of 692 ["model name", ModelName | _] -> 693 ModelName; 694 _ -> 695 %% ARM (at least some distros...) 696 try [string:trim(S) || S <- string:tokens(os:cmd("grep \"Processor\" /proc/cpuinfo"), [$:,$\n])] of 697 ["Processor", Proc | _] -> 698 Proc; 699 _ -> 700 %% Ok, we give up 701 throw(noinfo) 702 catch 703 _:_:_ -> 704 throw(noinfo) 705 end 706 catch 707 _:_:_ -> 708 throw(noinfo) 709 end, 710 try [string:trim(S) || S <- string:tokens(os:cmd("grep -i \"bogomips\" /proc/cpuinfo"), [$:,$\n])] of 711 [_, BMips | _] -> 712 {ok, {CPU, BMips}}; 713 _ -> 714 {ok, CPU} 715 catch 716 _:_:_ -> 717 {ok, CPU} 718 end. 719 720%% We *add* the value this return to the Factor. 721linux_which_meminfo() -> 722 try [string:trim(S) || S <- string:tokens(os:cmd("grep MemTotal /proc/meminfo"), [$:])] of 723 [_, MemTotal] -> 724 io:format("Memory:" 725 "~n ~s" 726 "~n", [MemTotal]), 727 case string:tokens(MemTotal, [$ ]) of 728 [MemSzStr, MemUnit] -> 729 MemSz2 = list_to_integer(MemSzStr), 730 MemSz3 = 731 case string:to_lower(MemUnit) of 732 "kb" -> 733 MemSz2; 734 "mb" -> 735 MemSz2*1024; 736 "gb" -> 737 MemSz2*1024*1024; 738 _ -> 739 throw(noinfo) 740 end, 741 if 742 (MemSz3 >= 8388608) -> 743 0; 744 (MemSz3 >= 4194304) -> 745 1; 746 (MemSz3 >= 2097152) -> 747 3; 748 true -> 749 5 750 end; 751 _X -> 752 0 753 end; 754 _ -> 755 0 756 catch 757 _:_:_ -> 758 0 759 end. 760 761 762%% Just to be clear: This is ***not*** scientific... 763analyze_and_print_openbsd_host_info(Version) -> 764 io:format("OpenBSD:" 765 "~n Version: ~p" 766 "~n", [Version]), 767 Extract = 768 fun(Key) -> 769 string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=]) 770 end, 771 try 772 begin 773 CPU = 774 case Extract("hw.model") of 775 ["hw.model", Model] -> 776 string:trim(Model); 777 _ -> 778 "-" 779 end, 780 CPUSpeed = 781 case Extract("hw.cpuspeed") of 782 ["hw.cpuspeed", Speed] -> 783 list_to_integer(Speed); 784 _ -> 785 -1 786 end, 787 NCPU = 788 case Extract("hw.ncpufound") of 789 ["hw.ncpufound", N] -> 790 list_to_integer(N); 791 _ -> 792 -1 793 end, 794 Memory = 795 case Extract("hw.physmem") of 796 ["hw.physmem", PhysMem] -> 797 list_to_integer(PhysMem) div 1024; 798 _ -> 799 -1 800 end, 801 io:format("CPU:" 802 "~n Model: ~s" 803 "~n Speed: ~w" 804 "~n N: ~w" 805 "~nMemory:" 806 "~n ~w KB" 807 "~n", [CPU, CPUSpeed, NCPU, Memory]), 808 CPUFactor = 809 if 810 (CPUSpeed =:= -1) -> 811 1; 812 (CPUSpeed >= 2000) -> 813 if 814 (NCPU >= 4) -> 815 1; 816 (NCPU >= 2) -> 817 2; 818 true -> 819 3 820 end; 821 true -> 822 if 823 (NCPU >= 4) -> 824 2; 825 (NCPU >= 2) -> 826 3; 827 true -> 828 4 829 end 830 end, 831 MemAddFactor = 832 if 833 (Memory =:= -1) -> 834 0; 835 (Memory >= 8388608) -> 836 0; 837 (Memory >= 4194304) -> 838 1; 839 (Memory >= 2097152) -> 840 2; 841 true -> 842 3 843 end, 844 CPUFactor + MemAddFactor 845 end 846 catch 847 _:_:_ -> 848 1 849 end. 850 851 852analyze_and_print_freebsd_host_info(Version) -> 853 io:format("FreeBSD:" 854 "~n Version: ~p" 855 "~n", [Version]), 856 %% This test require that the program 'sysctl' is in the path. 857 %% First test with 'which sysctl', if that does not work 858 %% try with 'which /sbin/sysctl'. If that does not work either, 859 %% we skip the test... 860 try 861 begin 862 SysCtl = 863 case string:trim(os:cmd("which sysctl")) of 864 [] -> 865 case string:trim(os:cmd("which /sbin/sysctl")) of 866 [] -> 867 throw(sysctl); 868 SC2 -> 869 SC2 870 end; 871 SC1 -> 872 SC1 873 end, 874 Extract = 875 fun(Key) -> 876 string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)), 877 [$:]) 878 end, 879 CPU = analyze_freebsd_cpu(Extract), 880 CPUSpeed = analyze_freebsd_cpu_speed(Extract), 881 NCPU = analyze_freebsd_ncpu(Extract), 882 Memory = analyze_freebsd_memory(Extract), 883 io:format("CPU:" 884 "~n Model: ~s" 885 "~n Speed: ~w" 886 "~n N: ~w" 887 "~n Num Schedulers: ~w" 888 "~nMemory:" 889 "~n ~w KB" 890 "~n", 891 [CPU, CPUSpeed, NCPU, 892 erlang:system_info(schedulers), Memory]), 893 CPUFactor = 894 if 895 (CPUSpeed =:= -1) -> 896 1; 897 (CPUSpeed >= 2000) -> 898 if 899 (NCPU >= 4) -> 900 1; 901 (NCPU >= 2) -> 902 2; 903 true -> 904 3 905 end; 906 true -> 907 if 908 (NCPU =:= -1) -> 909 1; 910 (NCPU >= 4) -> 911 2; 912 (NCPU >= 2) -> 913 3; 914 true -> 915 4 916 end 917 end, 918 MemAddFactor = 919 if 920 (Memory =:= -1) -> 921 0; 922 (Memory >= 8388608) -> 923 0; 924 (Memory >= 4194304) -> 925 1; 926 (Memory >= 2097152) -> 927 2; 928 true -> 929 3 930 end, 931 CPUFactor + MemAddFactor 932 end 933 catch 934 _:_:_ -> 935 io:format("CPU:" 936 "~n Num Schedulers: ~w" 937 "~n", [erlang:system_info(schedulers)]), 938 case erlang:system_info(schedulers) of 939 1 -> 940 10; 941 2 -> 942 5; 943 _ -> 944 2 945 end 946 end. 947 948analyze_freebsd_cpu(Extract) -> 949 analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-"). 950 951analyze_freebsd_cpu_speed(Extract) -> 952 analyze_freebsd_item(Extract, 953 "hw.clockrate", 954 fun(X) -> list_to_integer(X) end, 955 -1). 956 957analyze_freebsd_ncpu(Extract) -> 958 analyze_freebsd_item(Extract, 959 "hw.ncpu", 960 fun(X) -> list_to_integer(X) end, 961 -1). 962 963analyze_freebsd_memory(Extract) -> 964 analyze_freebsd_item(Extract, 965 "hw.physmem", 966 fun(X) -> list_to_integer(X) div 1024 end, 967 -1). 968 969analyze_freebsd_item(Extract, Key, Process, Default) -> 970 try 971 begin 972 case Extract(Key) of 973 [Key, Model] -> 974 Process(string:trim(Model)); 975 _ -> 976 Default 977 end 978 end 979 catch 980 _:_:_ -> 981 Default 982 end. 983 984 985analyze_and_print_solaris_host_info(Version) -> 986 Release = 987 case file:read_file_info("/etc/release") of 988 {ok, _} -> 989 case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of 990 [Rel | _] -> 991 Rel; 992 _ -> 993 "-" 994 end; 995 _ -> 996 "-" 997 end, 998 %% Display the firmware device tree root properties (prtconf -b) 999 Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) || 1000 Prop <- [string:tokens(S, [$:]) || 1001 S <- string:tokens(os:cmd("prtconf -b"), [$\n])]], 1002 BannerName = case lists:keysearch("banner-name", 1, Props) of 1003 {value, {_, BN}} -> 1004 string:trim(BN); 1005 _ -> 1006 "-" 1007 end, 1008 InstructionSet = 1009 case string:trim(os:cmd("isainfo -k")) of 1010 "Pseudo-terminal will not" ++ _ -> 1011 "-"; 1012 IS -> 1013 IS 1014 end, 1015 PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1], 1016 SysConf = 1017 case lists:keysearch("System Configuration", 1, PtrConf) of 1018 {value, {_, SC}} -> 1019 SC; 1020 _ -> 1021 "-" 1022 end, 1023 NumPhysProc = 1024 begin 1025 NPPStr = string:trim(os:cmd("psrinfo -p")), 1026 try list_to_integer(NPPStr) of 1027 _ -> 1028 NPPStr 1029 catch 1030 _:_:_ -> 1031 "-" 1032 end 1033 end, 1034 NumProc = try integer_to_list(length(string:tokens(os:cmd("psrinfo"), [$\n]))) of 1035 NPStr -> 1036 NPStr 1037 catch 1038 _:_:_ -> 1039 "-" 1040 end, 1041 MemSz = 1042 case lists:keysearch("Memory size", 1, PtrConf) of 1043 {value, {_, MS}} -> 1044 MS; 1045 _ -> 1046 "-" 1047 end, 1048 io:format("Solaris: ~s" 1049 "~n Release: ~s" 1050 "~n Banner Name: ~s" 1051 "~n Instruction Set: ~s" 1052 "~n CPUs: ~s (~s)" 1053 "~n System Config: ~s" 1054 "~n Memory Size: ~s" 1055 "~n Num Schedulers: ~s" 1056 "~n~n", [Version, Release, BannerName, InstructionSet, 1057 NumPhysProc, NumProc, 1058 SysConf, MemSz, 1059 str_num_schedulers()]), 1060 MemFactor = 1061 try string:tokens(MemSz, [$ ]) of 1062 [SzStr, "Mega" ++ _] -> 1063 try list_to_integer(SzStr) of 1064 Sz when Sz > 8192 -> 1065 0; 1066 Sz when Sz > 4096 -> 1067 1; 1068 Sz when Sz > 2048 -> 1069 2; 1070 _ -> 1071 5 1072 catch 1073 _:_:_ -> 1074 10 1075 end; 1076 [SzStr, "Giga" ++ _] -> 1077 try list_to_integer(SzStr) of 1078 Sz when Sz > 8 -> 1079 0; 1080 Sz when Sz > 4 -> 1081 1; 1082 Sz when Sz > 2 -> 1083 2; 1084 _ -> 1085 5 1086 catch 1087 _:_:_ -> 1088 10 1089 end; 1090 _ -> 1091 10 1092 catch 1093 _:_:_ -> 1094 10 1095 end, 1096 try erlang:system_info(schedulers) of 1097 1 -> 1098 10; 1099 2 -> 1100 5; 1101 N when (N =< 6) -> 1102 2; 1103 _ -> 1104 1 1105 catch 1106 _:_:_ -> 1107 10 1108 end + MemFactor. 1109 1110 1111analyze_and_print_win_host_info(Version) -> 1112 SysInfo = which_win_system_info(), 1113 OsName = win_sys_info_lookup(os_name, SysInfo), 1114 OsVersion = win_sys_info_lookup(os_version, SysInfo), 1115 SysMan = win_sys_info_lookup(system_manufacturer, SysInfo), 1116 NumProcs = win_sys_info_lookup(num_processors, SysInfo), 1117 TotPhysMem = win_sys_info_lookup(total_phys_memory, SysInfo), 1118 io:format("Windows: ~s" 1119 "~n OS Version: ~s (~p)" 1120 "~n System Manufacturer: ~s" 1121 "~n Number of Processor(s): ~s" 1122 "~n Total Physical Memory: ~s" 1123 "~n", [OsName, OsVersion, Version, SysMan, NumProcs, TotPhysMem]), 1124 MemFactor = 1125 try 1126 begin 1127 [MStr, MUnit|_] = 1128 string:tokens(lists:delete($,, TotPhysMem), [$\ ]), 1129 case string:to_lower(MUnit) of 1130 "gb" -> 1131 try list_to_integer(MStr) of 1132 M when M > 8 -> 1133 0; 1134 M when M > 4 -> 1135 1; 1136 M when M > 2 -> 1137 2; 1138 _ -> 1139 5 1140 catch 1141 _:_:_ -> 1142 10 1143 end; 1144 "mb" -> 1145 try list_to_integer(MStr) of 1146 M when M > 8192 -> 1147 0; 1148 M when M > 4096 -> 1149 1; 1150 M when M > 2048 -> 1151 2; 1152 _ -> 1153 5 1154 catch 1155 _:_:_ -> 1156 10 1157 end; 1158 _ -> 1159 10 1160 end 1161 end 1162 catch 1163 _:_:_ -> 1164 10 1165 end, 1166 CPUFactor = 1167 case erlang:system_info(schedulers) of 1168 1 -> 1169 10; 1170 2 -> 1171 5; 1172 _ -> 1173 2 1174 end, 1175 CPUFactor + MemFactor. 1176 1177win_sys_info_lookup(Key, SysInfo) -> 1178 win_sys_info_lookup(Key, SysInfo, "-"). 1179 1180win_sys_info_lookup(Key, SysInfo, Def) -> 1181 case lists:keysearch(Key, 1, SysInfo) of 1182 {value, {Key, Value}} -> 1183 Value; 1184 false -> 1185 Def 1186 end. 1187 1188%% This function only extracts the prop we actually care about! 1189which_win_system_info() -> 1190 SysInfo = os:cmd("systeminfo"), 1191 try process_win_system_info(string:tokens(SysInfo, [$\r, $\n]), []) 1192 catch 1193 _:_:_ -> 1194 io:format("Failed process System info: " 1195 "~s~n", [SysInfo]), 1196 [] 1197 end. 1198 1199process_win_system_info([], Acc) -> 1200 Acc; 1201process_win_system_info([H|T], Acc) -> 1202 case string:tokens(H, [$:]) of 1203 [Key, Value] -> 1204 case string:to_lower(Key) of 1205 "os name" -> 1206 process_win_system_info(T, 1207 [{os_name, string:trim(Value)}|Acc]); 1208 "os version" -> 1209 process_win_system_info(T, 1210 [{os_version, string:trim(Value)}|Acc]); 1211 "system manufacturer" -> 1212 process_win_system_info(T, 1213 [{system_manufacturer, string:trim(Value)}|Acc]); 1214 "processor(s)" -> 1215 [NumProcStr|_] = string:tokens(Value, [$\ ]), 1216 T2 = lists:nthtail(list_to_integer(NumProcStr), T), 1217 process_win_system_info(T2, 1218 [{num_processors, NumProcStr}|Acc]); 1219 "total physical memory" -> 1220 process_win_system_info(T, 1221 [{total_phys_memory, string:trim(Value)}|Acc]); 1222 _ -> 1223 process_win_system_info(T, Acc) 1224 end; 1225 _ -> 1226 process_win_system_info(T, Acc) 1227 end. 1228 1229 1230 1231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1232%% Set kill timer 1233 1234set_kill_timer(Config) -> 1235 case init:get_argument(megaco_test_timeout) of 1236 {ok, _} -> 1237 Config; 1238 _ -> 1239 Time = 1240 case lookup_config(tc_timeout, Config) of 1241 [] -> 1242 timer:minutes(5); 1243 ConfigTime when is_integer(ConfigTime) -> 1244 ConfigTime 1245 end, 1246 Dog = test_server:timetrap(Time), 1247 [{kill_timer, Dog}|Config] 1248 1249 1250 end. 1251 1252reset_kill_timer(Config) -> 1253 case lists:keysearch(kill_timer, 1, Config) of 1254 {value, {kill_timer, Dog}} -> 1255 test_server:timetrap_cancel(Dog), 1256 lists:keydelete(kill_timer, 1, Config); 1257 _ -> 1258 Config 1259 end. 1260 1261 1262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1263 1264try_tc(TCName, Name, Verbosity, Pre, Case, Post) 1265 when is_function(Pre, 0) andalso 1266 is_function(Case, 1) andalso 1267 is_function(Post, 1) -> 1268 process_flag(trap_exit, true), 1269 put(verbosity, Verbosity), 1270 put(sname, Name), 1271 put(tc, TCName), 1272 p("try_tc -> starting: try pre"), 1273 try Pre() of 1274 State -> 1275 p("try_tc -> pre done: try test case"), 1276 try Case(State) of 1277 Res -> 1278 p("try_tc -> test case done: try post"), 1279 (catch Post(State)), 1280 p("try_tc -> done"), 1281 Res 1282 catch 1283 throw:{skip, _} = SKIP:_ -> 1284 p("try_tc -> test case (throw) skip: try post"), 1285 (catch Post(State)), 1286 p("try_tc -> test case (throw) skip: done"), 1287 SKIP; 1288 exit:{skip, _} = SKIP:_ -> 1289 p("try_tc -> test case (exit) skip: try post"), 1290 (catch Post(State)), 1291 p("try_tc -> test case (exit) skip: done"), 1292 SKIP; 1293 C:E:S -> 1294 p("try_tc -> test case failed: try post"), 1295 (catch Post(State)), 1296 case megaco_test_global_sys_monitor:events() of 1297 [] -> 1298 p("try_tc -> test case failed: done"), 1299 exit({case_catched, C, E, S}); 1300 SysEvs -> 1301 p("try_tc -> test case failed with system event(s): " 1302 "~n ~p", [SysEvs]), 1303 {skip, "TC failure with system events"} 1304 end 1305 end 1306 catch 1307 throw:{skip, _} = SKIP:_ -> 1308 p("try_tc -> pre (throw) skip"), 1309 SKIP; 1310 exit:{skip, _} = SKIP:_ -> 1311 p("try_tc -> pre (exit) skip"), 1312 SKIP; 1313 C:E:S -> 1314 case megaco_test_global_sys_monitor:events() of 1315 [] -> 1316 p("try_tc -> pre failed: done"), 1317 exit({pre_catched, C, E, S}); 1318 SysEvs -> 1319 p("try_tc -> pre failed with system event(s): " 1320 "~n ~p", [SysEvs]), 1321 {skip, "TC pre failure with system events"} 1322 end 1323 end. 1324 1325 1326 1327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1328 1329prepare_test_case(Actions, N, Config, File, Line) -> 1330 OrigNodes = lookup_config(nodes, Config), 1331 TestNodes = lookup_config(nodenames, Config), %% For testserver 1332 This = node(), 1333 SomeNodes = OrigNodes ++ (TestNodes -- OrigNodes), 1334 AllNodes = [This | (SomeNodes -- [This])], 1335 Nodes = pick_n_nodes(N, AllNodes, File, Line), 1336 start_nodes(Nodes, File, Line), 1337 do_prepare_test_case(Actions, Nodes, Config, File, Line). 1338 1339do_prepare_test_case([init | Actions], Nodes, Config, File, Line) -> 1340 process_flag(trap_exit, true), 1341 megaco_test_lib:flush(), 1342 do_prepare_test_case(Actions, Nodes, Config, File, Line); 1343do_prepare_test_case([{stop_app, App} | Actions], Nodes, Config, File, Line) -> 1344 _Res = rpc:multicall(Nodes, application, stop, [App]), 1345 do_prepare_test_case(Actions, Nodes, Config, File, Line); 1346do_prepare_test_case([], Nodes, _Config, _File, _Line) -> 1347 Nodes. 1348 1349pick_n_nodes(all, AllNodes, _File, _Line) -> 1350 AllNodes; 1351pick_n_nodes(N, AllNodes, _File, _Line) 1352 when is_integer(N) andalso (length(AllNodes) >= N) -> 1353 AllNodes -- lists:nthtail(N, AllNodes); 1354pick_n_nodes(N, AllNodes, File, Line) -> 1355 fatal_skip({too_few_nodes, N, AllNodes}, File, Line). 1356 1357lookup_config(Key,Config) -> 1358 case lists:keysearch(Key, 1, Config) of 1359 {value,{Key,Val}} -> 1360 Val; 1361 _ -> 1362 [] 1363 end. 1364 1365mk_nodes(N) when (N > 0) -> 1366 mk_nodes(N, []). 1367 1368mk_nodes(0, Nodes) -> 1369 Nodes; 1370mk_nodes(N, []) -> 1371 mk_nodes(N - 1, [node()]); 1372mk_nodes(N, Nodes) when N > 0 -> 1373 Head = hd(Nodes), 1374 [Name, Host] = node_to_name_and_host(Head), 1375 Nodes ++ [mk_node(I, Name, Host) || I <- lists:seq(1, N)]. 1376 1377mk_node(N, Name, Host) -> 1378 list_to_atom(lists:concat([Name ++ integer_to_list(N) ++ "@" ++ Host])). 1379 1380%% Returns [Name, Host] 1381node_to_name_and_host(Node) -> 1382 string:tokens(atom_to_list(Node), [$@]). 1383 1384 1385start_nodes(Nodes, File, Line) when is_list(Nodes) -> 1386 start_nodes(Nodes, false, File, Line). 1387 1388start_nodes(Nodes, Force, File, Line) 1389 when is_list(Nodes) andalso is_boolean(Force) -> 1390 start_nodes(Nodes, Force, File, Line, []). 1391 1392start_nodes([], _Force, _File, _Line, _Started) -> 1393 ok; 1394start_nodes([Node|Nodes], Force, File, Line, Started) -> 1395 try start_node(Node, Force, true, File, Line) of 1396 ok -> 1397 start_nodes(Nodes, Force, File, Line, [Node|Started]) 1398 catch 1399 exit:{skip, _} = SKIP:_ -> 1400 (catch stop_nodes(lists:reverse(Started), File, Line)), 1401 exit(SKIP); 1402 C:E:S -> 1403 (catch stop_nodes(lists:reverse(Started), File, Line)), 1404 erlang:raise(C, E, S) 1405 end. 1406 1407start_node(Node, File, Line) -> 1408 start_node(Node, false, false, File, Line). 1409 1410start_node(Node, Force, File, Line) 1411 when is_atom(Node) andalso is_boolean(Force) -> 1412 start_node(Node, Force, false, File, Line). 1413 1414start_node(Node, Force, Retry, File, Line) -> 1415 case net_adm:ping(Node) of 1416 %% Do not require a *new* node 1417 pong when (Force =:= false) -> 1418 p("node ~p already running", [Node]), 1419 ok; 1420 1421 %% Do require a *new* node, so kill this one and try again 1422 pong when ((Force =:= true) andalso (Retry =:= true)) -> 1423 e("node ~p already running - kill and retry", [Node]), 1424 case stop_node(Node) of 1425 ok -> 1426 start_node(Node, Force, false, File, Line); 1427 error -> 1428 e("node ~p already running - failed kill (no retry)", [Node]), 1429 fatal_skip({node_already_running, Node}, File, Line) 1430 end; 1431 1432 %% Do require a *new* node, but no retry so give up and fail 1433 pong when (Force =:= true) -> 1434 e("node ~p already running", [Node]), 1435 fatal_skip({node_already_running, Node}, File, Line); 1436 1437 % Not (yet) running 1438 pang -> 1439 [Name, Host] = node_to_name_and_host(Node), 1440 Pa = filename:dirname(code:which(?MODULE)), 1441 Args = " -pa " ++ Pa ++ 1442 " -s " ++ atom_to_list(megaco_test_sys_monitor) ++ " start" ++ 1443 " -s global sync", 1444 p("try start node ~p", [Node]), 1445 case slave:start_link(Host, Name, Args) of 1446 {ok, NewNode} when NewNode =:= Node -> 1447 p("node ~p started - now set path, cwd and sync", [Node]), 1448 Path = code:get_path(), 1449 {ok, Cwd} = file:get_cwd(), 1450 true = rpc:call(Node, code, set_path, [Path]), 1451 ok = rpc:call(Node, file, set_cwd, [Cwd]), 1452 true = rpc:call(Node, code, set_path, [Path]), 1453 {_, []} = rpc:multicall(global, sync, []), 1454 ok; 1455 Other -> 1456 e("failed starting node ~p: ~p", [Node, Other]), 1457 fatal_skip({cannot_start_node, Node, Other}, File, Line) 1458 end 1459 end. 1460 1461 1462stop_nodes(Nodes, File, Line) when is_list(Nodes) -> 1463 stop_nodes(Nodes, [], File, Line). 1464 1465stop_nodes([], [], _File, _Line) -> 1466 ok; 1467stop_nodes([], StillRunning, File, Line) -> 1468 e("Failed stopping nodes: " 1469 "~n ~p", [StillRunning]), 1470 fatal_skip({failed_stop_nodes, lists:reverse(StillRunning)}, File, Line); 1471stop_nodes([Node|Nodes], Acc, File, Line) -> 1472 case stop_node(Node) of 1473 ok -> 1474 stop_nodes(Nodes, Acc, File, Line); 1475 error -> 1476 stop_nodes(Nodes, [Node|Acc], File, Line) 1477 end. 1478 1479 1480stop_node(Node, File, Line) when is_atom(Node) -> 1481 p("try stop node ~p", [Node]), 1482 case stop_node(Node) of 1483 ok -> 1484 ok; 1485 error -> 1486 fatal_skip({failed_stop_node, Node}, File, Line) 1487 end. 1488 1489stop_node(Node) -> 1490 p("try stop node ~p", [Node]), 1491 erlang:monitor_node(Node, true), 1492 rpc:call(Node, erlang, halt, []), 1493 receive 1494 {nodedown, Node} -> 1495 ok 1496 after 10000 -> 1497 e("failed stop node ~p", [Node]), 1498 error 1499 end. 1500 1501 1502 1503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1504 1505f(F, A) -> 1506 lists:flatten(io_lib:format(F, A)). 1507 1508e(F, A) -> 1509 print("ERROR", F, A). 1510 1511p(F) -> 1512 p(F, []). 1513 1514p(F, A) -> 1515 print("INFO", F, A). 1516 1517print(Pre, F, A) -> 1518 io:format("*** [~s] [~s] ~p " ++ F ++ "~n", [?FTS(), Pre, self() | A]). 1519 1520