1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2002-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-module(snmp_test_lib). 22 23-include_lib("kernel/include/file.hrl"). 24 25 26-export([tc_try/2, tc_try/3, 27 tc_try/4, tc_try/5]). 28-export([proxy_call/3]). 29-export([hostname/0, hostname/1, localhost/0, localhost/1, os_type/0, sz/1, 30 display_suite_info/1]). 31-export([non_pc_tc_maybe_skip/4, 32 os_based_skip/1, 33 has_support_ipv6/0 34 ]). 35-export([init_per_suite/1, end_per_suite/1, 36 init_suite_top_dir/2, init_group_top_dir/2, init_testcase_top_dir/2, 37 fix_data_dir/1, 38 lookup/2, 39 replace_config/3, set_config/3, get_config/2, get_config/3]). 40-export([fail/3, skip/3]). 41-export([hours/1, minutes/1, seconds/1, sleep/1]). 42-export([flush_mqueue/0, mqueue/0, mqueue/1, trap_exit/0, trap_exit/1]). 43-export([ping/1, local_nodes/0, nodes_on/1]). 44-export([start_node/2, stop_node/1]). 45-export([is_app_running/1, 46 is_crypto_running/0, is_mnesia_running/0, is_snmp_running/0, 47 ensure_not_running/3]). 48-export([crypto_start/0, crypto_support/0]). 49-export([watchdog/3, watchdog_start/1, watchdog_start/2, watchdog_stop/1]). 50-export([del_dir/1]). 51-export([f/2, formated_timestamp/0]). 52-export([p/2, print1/2, print2/2, print/5]). 53-export([eprint/2, wprint/2, nprint/2, iprint/2]). 54-export([explicit_inet_backend/0, test_inet_backends/0]). 55-export([which_host_ip/2]). 56 57 58-define(SKIP(R), skip(R, ?MODULE, ?LINE)). 59 60 61%% ---------------------------------------------------------------------- 62%% Run test-case 63%% 64 65%% *** tc_try/2,3,4,5 *** 66%% Case: Basically the test case name 67%% TCCond: A fun that is evaluated before the actual test case 68%% The point of this is that it can performs checks to 69%% see if we shall run the test case at all. 70%% For instance, the test case may only work in specific 71%% conditions. 72%% Pre: A fun that is nominally part of the test case 73%% but is an initiation that must be "undone". This is 74%% done by the Post fun (regardless if the TC is successfull 75%% or not). Example: Starts a couple of nodes, 76%% TC: The test case fun 77%% Post: A fun that undo what was done by the Pre fun. 78%% Example: Stops the nodes created by the Pre function. 79tc_try(Case, TC) -> 80 tc_try(Case, fun() -> ok end, TC). 81 82tc_try(Case, TCCond, TC0) when is_function(TC0, 0) -> 83 Pre = fun() -> undefined end, 84 TC = fun(_) -> TC0() end, 85 Post = fun(_) -> ok end, 86 tc_try(Case, TCCond, Pre, TC, Post). 87 88tc_try(Case, Pre, TC, Post) 89 when is_atom(Case) andalso 90 is_function(Pre, 0) andalso 91 is_function(TC, 1) andalso 92 is_function(Post, 1) -> 93 TCCond = fun() -> ok end, 94 tc_try(Case, TCCond, Pre, TC, Post). 95 96tc_try(Case, TCCond, Pre, TC, Post) 97 when is_atom(Case) andalso 98 is_function(TCCond, 0) andalso 99 is_function(Pre, 0) andalso 100 is_function(TC, 1) andalso 101 is_function(Post, 1) -> 102 tc_begin(Case), 103 try TCCond() of 104 ok -> 105 tc_print("starting: try pre"), 106 try Pre() of 107 State -> 108 tc_print("pre done: try test case"), 109 try 110 begin 111 TC(State), 112 sleep(seconds(1)), 113 tc_print("test case done: try post"), 114 (catch Post(State)), 115 tc_end("ok") 116 end 117 catch 118 C:{skip, _} = SKIP when (C =:= throw) orelse 119 (C =:= exit) -> 120 tc_print("test case (~w) skip: try post", [C]), 121 (catch Post(State)), 122 tc_end( f("skipping(catched,~w,tc)", [C]) ), 123 SKIP; 124 C:E:S -> 125 %% We always check the system events 126 %% before we accept a failure. 127 %% We do *not* run the Post here because it might 128 %% generate sys events itself... 129 case snmp_test_global_sys_monitor:events() of 130 [] -> 131 tc_print("test case failed: try post"), 132 (catch Post(State)), 133 tc_end( f("failed(catched,~w,tc)", [C]) ), 134 erlang:raise(C, E, S); 135 SysEvs -> 136 tc_print("System Events received during tc: " 137 "~n ~p" 138 "~nwhen tc failed:" 139 "~n C: ~p" 140 "~n E: ~p" 141 "~n S: ~p", 142 [SysEvs, C, E, S]), 143 (catch Post(State)), 144 tc_end( f("skipping(catched-sysevs,~w,tc)", 145 [C]) ), 146 SKIP = {skip, "TC failure with system events"}, 147 SKIP 148 end 149 end 150 catch 151 C:{skip, _} = SKIP when (C =:= throw) orelse 152 (C =:= exit) -> 153 tc_end( f("skipping(catched,~w,tc-pre)", [C]) ), 154 SKIP; 155 C:E:S -> 156 %% We always check the system events 157 %% before we accept a failure 158 case snmp_test_global_sys_monitor:events() of 159 [] -> 160 tc_print("tc-pre failed: auto-skip" 161 "~n C: ~p" 162 "~n E: ~p" 163 "~n S: ~p", 164 [C, E, S]), 165 tc_end( f("auto-skip(catched,~w,tc-pre)", [C]) ), 166 SKIP = {skip, f("TC-Pre failure (~w)", [C])}, 167 SKIP; 168 SysEvs -> 169 tc_print("System Events received: " 170 "~n ~p" 171 "~nwhen tc-pre failed:" 172 "~n C: ~p" 173 "~n E: ~p" 174 "~n S: ~p", 175 [SysEvs, C, E, S], "", ""), 176 tc_end( f("skipping(catched-sysevs,~w,tc-pre)", [C]) ), 177 SKIP = {skip, "TC-Pre failure with system events"}, 178 SKIP 179 end 180 end; 181 {skip, _} = SKIP -> 182 tc_end("skipping(cond)"), 183 SKIP; 184 {error, Reason} -> 185 tc_end("failed(cond)"), 186 exit({tc_cond_failed, Reason}) 187 catch 188 C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> 189 tc_end( f("skipping(catched,~w,cond)", [C]) ), 190 SKIP; 191 C:E:S -> 192 %% We always check the system events before we accept a failure 193 case snmp_test_global_sys_monitor:events() of 194 [] -> 195 tc_end( f("failed(catched,~w,cond)", [C]) ), 196 erlang:raise(C, E, S); 197 SysEvs -> 198 tc_print("System Events received: " 199 "~n ~p", [SysEvs], "", ""), 200 tc_end( f("skipping(catched-sysevs,~w,cond)", [C]) ), 201 SKIP = {skip, "TC cond failure with system events"}, 202 SKIP 203 end 204 end. 205 206 207tc_set_name(N) when is_atom(N) -> 208 tc_set_name(atom_to_list(N)); 209tc_set_name(N) when is_list(N) -> 210 put(tc_name, N). 211 212tc_get_name() -> 213 get(tc_name). 214 215tc_begin(TC) -> 216 OldVal = process_flag(trap_exit, true), 217 put(old_trap_exit, OldVal), 218 tc_set_name(TC), 219 tc_print("begin ***", 220 "~n----------------------------------------------------~n", ""). 221 222tc_end(Result) when is_list(Result) -> 223 OldVal = erase(old_trap_exit), 224 process_flag(trap_exit, OldVal), 225 tc_print("done: ~s", [Result], 226 "", "----------------------------------------------------~n~n"), 227 ok. 228 229tc_print(F) -> 230 tc_print(F, [], "", ""). 231 232tc_print(F, A) -> 233 tc_print(F, A, "", ""). 234 235tc_print(F, Before, After) -> 236 tc_print(F, [], Before, After). 237 238tc_print(F, A, Before, After) -> 239 Name = tc_which_name(), 240 FStr = f("*** [~s][~s][~p] " ++ F ++ "~n", 241 [formated_timestamp(), Name, self() | A]), 242 io:format(user, Before ++ FStr ++ After, []), 243 io:format(standard_io, Before ++ FStr ++ After, []). 244 245tc_which_name() -> 246 case tc_get_name() of 247 undefined -> 248 case get(sname) of 249 undefined -> 250 ""; 251 SName when is_list(SName) -> 252 SName 253 end; 254 Name when is_list(Name) -> 255 Name 256 end. 257 258 259%% ---------------------------------------------------------------------- 260%% Misc functions 261%% 262 263explicit_inet_backend() -> 264 %% This is intentional! 265 %% This is a kernel flag, which if set disables 266 %% our own special handling of the inet_backend 267 %% in our test suites. 268 case application:get_all_env(kernel) of 269 Env when is_list(Env) -> 270 case lists:keysearch(inet_backend, 1, Env) of 271 {value, {inet_backend, _}} -> 272 true; 273 _ -> 274 false 275 end; 276 _ -> 277 false 278 end. 279 280test_inet_backends() -> 281 case init:get_argument(snmp) of 282 {ok, SnmpArgs} when is_list(SnmpArgs) -> 283 test_inet_backends(SnmpArgs, atom_to_list(?FUNCTION_NAME)); 284 error -> 285 false 286 end. 287 288test_inet_backends([], _) -> 289 false; 290test_inet_backends([[Key, Val] | _], Key) -> 291 case list_to_atom(string:to_lower(Val)) of 292 Bool when is_boolean(Bool) -> 293 Bool; 294 _ -> 295 false 296 end; 297test_inet_backends([_|Args], Key) -> 298 test_inet_backends(Args, Key). 299 300 301 302proxy_call(F, Timeout, Default) 303 when is_function(F, 0) andalso is_integer(Timeout) andalso (Timeout > 0) -> 304 {P, M} = erlang:spawn_monitor(fun() -> exit(F()) end), 305 receive 306 {'DOWN', M, process, P, Reply} -> 307 Reply 308 after Timeout -> 309 erlang:demonitor(M, [flush]), 310 exit(P, kill), 311 Default 312 end. 313 314 315hostname() -> 316 hostname(node()). 317 318hostname(Node) -> 319 case string:tokens(atom_to_list(Node), [$@]) of 320 [_, Host] -> 321 Host; 322 _ -> 323 [] 324 end. 325 326 327which_host_ip(Hostname, Family) -> 328 case snmp_misc:ip(Hostname, Family) of 329 {ok, {A, _, _, _}} = OK 330 when (A =/= 127) -> 331 OK; 332 {ok, {A, _, _, _, _, _, _, _}} = OK 333 when (A =/= 0) andalso 334 (A =/= 16#fe80) -> 335 OK; 336 {ok, _} -> 337 try localhost(Family) of 338 Addr -> 339 {ok, Addr} 340 catch 341 C:E:S -> 342 {error, {C, E, S}} 343 end; 344 {error, _} = ERROR -> 345 ERROR 346 end. 347 348 349localhost() -> 350 localhost(inet). 351 352localhost(Family) -> 353 case inet:getaddr(net_adm:localhost(), Family) of 354 {ok, {127, _, _, _}} when (Family =:= inet) -> 355 %% Ouch, we need to use something else 356 case inet:getifaddrs() of 357 {ok, IfList} -> 358 which_addr(Family, IfList); 359 {error, Reason1} -> 360 fail({getifaddrs, Reason1}, ?MODULE, ?LINE) 361 end; 362 {ok, {A1, _, _, _, _, _, _, _}} when (Family =:= inet6) andalso 363 ((A1 =:= 0) orelse 364 (A1 =:= 16#fe80)) -> 365 %% Ouch, we need to use something else 366 case inet:getifaddrs() of 367 {ok, IfList} -> 368 which_addr(Family, IfList); 369 {error, Reason1} -> 370 fail({getifaddrs, Reason1}, ?MODULE, ?LINE) 371 end; 372 {ok, Addr} -> 373 Addr; 374 {error, Reason2} -> 375 fail({getaddr, Reason2}, ?MODULE, ?LINE) 376 end. 377 378which_addr(_Family, []) -> 379 fail(no_valid_addr, ?MODULE, ?LINE); 380which_addr(Family, [{"lo", _} | IfList]) -> 381 which_addr(Family, IfList); 382which_addr(Family, [{"tun" ++ _, _} | IfList]) -> 383 which_addr(Family, IfList); 384which_addr(Family, [{"docker" ++ _, _} | IfList]) -> 385 which_addr(Family, IfList); 386which_addr(Family, [{"br-" ++ _, _} | IfList]) -> 387 which_addr(Family, IfList); 388which_addr(Family, [{_Name, IfOpts} | IfList]) -> 389 case which_addr2(Family, IfOpts) of 390 {ok, Addr} -> 391 Addr; 392 {error, _} -> 393 which_addr(Family, IfList) 394 end. 395 396which_addr2(_Family, []) -> 397 {error, not_found}; 398which_addr2(Family, [{addr, Addr}|_]) 399 when (Family =:= inet) andalso (size(Addr) =:= 4) -> 400 {ok, Addr}; 401which_addr2(Family, [{addr, Addr}|_]) 402 when (Family =:= inet6) andalso (size(Addr) =:= 8) -> 403 {ok, Addr}; 404which_addr2(Family, [_|IfOpts]) -> 405 which_addr2(Family, IfOpts). 406 407 408sz(L) when is_list(L) -> 409 length(L); 410sz(B) when is_binary(B) -> 411 size(B); 412sz(O) -> 413 {unknown_size,O}. 414 415 416os_type() -> 417 case (catch test_server:os_type()) of 418 {'EXIT', _} -> 419 %% Pre-R10 test server does not have this function 420 os:type(); 421 OsType -> 422 OsType 423 end. 424 425display_suite_info(SUITE) when is_atom(SUITE) -> 426 (catch do_display_suite_info(SUITE)). 427 428do_display_suite_info(SUITE) -> 429 MI = SUITE:module_info(), 430 case (catch display_version(MI)) of 431 ok -> 432 ok; 433 _ -> 434 case (catch display_app_version(MI)) of 435 ok -> 436 ok; 437 _ -> 438 io:format("No version info available for test suite ~p~n", 439 [?MODULE]) 440 end 441 end. 442 443display_version(MI) -> 444 {value, {compile, CI}} = lists:keysearch(compile, 1, MI), 445 {value, {options, CO}} = lists:keysearch(options, 1, CI), 446 Version = version_of_compiler_options(CO), 447 io:format("~p version info: " 448 "~n Version: ~p" 449 "~n", [?MODULE, Version]), 450 ok. 451 452version_of_compiler_options([{d, version, Version} | _]) -> 453 Version; 454version_of_compiler_options([_ | T]) -> 455 version_of_compiler_options(T). 456 457display_app_version(MI) -> 458 {value, {attributes, Attrs}} = lists:keysearch(attributes, 1, MI), 459 {value, {vsn, Vsn}} = lists:keysearch(vsn, 1, Attrs), 460 {value, {app_vsn, AppVsn}} = lists:keysearch(app_vsn, 1, Attrs), 461 io:format("~p version info: " 462 "~n VSN: ~p" 463 "~n App vsn: ~s" 464 "~n", [?MODULE, Vsn, AppVsn]), 465 ok. 466 467 468%% ---------------------------------------------------------------- 469%% Conditional skip of testcases 470%% 471 472non_pc_tc_maybe_skip(Config, Condition, File, Line) 473 when is_list(Config) andalso is_function(Condition) -> 474 %% Check if we shall skip the skip 475 case os:getenv("TS_OS_BASED_SKIP") of 476 "false" -> 477 ok; 478 _ -> 479 case lists:keysearch(ts, 1, Config) of 480 {value, {ts, snmp}} -> 481 %% Always run the testcase if we are using our own 482 %% test-server... 483 ok; 484 _ -> 485 try Condition() of 486 true -> 487 skip(non_pc_testcase, File, Line); 488 false -> 489 ok 490 catch 491 C:E:S -> 492 skip({condition, C, E, S}, File, Line) 493 end 494 end 495 end. 496 497 498%% The type and spec'ing is just to increase readability 499-type os_family() :: win32 | unix. 500-type os_name() :: atom(). 501-type os_version() :: string() | {non_neg_integer(), 502 non_neg_integer(), 503 non_neg_integer()}. 504-type os_skip_check() :: fun(() -> boolean()) | 505 fun((os_version()) -> boolean()). 506-type skippable() :: any | [os_family() | 507 {os_family(), os_name() | 508 [os_name() | {os_name(), 509 os_skip_check()}]}]. 510 511-spec os_based_skip(skippable()) -> boolean(). 512 513os_based_skip(any) -> 514 true; 515os_based_skip(Skippable) when is_list(Skippable) -> 516 os_base_skip(Skippable, os:type()); 517os_based_skip(_Crap) -> 518 false. 519 520os_base_skip(Skippable, {OsFam, OsName}) -> 521 os_base_skip(Skippable, OsFam, OsName); 522os_base_skip(Skippable, OsFam) -> 523 os_base_skip(Skippable, OsFam, undefined). 524 525os_base_skip(Skippable, OsFam, OsName) -> 526 %% Check if the entire family is to be skipped 527 %% Example: [win32, unix] 528 case lists:member(OsFam, Skippable) of 529 true -> 530 true; 531 false -> 532 %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}] 533 case lists:keysearch(OsFam, 1, Skippable) of 534 {value, {OsFam, OsName}} -> 535 true; 536 {value, {OsFam, Check}} when is_function(Check, 0) -> 537 Check(); 538 {value, {OsFam, Check}} when is_function(Check, 1) -> 539 Check(os:version()); 540 {value, {OsFam, OsNames}} when is_list(OsNames) -> 541 %% OsNames is a list of: 542 %% [atom()|{atom(), function/0 | function/1}] 543 case lists:member(OsName, OsNames) of 544 true -> 545 true; 546 false -> 547 os_based_skip_check(OsName, OsNames) 548 end; 549 _ -> 550 false 551 end 552 end. 553 554%% Performs a check via a provided fun with arity 0 or 1. 555%% The argument is the result of os:version(). 556os_based_skip_check(OsName, OsNames) -> 557 case lists:keysearch(OsName, 1, OsNames) of 558 {value, {OsName, Check}} when is_function(Check, 0) -> 559 Check(); 560 {value, {OsName, Check}} when is_function(Check, 1) -> 561 Check(os:version()); 562 _ -> 563 false 564 end. 565 566 567%% A modern take on the "Check if our host handle IPv6" question. 568%% 569has_support_ipv6() -> 570 case os:type() of 571 {win32, _} -> 572 %% We do not yet have support for windows in the socket nif, 573 %% so for windows we need to use the old style... 574 old_has_support_ipv6(); 575 _ -> 576 socket:is_supported(ipv6) andalso has_valid_ipv6_address() 577 end. 578 579has_valid_ipv6_address() -> 580 case net:getifaddrs(fun(#{addr := #{family := inet6}, 581 flags := Flags}) -> 582 not lists:member(loopback, Flags); 583 (_) -> 584 false 585 end) of 586 {ok, [#{addr := #{addr := LocalAddr}}|_]} -> 587 %% At least one valid address, we pick the first... 588 try validate_ipv6_address(LocalAddr) 589 catch 590 _:_:_ -> 591 false 592 end; 593 {ok, _} -> 594 false; 595 {error, _} -> 596 false 597 end. 598 599validate_ipv6_address(LocalAddr) -> 600 Domain = inet6, 601 ServerSock = 602 case socket:open(Domain, dgram, udp) of 603 {ok, SS} -> 604 SS; 605 {error, R2} -> 606 ?SKIP(f("(server) socket open failed: ~p", [R2])) 607 end, 608 LocalSA = #{family => Domain, addr => LocalAddr}, 609 ServerPort = 610 case socket:bind(ServerSock, LocalSA) of 611 ok -> 612 {ok, #{port := P1}} = socket:sockname(ServerSock), 613 P1; 614 {error, R3} -> 615 socket:close(ServerSock), 616 ?SKIP(f("(server) socket bind failed: ~p", [R3])) 617 end, 618 ServerSA = LocalSA#{port => ServerPort}, 619 ClientSock = 620 case socket:open(Domain, dgram, udp) of 621 {ok, CS} -> 622 CS; 623 {error, R4} -> 624 ?SKIP(f("(client) socket open failed: ~p", [R4])) 625 end, 626 case socket:bind(ClientSock, LocalSA) of 627 ok -> 628 ok; 629 {error, R5} -> 630 socket:close(ServerSock), 631 socket:close(ClientSock), 632 ?SKIP(f("(client) socket bind failed: ~p", [R5])) 633 end, 634 case socket:sendto(ClientSock, <<"hejsan">>, ServerSA) of 635 ok -> 636 ok; 637 {error, R6} -> 638 socket:close(ServerSock), 639 socket:close(ClientSock), 640 ?SKIP(f("failed socket sendto test: ~p", [R6])) 641 end, 642 case socket:recvfrom(ServerSock) of 643 {ok, {_, <<"hejsan">>}} -> 644 socket:close(ServerSock), 645 socket:close(ClientSock), 646 true; 647 {error, R7} -> 648 socket:close(ServerSock), 649 socket:close(ClientSock), 650 ?SKIP(f("failed socket recvfrom test: ~p", [R7])) 651 end. 652 653 654 655 656old_has_support_ipv6() -> 657 case inet:gethostname() of 658 {ok, Hostname} -> 659 old_has_support_ipv6(Hostname) andalso old_is_ipv6_host(Hostname); 660 _ -> 661 false 662 end. 663 664old_has_support_ipv6(Hostname) -> 665 case inet:getaddr(Hostname, inet6) of 666 {ok, Addr} when (size(Addr) =:= 8) andalso 667 (element(1, Addr) =/= 0) andalso 668 (element(1, Addr) =/= 16#fe80) -> 669 true; 670 _ -> 671 false 672 end. 673 674old_is_ipv6_host(Hostname) -> 675 case ct:require(ipv6_hosts) of 676 ok -> 677 lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts)); 678 _ -> 679 false 680 end. 681 682 683 684%% ---------------------------------------------------------------- 685%% Test suite utility functions 686%% 687 688%% Common suite init function 689%% This should be used by "all" suite init functions. 690 691init_per_suite(Config) -> 692 693 iprint("snmp environment: " 694 "~n (snmp) app: ~p" 695 "~n (all) init: ~p" 696 "~n (snmp) init: ~p", 697 [application:get_all_env(snmp), 698 init:get_arguments(), 699 case init:get_argument(snmp) of 700 {ok, Args} -> Args; 701 error -> undefined 702 end]), 703 704 ct:timetrap(minutes(2)), 705 706 try analyze_and_print_host_info() of 707 {Factor, HostInfo} when is_integer(Factor) -> 708 try maybe_skip(HostInfo) of 709 true -> 710 {skip, "Unstable host and/or os (or combo thererof)"}; 711 false -> 712 snmp_test_global_sys_monitor:start(), 713 [{snmp_factor, Factor} | Config] 714 catch 715 throw:{skip, _} = SKIP -> 716 SKIP 717 end 718 catch 719 throw:{skip, _} = SKIP -> 720 SKIP 721 end. 722 723maybe_skip(_HostInfo) -> 724 725 %% We have some crap machines that causes random test case failures 726 %% for no obvious reason. So, attempt to identify those without actually 727 %% checking for the host name... 728 729 LinuxVersionVerify = 730 fun(V) when (V > {3,6,11}) -> 731 false; % OK - No skip 732 (V) when (V =:= {3,6,11}) -> 733 case string:trim(os:cmd("cat /etc/issue")) of 734 "Fedora release 16 " ++ _ -> % Stone age Fedora => Skip 735 true; 736 _ -> 737 false 738 end; 739 (V) when (V =:= {3,4,20}) -> 740 case string:trim(os:cmd("cat /etc/issue")) of 741 "Wind River Linux 5.0.1.0" ++ _ -> % *Old* Wind River => skip 742 true; 743 _ -> 744 false 745 end; 746 (V) when (V =:= {2,6,32}) -> 747 case string:trim(os:cmd("cat /etc/issue")) of 748 "Debian GNU/Linux 6.0 " ++ _ -> % Stone age Debian => Skip 749 true; 750 _ -> 751 false 752 end; 753 (V) when (V > {2,6,24}) -> 754 false; % OK - No skip 755 (V) when (V =:= {2,6,10}) -> 756 case string:trim(os:cmd("cat /etc/issue")) of 757 "MontaVista" ++ _ -> % Stone age MontaVista => Skip 758 %% The real problem is that the machine is *very* slow 759 true; 760 _ -> 761 false 762 end; 763 (_) -> 764 %% We are specifically checking for 765 %% a *really* old gento... 766 case string:find(string:strip(os:cmd("uname -a")), "gentoo") of 767 nomatch -> 768 false; 769 _ -> % Stone age gentoo => Skip 770 true 771 end 772 end, 773 DarwinVersionVerify = 774 fun(V) when (V > {9, 8, 0}) -> 775 %% This version is OK: No Skip 776 false; 777 (_V) -> 778 %% This version is *not* ok: Skip 779 true 780 end, 781 SkipWindowsOnVirtual = 782 %% fun() -> 783 %% SysMan = win_sys_info_lookup(system_manufacturer, HostInfo), 784 %% case string:to_lower(SysMan) of 785 %% "vmware" ++ _ -> 786 %% true; 787 %% _ -> 788 %% false 789 %% end 790 %% end, 791 fun() -> 792 %% The host has been replaced and the VM has been reinstalled 793 %% so for now we give it a chance... 794 false 795 end, 796 COND = [{unix, [{linux, LinuxVersionVerify}, 797 {darwin, DarwinVersionVerify}]}, 798 {win32, SkipWindowsOnVirtual}], 799 os_based_skip(COND). 800 801 802end_per_suite(Config) when is_list(Config) -> 803 804 snmp_test_global_sys_monitor:stop(), 805 806 Config. 807 808 809 810fix_data_dir(Config) -> 811 DataDir0 = lookup(data_dir, Config), 812 DataDir1 = filename:split(filename:absname(DataDir0)), 813 [_|DataDir2] = lists:reverse(DataDir1), 814 DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), 815 Config1 = lists:keydelete(data_dir, 1, Config), 816 [{data_dir, DataDir ++ "/"} | Config1]. 817 818 819init_suite_top_dir(Suite, Config0) -> 820 io:format("~w:init_suite_top_dir -> entry with" 821 "~n Suite: ~p" 822 "~n Config0: ~p" 823 "~n", [?MODULE, Suite, Config0]), 824 Dir = lookup(priv_dir, Config0), 825 SuiteTopDir = filename:join(Dir, Suite), 826 case file:make_dir(SuiteTopDir) of 827 ok -> 828 ok; 829 {error, eexist} -> 830 ok; 831 {error, Reason} -> 832 fail({failed_creating_suite_top_dir, SuiteTopDir, Reason}, 833 ?MODULE, ?LINE) 834 end, 835 836 %% This is just in case... 837 Config1 = lists:keydelete(snmp_group_top_dir, 1, Config0), 838 Config2 = lists:keydelete(snmp_suite_top_dir, 1, Config1), 839 [{snmp_suite_top_dir, SuiteTopDir} | Config2]. 840 841 842init_group_top_dir(GroupName, Config) -> 843 io:format("~w:init_group_top_dir -> entry with" 844 "~n GroupName: ~p" 845 "~n Config: ~p" 846 "~n", [?MODULE, GroupName, Config]), 847 case lists:keysearch(snmp_group_top_dir, 1, Config) of 848 {value, {_Key, Dir}} -> 849 %% This is a sub-group, so create our dir within Dir 850 GroupTopDir = filename:join(Dir, GroupName), 851 case file:make_dir(GroupTopDir) of 852 ok -> 853 ok; 854 {error, Reason} -> 855 fail({failed_creating_group_top_dir, GroupTopDir, Reason}, 856 ?MODULE, ?LINE) 857 end, 858 [{snmp_group_top_dir, GroupTopDir} | Config]; 859 860 _ -> 861 %% This is a "top level" group, that is, there is only the suite 862 case lists:keysearch(snmp_suite_top_dir, 1, Config) of 863 {value, {_Key, Dir}} -> 864 GroupTopDir = filename:join(Dir, GroupName), 865 case file:make_dir(GroupTopDir) of 866 ok -> 867 ok; 868 {error, Reason} -> 869 fail({failed_creating_group_top_dir, 870 GroupTopDir, Reason}, 871 ?MODULE, ?LINE) 872 end, 873 [{snmp_group_top_dir, GroupTopDir} | Config]; 874 _ -> 875 fail(could_not_find_suite_top_dir, ?MODULE, ?LINE) 876 end 877 end. 878 879 880init_testcase_top_dir(Case, Config) -> 881 io:format("~w:init_testcase_top_dir -> entry with" 882 "~n Case: ~p" 883 "~n Config: ~p" 884 "~n", [?MODULE, Case, Config]), 885 case lists:keysearch(snmp_group_top_dir, 1, Config) of 886 {value, {_Key, Dir}} -> 887 CaseTopDir = filename:join(Dir, Case), 888 ok = file:make_dir(CaseTopDir), 889 CaseTopDir; 890 false -> 891 case lists:keysearch(snmp_suite_top_dir, 1, Config) of 892 {value, {_Key, Dir}} -> 893 CaseTopDir = filename:join(Dir, Case), 894 ok = file:make_dir(CaseTopDir), 895 CaseTopDir; 896 false -> 897 fail(failed_creating_case_top_dir, ?MODULE, ?LINE) 898 end 899 end. 900 901 902replace_config(Key, Config, NewValue) -> 903 lists:keyreplace(Key, 1, Config, {Key, NewValue}). 904 905set_config(Key, Def, Config) -> 906 case get_config(Key, Config) of 907 undefined -> 908 [{Key, Def}|Config]; 909 _ -> 910 Config 911 end. 912 913get_config(Key,C) -> 914 get_config(Key,C,undefined). 915 916get_config(Key,C,Default) -> 917 case lists:keysearch(Key,1,C) of 918 {value,{Key,Val}} -> 919 Val; 920 _ -> 921 Default 922 end. 923 924lookup(Key, Config) -> 925 {value, {Key, Value}} = lists:keysearch(Key, 1, Config), 926 Value. 927 928fail(Reason, Mod, Line) -> 929 exit({suite_failed, Reason, Mod, Line}). 930 931skip(Reason, Module, Line) -> 932 String = lists:flatten(io_lib:format("Skipping ~p(~p): ~p~n", 933 [Module, Line, Reason])), 934 exit({skip, String}). 935 936 937%% This function prints various host info, which might be usefull 938%% when analyzing the test suite (results). 939%% It also returns a "factor" that can be used when deciding 940%% the load for some test cases. Such as run time or number of 941%% iterations. This only works for some OSes. 942%% 943%% We make some calculations on Linux, OpenBSD and FreeBSD. 944%% On SunOS we always set the factor to 2 (just to be on the safe side) 945%% On all other os:es (mostly windows) we check the number of schedulers, 946%% but at least the factor will be 2. 947analyze_and_print_host_info() -> 948 {OsFam, OsName} = os:type(), 949 Version = 950 case os:version() of 951 {Maj, Min, Rel} -> 952 f("~w.~w.~w", [Maj, Min, Rel]); 953 VStr -> 954 VStr 955 end, 956 case {OsFam, OsName} of 957 {unix, linux} -> 958 analyze_and_print_linux_host_info(Version); 959 {unix, openbsd} -> 960 analyze_and_print_openbsd_host_info(Version); 961 {unix, freebsd} -> 962 analyze_and_print_freebsd_host_info(Version); 963 {unix, netbsd} -> 964 analyze_and_print_netbsd_host_info(Version); 965 {unix, darwin} -> 966 analyze_and_print_darwin_host_info(Version); 967 {unix, sunos} -> 968 analyze_and_print_solaris_host_info(Version); 969 {win32, nt} -> 970 analyze_and_print_win_host_info(Version); 971 _ -> 972 io:format("OS Family: ~p" 973 "~n OS Type: ~p" 974 "~n Version: ~p" 975 "~n Num Online Schedulers: ~s" 976 "~n", [OsFam, OsName, Version, str_num_schedulers()]), 977 {num_schedulers_to_factor(), []} 978 end. 979 980linux_which_distro(Version) -> 981 case file:read_file_info("/etc/issue") of 982 {ok, _} -> 983 case [string:trim(S) || 984 S <- string:tokens(os:cmd("cat /etc/issue"), [$\n])] of 985 [DistroStr|_] -> 986 io:format("Linux: ~s" 987 "~n ~s" 988 "~n", 989 [Version, DistroStr]), 990 case DistroStr of 991 "Wind River Linux" ++ _ -> 992 wind_river; 993 "MontaVista" ++ _ -> 994 montavista; 995 "Yellow Dog" ++ _ -> 996 yellow_dog; 997 _ -> 998 other 999 end; 1000 X -> 1001 io:format("Linux: ~s" 1002 "~n ~p" 1003 "~n", 1004 [Version, X]), 1005 other 1006 end; 1007 _ -> 1008 io:format("Linux: ~s" 1009 "~n", [Version]), 1010 other 1011 end. 1012 1013analyze_and_print_linux_host_info(Version) -> 1014 Distro = 1015 case file:read_file_info("/etc/issue") of 1016 {ok, _} -> 1017 linux_which_distro(Version); 1018 _ -> 1019 io:format("Linux: ~s" 1020 "~n", [Version]), 1021 other 1022 end, 1023 Factor = 1024 case (catch linux_which_cpuinfo(Distro)) of 1025 {ok, {CPU, BogoMIPS}} -> 1026 io:format("CPU: " 1027 "~n Model: ~s" 1028 "~n BogoMIPS: ~w" 1029 "~n Num Online Schedulers: ~s" 1030 "~n", [CPU, BogoMIPS, str_num_schedulers()]), 1031 if 1032 (BogoMIPS > 20000) -> 1033 1; 1034 (BogoMIPS > 10000) -> 1035 2; 1036 (BogoMIPS > 5000) -> 1037 3; 1038 (BogoMIPS > 2000) -> 1039 5; 1040 (BogoMIPS > 1000) -> 1041 8; 1042 true -> 1043 10 1044 end; 1045 {ok, CPU} -> 1046 io:format("CPU: " 1047 "~n Model: ~s" 1048 "~n Num Online Schedulers: ~s" 1049 "~n", [CPU, str_num_schedulers()]), 1050 NumChed = erlang:system_info(schedulers), 1051 if 1052 (NumChed > 2) -> 1053 2; 1054 true -> 1055 5 1056 end; 1057 _ -> 1058 5 1059 end, 1060 %% Check if we need to adjust the factor because of the memory 1061 try linux_which_meminfo() of 1062 AddFactor -> 1063 io:format("TS Scale Factor: ~w (~w + ~w)~n", 1064 [timetrap_scale_factor(), Factor, AddFactor]), 1065 {Factor + AddFactor, []} 1066 catch 1067 _:_:_ -> 1068 io:format("TS Scale Factor: ~w (~w)~n", 1069 [timetrap_scale_factor(), Factor]), 1070 {Factor, []} 1071 end. 1072 1073 1074 1075linux_cpuinfo_lookup(Key) when is_list(Key) -> 1076 linux_info_lookup(Key, "/proc/cpuinfo"). 1077 1078linux_cpuinfo_cpu() -> 1079 case linux_cpuinfo_lookup("cpu") of 1080 [Model] -> 1081 Model; 1082 _ -> 1083 "-" 1084 end. 1085 1086linux_cpuinfo_motherboard() -> 1087 case linux_cpuinfo_lookup("motherboard") of 1088 [MB] -> 1089 MB; 1090 _ -> 1091 "-" 1092 end. 1093 1094linux_cpuinfo_bogomips() -> 1095 case linux_cpuinfo_lookup("bogomips") of 1096 BMips when is_list(BMips) -> 1097 try lists:sum([bogomips_to_int(BM) || BM <- BMips]) 1098 catch 1099 _:_:_ -> 1100 "-" 1101 end; 1102 _ -> 1103 "-" 1104 end. 1105 1106linux_cpuinfo_BogoMIPS() -> 1107 case linux_cpuinfo_lookup("BogoMIPS") of 1108 BMips when is_list(BMips) -> 1109 try lists:sum([bogomips_to_int(BM) || BM <- BMips]) 1110 catch 1111 _:_:_ -> 1112 "-" 1113 end; 1114 _ -> 1115 "-" 1116 end. 1117 1118linux_cpuinfo_total_bogomips() -> 1119 case linux_cpuinfo_lookup("total bogomips") of 1120 [TBM] -> 1121 try bogomips_to_int(TBM) 1122 catch 1123 _:_:_ -> 1124 "-" 1125 end; 1126 _ -> 1127 "-" 1128 end. 1129 1130bogomips_to_int(BM) -> 1131 try list_to_float(BM) of 1132 F -> 1133 floor(F) 1134 catch 1135 _:_:_ -> 1136 try list_to_integer(BM) of 1137 I -> 1138 I 1139 catch 1140 _:_:_ -> 1141 throw(noinfo) 1142 end 1143 end. 1144 1145linux_cpuinfo_model() -> 1146 case linux_cpuinfo_lookup("model") of 1147 [M|_] -> 1148 M; 1149 _X -> 1150 "-" 1151 end. 1152 1153linux_cpuinfo_platform() -> 1154 case linux_cpuinfo_lookup("platform") of 1155 [P] -> 1156 P; 1157 _ -> 1158 "-" 1159 end. 1160 1161linux_cpuinfo_model_name() -> 1162 case linux_cpuinfo_lookup("model name") of 1163 [M|_] -> 1164 M; 1165 _ -> 1166 "-" 1167 end. 1168 1169linux_cpuinfo_processor() -> 1170 case linux_cpuinfo_lookup("Processor") of 1171 [P] -> 1172 P; 1173 _ -> 1174 "-" 1175 end. 1176 1177 1178linux_which_cpuinfo(montavista) -> 1179 CPU = 1180 case linux_cpuinfo_cpu() of 1181 "-" -> 1182 throw(noinfo); 1183 Model -> 1184 case linux_cpuinfo_motherboard() of 1185 "-" -> 1186 Model; 1187 MB -> 1188 Model ++ " (" ++ MB ++ ")" 1189 end 1190 end, 1191 case linux_cpuinfo_bogomips() of 1192 "-" -> 1193 {ok, CPU}; 1194 BMips -> 1195 {ok, {CPU, BMips}} 1196 end; 1197 1198linux_which_cpuinfo(yellow_dog) -> 1199 CPU = 1200 case linux_cpuinfo_cpu() of 1201 "-" -> 1202 throw(noinfo); 1203 Model -> 1204 case linux_cpuinfo_motherboard() of 1205 "-" -> 1206 Model; 1207 MB -> 1208 Model ++ " (" ++ MB ++ ")" 1209 end 1210 end, 1211 {ok, CPU}; 1212 1213linux_which_cpuinfo(wind_river) -> 1214 Model = 1215 case linux_cpuinfo_model() of 1216 "-" -> 1217 %% Try 'model name' 1218 case linux_cpuinfo_model_name() of 1219 "-" -> 1220 throw(noinfo); 1221 MN -> 1222 MN 1223 end; 1224 M -> 1225 M 1226 end, 1227 CPU = 1228 case linux_cpuinfo_platform() of 1229 "-" -> 1230 Model; 1231 Platform -> 1232 Model ++ " (" ++ Platform ++ ")" 1233 end, 1234 case linux_cpuinfo_total_bogomips() of 1235 "-" -> 1236 case linux_cpuinfo_BogoMIPS() of 1237 "-" -> 1238 {ok, CPU}; 1239 BMips -> 1240 {ok, {CPU, BMips}} 1241 end; 1242 BMips -> 1243 {ok, {CPU, BMips}} 1244 end; 1245 1246%% Check for x86 (Intel or AMD) 1247linux_which_cpuinfo(other) -> 1248 CPU = 1249 case linux_cpuinfo_model_name() of 1250 "-" -> 1251 %% ARM (at least some distros...) 1252 case linux_cpuinfo_processor() of 1253 "-" -> 1254 %% Ok, we give up 1255 throw(noinfo); 1256 Proc -> 1257 Proc 1258 end; 1259 ModelName -> 1260 ModelName 1261 end, 1262 case linux_cpuinfo_bogomips() of 1263 "-" -> 1264 {ok, CPU}; 1265 BMips -> 1266 {ok, {CPU, BMips}} 1267 end. 1268 1269linux_meminfo_lookup(Key) when is_list(Key) -> 1270 linux_info_lookup(Key, "/proc/meminfo"). 1271 1272linux_meminfo_memtotal() -> 1273 case linux_meminfo_lookup("MemTotal") of 1274 [X] -> 1275 X; 1276 _ -> 1277 "-" 1278 end. 1279 1280%% We *add* the value this return to the Factor. 1281linux_which_meminfo() -> 1282 case linux_meminfo_memtotal() of 1283 "-" -> 1284 0; 1285 MemTotal -> 1286 io:format("Memory:" 1287 "~n ~s" 1288 "~n", [MemTotal]), 1289 case string:tokens(MemTotal, [$ ]) of 1290 [MemSzStr, MemUnit] -> 1291 MemSz2 = list_to_integer(MemSzStr), 1292 MemSz3 = 1293 case string:to_lower(MemUnit) of 1294 "kb" -> 1295 MemSz2; 1296 "mb" -> 1297 MemSz2*1024; 1298 "gb" -> 1299 MemSz2*1024*1024; 1300 _ -> 1301 throw(noinfo) 1302 end, 1303 if 1304 (MemSz3 >= 8388608) -> 1305 0; 1306 (MemSz3 >= 4194304) -> 1307 1; 1308 (MemSz3 >= 2097152) -> 1309 3; 1310 true -> 1311 5 1312 end; 1313 _X -> 1314 0 1315 end 1316 end. 1317 1318 1319%% Just to be clear: This is ***not*** scientific... 1320analyze_and_print_openbsd_host_info(Version) -> 1321 io:format("OpenBSD:" 1322 "~n Version: ~p" 1323 "~n", [Version]), 1324 Extract = 1325 fun(Key) -> 1326 string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=]) 1327 end, 1328 try 1329 begin 1330 CPU = 1331 case Extract("hw.model") of 1332 ["hw.model", Model] -> 1333 string:trim(Model); 1334 _ -> 1335 "-" 1336 end, 1337 CPUSpeed = 1338 case Extract("hw.cpuspeed") of 1339 ["hw.cpuspeed", Speed] -> 1340 list_to_integer(Speed); 1341 _ -> 1342 -1 1343 end, 1344 NCPU = 1345 case Extract("hw.ncpufound") of 1346 ["hw.ncpufound", N] -> 1347 list_to_integer(N); 1348 _ -> 1349 -1 1350 end, 1351 Memory = 1352 case Extract("hw.physmem") of 1353 ["hw.physmem", PhysMem] -> 1354 list_to_integer(PhysMem) div 1024; 1355 _ -> 1356 -1 1357 end, 1358 io:format("CPU:" 1359 "~n Model: ~s" 1360 "~n Speed: ~w" 1361 "~n N: ~w" 1362 "~nMemory:" 1363 "~n ~w KB" 1364 "~n", [CPU, CPUSpeed, NCPU, Memory]), 1365 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 1366 CPUFactor = 1367 if 1368 (CPUSpeed =:= -1) -> 1369 1; 1370 (CPUSpeed >= 2000) -> 1371 if 1372 (NCPU >= 4) -> 1373 1; 1374 (NCPU >= 2) -> 1375 2; 1376 true -> 1377 3 1378 end; 1379 true -> 1380 if 1381 (NCPU >= 4) -> 1382 2; 1383 (NCPU >= 2) -> 1384 3; 1385 true -> 1386 4 1387 end 1388 end, 1389 MemAddFactor = 1390 if 1391 (Memory =:= -1) -> 1392 0; 1393 (Memory >= 8388608) -> 1394 0; 1395 (Memory >= 4194304) -> 1396 1; 1397 (Memory >= 2097152) -> 1398 2; 1399 true -> 1400 3 1401 end, 1402 {CPUFactor + MemAddFactor, []} 1403 end 1404 catch 1405 _:_:_ -> 1406 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 1407 {2, []} 1408 end. 1409 1410 1411analyze_and_print_freebsd_host_info(Version) -> 1412 io:format("FreeBSD:" 1413 "~n Version: ~p" 1414 "~n", [Version]), 1415 %% This test require that the program 'sysctl' is in the path. 1416 %% First test with 'which sysctl', if that does not work 1417 %% try with 'which /sbin/sysctl'. If that does not work either, 1418 %% we skip the test... 1419 try 1420 begin 1421 SysCtl = 1422 case string:trim(os:cmd("which sysctl")) of 1423 [] -> 1424 case string:trim(os:cmd("which /sbin/sysctl")) of 1425 [] -> 1426 throw(sysctl); 1427 SC2 -> 1428 SC2 1429 end; 1430 SC1 -> 1431 SC1 1432 end, 1433 Extract = 1434 fun(Key) -> 1435 string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)), 1436 [$:]) 1437 end, 1438 CPU = analyze_freebsd_cpu(Extract), 1439 CPUSpeed = analyze_freebsd_cpu_speed(Extract), 1440 NCPU = analyze_freebsd_ncpu(Extract), 1441 Memory = analyze_freebsd_memory(Extract), 1442 io:format("CPU:" 1443 "~n Model: ~s" 1444 "~n Speed: ~w" 1445 "~n N: ~w" 1446 "~n Num Online Schedulers: ~s" 1447 "~nMemory:" 1448 "~n ~w KB" 1449 "~n", 1450 [CPU, CPUSpeed, NCPU, str_num_schedulers(), Memory]), 1451 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 1452 CPUFactor = 1453 if 1454 (CPUSpeed =:= -1) -> 1455 1; 1456 (CPUSpeed >= 2000) -> 1457 if 1458 (NCPU >= 4) -> 1459 1; 1460 (NCPU >= 2) -> 1461 2; 1462 true -> 1463 3 1464 end; 1465 true -> 1466 if 1467 (NCPU =:= -1) -> 1468 1; 1469 (NCPU >= 4) -> 1470 2; 1471 (NCPU >= 2) -> 1472 3; 1473 true -> 1474 4 1475 end 1476 end, 1477 MemAddFactor = 1478 if 1479 (Memory =:= -1) -> 1480 0; 1481 (Memory >= 8388608) -> 1482 0; 1483 (Memory >= 4194304) -> 1484 1; 1485 (Memory >= 2097152) -> 1486 2; 1487 true -> 1488 3 1489 end, 1490 {CPUFactor + MemAddFactor, []} 1491 end 1492 catch 1493 _:_:_ -> 1494 io:format("CPU:" 1495 "~n Num Online Schedulers: ~s" 1496 "~n", [str_num_schedulers()]), 1497 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 1498 Factor = case erlang:system_info(schedulers) of 1499 1 -> 1500 10; 1501 2 -> 1502 5; 1503 _ -> 1504 2 1505 end, 1506 {Factor, []} 1507 end. 1508 1509 1510analyze_freebsd_cpu(Extract) -> 1511 analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-"). 1512 1513analyze_freebsd_cpu_speed(Extract) -> 1514 analyze_freebsd_item(Extract, 1515 "hw.clockrate", 1516 fun(X) -> list_to_integer(X) end, 1517 -1). 1518 1519analyze_freebsd_ncpu(Extract) -> 1520 analyze_freebsd_item(Extract, 1521 "hw.ncpu", 1522 fun(X) -> list_to_integer(X) end, 1523 -1). 1524 1525analyze_freebsd_memory(Extract) -> 1526 analyze_freebsd_item(Extract, 1527 "hw.physmem", 1528 fun(X) -> list_to_integer(X) div 1024 end, 1529 -1). 1530 1531analyze_freebsd_item(Extract, Key, Process, Default) -> 1532 try 1533 begin 1534 case Extract(Key) of 1535 [Key, Model] -> 1536 Process(string:trim(Model)); 1537 _ -> 1538 Default 1539 end 1540 end 1541 catch 1542 _:_:_ -> 1543 Default 1544 end. 1545 1546 1547analyze_and_print_netbsd_host_info(Version) -> 1548 io:format("NetBSD:" 1549 "~n Version: ~p" 1550 "~n", [Version]), 1551 %% This test require that the program 'sysctl' is in the path. 1552 %% First test with 'which sysctl', if that does not work 1553 %% try with 'which /sbin/sysctl'. If that does not work either, 1554 %% we skip the test... 1555 try 1556 begin 1557 SysCtl = 1558 case string:trim(os:cmd("which sysctl")) of 1559 [] -> 1560 case string:trim(os:cmd("which /sbin/sysctl")) of 1561 [] -> 1562 throw(sysctl); 1563 SC2 -> 1564 SC2 1565 end; 1566 SC1 -> 1567 SC1 1568 end, 1569 Extract = 1570 fun(Key) -> 1571 [string:trim(S) || 1572 S <- 1573 string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)), 1574 [$=])] 1575 end, 1576 CPU = analyze_netbsd_cpu(Extract), 1577 Machine = analyze_netbsd_machine(Extract), 1578 Arch = analyze_netbsd_machine_arch(Extract), 1579 CPUSpeed = analyze_netbsd_cpu_speed(Extract), 1580 NCPU = analyze_netbsd_ncpu(Extract), 1581 Memory = analyze_netbsd_memory(Extract), 1582 io:format("CPU:" 1583 "~n Model: ~s (~s, ~s)" 1584 "~n Speed: ~w MHz" 1585 "~n N: ~w" 1586 "~n Num Schedulers: ~w" 1587 "~nMemory:" 1588 "~n ~w KB" 1589 "~n", 1590 [CPU, Machine, Arch, CPUSpeed, NCPU, 1591 erlang:system_info(schedulers), Memory]), 1592 CPUFactor = 1593 if 1594 (CPUSpeed =:= -1) -> 1595 1; 1596 (CPUSpeed >= 2000) -> 1597 if 1598 (NCPU >= 4) -> 1599 1; 1600 (NCPU >= 2) -> 1601 2; 1602 true -> 1603 3 1604 end; 1605 true -> 1606 if 1607 (NCPU =:= -1) -> 1608 1; 1609 (NCPU >= 4) -> 1610 2; 1611 (NCPU >= 2) -> 1612 3; 1613 true -> 1614 4 1615 end 1616 end, 1617 MemAddFactor = 1618 if 1619 (Memory =:= -1) -> 1620 0; 1621 (Memory >= 8388608) -> 1622 0; 1623 (Memory >= 4194304) -> 1624 1; 1625 (Memory >= 2097152) -> 1626 2; 1627 true -> 1628 3 1629 end, 1630 {CPUFactor + MemAddFactor, []} 1631 end 1632 catch 1633 _:_:_ -> 1634 io:format("CPU:" 1635 "~n Num Schedulers: ~w" 1636 "~n", [erlang:system_info(schedulers)]), 1637 Factor = case erlang:system_info(schedulers) of 1638 1 -> 1639 10; 1640 2 -> 1641 5; 1642 _ -> 1643 2 1644 end, 1645 {Factor, []} 1646 end. 1647 1648analyze_netbsd_cpu(Extract) -> 1649 analyze_netbsd_item(Extract, "hw.model", fun(X) -> X end, "-"). 1650 1651analyze_netbsd_machine(Extract) -> 1652 analyze_netbsd_item(Extract, "hw.machine", fun(X) -> X end, "-"). 1653 1654analyze_netbsd_machine_arch(Extract) -> 1655 analyze_netbsd_item(Extract, "hw.machine_arch", fun(X) -> X end, "-"). 1656 1657analyze_netbsd_cpu_speed(Extract) -> 1658 analyze_netbsd_item(Extract, "machdep.dmi.processor-frequency", 1659 fun(X) -> case string:tokens(X, [$\ ]) of 1660 [MHz, "MHz"] -> 1661 list_to_integer(MHz); 1662 _ -> 1663 -1 1664 end 1665 end, "-"). 1666 1667analyze_netbsd_ncpu(Extract) -> 1668 analyze_netbsd_item(Extract, 1669 "hw.ncpu", 1670 fun(X) -> list_to_integer(X) end, 1671 -1). 1672 1673analyze_netbsd_memory(Extract) -> 1674 analyze_netbsd_item(Extract, 1675 "hw.physmem64", 1676 fun(X) -> list_to_integer(X) div 1024 end, 1677 -1). 1678 1679analyze_netbsd_item(Extract, Key, Process, Default) -> 1680 analyze_freebsd_item(Extract, Key, Process, Default). 1681 1682 1683 1684%% Model Identifier: Macmini7,1 1685%% Processor Name: Intel Core i5 1686%% Processor Speed: 2,6 GHz 1687%% Number of Processors: 1 1688%% Total Number of Cores: 2 1689%% L2 Cache (per Core): 256 KB 1690%% L3 Cache: 3 MB 1691%% Hyper-Threading Technology: Enabled 1692%% Memory: 16 GB 1693 1694analyze_and_print_darwin_host_info(Version) -> 1695 %% This stuff is for macOS. 1696 %% If we ever tested on a pure darwin machine, 1697 %% we need to find some other way to find some info... 1698 %% Also, I suppose its possible that we for some other 1699 %% reason *fail* to get the info... 1700 case analyze_darwin_software_info() of 1701 [] -> 1702 io:format("Darwin:" 1703 "~n Version: ~s" 1704 "~n Num Online Schedulers: ~s" 1705 "~n", [Version, str_num_schedulers()]), 1706 {num_schedulers_to_factor(), []}; 1707 SwInfo when is_list(SwInfo) -> 1708 SystemVersion = analyze_darwin_sw_system_version(SwInfo), 1709 KernelVersion = analyze_darwin_sw_kernel_version(SwInfo), 1710 HwInfo = analyze_darwin_hardware_info(), 1711 ModelName = analyze_darwin_hw_model_name(HwInfo), 1712 ModelId = analyze_darwin_hw_model_identifier(HwInfo), 1713 ProcName = analyze_darwin_hw_processor_name(HwInfo), 1714 ProcSpeed = analyze_darwin_hw_processor_speed(HwInfo), 1715 NumProc = analyze_darwin_hw_number_of_processors(HwInfo), 1716 NumCores = analyze_darwin_hw_total_number_of_cores(HwInfo), 1717 Memory = analyze_darwin_hw_memory(HwInfo), 1718 io:format("Darwin:" 1719 "~n System Version: ~s" 1720 "~n Kernel Version: ~s" 1721 "~n Model: ~s (~s)" 1722 "~n Processor: ~s (~s, ~s, ~s)" 1723 "~n Memory: ~s" 1724 "~n Num Online Schedulers: ~s" 1725 "~n", [SystemVersion, KernelVersion, 1726 ModelName, ModelId, 1727 ProcName, ProcSpeed, NumProc, NumCores, 1728 Memory, 1729 str_num_schedulers()]), 1730 CPUFactor = analyze_darwin_cpu_to_factor(ProcName, 1731 ProcSpeed, 1732 NumProc, 1733 NumCores), 1734 MemFactor = analyze_darwin_memory_to_factor(Memory), 1735 if (MemFactor =:= 1) -> 1736 {CPUFactor, []}; 1737 true -> 1738 {CPUFactor + MemFactor, []} 1739 end 1740 end. 1741 1742analyze_darwin_sw_system_version(SwInfo) -> 1743 proplists:get_value("system version", SwInfo, "-"). 1744 1745analyze_darwin_sw_kernel_version(SwInfo) -> 1746 proplists:get_value("kernel version", SwInfo, "-"). 1747 1748analyze_darwin_software_info() -> 1749 analyze_darwin_system_profiler("SPSoftwareDataType"). 1750 1751analyze_darwin_hw_model_name(HwInfo) -> 1752 proplists:get_value("model name", HwInfo, "-"). 1753 1754analyze_darwin_hw_model_identifier(HwInfo) -> 1755 proplists:get_value("model identifier", HwInfo, "-"). 1756 1757analyze_darwin_hw_processor_name(HwInfo) -> 1758 proplists:get_value("processor name", HwInfo, "-"). 1759 1760analyze_darwin_hw_processor_speed(HwInfo) -> 1761 proplists:get_value("processor speed", HwInfo, "-"). 1762 1763analyze_darwin_hw_number_of_processors(HwInfo) -> 1764 proplists:get_value("number of processors", HwInfo, "-"). 1765 1766analyze_darwin_hw_total_number_of_cores(HwInfo) -> 1767 proplists:get_value("total number of cores", HwInfo, "-"). 1768 1769analyze_darwin_hw_memory(HwInfo) -> 1770 proplists:get_value("memory", HwInfo, "-"). 1771 1772analyze_darwin_hardware_info() -> 1773 analyze_darwin_system_profiler("SPHardwareDataType"). 1774 1775%% This basically has the structure: "Key: Value" 1776%% But could also be (for example): 1777%% "Something:" (which we ignore) 1778%% "Key: Value1:Value2" 1779analyze_darwin_system_profiler(DataType) -> 1780 %% First, make sure the program actually exist: 1781 case os:cmd("which system_profiler") of 1782 [] -> 1783 []; 1784 _ -> 1785 D0 = os:cmd("system_profiler " ++ DataType), 1786 D1 = string:tokens(D0, [$\n]), 1787 D2 = [string:trim(S1) || S1 <- D1], 1788 D3 = [string:tokens(S2, [$:]) || S2 <- D2], 1789 analyze_darwin_system_profiler2(D3) 1790 end. 1791 1792analyze_darwin_system_profiler2(L) -> 1793 analyze_darwin_system_profiler2(L, []). 1794 1795analyze_darwin_system_profiler2([], Acc) -> 1796 [{string:to_lower(K), V} || {K, V} <- lists:reverse(Acc)]; 1797analyze_darwin_system_profiler2([[_]|T], Acc) -> 1798 analyze_darwin_system_profiler2(T, Acc); 1799analyze_darwin_system_profiler2([[H1,H2]|T], Acc) -> 1800 analyze_darwin_system_profiler2(T, [{H1, string:trim(H2)}|Acc]); 1801analyze_darwin_system_profiler2([[H|TH0]|T], Acc) -> 1802 %% Some value parts has ':' in them, so put them together 1803 TH1 = colonize(TH0), 1804 analyze_darwin_system_profiler2(T, [{H, string:trim(TH1)}|Acc]). 1805 1806%% This is only called if the length is at least 2 1807colonize([L1, L2]) -> 1808 L1 ++ ":" ++ L2; 1809colonize([H|T]) -> 1810 H ++ ":" ++ colonize(T). 1811 1812 1813%% The memory looks like this "<size> <unit>". Example: "2 GB" 1814analyze_darwin_memory_to_factor(Mem) -> 1815 case [string:to_lower(S) || S <- string:tokens(Mem, [$\ ])] of 1816 [_SzStr, "tb"] -> 1817 1; 1818 [SzStr, "gb"] -> 1819 try list_to_integer(SzStr) of 1820 Sz when Sz < 2 -> 1821 20; 1822 Sz when Sz < 4 -> 1823 10; 1824 Sz when Sz < 8 -> 1825 5; 1826 Sz when Sz < 16 -> 1827 2; 1828 _ -> 1829 1 1830 catch 1831 _:_:_ -> 1832 20 1833 end; 1834 [_SzStr, "mb"] -> 1835 20; 1836 _ -> 1837 20 1838 end. 1839 1840 1841%% The speed is a string: "<speed> <unit>" 1842%% the speed may be a float, which we transforms into an integer of MHz. 1843%% To calculate a factor based on processor speed, number of procs 1844%% and number of cores is ... not an exact ... science ... 1845analyze_darwin_cpu_to_factor(_ProcName, 1846 ProcSpeedStr, NumProcStr, NumCoresStr) -> 1847 Speed = 1848 case [string:to_lower(S) || S <- string:tokens(ProcSpeedStr, [$\ ])] of 1849 [SpeedStr, "mhz"] -> 1850 try list_to_integer(SpeedStr) of 1851 SpeedI -> 1852 SpeedI 1853 catch 1854 _:_:_ -> 1855 try list_to_float(SpeedStr) of 1856 SpeedF -> 1857 trunc(SpeedF) 1858 catch 1859 _:_:_ -> 1860 -1 1861 end 1862 end; 1863 [SpeedStr, "ghz"] -> 1864 try list_to_float(SpeedStr) of 1865 SpeedF -> 1866 trunc(1000*SpeedF) 1867 catch 1868 _:_:_ -> 1869 try list_to_integer(SpeedStr) of 1870 SpeedI -> 1871 1000*SpeedI 1872 catch 1873 _:_:_ -> 1874 -1 1875 end 1876 end; 1877 _ -> 1878 -1 1879 end, 1880 NumProc = try list_to_integer(NumProcStr) of 1881 NumProcI -> 1882 NumProcI 1883 catch 1884 _:_:_ -> 1885 1 1886 end, 1887 NumCores = try list_to_integer(NumCoresStr) of 1888 NumCoresI -> 1889 NumCoresI 1890 catch 1891 _:_:_ -> 1892 1 1893 end, 1894 if 1895 (Speed > 3000) -> 1896 if 1897 (NumProc =:= 1) -> 1898 if 1899 (NumCores < 2) -> 1900 5; 1901 (NumCores < 4) -> 1902 3; 1903 (NumCores < 6) -> 1904 2; 1905 true -> 1906 1 1907 end; 1908 true -> 1909 if 1910 (NumCores < 4) -> 1911 2; 1912 true -> 1913 1 1914 end 1915 end; 1916 (Speed > 2000) -> 1917 if 1918 (NumProc =:= 1) -> 1919 if 1920 (NumCores < 2) -> 1921 8; 1922 (NumCores < 4) -> 1923 5; 1924 (NumCores < 6) -> 1925 3; 1926 true -> 1927 1 1928 end; 1929 true -> 1930 if 1931 (NumCores < 4) -> 1932 5; 1933 (NumCores < 8) -> 1934 2; 1935 true -> 1936 1 1937 end 1938 end; 1939 true -> 1940 if 1941 (NumProc =:= 1) -> 1942 if 1943 (NumCores < 2) -> 1944 10; 1945 (NumCores < 4) -> 1946 7; 1947 (NumCores < 6) -> 1948 5; 1949 (NumCores < 8) -> 1950 3; 1951 true -> 1952 1 1953 end; 1954 true -> 1955 if 1956 (NumCores < 4) -> 1957 8; 1958 (NumCores < 8) -> 1959 4; 1960 true -> 1961 1 1962 end 1963 end 1964 end. 1965 1966 1967analyze_and_print_solaris_host_info(Version) -> 1968 Release = 1969 case file:read_file_info("/etc/release") of 1970 {ok, _} -> 1971 case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of 1972 [Rel | _] -> 1973 Rel; 1974 _ -> 1975 "-" 1976 end; 1977 _ -> 1978 "-" 1979 end, 1980 %% Display the firmware device tree root properties (prtconf -b) 1981 Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) || 1982 Prop <- [string:tokens(S, [$:]) || 1983 S <- string:tokens(os:cmd("prtconf -b"), [$\n])]], 1984 BannerName = case lists:keysearch("banner-name", 1, Props) of 1985 {value, {_, BN}} -> 1986 string:trim(BN); 1987 _ -> 1988 "-" 1989 end, 1990 InstructionSet = 1991 case string:trim(os:cmd("isainfo -k")) of 1992 "Pseudo-terminal will not" ++ _ -> 1993 "-"; 1994 IS -> 1995 IS 1996 end, 1997 PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1], 1998 SysConf = 1999 case lists:keysearch("System Configuration", 1, PtrConf) of 2000 {value, {_, SC}} -> 2001 SC; 2002 _ -> 2003 "-" 2004 end, 2005 %% Because we count the lines of the output (which may contain 2006 %% any number of extra crap lines) we need to ensure we only 2007 %% count the "proper" stdout. So send it to a tmp file first 2008 %% and then count its number of lines... 2009 NumPhysCPU = 2010 try 2011 begin 2012 File1 = f("/tmp/psrinfo_p.~s.~w", [os:getpid(), os:system_time()]), 2013 os:cmd("psrinfo -p > " ++ File1), 2014 string:trim(os:cmd("cat " ++ File1)) 2015 end 2016 catch 2017 _:_:_ -> 2018 "-" 2019 end, 2020 %% Because we count the lines of the output (which may contain 2021 %% any number of extra crap lines) we need to ensure we only 2022 %% count the "proper" stdout. So send it to a tmp file first 2023 %% and then count its number of lines... 2024 NumVCPU = 2025 try 2026 begin 2027 File2 = f("/tmp/psrinfo.~s.~w", [os:getpid(), os:system_time()]), 2028 os:cmd("psrinfo > " ++ File2), 2029 [NumVCPUStr | _] = string:tokens(os:cmd("wc -l " ++ File2), [$\ ]), 2030 NumVCPUStr 2031 end 2032 catch 2033 _:_:_ -> 2034 "-" 2035 end, 2036 MemSz = 2037 case lists:keysearch("Memory size", 1, PtrConf) of 2038 {value, {_, MS}} -> 2039 MS; 2040 _ -> 2041 "-" 2042 end, 2043 io:format("Solaris: ~s" 2044 "~n Release: ~s" 2045 "~n Banner Name: ~s" 2046 "~n Instruction Set: ~s" 2047 "~n CPUs: ~s (~s)" 2048 "~n System Config: ~s" 2049 "~n Memory Size: ~s" 2050 "~n Num Online Schedulers: ~s" 2051 "~n~n", [Version, Release, BannerName, InstructionSet, 2052 NumPhysCPU, NumVCPU, 2053 SysConf, MemSz, 2054 str_num_schedulers()]), 2055 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 2056 MemFactor = 2057 try string:tokens(MemSz, [$ ]) of 2058 [SzStr, "Mega" ++ _] -> 2059 try list_to_integer(SzStr) of 2060 Sz when Sz > 8192 -> 2061 0; 2062 Sz when Sz > 4096 -> 2063 1; 2064 Sz when Sz > 2048 -> 2065 2; 2066 _ -> 2067 5 2068 catch 2069 _:_:_ -> 2070 10 2071 end; 2072 [SzStr, "Giga" ++ _] -> 2073 try list_to_integer(SzStr) of 2074 Sz when Sz > 8 -> 2075 0; 2076 Sz when Sz > 4 -> 2077 1; 2078 Sz when Sz > 2 -> 2079 2; 2080 _ -> 2081 5 2082 catch 2083 _:_:_ -> 2084 10 2085 end; 2086 _ -> 2087 10 2088 catch 2089 _:_:_ -> 2090 10 2091 end, 2092 {try erlang:system_info(schedulers) of 2093 1 -> 2094 10; 2095 2 -> 2096 5; 2097 N when (N =< 6) -> 2098 2; 2099 _ -> 2100 1 2101 catch 2102 _:_:_ -> 2103 10 2104 end + MemFactor, []}. 2105 2106 2107 2108analyze_and_print_win_host_info(Version) -> 2109 SysInfo = which_win_system_info(), 2110 OsName = win_sys_info_lookup(os_name, SysInfo), 2111 OsVersion = win_sys_info_lookup(os_version, SysInfo), 2112 SysMan = win_sys_info_lookup(system_manufacturer, SysInfo), 2113 SysMod = win_sys_info_lookup(system_model, SysInfo), 2114 NumProcs = win_sys_info_lookup(num_processors, SysInfo), 2115 TotPhysMem = win_sys_info_lookup(total_phys_memory, SysInfo), 2116 io:format("Windows: ~s" 2117 "~n OS Version: ~s (~p)" 2118 "~n System Manufacturer: ~s" 2119 "~n System Model: ~s" 2120 "~n Number of Processor(s): ~s" 2121 "~n Total Physical Memory: ~s" 2122 "~n Num Online Schedulers: ~s" 2123 "~n~n", [OsName, OsVersion, Version, 2124 SysMan, SysMod, NumProcs, TotPhysMem, 2125 str_num_schedulers()]), 2126 io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), 2127 MemFactor = 2128 try 2129 begin 2130 [MStr, MUnit|_] = 2131 string:tokens(lists:delete($,, TotPhysMem), [$\ ]), 2132 case string:to_lower(MUnit) of 2133 "gb" -> 2134 try list_to_integer(MStr) of 2135 M when M > 8 -> 2136 0; 2137 M when M > 4 -> 2138 1; 2139 M when M > 2 -> 2140 2; 2141 _ -> 2142 5 2143 catch 2144 _:_:_ -> 2145 10 2146 end; 2147 "mb" -> 2148 try list_to_integer(MStr) of 2149 M when M > 8192 -> 2150 0; 2151 M when M > 4096 -> 2152 1; 2153 M when M > 2048 -> 2154 2; 2155 _ -> 2156 5 2157 catch 2158 _:_:_ -> 2159 10 2160 end; 2161 _ -> 2162 10 2163 end 2164 end 2165 catch 2166 _:_:_ -> 2167 10 2168 end, 2169 CPUFactor = 2170 case erlang:system_info(schedulers) of 2171 1 -> 2172 10; 2173 2 -> 2174 5; 2175 _ -> 2176 2 2177 end, 2178 {CPUFactor + MemFactor, SysInfo}. 2179 2180win_sys_info_lookup(Key, SysInfo) -> 2181 win_sys_info_lookup(Key, SysInfo, "-"). 2182 2183win_sys_info_lookup(Key, SysInfo, Def) -> 2184 case lists:keysearch(Key, 1, SysInfo) of 2185 {value, {Key, Value}} -> 2186 Value; 2187 false -> 2188 Def 2189 end. 2190 2191%% This function only extracts the prop we actually care about! 2192which_win_system_info() -> 2193 F = fun() -> 2194 try 2195 begin 2196 SysInfo = os:cmd("systeminfo"), 2197 process_win_system_info( 2198 string:tokens(SysInfo, [$\r, $\n]), []) 2199 end 2200 catch 2201 C:E:S -> 2202 io:format("Failed get or process System info: " 2203 " Error Class: ~p" 2204 " Error: ~p" 2205 " Stack: ~p" 2206 "~n", [C, E, S]), 2207 [] 2208 end 2209 end, 2210 proxy_call(F, minutes(1), []). 2211 2212process_win_system_info([], Acc) -> 2213 Acc; 2214process_win_system_info([H|T], Acc) -> 2215 case string:tokens(H, [$:]) of 2216 [Key, Value] -> 2217 case string:to_lower(Key) of 2218 "os name" -> 2219 process_win_system_info(T, 2220 [{os_name, string:trim(Value)}|Acc]); 2221 "os version" -> 2222 process_win_system_info(T, 2223 [{os_version, string:trim(Value)}|Acc]); 2224 "system manufacturer" -> 2225 process_win_system_info(T, 2226 [{system_manufacturer, string:trim(Value)}|Acc]); 2227 "system model" -> 2228 process_win_system_info(T, 2229 [{system_model, string:trim(Value)}|Acc]); 2230 "processor(s)" -> 2231 [NumProcStr|_] = string:tokens(Value, [$\ ]), 2232 T2 = lists:nthtail(list_to_integer(NumProcStr), T), 2233 process_win_system_info(T2, 2234 [{num_processors, NumProcStr}|Acc]); 2235 "total physical memory" -> 2236 process_win_system_info(T, 2237 [{total_phys_memory, string:trim(Value)}|Acc]); 2238 _ -> 2239 process_win_system_info(T, Acc) 2240 end; 2241 _ -> 2242 process_win_system_info(T, Acc) 2243 end. 2244 2245 2246 2247str_num_schedulers() -> 2248 try erlang:system_info(schedulers_online) of 2249 N -> f("~w", [N]) 2250 catch 2251 _:_:_ -> "-" 2252 end. 2253 2254num_schedulers_to_factor() -> 2255 try erlang:system_info(schedulers_online) of 2256 1 -> 2257 10; 2258 2 -> 2259 5; 2260 N when (N =< 6) -> 2261 2; 2262 _ -> 2263 1 2264 catch 2265 _:_:_ -> 2266 10 2267 end. 2268 2269 2270linux_info_lookup(Key, File) -> 2271 %% try 2272 %% begin 2273 %% GREP = os:cmd("grep " ++ "\"" ++ Key ++ "\"" ++ " " ++ File), 2274 %% io:format("linux_info_lookup() -> GREP: ~p~n", [GREP]), 2275 %% TOKENS = string:tokens(GREP, [$:,$\n]), 2276 %% io:format("linux_info_lookup() -> TOKENS: ~p~n", [TOKENS]), 2277 %% INFO = [string:trim(S) || S <- TOKENS], 2278 %% io:format("linux_info_lookup() -> INFO: ~p~n", [INFO]), 2279 %% linux_info_lookup_collect(Key, INFO, []) 2280 %% end 2281 %% catch 2282 %% _:_:_ -> 2283 %% "-" 2284 %% end. 2285 try [string:trim(S) || S <- string:tokens(os:cmd("grep " ++ "\"" ++ Key ++ "\"" ++ " " ++ File), [$:,$\n])] of 2286 Info -> 2287 linux_info_lookup_collect(Key, Info, []) 2288 catch 2289 _:_:_ -> 2290 "-" 2291 end. 2292 2293linux_info_lookup_collect(_Key, [], Values) -> 2294 lists:reverse(Values); 2295linux_info_lookup_collect(Key, [Key, Value|Rest], Values) -> 2296 linux_info_lookup_collect(Key, Rest, [Value|Values]); 2297linux_info_lookup_collect(_, _, Values) -> 2298 lists:reverse(Values). 2299 2300 2301 2302%% ---------------------------------------------------------------- 2303%% Time related function 2304%% 2305 2306hours(N) -> trunc(N * 1000 * 60 * 60). 2307minutes(N) -> trunc(N * 1000 * 60). 2308seconds(N) -> trunc(N * 1000). 2309 2310 2311sleep(infinity) -> 2312 receive 2313 after infinity -> 2314 ok 2315 end; 2316sleep(MSecs) -> 2317 receive 2318 after trunc(MSecs) -> 2319 ok 2320 end, 2321 ok. 2322 2323 2324%% ---------------------------------------------------------------- 2325%% Process utility function 2326%% 2327 2328mqueue() -> 2329 mqueue(self()). 2330mqueue(Pid) when is_pid(Pid) -> 2331 Key = messages, 2332 case process_info(Pid, Key) of 2333 {Key, Msgs} -> 2334 Msgs; 2335 _ -> 2336 [] 2337 end. 2338 2339flush_mqueue() -> 2340 io:format("~p~n", [lists:reverse(flush_mqueue([]))]). 2341 2342flush_mqueue(MQ) -> 2343 receive 2344 Any -> 2345 flush_mqueue([Any|MQ]) 2346 after 0 -> 2347 MQ 2348 end. 2349 2350 2351trap_exit() -> 2352 {trap_exit, Flag} = process_info(self(),trap_exit), Flag. 2353 2354trap_exit(Flag) -> 2355 process_flag(trap_exit, Flag). 2356 2357 2358 2359%% ---------------------------------------------------------------- 2360%% Node utility functions 2361%% 2362 2363ping(N) -> 2364 case net_adm:ping(N) of 2365 pang -> 2366 error; 2367 pong -> 2368 ok 2369 end. 2370 2371local_nodes() -> 2372 nodes_on(net_adm:localhost()). 2373 2374nodes_on(Host) when is_list(Host) -> 2375 net_adm:world_list([list_to_atom(Host)]). 2376 2377 2378start_node(Name, Args) -> 2379 Opts = [{cleanup, false}, {args, Args}], 2380 test_server:start_node(Name, slave, Opts). 2381 2382 2383stop_node(Node) -> 2384 test_server:stop_node(Node). 2385 2386 2387%% ---------------------------------------------------------------- 2388%% Application and Crypto utility functions 2389%% 2390 2391is_app_running(App) when is_atom(App) -> 2392 Apps = application:which_applications(), 2393 lists:keymember(App,1,Apps). 2394 2395is_crypto_running() -> 2396 is_app_running(crypto). 2397 2398is_mnesia_running() -> 2399 is_app_running(mnesia). 2400 2401is_snmp_running() -> 2402 is_app_running(snmp). 2403 2404crypto_start() -> 2405 try crypto:start() of 2406 ok -> 2407 ok; 2408 {error, {already_started,crypto}} -> 2409 ok 2410 catch 2411 exit:{undef, [{crypto, start, [], []} | _]}:_ -> 2412 {error, no_crypto}; 2413 C:E:S -> 2414 {error, {C, E, S}} 2415 end. 2416 2417crypto_support() -> 2418 crypto_support([md5, sha], []). 2419 2420crypto_support([], []) -> 2421 yes; 2422crypto_support([], Acc) -> 2423 {no, Acc}; 2424crypto_support([Func|Funcs], Acc) -> 2425 case is_crypto_supported(Func) of 2426 true -> 2427 crypto_support(Funcs, Acc); 2428 false -> 2429 crypto_support(Funcs, [Func|Acc]) 2430 end. 2431 2432is_crypto_supported(Func) -> 2433 snmp_misc:is_crypto_supported(Func). 2434 2435 2436%% This function ensures that a *named* process on the local node is not running. 2437%% It does so by: 2438%% 1) Wait for 'Timeout' msec 2439%% 2) If 1 did not work, issue 'stop' and then wait 'Timeout' msec 2440%% 3) And finally, if 2 did not work, issue exit(kill). 2441ensure_not_running(Name, Stopper, Timeout) 2442 when is_atom(Name) andalso 2443 is_function(Stopper, 0) andalso 2444 is_integer(Timeout) -> 2445 ensure_not_running(whereis(Name), Name, Stopper, Timeout). 2446 2447ensure_not_running(Pid, Name, Stopper, Timeout) when is_pid(Pid) -> 2448 MRef = erlang:monitor(process, Pid), 2449 try 2450 begin 2451 ensure_not_running_wait(Pid, MRef, Timeout), 2452 ensure_not_running_stop(Pid, MRef, Stopper, Timeout), 2453 ensure_not_running_kill(Pid, MRef, Timeout), 2454 exit({failed_ensure_not_running, Name}) 2455 end 2456 catch 2457 throw:ok -> 2458 sleep(1000), 2459 ok 2460 end; 2461ensure_not_running(_, _, _, _) -> 2462 iprint("ensure_not_running -> not running", []), 2463 sleep(1000), % This should not actually be necessary! 2464 ok. 2465 2466 2467ensure_not_running_wait(Pid, MRef, Timeout) -> 2468 receive 2469 {'DOWN', MRef, process, Pid, _Info} -> 2470 iprint("ensure_not_running_wait -> died peacefully", []), 2471 throw(ok) 2472 after Timeout -> 2473 wprint("ensure_not_running_wait -> giving up", []), 2474 ok 2475 end. 2476 2477ensure_not_running_stop(Pid, MRef, Stopper, Timeout) -> 2478 %% Spawn a stop'er process 2479 StopPid = spawn(Stopper), 2480 receive 2481 {'DOWN', MRef, process, Pid, _Info} -> 2482 nprint("ensure_not_running_stop -> dead (stopped)", []), 2483 throw(ok) 2484 after Timeout -> 2485 wprint("ensure_not_running_stop -> giving up", []), 2486 exit(StopPid, kill), 2487 ok 2488 end. 2489 2490ensure_not_running_kill(Pid, MRef, Timeout) -> 2491 exit(Pid, kill), 2492 receive 2493 {'DOWN', MRef, process, Pid, _Info} -> 2494 nprint("ensure_not_running_kill -> dead (killed)", []), 2495 throw(ok) 2496 after Timeout -> 2497 wprint("ensure_not_running_kill -> giving up", []), 2498 ok 2499 end. 2500 2501 2502%% ---------------------------------------------------------------- 2503%% Watchdog functions 2504%% 2505 2506watchdog_start(Timeout) -> 2507 watchdog_start(unknown, Timeout). 2508 2509watchdog_start(Case, Timeout) -> 2510 spawn_link(?MODULE, watchdog, [Case, Timeout, self()]). 2511 2512watchdog_stop(Pid) -> 2513 unlink(Pid), 2514 exit(Pid, kill), 2515 ok. 2516 2517watchdog(Case, Timeout0, Pid) -> 2518 process_flag(priority, max), 2519 Timeout = timeout(Timeout0), 2520 receive 2521 after Timeout -> 2522 Mon = erlang:monitor(process, Pid), 2523 case erlang:process_info(Pid) of 2524 undefined -> 2525 ok; 2526 ProcInfo -> 2527 Line = 2528 case lists:keysearch(dictionary, 1, ProcInfo) of 2529 {value, {_, Dict}} when is_list(Dict) -> 2530 case lists:keysearch(test_server_loc, 1, Dict) of 2531 {value, {_, {_Mod, L}}} when is_integer(L) -> 2532 L; 2533 _ -> 2534 0 2535 end; 2536 _ -> % This borders on paranoia, but... 2537 0 2538 end, 2539 Trap = {timetrap_timeout, Timeout, Line}, 2540 exit(Pid, Trap), 2541 receive 2542 {'DOWN', Mon, process, Pid, _} -> 2543 ok 2544 after 10000 -> 2545 warning_msg("Failed stopping " 2546 "test case ~p process ~p " 2547 "[~w] after ~w: killing instead", 2548 [Case, Pid, Line, Timeout]), 2549 exit(Pid, kill) 2550 end 2551 end 2552 end. 2553 2554warning_msg(F, A) -> 2555 (catch error_logger:warning_msg(F ++ "~n", A)). 2556 2557timeout(T) -> 2558 trunc(timeout(T, os:type())). 2559 2560timeout(T, _) -> 2561 T * timetrap_scale_factor(). 2562 2563timetrap_scale_factor() -> 2564 case (catch test_server:timetrap_scale_factor()) of 2565 {'EXIT', _} -> 2566 1; 2567 N -> 2568 N 2569 end. 2570 2571 2572%% ---------------------------------------------------------------------- 2573%% file & dir functions 2574%% 2575 2576del_dir(Dir) when is_list(Dir) -> 2577 (catch do_del_dir(Dir)). 2578 2579do_del_dir(Dir) -> 2580 io:format("delete directory ~s~n", [Dir]), 2581 case file:list_dir(Dir) of 2582 {ok, Files} -> 2583 Files2 = [filename:join(Dir, File) || File <- Files], 2584 del_dir2(Files2), 2585 case file:del_dir(Dir) of 2586 ok -> 2587 io:format("directory ~s deleted~n", [Dir]), 2588 ok; 2589 {error, eexist} = Error1 -> 2590 io:format("directory not empty: ~n", []), 2591 {ok, Files3} = file:list_dir(Dir), 2592 io:format("found additional files: ~n~p~n", 2593 [Files3]), 2594 throw(Error1); 2595 {error, Reason2} = Error2 -> 2596 io:format("failed deleting directory: ~w~n", [Reason2]), 2597 throw(Error2) 2598 end; 2599 Else -> 2600 Else 2601 end. 2602 2603del_dir2([]) -> 2604 ok; 2605del_dir2([File|Files]) -> 2606 del_file_or_dir(File), 2607 del_dir2(Files). 2608 2609del_file_or_dir(FileOrDir) -> 2610 case file:read_file_info(FileOrDir) of 2611 {ok, #file_info{type = directory}} -> 2612 do_del_dir(FileOrDir); 2613 {ok, _} -> 2614 io:format(" delete file ~s~n", [FileOrDir]), 2615 case file:delete(FileOrDir) of 2616 ok -> 2617 io:format(" => deleted~n", []), 2618 ok; 2619 {error, Reason} = Error -> 2620 io:format(" => failed - ~w~n", [Reason]), 2621 throw(Error) 2622 end; 2623 2624 _ -> 2625 ok 2626 end. 2627 2628 2629%% ---------------------------------------------------------------------- 2630%% (debug) Print functions 2631%% 2632 2633f(F, A) -> 2634 lists:flatten(io_lib:format(F, A)). 2635 2636p(Mod, Case) when is_atom(Mod) andalso is_atom(Case) -> 2637 case get(test_case) of 2638 undefined -> 2639 put(test_case, Case), 2640 p("~n~n************ ~w:~w ************", [Mod, Case]); 2641 _ -> 2642 ok 2643 end; 2644 2645p(F, A) when is_list(F) andalso is_list(A) -> 2646 io:format(user, F ++ "~n", A). 2647 2648%% This is just a bog standard printout, with a (formatted) timestamp 2649%% prefix and a newline after. 2650%% print1 - prints to both standard_io and user. 2651%% print2 - prints to just standard_io. 2652 2653print_format(F, A) -> 2654 FTS = snmp_test_lib:formated_timestamp(), 2655 io_lib:format("[~s] " ++ F ++ "~n", [FTS | A]). 2656 2657print1(F, A) -> 2658 S = print_format(F, A), 2659 io:format("~s", [S]), 2660 io:format(user, "~s", [S]). 2661 2662print2(F, A) -> 2663 S = print_format(F, A), 2664 io:format("~s", [S]). 2665 2666 2667print(Prefix, Module, Line, Format, Args) -> 2668 io:format("*** [~s] ~s ~p ~p ~p:~p *** " ++ Format ++ "~n", 2669 [formated_timestamp(), 2670 Prefix, node(), self(), Module, Line|Args]). 2671 2672formated_timestamp() -> 2673 snmp_misc:formated_timestamp(). 2674 2675 2676%% ---------------------------------------------------------------------- 2677%% 2678%% General purpose print functions 2679%% ERROR, WARNING and NOTICE are written both to 'user' and 'standard_io'. 2680%% INFO only to 'standard_io'. 2681%% 2682%% Should we also allow for (optional) a "short name" (sname)? 2683%% 2684 2685%% ERROR print (both to user and standard_io) 2686eprint(F, A) -> 2687 Str = format_print("ERROR", F, A), 2688 io:format(user, "~s~n", [Str]), 2689 io:format(standard_io, "~s~n", [Str]). 2690 2691%% WARNING print (both to user and standard_io) 2692wprint(F, A) -> 2693 Str = format_print("WARNING", F, A), 2694 io:format(user, "~s~n", [Str]), 2695 io:format(standard_io, "~s~n", [Str]). 2696 2697%% NOTICE print (both to user and standard_io) 2698nprint(F, A) -> 2699 Str = format_print("NOTICE", F, A), 2700 io:format(user, "~s~n", [Str]), 2701 io:format(standard_io, "~s~n", [Str]). 2702 2703%% INFO print (only to user) 2704iprint(F, A) -> 2705 Str = format_print("INFO", F, A), 2706 io:format(standard_io, "~s~n", [Str]). 2707 2708format_print(Prefix, F, A) -> 2709 format_print(get(tname), Prefix, F, A). 2710 2711format_print(undefined, Prefix, F, A) -> 2712 f("*** [~s] ~s ~p ~p *** ~n" ++ F ++ "~n", 2713 [formated_timestamp(), Prefix, node(), self() | A]); 2714format_print(TName, Prefix, F, A) when is_atom(TName) -> 2715 format_print(atom_to_list(TName), Prefix, F, A); 2716format_print(TName, Prefix, F, A) when is_list(TName) -> 2717 f("*** [~s] ~s ~s ~p ~p *** ~n" ++ F ++ "~n", 2718 [formated_timestamp(), Prefix, TName, node(), self() | A]). 2719 2720 2721