1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(snmp_agent_test_lib).
22
23
24-export([
25	 start_v1_agent/1,        start_v1_agent/2,
26	 start_v2_agent/1,        start_v2_agent/2,
27	 start_v3_agent/1,        start_v3_agent/2,
28	 start_bilingual_agent/1, start_bilingual_agent/2,
29	 start_mt_agent/1,        start_mt_agent/2,         start_mt_agent/3,
30	 stop_agent/1,
31
32	 %% start_sup/0,      stop_sup/2,
33	 start_subagent/3, stop_subagent/1,
34	 start_sub_sup/1,  start_sub_sup/2,
35
36	 start_node/1, stop_node/1,
37
38	 load_master/1, load_master_std/1, unload_master/1,
39	 loaded_mibs/0, unload_mibs/1,
40
41	 get_req/2, get_next_req/1,
42
43	 config/5, config/6,
44	 delete_files/1,
45	 copy_file/2,
46	 update_usm/2,
47	 update_usm_mgr/2, rewrite_usm_mgr/3, reset_usm_mgr/1,
48	 update_community/2,
49	 update_vacm/2,
50	 write_community_conf/2,
51	 write_target_addr_conf/2, write_target_addr_conf/4,
52	 rewrite_target_addr_conf/2, reset_target_addr_conf/1,
53	 write_target_params_conf/2, rewrite_target_params_conf/3,
54	 reset_target_params_conf/1,
55	 write_notify_conf/1, write_view_conf/1,
56
57	 display_memory_usage/0,
58
59	 init_all/1, finish_all/1,
60	 init_case/1,
61	 try_test/2, try_test/3, try_test/4,
62	 expect/3, expect/4, expect/5, expect/7,
63
64	 regs/0,
65	 rpc/3
66	]).
67
68%% Internal exports
69-export([tc_wait/5, tc_run/4]).
70
71-include_lib("kernel/include/file.hrl").
72-include_lib("common_test/include/ct.hrl").
73-include("snmp_test_lib.hrl").
74-define(SNMP_USE_V3, true).
75-include_lib("snmp/include/snmp_types.hrl").
76
77-define(TRAP_UDP, 5000).
78
79-define(v1_2(V1,V2),
80	case get(vsn) of
81	    v1 -> V1;
82	    _  -> V2
83	end).
84
85-define(v1_2_3(V1,V2,V3),
86	case get(vsn) of
87	    v1 -> V1;
88	    v2 -> V2;
89	    _  -> V3
90	end).
91
92
93%%%-----------------------------------------------------------------
94%%% The test case structure is as follows:
95%%%
96%%% init_all - starts mnesia,
97%%%
98%%%    init_v1 - starts agent
99%%%       simple
100%%%       big  - e.g. starts/stops subagent, load/unloads mibs
101%%%       init_mul
102%%%          mul_get
103%%%          mul_set
104%%%          <etc>
105%%%       finish_mul
106%%%       <etc>
107%%%    finish_v1
108%%%
109%%%    init_v2 - starts agent
110%%%    finish_v2
111%%%
112%%%    init_bilingual - starts agent
113%%%    finish_bilingual
114%%%
115%%% finish_all
116%%%
117%%% There is still one problem with these testsuites.  If one test
118%%% fails, it may not be possible to run some other cases, as it
119%%% may have e.g. created some row or loaded some table, that it
120%%% didn't undo (since it failed).
121%%%-----------------------------------------------------------------
122
123init_all(Config) when is_list(Config) ->
124
125    ?IPRINT("init_all -> entry with"
126            "~n   Config: ~p"
127            "~n   Nodes:  ~p", [Config, nodes()]),
128
129    %% --
130    %% Start nodes
131    %%
132
133    ?IPRINT("init_all -> start sub-agent node"),
134    ?line {ok, SaNode}  = start_node(snmp_sa),
135
136    ?IPRINT("init_all -> start manager node"),
137    ?line {ok, MgrNode} = start_node(snmp_mgr),
138
139
140    %% --
141    %% Create necessary files ( and dirs )
142    %%
143
144    ?IPRINT("init_all -> lookup group top dir"),
145    GroupTopDir = ?config(snmp_group_top_dir, Config),
146    ?DBG("init_all -> GroupTopDir ~p", [GroupTopDir]),
147
148    ?IPRINT("init_all -> create agent dir"),
149    AgentDir = join(GroupTopDir, "agent/"),
150    ?line ok = file:make_dir(AgentDir),
151    ?DBG("init_all -> AgentDir ~p", [AgentDir]),
152
153    ?IPRINT("init_all -> create agent db dir"),
154    AgentDbDir = join(AgentDir, "db/"),
155    ?line ok   = file:make_dir(AgentDbDir),
156    ?DBG("init_all -> AgentDbDir ~p", [AgentDbDir]),
157
158    ?IPRINT("init_all -> create agent log dir"),
159    AgentLogDir = join(AgentDir, "log/"),
160    ?line ok    = file:make_dir(AgentLogDir),
161    ?DBG("init_all -> AgentLogDir ~p", [AgentLogDir]),
162
163    ?IPRINT("init_all -> create agent config dir"),
164    AgentConfDir = join(AgentDir, "conf/"),
165    ?line ok     = file:make_dir(AgentConfDir),
166    ?DBG("init_all -> AgentConfDir ~p", [AgentConfDir]),
167
168    ?IPRINT("init_all -> create manager dir"),
169    MgrDir   = join(GroupTopDir, "mgr/"),
170    ?line ok = file:make_dir(MgrDir),
171    ?DBG("init_all -> MgrDir ~p", [MgrDir]),
172
173    ?IPRINT("init_all -> create sub-agent dir"),
174    SaDir    = join(GroupTopDir, "sa/"),
175    ?line ok = file:make_dir(SaDir),
176    ?DBG("init_all -> SaDir ~p", [SaDir]),
177
178    ?IPRINT("init_all -> create sub-agent db dir"),
179    SaDbDir  = join(SaDir, "db/"),
180    ?line ok = file:make_dir(SaDbDir),
181    ?DBG("init_all -> SaDbDir ~p", [SaDbDir]),
182
183    %% MibDir = ?config(mib_dir, Config),
184    %% ?DBG("init_all -> MibDir ~p", [DataDir]),
185
186
187    %% --
188    %% Start and initiate mnesia
189    %%
190
191    ?IPRINT("init_all -> load mnesia application (local)"),
192    ?line ok = application:load(mnesia),
193
194    ?IPRINT("init_all -> load application mnesia on node ~p", [SaNode]),
195    ?line ok = rpc:call(SaNode, application, load, [mnesia]),
196
197    ?IPRINT("init_all -> application mnesia (local): set_env dir"),
198    ?line application_controller:set_env(mnesia, dir,
199					 join(AgentDbDir, "Mnesia1")),
200
201    ?IPRINT("init_all -> application mnesia: set_env dir on node ~p", [SaNode]),
202    ?line rpc:call(SaNode, application_controller, set_env,
203		   [mnesia, dir,  join(SaDir, "Mnesia2")]),
204
205    ?IPRINT("init_all -> create mnesia schema"),
206    ?line ok = mnesia:create_schema([SaNode, node()]),
207
208    ?IPRINT("init_all -> start application mnesia (local)"),
209    ?line ok = application:start(mnesia),
210
211    ?IPRINT("init_all -> start application mnesia on ~p", [SaNode]),
212    ?line ok = rpc:call(SaNode, application, start, [mnesia]),
213
214    ?IPRINT("init_all -> get localhost"),
215    Ip = ?LOCALHOST(),
216
217    ?IPRINT("init_all -> done when"
218            "~n   Nodes: ~p", [nodes()]),
219    [{snmp_sa,        SaNode},
220     {snmp_mgr,       MgrNode},
221     {snmp_master,    node()},
222     {agent_dir,      AgentDir ++ "/"},
223     {agent_db_dir,   AgentDbDir ++ "/"},
224     {agent_log_dir,  AgentLogDir ++ "/"},
225     {agent_conf_dir, AgentConfDir ++ "/"},
226     {sa_dir,         SaDir ++ "/"},
227     {sa_db_dir,      SaDbDir ++ "/"},
228     {mgr_dir,        MgrDir ++ "/"},
229     %% {mib_dir,        DataDir},
230     {ip,             Ip} |
231     Config].
232
233
234finish_all(Config) when is_list(Config) ->
235
236    ?IPRINT("finish_all -> entry with"
237            "~n   Config: ~p"
238            "~n   Nodes:  ~p", [Config, nodes()]),
239
240    SaNode  = ?config(snmp_sa, Config),
241    MgrNode = ?config(snmp_mgr, Config),
242
243    ?IPRINT("finish_all -> stop sub-agent node ~p", [SaNode]),
244    stop_node(SaNode),
245
246    ?IPRINT("finish_all -> stop manager node ~p", [MgrNode]),
247    stop_node(MgrNode),
248
249    ?IPRINT("finish_all -> stop mnesia application"),
250    application:stop(mnesia),
251
252    ?IPRINT("finish_all -> unload mnesia application"),
253    application:unload(mnesia),
254
255    ?IPRINT("finish_all -> stop when"
256            "~n   Nodes: ~p", [nodes()]),
257    ok.
258
259
260%% --- This one *must* be run first in each case ---
261
262init_case(Config) when is_list(Config) ->
263
264    ?DBG("init_case -> entry with"
265	 "~n   Config: ~p", [Config]),
266
267    SaNode     = ?config(snmp_sa,     Config),
268    MgrNode    = ?config(snmp_mgr,    Config),
269    MasterNode = ?config(snmp_master, Config),
270    %% MasterNode = node(),
271    IpFamily  = proplists:get_value(ipfamily, Config, inet),
272
273    SaHost         = ?HOSTNAME(SaNode),
274    MgrHost        = ?HOSTNAME(MgrNode),
275    MasterHost     = ?HOSTNAME(MasterNode),
276    {ok, MasterIP} = ?LIB:which_host_ip(MasterHost, IpFamily),
277    {ok, MIP}      = ?LIB:which_host_ip(MgrHost,    IpFamily),
278    {ok, SIP}      = ?LIB:which_host_ip(SaHost,     IpFamily),
279
280    ?IPRINT("init_case -> "
281            "~n   SaHost:     ~p"
282            "~n   MgrHost:    ~p"
283            "~n   MasterHost: ~p"
284            "~n   MasterIP:   ~p"
285            "~n   MIP:        ~p"
286            "~n   SIP:        ~p",
287            [SaHost, MgrHost, MasterHost, MasterIP, MIP, SIP]),
288
289    put(mgr_node,    MgrNode),
290    put(sa_node,     SaNode),
291    put(master_node, MasterNode),
292    put(sa_host,     SaHost),
293    put(mgr_host,    MgrHost),
294    put(master_host, MasterHost),
295    put(mip,         tuple_to_list(MIP)),
296    put(masterip,    tuple_to_list(MasterIP)),
297    put(sip,         tuple_to_list(SIP)),
298    put(ipfamily,    IpFamily),
299
300    MibDir = ?config(mib_dir, Config),
301    put(mib_dir, MibDir),
302    StdM = join(code:priv_dir(snmp), "mibs") ++ "/",
303    put(std_mib_dir, StdM),
304
305    MgrDir = ?config(mgr_dir, Config),
306    put(mgr_dir, MgrDir),
307
308    put(vsn, ?config(vsn, Config)),
309
310    ?IPRINT("init_case -> done with"
311            "~n   MasterNode: ~p"
312            "~n   SaNode:     ~p"
313            "~n   MgrNode:    ~p"
314            "~n   MibDir:     ~p", [MasterNode, SaNode, MgrNode, MibDir]),
315
316    {SaNode, MgrNode, MibDir}.
317
318
319%%%--------------------------------------------------
320%%% Used to test the standard mib with our
321%%% configuration.
322%%%--------------------------------------------------
323
324try_test(TcRunMod, TcRunFunc) ->
325    try_test(TcRunMod, TcRunFunc, []).
326
327try_test(TcRunMod, TcRunFunc, TcRunArgs) ->
328    try_test(TcRunMod, TcRunFunc, TcRunArgs, []).
329
330try_test(TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts) ->
331    Node      = get(mgr_node),
332    Mod       = ?MODULE,
333    Func      = tc_run,
334    Args      = [TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts],
335    tc_try(Node, Mod, Func, Args).
336
337%% We spawn a test case runner process on the manager node.
338%% The assumption is that the manager shall do something, but
339%% not all test cases have the manager perform actions.
340%% In some cases we make a rpc call back to the agent node directly
341%% and call something in the agent... (for example the info_test
342%% test case).
343%% We should use link (instead of monitor) in order for the test case
344%% timeout cleanup (kills) should have effect on the test case runner
345%% process as well.
346
347tc_try(N, M, F, A) ->
348    ?IPRINT("tc_try -> entry with"
349            "~n      N:     ~p"
350            "~n      M:     ~p"
351            "~n      F:     ~p"
352            "~n      A:     ~p"
353            "~n   when"
354            "~n      get(): ~p"
355            "~n", [N,
356                   M, F, A,
357                   get()]),
358    case net_adm:ping(N) of
359        pong ->
360            ?IPRINT("tc_try -> ~p still running - start runner~n", [N]),
361            OldFlag = trap_exit(true), % Make sure we catch it
362            Runner  = spawn_link(N, ?MODULE, tc_wait, [self(), get(), M, F, A]),
363            await_tc_runner_started(Runner, OldFlag),
364            await_tc_runner_done(Runner, OldFlag);
365        pang ->
366            ?WPRINT("tc_try -> ~p *not* running~n", [N]),
367            skip({node_not_running, N})
368    end.
369
370await_tc_runner_started(Runner, OldFlag) ->
371    ?IPRINT("await tc-runner (~p) start ack~n", [Runner]),
372    receive
373        {'EXIT', Runner, Reason} ->
374            ?EPRINT("TC runner start failed: "
375                    "~n   ~p~n", [Reason]),
376            exit({tc_runner_start_failed, Reason});
377        {tc_runner_started, Runner} ->
378            ?IPRINT("TC runner start acknowledged~n"),
379            ok
380    after 10000 -> %% We should *really* not have to wait this long, but...
381            trap_exit(OldFlag),
382            unlink_and_flush_exit(Runner),
383            RunnerInfo = ?PINFO(Runner),
384            ?EPRINT("TC runner start timeout: "
385                    "~n   ~p", [RunnerInfo]),
386            %% If we don't get a start ack within 10 seconds, we are f*ed
387            exit(Runner, kill),
388            exit({tc_runner_start, timeout, RunnerInfo})
389    end.
390
391await_tc_runner_done(Runner, OldFlag) ->
392    receive
393        {'EXIT', Runner, {udp_error, _} = Reason} ->
394	    ?EPRINT("TC runner failed with an udp error: "
395		    "~n   Reason: ~p"
396		    "~n", [Reason]),
397	    skip([{reason, Reason}]);
398
399	{'EXIT', Runner, Reason} ->
400            %% This is not a normal (tc) failure (that is the clause below).
401            %% Instead the tc runner process crashed, for some reason. So
402            %% check if have got any system events, and if so, skip.
403            SysEvs = snmp_test_global_sys_monitor:events(),
404            if
405                (SysEvs =:= []) ->
406                    ?EPRINT("TC runner failed: "
407                            "~n   ~p"
408			    "~n", [Reason]),
409                    exit({tc_runner_failed, Reason});
410                true ->
411                    ?WPRINT("TC runner failed when we got system events: "
412                            "~n   Reason:     ~p"
413                            "~n   Sys Events: ~p"
414                            "~n", [Reason, SysEvs]),
415                    skip([{reason, Reason}, {system_events, SysEvs}])
416            end;
417	{tc_runner_done, Runner, {'EXIT', {skip, Reason}}, Loc} ->
418	    ?WPRINT("call -> done with skip: "
419                    "~n   Reason: ~p"
420                    "~n   Loc:    ~p"
421                    "~n", [Reason, Loc]),
422            trap_exit(OldFlag),
423            unlink_and_flush_exit(Runner),
424	    put(test_server_loc, Loc),
425	    skip(Reason);
426	{tc_runner_done, Runner, {'EXIT', Rn}, Loc} ->
427	    ?EPRINT("call -> done with exit: "
428                    "~n   Rn:  ~p"
429                    "~n   Loc: ~p"
430                    "~n", [Rn, Loc]),
431            trap_exit(OldFlag),
432            unlink_and_flush_exit(Runner),
433	    put(test_server_loc, Loc),
434	    exit(Rn);
435	{tc_runner_done, Runner, Ret, _Loc} ->
436	    ?IPRINT("call -> done:"
437                    "~n   Ret: ~p"
438                    "~n   Loc: ~p", [Ret, _Loc]),
439            trap_exit(OldFlag),
440            unlink_and_flush_exit(Runner),
441	    case Ret of
442		{error, Reason} ->
443                    %% Any failures while we have system events are skipped
444                    SysEvs = snmp_test_global_sys_monitor:events(),
445                    if
446                        (SysEvs =:= []) ->
447                            ?EPRINT("TC failure: "
448                                    "~n   ~p"
449                                    "~n", [Reason]),
450                            exit(Reason);
451                        true ->
452                            ?WPRINT("TC failure when we got system events: "
453                                    "~n   Reason:     ~p"
454                                    "~n   Sys Events: ~p"
455                                    "~n", [Reason, SysEvs]),
456                            skip([{reason, Reason}, {system_events, SysEvs}])
457                    end;
458		{skip, Reason} ->
459		    skip(Reason);
460		OK ->
461		    OK
462	    end
463    end.
464
465trap_exit(Flag) when is_boolean(Flag) ->
466    erlang:process_flag(trap_exit, Flag).
467
468unlink_and_flush_exit(Pid) ->
469    unlink(Pid),
470    receive
471        {'EXIT', Pid, _} ->
472            ok
473    after 0 ->
474            ok
475    end.
476
477tc_wait(From, Env, M, F, A) ->
478    ?IPRINT("tc_wait -> entry with"
479            "~n   From: ~p"
480            "~n   Env:  ~p"
481            "~n   M:    ~p"
482            "~n   F:    ~p"
483            "~n   A:    ~p", [From, Env, M, F, A]),
484    From ! {tc_runner_started, self()},
485    lists:foreach(fun({K,V}) -> put(K,V) end, Env),
486    ?IPRINT("tc_wait -> env set - now run tc~n"),
487    Res = (catch apply(M, F, A)),
488    ?IPRINT("tc_wait -> tc run done: "
489            "~n   ~p"
490            "~n", [Res]),
491    From ! {tc_runner_done, self(), Res, get(test_server_loc)},
492    %% The point of this is that in some cases we have seen that the
493    %% exit signal having been "passed on" to the CT, which consider any
494    %% exit a fail (even if its {'EXIT', ok}).
495    %% So, just to be on the safe side, convert an 'ok' to a 'normal'.
496    case Res of
497        ok ->
498            exit(normal);
499        {ok, _} ->
500            exit(normal);
501        _ ->
502            exit(Res)
503    end.
504
505tc_run(Mod, Func, Args, Opts) ->
506    ?IPRINT("tc_run -> entry with"
507            "~n   Mod:  ~p"
508            "~n   Func: ~p"
509            "~n   Args: ~p"
510            "~n   Opts: ~p"
511            "~n", [Mod, Func, Args, Opts]),
512    (catch snmp_test_mgr:stop()), % If we had a running mgr from a failed case
513    M           = get(mib_dir),
514    Dir         = get(mgr_dir),
515    User        = snmp_misc:get_option(user, Opts, "all-rights"),
516    SecLevel    = snmp_misc:get_option(sec_level, Opts, noAuthNoPriv),
517    EngineID    = snmp_misc:get_option(engine_id, Opts, "agentEngine"),
518    CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
519    Community   = snmp_misc:get_option(community, Opts, "all-rights"),
520    ?DBG("tc_run -> start crypto app",[]),
521    _CryptoRes  = ?CRYPTO_START(),
522    ?DBG("tc_run -> Crypto: ~p", [_CryptoRes]),
523    StdM        = join(code:priv_dir(snmp), "mibs") ++ "/",
524    Vsn         = get(vsn),
525    ?IPRINT("tc_run -> config:"
526            "~n   M:           ~p"
527            "~n   Vsn:         ~p"
528            "~n   Dir:         ~p"
529            "~n   User:        ~p"
530            "~n   SecLevel:    ~p"
531            "~n   EngineID:    ~p"
532            "~n   CtxEngineID: ~p"
533            "~n   Community:   ~p"
534            "~n   StdM:        ~p"
535            "~n", [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]),
536    case snmp_test_mgr:start_link([%% {agent, snmp_test_lib:hostname()},
537                                   {packet_server_debug, true},
538                                   {debug,               false},
539                                   {agent,               get(master_host)},
540                                   {ipfamily,            get(ipfamily)},
541                                   {agent_udp,           4000},
542                                   %% <SEP-TRANSPORTS>
543                                   %% First port is used to request replies
544                                   %% Second port is used for traps sent
545                                   %% by the agent.
546                                   %% {agent_udp,           {4000, 4001}},
547                                   %% </SEP-TRANSPORTS>
548                                   {trap_udp,            5000},
549                                   {recbuf,              65535},
550                                   quiet,
551                                   Vsn,
552                                   {community,           Community},
553                                   {user,                User},
554                                   {sec_level,           SecLevel},
555                                   {engine_id,           EngineID},
556                                   {context_engine_id,   CtxEngineID},
557                                   {dir,                 Dir},
558                                   {mibs,                mibs(StdM, M)}]) of
559	{ok, _Pid} ->
560	    try apply(Mod, Func, Args) of
561                Res ->
562		    (catch snmp_test_mgr:stop()),
563		    Res
564            catch
565                C:{skip, Reason} ->
566                    ?WPRINT("apply (~w-) skip detected: "
567                            "~n   ~p", [C, Reason]),
568		    (catch snmp_test_mgr:stop()),
569                    ?SKIP(Reason);
570
571                throw:{error, Reason} ->
572                    tc_run_skip_sheck(Mod, Func, Args, Reason, throw);
573
574		exit:Reason ->
575                    tc_run_skip_sheck(Mod, Func, Args, Reason, exit)
576	    end;
577
578	{error, Reason} ->
579	    ?EPRINT("Failed starting (test) manager: "
580                    "~n   ~p", [Reason]),
581	    (catch snmp_test_mgr:stop()),
582	    ?line ?FAIL({mgr_start_error, Reason});
583
584	Err ->
585	    ?EPRINT("Failed starting (test) manager: "
586                    "~n   ~p", [Err]),
587	    (catch snmp_test_mgr:stop()),
588	    ?line ?FAIL({mgr_start_failure, Err})
589    end.
590
591%% We have hosts (mostly *very* slooow VMs) that
592%% can timeout anything. Since we are basically
593%% testing communication, we therefor must check
594%% for system events at every failure. Grrr!
595tc_run_skip_sheck(Mod, Func, Args, Reason, Cat) ->
596    SysEvs = snmp_test_global_sys_monitor:events(),
597    (catch snmp_test_mgr:stop()),
598    if
599        (SysEvs =:= []) ->
600            ?EPRINT("TC runner (~w-) failed: "
601                    "~n   ~p~n", [Cat, Reason]),
602            ?FAIL({apply_failed, {Mod, Func, Args}, Reason});
603        true ->
604            ?WPRINT("apply (~w) catched "
605                    "when we got system events: "
606                    "~n   Reason:     ~p"
607                    "~n   Sys Events: ~p"
608                    "~n", [Cat, Reason, SysEvs]),
609            ?SKIP([{category, Cat},
610                   {reason, Reason}, {system_events, SysEvs}])
611    end.
612
613
614%% ---------------------------------------------------------------
615%% ---                                                         ---
616%% ---                   Start the agent                       ---
617%% ---                                                         ---
618%% ---------------------------------------------------------------
619
620start_v1_agent(Config) when is_list(Config) ->
621    start_agent(Config, [v1]).
622
623start_v1_agent(Config, Opts) when is_list(Config) andalso is_list(Opts)  ->
624    start_agent(Config, [v1], Opts).
625
626start_v2_agent(Config) when is_list(Config) ->
627    start_agent(Config, [v2]).
628
629start_v2_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) ->
630    start_agent(Config, [v2], Opts).
631
632start_v3_agent(Config) when is_list(Config) ->
633    start_agent(Config, [v3]).
634
635start_v3_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) ->
636    start_agent(Config, [v3], Opts).
637
638start_bilingual_agent(Config) when is_list(Config) ->
639    start_agent(Config, [v1,v2]).
640
641start_bilingual_agent(Config, Opts)
642  when is_list(Config) andalso is_list(Opts) ->
643    start_agent(Config, [v1,v2], Opts).
644
645start_mt_agent(Config) ->
646    start_mt_agent(Config, true, []).
647
648start_mt_agent(Config, MT) ->
649    start_mt_agent(Config, MT, []).
650
651start_mt_agent(Config, MT, Opts)
652  when is_list(Config) andalso
653       ((MT =:= true) orelse (MT =:= extended)) andalso
654       is_list(Opts) ->
655    start_agent(Config, [v2], [{multi_threaded, MT} | Opts]).
656
657start_agent(Config, Vsns) ->
658    start_agent(Config, Vsns, []).
659start_agent(Config, Vsns, Opts) ->
660
661    ?IPRINT("start_agent -> entry (~p) with"
662            "~n   Config: ~p"
663            "~n   Vsns:   ~p"
664            "~n   Opts:   ~p", [node(), Config, Vsns, Opts]),
665
666    ?line AgentLogDir  = ?config(agent_log_dir,      Config),
667    ?line AgentConfDir = ?config(agent_conf_dir,     Config),
668    ?line AgentDbDir   = ?config(agent_db_dir,       Config),
669    ?line SaNode       = ?config(snmp_sa,            Config),
670    ?line InetBackend  = ?config(socket_create_opts, Config),
671
672    Env = app_agent_env_init(
673	    [{versions,         Vsns},
674	     {agent_type,       master},
675	     {agent_verbosity,  trace},
676             {get_mechanism,    snmp_agent_test_get},
677	     {db_dir,           AgentDbDir},
678	     {audit_trail_log,  [{type, read_write},
679				 {dir,  AgentLogDir},
680				 {size, {10240, 10}}]},
681	     {config,           [{dir, AgentConfDir},
682				 {force_load, false},
683				 {verbosity,  trace}]},
684	     {local_db,         [{repair,    true},
685				 {verbosity, log}]},
686	     {mib_server,       [{verbosity, log}]},
687	     {symbolic_store,   [{verbosity, log}]},
688	     {note_store,       [{verbosity, log}]},
689	     {net_if,           [{verbosity, trace},
690                                 {options,   InetBackend}]}],
691	    Opts),
692
693
694    process_flag(trap_exit,true),
695
696    ?IPRINT("start_agent -> try start snmp app supervisor", []),
697    {ok, AppSup} = snmp_app_sup:start_link(),
698    unlink(AppSup),
699    ?DBG("start_agent -> snmp app supervisor: ~p", [AppSup]),
700
701    ?IPRINT("start_agent -> try start master agent",[]),
702    ?line Sup = start_sup(Env),
703    ?line unlink(Sup),
704    ?DBG("start_agent -> snmp supervisor: ~p", [Sup]),
705
706    ?IPRINT("start_agent -> try (rpc) start sub agent on ~p", [SaNode]),
707    ?line SaDir = ?config(sa_dir, Config),
708    ?line {ok, Sub} = start_sub_sup(SaNode, SaDir),
709    ?DBG("start_agent -> done", []),
710
711    ?line [{snmp_app_sup, AppSup},
712           {snmp_sup,     {Sup, self()}},
713           {snmp_sub,     Sub} | Config].
714
715
716app_agent_env_init(Env0, Opts) ->
717    ?DBG("app_agent_env_init -> unload snmp",[]),
718    ?line application:unload(snmp),
719
720    ?DBG("app_agent_env_init -> load snmp",[]),
721    ?line application:load(snmp),
722
723    ?DBG("app_agent_env_init -> "
724	 "merge or maybe replace (snmp agent) app env",[]),
725    Env = add_or_maybe_merge_agent_env(Opts, Env0),
726    ?DBG("app_agent_env_init -> merged env: "
727	 "~n   ~p", [Env]),
728
729    %% We put it into the app environment just as
730    %% a precaution, since when starting normally,
731    %% this is where the environment is extracted from.
732    app_agent_set_env(Env),
733    Env.
734
735app_agent_set_env(Value) ->
736    application_controller:set_env(snmp, agent, Value).
737
738add_or_maybe_merge_agent_env([], Env) ->
739    ?DBG("merging agent env -> merged", []),
740    lists:keysort(1, Env);
741add_or_maybe_merge_agent_env([{Key, Value1}|Opts], Env) ->
742    ?DBG("merging agent env -> add, replace or merge ~p", [Key]),
743    case lists:keysearch(Key, 1, Env) of
744	{value, {Key, Value1}} ->
745	    %% Identical, move on
746	    ?DBG("merging agent env -> "
747		 "no need to merge ~p - identical - keep: "
748		 "~n   ~p", [Key, Value1]),
749	    add_or_maybe_merge_agent_env(Opts, Env);
750	{value, {Key, Value2}} ->
751	    %% Another value, merge or replace
752	    NewValue = merge_or_replace_agent_env(Key, Value1, Value2),
753	    Env2 = lists:keyreplace(Key, 1, Env, {Key, NewValue}),
754	    add_or_maybe_merge_agent_env(Opts, Env2);
755	false ->
756	    ?DBG("merging agent env -> no old ~p to merge with - add: "
757		 "~n   ~p", [Key, Value1]),
758	    add_or_maybe_merge_agent_env(Opts, [{Key, Value1}|Env])
759    end.
760
761merge_or_replace_agent_env(versions, NewVersions, _OldVersions) ->
762    ?DBG("merging agent env -> versions replaced: ~p -> ~p",
763	 [NewVersions, _OldVersions]),
764    NewVersions;
765merge_or_replace_agent_env(agent_type, NewType, _OldType) ->
766    ?DBG("merging agent env -> agent type replaced: ~p -> ~p",
767	 [NewType, _OldType]),
768    NewType;
769merge_or_replace_agent_env(agent_verbosity, NewVerbosity, _OldVerbosity) ->
770    ?DBG("merging agent env -> agent verbosity replaced: ~p -> ~p",
771	 [NewVerbosity, _OldVerbosity]),
772    NewVerbosity;
773merge_or_replace_agent_env(db_dir, NewDbDir, _OldDbDir) ->
774    ?DBG("merging agent env -> db-dir replaced: ~p -> ~p",
775	 [NewDbDir, _OldDbDir]),
776    NewDbDir;
777merge_or_replace_agent_env(audit_trail_log, NewATL, OldATL) ->
778    merge_or_replace_agent_env_atl(NewATL, OldATL);
779merge_or_replace_agent_env(config, NewConfig, OldConfig) ->
780    merge_or_replace_agent_env_config(NewConfig, OldConfig);
781merge_or_replace_agent_env(local_db, NewLdb, OldLdb) ->
782    merge_or_replace_agent_env_ldb(NewLdb, OldLdb);
783merge_or_replace_agent_env(mib_storage, NewMst, OldMst) ->
784    merge_or_replace_agent_env_mib_storage(NewMst, OldMst);
785merge_or_replace_agent_env(mib_server, NewMibs, OldMibs) ->
786    merge_or_replace_agent_env_mib_server(NewMibs, OldMibs);
787merge_or_replace_agent_env(symbolic_store, NewSymStore, OldSymStore) ->
788    merge_or_replace_agent_env_symbolic_store(NewSymStore, OldSymStore);
789merge_or_replace_agent_env(note_store, NewNoteStore, OldNoteStore) ->
790    merge_or_replace_agent_env_note_store(NewNoteStore, OldNoteStore);
791merge_or_replace_agent_env(net_if, NewNetIf, OldNetIf) ->
792    merge_or_replace_agent_env_net_if(NewNetIf, OldNetIf);
793merge_or_replace_agent_env(Key, NewValue, OldValue) ->
794    ?FAIL({not_implemented_merge_or_replace,
795	   Key, NewValue, OldValue}).
796
797merge_or_replace_agent_env_atl(New, Old) ->
798    ATL = merge_agent_options(New, Old),
799    ?DBG("merging agent env -> audit-trail-log merged: "
800	 "~n   ~p | ~p -> ~p", [New, Old, ATL]),
801    ATL.
802
803merge_or_replace_agent_env_config(New, Old) ->
804    Config = merge_agent_options(New, Old),
805    case lists:keymember(dir, 1, Config) of
806	true ->
807	    ?DBG("merging agent env -> config merged: "
808		 "~n   ~p | ~p -> ~p", [New, Old, Config]),
809	    Config;
810	false ->
811	    ?FAIL({missing_mandatory_option, {config, dir}})
812    end.
813
814merge_or_replace_agent_env_ldb(New, Old) ->
815    LDB = merge_agent_options(New, Old),
816    ?DBG("merging agent env -> local-db merged: "
817	 "~n   ~p | ~p -> ~p", [New, Old, LDB]),
818    LDB.
819
820merge_or_replace_agent_env_mib_storage(NewMibStorage, OldMibStorage) ->
821    %% Shall we merge or replace?
822    %% module is mandatory. We will only merge if NewModule is
823    %% equal to OldModule.
824    NewModule =
825	case lists:keysearch(module, 1, NewMibStorage) of
826	    {value, {module, M}} ->
827		M;
828	    false ->
829		?FAIL({missing_mandatory_option, {mib_storage, module}})
830	end,
831    case lists:keysearch(module, 1, OldMibStorage) of
832	{value, {module, NewModule}} ->
833	    %% Same module => merge
834	    %% Non-ex new options => remove
835	    %% Ex new options and non-ex old options => replace
836	    %% Otherwise merge
837	    case lists:keysearch(options, 1, NewMibStorage) of
838		false ->
839		    ?DBG("merging agent env -> "
840			 "no mib-storage ~p merge needed - "
841			 "no new options (= remove old options)", [NewModule]),
842		    NewMibStorage;
843		{value, {options, NewOptions}} ->
844		    case lists:keysearch(options, 1, OldMibStorage) of
845			false ->
846			    ?DBG("merging agent env -> "
847				 "no mib-storage ~p merge needed - "
848				 "no old options", [NewModule]),
849			    NewMibStorage;
850			{value, {options, OldOptions}} ->
851			    MergedOptions =
852				merge_agent_options(NewOptions, OldOptions),
853			    ?DBG("merging agent env -> mib-storage ~p merged: "
854				 "~n   Options: ~p | ~p -> ~p",
855				 [NewModule,
856				  NewOptions, OldOptions, MergedOptions]),
857			    [{module,  NewModule},
858			     {options, MergedOptions}]
859		    end
860	    end;
861	_ ->
862	    %% Diff module => replace
863	    ?DBG("merging agent env -> "
864		 "no mib-storage ~p merge needed - "
865		 "new module", [NewModule]),
866	    NewMibStorage
867    end.
868
869merge_or_replace_agent_env_mib_server(New, Old) ->
870    MibServer = merge_agent_options(New, Old),
871    ?DBG("merging agent env -> mib-server merged: "
872	 "~n   ~p | ~p -> ~p", [New, Old, MibServer]),
873    MibServer.
874
875merge_or_replace_agent_env_symbolic_store(New, Old) ->
876    SymbolicStore = merge_agent_options(New, Old),
877    ?DBG("merging agent env -> symbolic-store merged: "
878	 "~n   ~p | ~p -> ~p", [New, Old, SymbolicStore]),
879    SymbolicStore.
880
881merge_or_replace_agent_env_note_store(New, Old) ->
882    NoteStore = merge_agent_options(New, Old),
883    ?DBG("merging agent env -> note-store merged: "
884	 "~n   ~p | ~p -> ~p", [New, Old, NoteStore]),
885    NoteStore.
886
887merge_or_replace_agent_env_net_if(New, Old) ->
888    NetIf = merge_agent_options(New, Old),
889    ?DBG("merging agent env -> net-if merged: "
890	 "~n   ~p | ~p -> ~p", [New, Old, NetIf]),
891    NetIf.
892
893merge_agent_options([], Options) ->
894    lists:keysort(1, Options);
895merge_agent_options([{Key, _Value} = Opt|Opts], Options) ->
896    case lists:keysearch(Key, 1, Options) of
897	{value, _} ->
898	    NewOptions = lists:keyreplace(Key, 1, Options, Opt),
899	    merge_agent_options(Opts, NewOptions);
900	false ->
901	    merge_agent_options(Opts, [Opt|Options])
902    end.
903
904
905stop_agent(Config) when is_list(Config) ->
906    ?IPRINT("stop_agent -> entry with"
907            "~n   Config: ~p",[Config]),
908
909
910    %% Stop the sub-agent (the agent supervisor)
911    {SubSup, SubPar} = ?config(snmp_sub, Config),
912    ?IPRINT("stop_agent -> attempt to stop sub agent (~p)"
913            "~n   Sub Sup info: "
914            "~n      ~p"
915            "~n   Sub Par info: "
916            "~n      ~p",
917            [SubSup, ?PINFO(SubSup), ?PINFO(SubPar)]),
918    stop_sup(SubSup, SubPar),
919    Config2 = lists:keydelete(snmp_sub, 1, Config),
920
921
922    %% Stop the master-agent (the top agent supervisor)
923    {MasterSup, MasterPar} = ?config(snmp_sup, Config),
924    ?IPRINT("stop_agent -> attempt to stop master agent (~p)"
925            "~n   Master Sup: "
926            "~n      ~p"
927            "~n   Master Par: "
928            "~n      ~p"
929            "~n   Agent Info: "
930            "~n      ~p",
931            [MasterSup,
932             ?PINFO(MasterSup), ?PINFO(MasterPar),
933             agent_info(MasterSup)]),
934    stop_sup(MasterSup, MasterPar),
935    Config3 = lists:keydelete(snmp_sup, 1, Config2),
936
937
938    %% Stop the top supervisor (of the snmp app)
939    AppSup = ?config(snmp_app_sup, Config),
940    ?IPRINT("stop_agent -> attempt to app sup ~p"
941            "~n   App Sup: ~p",
942            [AppSup, ?PINFO(AppSup)]),
943    Config4 = lists:keydelete(snmp_app_sup, 1, Config3),
944
945
946    ?IPRINT("stop_agent -> done", []),
947    Config4.
948
949
950start_sup(Env) ->
951    case (catch snmp_app_sup:start_agent(normal, Env)) of
952	{ok, S} ->
953	    ?DBG("start_agent -> started, Sup: ~p", [S]),
954	    S;
955
956	Else ->
957	    ?EPRINT("start_agent -> unknown result: ~n~p", [Else]),
958	    %% Get info about the apps we depend on
959	    ?FAIL({start_failed, Else, ?IS_MNESIA_RUNNING()})
960    end.
961
962stop_sup(Pid, _) when (node(Pid) =:= node()) ->
963    case (catch process_info(Pid)) of
964	PI when is_list(PI) ->
965	    ?IPRINT("stop_sup -> attempt to stop ~p", [Pid]),
966	    Ref = erlang:monitor(process, Pid),
967	    exit(Pid, kill),
968	    await_stopped(Pid, Ref);
969	{'EXIT', _Reason} ->
970	    ?IPRINT("stop_sup -> ~p not running", [Pid]),
971	    ok
972    end;
973stop_sup(Pid, _) ->
974    ?IPRINT("stop_sup -> attempt to stop ~p", [Pid]),
975    Ref = erlang:monitor(process, Pid),
976    ?IPRINT("stop_sup -> Ref: ~p", [Ref]),
977    exit(Pid, kill),
978    await_stopped(Pid, Ref).
979
980await_stopped(Pid, Ref) ->
981    receive
982        {'DOWN', Ref, process, Pid, _Reason} ->
983            ?DBG("received down message for ~p", [Pid]),
984            ok
985    after 10000 ->
986	    ?EPRINT("await_stopped -> timeout for ~p",[Pid]),
987	    erlang:demonitor(Ref),
988	    ?FAIL({failed_stop,Pid})
989    end.
990
991
992%% --- start subagent supervisor ---
993
994start_sub_sup(Node, Dir) ->
995    rpc:call(Node, ?MODULE, start_sub_sup, [Dir]).
996
997start_sub_sup(Dir) ->
998    ?DBG("start_sub -> entry",[]),
999    Opts = [{db_dir,     Dir},
1000            {supervisor, [{verbosity, trace}]}],
1001    {ok, P} = snmpa_supervisor:start_sub_sup(Opts),
1002    unlink(P),
1003    {ok, {P, self()}}.
1004
1005
1006%% --- start and stop subagents ---
1007
1008start_subagent(SaNode, RegTree, Mib) ->
1009    ?DBG("start_subagent -> entry with"
1010	"~n   SaNode:  ~p"
1011	"~n   RegTree: ~p"
1012	"~n   Mib:     ~p", [SaNode, RegTree, Mib]),
1013    MA = whereis(snmp_master_agent),
1014    ?DBG("start_subagent -> MA: ~p", [MA]),
1015    MibDir = get(mib_dir),
1016    Mib1   = join(MibDir, Mib),
1017    Mod    = snmpa_supervisor,
1018    Func   = start_sub_agent,
1019    Args   = [MA, RegTree, [Mib1]],
1020    case rpc:call(SaNode, Mod, Func, Args) of
1021	{ok, SA} ->
1022	    ?DBG("start_subagent -> SA: ~p", [SA]),
1023	    {ok, SA};
1024	Error ->
1025	    ?FAIL({subagent_start_failed, SaNode, Error, [MA, RegTree, Mib1]})
1026    end.
1027
1028stop_subagent(SA) ->
1029    ?DBG("stop_subagent -> entry with"
1030	 "~n   SA: ~p", [SA]),
1031    rpc:call(node(SA), snmpa_supervisor, stop_sub_agent, [SA]).
1032
1033
1034mibs(StdMibDir,MibDir) ->
1035    [join(StdMibDir, ?v1_2("STANDARD-MIB.bin", "SNMPv2-MIB.bin")),
1036     join(MibDir, "OLD-SNMPEA-MIB.bin"),
1037     join(StdMibDir, "SNMP-FRAMEWORK-MIB"),
1038     join(StdMibDir, "SNMP-MPD-MIB"),
1039     join(StdMibDir, "SNMP-VIEW-BASED-ACM-MIB"),
1040     join(StdMibDir, "SNMP-USER-BASED-SM-MIB"),
1041     join(StdMibDir, "SNMP-TARGET-MIB"),
1042     join(StdMibDir, "SNMP-NOTIFICATION-MIB"),
1043     join(MibDir, "Klas1.bin"),
1044     join(MibDir, "Klas2.bin"),
1045     join(MibDir, "Klas3.bin"),
1046     join(MibDir, "Klas4.bin"),
1047     join(MibDir, "SA-MIB.bin"),
1048     join(MibDir, "TestTrap.bin"),
1049     join(MibDir, "Test1.bin"),
1050     join(MibDir, "Test2.bin"),
1051     join(MibDir, "TestTrapv2.bin")].
1052
1053
1054%% --- various mib load/unload functions ---
1055
1056load_master(Mib) ->
1057    ?DBG("load_master -> entry with"
1058	"~n   Mib: ~p", [Mib]),
1059    snmpa:unload_mib(snmp_master_agent, Mib),	% Unload for safety
1060    ok = snmpa:load_mib(snmp_master_agent, join(get(mib_dir), Mib)).
1061
1062load_master_std(Mib) ->
1063    ?DBG("load_master_std -> entry with"
1064	"~n   Mib: ~p", [Mib]),
1065    snmpa:unload_mib(snmp_master_agent, Mib),	% Unload for safety
1066    ok = snmpa:load_mibs(snmp_master_agent, join(get(std_mib_dir), Mib)).
1067
1068unload_master(Mib) ->
1069    ?DBG("unload_master -> entry with"
1070	"~n   Mib: ~p", [Mib]),
1071    ok = snmpa:unload_mib(snmp_master_agent, Mib).
1072
1073loaded_mibs() ->
1074    ?DBG("loaded_mibs -> entry",[]),
1075    Info = snmpa:info(snmp_master_agent),
1076    {value, {loaded_mibs, Mibs}} = lists:keysearch(loaded_mibs, 1, Info),
1077    [atom_to_list(Mib) || {Mib,_,_} <- Mibs].
1078
1079unload_mibs(Mibs) ->
1080    ?DBG("unload_mibs -> entry with"
1081	"~n   Mibs: ~p", [Mibs]),
1082    ok = snmpa:unload_mibs(snmp_master_agent, Mibs).
1083
1084
1085agent_info(Sup) ->
1086    ?DBG("agent_info -> entry with"
1087	 "~n   Sup: ~p", [Sup]),
1088    rpc:call(node(Sup), snmpa, info, []).
1089
1090
1091%% ---
1092%% The first two arguments are simple to be able to find where in the
1093%% (test) code this call is made.
1094
1095expect(Mod, Line, What) ->
1096    Fun = fun() -> do_expect(What) end,
1097    expect2(Mod, Line, Fun).
1098
1099expect(Mod, Line, What, ExpVBs) ->
1100    Fun = fun() -> do_expect(What, ExpVBs) end,
1101    expect2(Mod, Line, Fun).
1102
1103expect(Mod, Line, Error, Index, ExpVBS) ->
1104    Fun = fun() -> do_expect(Error, Index, ExpVBS) end,
1105    expect2(Mod, Line, Fun).
1106
1107expect(Mod, Line, Type, Enterp, Generic, Specific, ExpVBs) ->
1108    Fun = fun() -> do_expect(Type, Enterp, Generic, Specific, ExpVBs) end,
1109    expect2(Mod, Line, Fun).
1110
1111expect2(Mod, Line, F) ->
1112    io_format_expect("for ~w:~w", [Mod, Line]),
1113    case F() of
1114	{error, Reason} ->
1115	    io_format_expect("failed at ~w:~w => "
1116                             "~n      ~p", [Mod, Line, Reason]),
1117	    throw({error, {expect, Mod, Line, Reason}});
1118	Else ->
1119	    io_format_expect("result for ~w:~w => "
1120                             "~n      ~p", [Mod, Line, Else]),
1121	    Else
1122    end.
1123
1124
1125%% ----------------------------------------------------------------------
1126
1127-define(BASE_REQ_TIMEOUT, 3500).
1128
1129get_timeout() ->
1130    %% Try to figure out how "fast" a machine is.
1131    %% We assume that the number of schedulers
1132    %% (which depends on the number of core:s)
1133    %% effect the performance of the host...
1134    %% This is obviously not enough. The network
1135    %% also matterns, clock freq or the CPU, ...
1136    %% But its better than what we had before...
1137    case erlang:system_info(schedulers) of
1138        N when is_integer(N) ->
1139            ?BASE_REQ_TIMEOUT + timer:seconds(10 div N);
1140        _ ->
1141            ?BASE_REQ_TIMEOUT
1142    end.
1143
1144receive_pdu(To) ->
1145    receive
1146	{snmp_pdu, PDU} when is_record(PDU, pdu) ->
1147	    PDU
1148    after To ->
1149	    {error, timeout}
1150    end.
1151
1152receive_trap(To) ->
1153    receive
1154	{snmp_pdu, PDU} when is_record(PDU, trappdu) ->
1155	    PDU
1156    after To ->
1157	    {error, timeout}
1158    end.
1159
1160
1161io_format_expect(F) ->
1162    io_format_expect(F, []).
1163
1164io_format_expect(F, A) ->
1165    ?IPRINT("EXPECT " ++ F, A).
1166
1167
1168do_expect(Expect) when is_atom(Expect) ->
1169    do_expect({Expect, get_timeout()});
1170
1171do_expect({any_pdu, To})
1172  when is_integer(To) orelse (To =:= infinity) ->
1173    io_format_expect("any PDU"),
1174    receive_pdu(To);
1175
1176do_expect({any_trap, To}) ->
1177    io_format_expect("any TRAP within ~w", [To]),
1178    receive_trap(To);
1179
1180do_expect({timeout, To}) ->
1181    io_format_expect("nothing within ~w", [To]),
1182    receive
1183	X ->
1184	    {error, {unexpected, X}}
1185    after
1186	To ->
1187	    ok
1188    end;
1189
1190do_expect({Err, To})
1191  when (is_atom(Err) andalso
1192	((is_integer(To) andalso To > 0) orelse (To =:= infinity))) ->
1193    io_format_expect("error ~w within ~w", [Err, To]),
1194    do_expect({{error, Err}, To});
1195
1196do_expect({error, Err}) when is_atom(Err) ->
1197    Check = fun(_, R) -> R end,
1198    io_format_expect("error ~w", [Err]),
1199    do_expect2(Check, any, Err, any, any, get_timeout());
1200do_expect({{error, Err}, To}) ->
1201    Check = fun(_, R) -> R end,
1202    io_format_expect("error ~w within ~w", [Err, To]),
1203    do_expect2(Check, any, Err, any, any, To);
1204
1205%% exp_varbinds() -> [exp_varbind()]
1206%% exp_varbind()  -> any | {Oid, any} | {Oid, Value}
1207%% Oid            -> [integer()]
1208%% Value          -> term()
1209%% ExpVBs         -> exp_varbinds() | {VbsCondition, exp_varbinds()}
1210do_expect(ExpVBs) ->
1211    Check = fun(_, R) -> R end,
1212    io_format_expect("'get-response'"
1213                     "~n   with"
1214                     "~n      Varbinds: ~p", [ExpVBs]),
1215    do_expect2(Check, 'get-response', noError, 0, ExpVBs, get_timeout()).
1216
1217
1218do_expect(v2trap, ExpVBs) ->
1219    Check = fun(_, R) -> R end,
1220    io_format_expect("'snmpv2-trap' with"
1221                     "~n      Varbinds: ~p", [ExpVBs]),
1222    do_expect2(Check, 'snmpv2-trap', noError, 0, ExpVBs, get_timeout());
1223
1224
1225do_expect(report, ExpVBs) ->
1226    Check = fun(_, R) -> R end,
1227    io_format_expect("'report' with"
1228                     "~n      Varbinds: ~p", [ExpVBs]),
1229    do_expect2(Check, 'report', noError, 0, ExpVBs, get_timeout());
1230
1231
1232do_expect(inform, ExpVBs) ->
1233    do_expect({inform, true}, ExpVBs);
1234
1235do_expect({inform, false}, ExpVBs) ->
1236    Check = fun(_, R) -> R end,
1237    io_format_expect("'inform-request' (false) with"
1238                     "~n      Varbinds: ~p", [ExpVBs]),
1239    do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout());
1240
1241do_expect({inform, true}, ExpVBs) ->
1242    Check =
1243	fun(PDU, ok) ->
1244		RespPDU = PDU#pdu{type         = 'get-response',
1245				  error_status = noError,
1246				  error_index  = 0},
1247		snmp_test_mgr:rpl(RespPDU),
1248		ok;
1249	   (_, Err) ->
1250		Err
1251	end,
1252    io_format_expect("'inform-request' (true) with"
1253                     "~n      Varbinds: ~p", [ExpVBs]),
1254    do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout());
1255
1256do_expect({inform, {error, EStat, EIdx}}, ExpVBs)
1257  when is_atom(EStat) andalso is_integer(EIdx) ->
1258    Check =
1259	fun(PDU, ok) ->
1260		RespPDU = PDU#pdu{type         = 'get-response',
1261				  error_status = EStat,
1262				  error_index  = EIdx},
1263		snmp_test_mgr:rpl(RespPDU),
1264		ok;
1265	   (_, Err) ->
1266		Err
1267	end,
1268    io_format_expect("'inform-request' (error) with"
1269                     "~n      Error Status: ~p"
1270                     "~n      Error Index:  ~p"
1271                     "~n      Varbinds:     ~p", [EStat, EIdx, ExpVBs]),
1272    do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout()).
1273
1274
1275do_expect(Err, Idx, ExpVBs) ->
1276    do_expect(Err, Idx, ExpVBs, get_timeout()).
1277
1278do_expect(Err, Idx, ExpVBs, To)
1279  when is_atom(Err) andalso
1280       (is_integer(Idx) orelse is_list(Idx) orelse (Idx == any)) ->
1281    Check = fun(_, R) -> R end,
1282    io_format_expect("'get-response' withing ~w ms with"
1283                     "~n      Error:    ~p"
1284                     "~n      Index:    ~p"
1285                     "~n      Varbinds: ~p", [To, Err, Idx, ExpVBs]),
1286    do_expect2(Check, 'get-response', Err, Idx, ExpVBs, To).
1287
1288
1289do_expect(Type, Enterp, Generic, Specific, ExpVBs) ->
1290    do_expect(Type, Enterp, Generic, Specific, ExpVBs, get_timeout()).
1291
1292do_expect(trap, Enterp, Generic, Specific, ExpVBs, To) ->
1293    io_format_expect("trap within ~w ms with"
1294                     "~n      Enterp:   ~w"
1295                     "~n      Generic:  ~w"
1296                     "~n      Specific: ~w"
1297                     "~n      Varbinds: ~w",
1298                     [To, Enterp, Generic, Specific, ExpVBs]),
1299    PureE = purify_oid(Enterp),
1300    case receive_trap(To) of
1301	#trappdu{enterprise    = PureE,
1302		 generic_trap  = Generic,
1303		 specific_trap = Specific,
1304		 varbinds      = VBs} ->
1305	    check_vbs(purify_oids(ExpVBs), VBs);
1306
1307	#trappdu{enterprise    = Ent2,
1308		 generic_trap  = G2,
1309		 specific_trap = Spec2,
1310		 varbinds      = VBs} ->
1311	    {error, {unexpected_trap,
1312		     {PureE, Generic, Specific, ExpVBs},
1313		     {Ent2, G2, Spec2, VBs}}};
1314
1315	{error, timeout} = Error ->
1316            SysEvs = snmp_test_global_sys_monitor:events(),
1317	    io_format_expect("[expecting trap] got timeout when system events:"
1318                             "~n   ~p", [SysEvs]),
1319            if
1320                (SysEvs =:= []) ->
1321                    Error;
1322                true ->
1323                    skip({system_events, SysEvs})
1324            end;
1325
1326
1327	Error ->
1328	    Error
1329    end.
1330
1331
1332do_expect2(Check, Type, Err, Idx, ExpVBs, To)
1333  when is_function(Check) andalso
1334       is_atom(Type) andalso
1335       is_atom(Err) andalso
1336       (is_integer(Idx) orelse is_list(Idx) orelse (Idx =:= any)) andalso
1337       (is_list(ExpVBs) orelse (ExpVBs =:= any)) andalso
1338       (is_integer(To) orelse (To =:= infinity)) ->
1339
1340    case receive_pdu(To) of
1341
1342	#pdu{type         = Type,
1343	     error_status = Err,
1344	     error_index  = Idx} when ExpVBs =:= any ->
1345	    io_format_expect("received expected pdu (1)"),
1346	    ok;
1347
1348	#pdu{type         = Type,
1349	     request_id   = ReqId,
1350	     error_status = Err2,
1351	     error_index  = Idx} when ExpVBs =:= any ->
1352	    io_format_expect("received expected pdu with "
1353                             "unexpected error status (2): "
1354                             "~n   Error Status: ~p", [Err2]),
1355	    {error, {unexpected_error_status, Err, Err2, ReqId}};
1356
1357	#pdu{error_status = Err} when (Type   =:= any) andalso
1358				      (Idx    =:= any) andalso
1359				      (ExpVBs =:= any) ->
1360	    io_format_expect("received expected pdu (3)"),
1361	    ok;
1362
1363	#pdu{request_id   = ReqId,
1364	     error_status = Err2} when (Type   =:= any) andalso
1365				       (Idx    =:= any) andalso
1366				       (ExpVBs =:= any) ->
1367	    io_format_expect("received expected pdu with "
1368                             "unexpected error status (4): "
1369                             "~n   Error Status: ~p", [Err2]),
1370	    {error, {unexpected_error_status, Err, Err2, ReqId}};
1371
1372	#pdu{type         = Type,
1373	     error_status = Err} when (Idx =:= any) andalso
1374				      (ExpVBs =:= any) ->
1375	    io_format_expect("received expected pdu (5)", []),
1376	    ok;
1377
1378	#pdu{type         = Type,
1379	     request_id   = ReqId,
1380	     error_status = Err2} when (Idx =:= any) andalso
1381				       (ExpVBs =:= any) ->
1382	    io_format_expect("received expected pdu with "
1383                             "unexpected error status (6): "
1384                             "~n   Error Status: ~p", [Err2]),
1385	    {error, {unexpected_error_status, Err, Err2, ReqId}};
1386
1387	#pdu{type         = Type,
1388	     request_id   = ReqId,
1389	     error_status = Err,
1390	     error_index  = EI} when is_list(Idx) andalso (ExpVBs =:= any) ->
1391	    case lists:member(EI, Idx) of
1392		true ->
1393		    io_format_expect("received expected pdu with "
1394                                     "expected error index (7)"),
1395		    ok;
1396		false ->
1397		    io_format_expect("received expected pdu with "
1398                                     "unexpected error index (8): "
1399                                     "~n   Error Index: ~p", [EI]),
1400		    {error, {unexpected_error_index, EI, Idx, ReqId}}
1401	    end;
1402
1403	#pdu{type         = Type,
1404	     request_id   = ReqId,
1405	     error_status = Err2,
1406	     error_index  = EI} when is_list(Idx) andalso (ExpVBs =:= any) ->
1407	    case lists:member(EI, Idx) of
1408		true ->
1409		    io_format_expect("received expected pdu with "
1410                                     "unexpected error status (9): "
1411                                     "~n   Error Status: ~p", [Err2]),
1412		    {error, {unexpected_error_status, Err, Err2, ReqId}};
1413		false ->
1414		    io_format_expect("received expected pdu with "
1415                                     "unexpected error (10): "
1416                                     "~n   Error Status: ~p"
1417                                     "~n   Error index:  ~p", [Err2, EI]),
1418		    {error, {unexpected_error, {Err, Idx}, {Err2, EI}, ReqId}}
1419	    end;
1420
1421	#pdu{type         = Type2,
1422	     request_id   = ReqId,
1423	     error_status = Err2,
1424	     error_index  = Idx2} when ExpVBs =:= any ->
1425	    io_format_expect("received unexpected pdu with (11) "
1426                             "~n   Type:         ~p"
1427                             "~n   ReqId:        ~p"
1428                             "~n   Error status: ~p"
1429                             "~n   Error index:  ~p",
1430                             [Type2, ReqId, Err2, Idx2]),
1431	    {error,
1432	     {unexpected_pdu,
1433	      {Type, Err, Idx}, {Type2, Err2, Idx2}, ReqId}};
1434
1435	#pdu{type         = Type,
1436	     error_status = Err,
1437	     error_index  = Idx,
1438	     varbinds     = VBs} = PDU ->
1439	    io_format_expect("received pdu (12): "
1440                             "~n   [exp] Type:         ~p"
1441                             "~n   [exp] Error Status: ~p"
1442                             "~n   [exp] Error Index:  ~p"
1443                             "~n   VBs:                ~p"
1444                             "~nwhen"
1445                             "~n   ExpVBs:             ~p",
1446                             [Type, Err, Idx, VBs, ExpVBs]),
1447	    Check(PDU, check_vbs(purify_oids(ExpVBs), VBs));
1448
1449	#pdu{type         = Type,
1450	     error_status = Err,
1451	     varbinds     = VBs} = PDU when Idx =:= any ->
1452	    io_format_expect("received pdu (13): "
1453                             "~n   [exp] Type:         ~p"
1454                             "~n   [exp] Error Status: ~p"
1455                             "~n   VBs:                ~p"
1456                             "~nwhen"
1457                             "~n   ExpVBs:             ~p",
1458                             [Type, Err, VBs, ExpVBs]),
1459	    Check(PDU, check_vbs(purify_oids(ExpVBs), VBs));
1460
1461	#pdu{type         = Type,
1462	     request_id   = ReqId,
1463	     error_status = Err,
1464	     error_index  = EI,
1465	     varbinds     = VBs} = PDU when is_list(Idx) ->
1466	    io_format_expect("received pdu (14): "
1467                             "~n   [exp] Type:         ~p"
1468                             "~n   ReqId:              ~p"
1469                             "~n   [exp] Error Status: ~p"
1470                             "~n   [exp] Error Index:  ~p"
1471                             "~n   VBs:                ~p"
1472                             "~nwhen"
1473                             "~n   ExpVBs:             ~p",
1474                             [Type, ReqId, Err, EI, VBs, ExpVBs]),
1475	    PureVBs = purify_oids(ExpVBs),
1476	    case lists:member(EI, Idx) of
1477		true ->
1478		    Check(PDU, check_vbs(PureVBs, VBs));
1479		false ->
1480		    {error, {unexpected_error_index, Idx, EI, ReqId}}
1481	    end;
1482
1483	#pdu{type         = Type2,
1484	     request_id   = ReqId,
1485	     error_status = Err2,
1486	     error_index  = Idx2,
1487	     varbinds     = VBs2} ->
1488	    io_format_expect("received unexpected pdu with (15) "
1489                             "~n   Type:         ~p"
1490                             "~n   ReqId:        ~p"
1491                             "~n   Error status: ~p"
1492                             "~n   Error index:  ~p"
1493                             "~n   Varbinds:     ~p",
1494                             [Type2, ReqId, Err2, Idx2, VBs2]),
1495	    {error,
1496	     {unexpected_pdu,
1497	      {Type,  Err,  Idx, purify_oids(ExpVBs)},
1498	      {Type2, Err2, Idx2, VBs2},
1499	      ReqId}};
1500
1501
1502	{error, timeout} = Error ->
1503            SysEvs = snmp_test_global_sys_monitor:events(),
1504	    io_format_expect("got timeout (16) when system events:"
1505                             "~n   ~p", [SysEvs]),
1506            if
1507                (SysEvs =:= []) ->
1508                    Error;
1509                true ->
1510                    skip({system_events, SysEvs})
1511            end;
1512
1513
1514        Error ->
1515            io_format_expect("received error (17):  "
1516                             "~n   Error: ~p", [Error]),
1517            Error
1518    end.
1519
1520
1521
1522check_vbs([], []) ->
1523    ok;
1524check_vbs(Exp, []) ->
1525    {error, {to_few_vbs, Exp}};
1526check_vbs([], VBs) ->
1527    {error, {to_many_vbs, VBs}};
1528check_vbs([any|Exp], [_|VBs]) ->
1529    check_vbs(Exp, VBs);
1530check_vbs([{Oid, any}|Exp], [#varbind{oid = Oid}|VBs]) ->
1531    check_vbs(Exp, VBs);
1532check_vbs([{Oid, Val}|Exp], [#varbind{oid = Oid, value = Val}|VBs]) ->
1533    check_vbs(Exp, VBs);
1534check_vbs([{Oid, Val1}|_], [#varbind{oid = Oid, value = Val2}|_]) ->
1535    {error, {unexpected_vb_value, Oid, Val1, Val2}};
1536check_vbs([{Oid1, _}|_], [#varbind{oid = Oid2}|_]) ->
1537    {error, {unexpected_vb_oid, Oid1, Oid2}}.
1538
1539
1540purify_oids({VbsCondition, VBs})
1541  when ((VbsCondition =:= true) orelse (VbsCondition =:= false)) andalso
1542       is_list(VBs) ->
1543    {VbsCondition, do_purify_oids(VBs)};
1544purify_oids(VBs) when is_list(VBs) ->
1545    do_purify_oids(VBs).
1546
1547do_purify_oids([]) ->
1548    [];
1549do_purify_oids([{XOid, Q}|T]) ->
1550    [{purify_oid(XOid), Q} | do_purify_oids(T)].
1551
1552
1553purify_oid(Oid) ->
1554    io:format("~w:purify_oid -> entry with"
1555	      "~n   Oid:      ~w"
1556	      "~n",
1557	      [?MODULE, Oid]),
1558    case (catch snmp_test_mgr:purify_oid(Oid)) of
1559	{error, Reason} ->
1560	    io:format("~w:purify_oid -> error: "
1561		      "~n   Reason: ~p"
1562		      "~n",
1563		      [?MODULE, Reason]),
1564	    exit({malformed_oid, Reason});
1565	{ok, Oid2} when is_list(Oid2) ->
1566	    io:format("~w:purify_oid -> ok: "
1567		      "~n   Oid2: ~p"
1568		      "~n",
1569		      [?MODULE, Oid2]),
1570	    Oid2;
1571	Error ->
1572	    io:format("~w:purify_oid -> unexpected return value: "
1573		      "~n   Error: ~p"
1574		      "~n",
1575		      [?MODULE, Error]),
1576	    exit({unexpected_purify_result, Error})
1577
1578    end.
1579
1580
1581%% ----------------------------------------------------------------------
1582
1583get_req(Id, Vars) ->
1584    ?DBG("get_req -> entry with"
1585	   "~n   Id:   ~p"
1586	   "~n   Vars: ~p",[Id,Vars]),
1587    snmp_test_mgr:g(Vars),
1588    ?DBG("get_req -> await response",[]),
1589    case snmp_test_mgr:get_response(Id, Vars) of
1590	{ok, Val} ->
1591	    ?DBG("get_req -> response: ~p",[Val]),
1592	    Val;
1593	{error, _, {_ExpFmt, ExpArg}, {_ActFmt, ActArg}} ->
1594	    ?DBG("get_req -> error for ~p: "
1595		 "~n   " ++ _ExpFmt ++
1596		 "~n   " ++ _ActFmt,
1597		 [Id] ++ ExpArg ++ ActArg),
1598	    exit({unexpected_response, ExpArg, ActArg});
1599	Error ->
1600	    ?DBG("get_req -> error: ~n~p",[Error]),
1601	    exit({unknown, Error})
1602    end.
1603
1604
1605get_next_req(Vars) ->
1606    ?DBG("get_next_req -> entry with"
1607	 "~n   Vars: ~p", [Vars]),
1608    snmp_test_mgr:gn(Vars),
1609    ?DBG("get_next_req -> await response",[]),
1610    Response = snmp_test_mgr:receive_response(),
1611    ?DBG("get_next_req -> response: ~p",[Response]),
1612    Response.
1613
1614
1615%% --- start and stop nodes ---
1616
1617start_node(Name) ->
1618    ?IPRINT("start_node -> entry with"
1619            "~n   Name: ~p"
1620            "~n when"
1621            "~n   hostname of this node: ~p",
1622            [Name, list_to_atom(?HOSTNAME(node()))]),
1623
1624    Pa = filename:dirname(code:which(?MODULE)),
1625    ?DBG("start_node -> Pa: ~p", [Pa]),
1626
1627    A = " -pa " ++ Pa ++
1628        " -s " ++ atom_to_list(snmp_test_sys_monitor) ++ " start" ++
1629        " -s global sync",
1630    case ?START_NODE(Name, A) of
1631	{ok, Node} ->
1632	    ?DBG("start_node -> Node: ~p", [Node]),
1633            global:sync(),
1634	    {ok, Node};
1635	{error, Reason}  ->
1636	    ?WPRINT("start_node -> failed starting node ~p:"
1637                    "~n      Reason: ~p", [Name, Reason]),
1638	    ?line ?SKIP({failed_start_node, Reason});
1639	Else  ->
1640	    ?EPRINT("start_node -> failed starting node ~p:"
1641                    "~n      ~p", [Name, Else]),
1642	    ?line ?FAIL(Else)
1643    end.
1644
1645
1646stop_node(Node) ->
1647    ?IPRINT("stop_node -> Node: ~p", [Node]),
1648    ?STOP_NODE(Node).
1649
1650
1651%%%-----------------------------------------------------------------
1652%%% Configuration
1653%%%-----------------------------------------------------------------
1654
1655config(Vsns, MgrDir, AgentConfDir, MIp, AIp) ->
1656    config(Vsns, MgrDir, AgentConfDir, MIp, AIp, inet).
1657
1658config(Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily) ->
1659    ?IPRINT("config -> entry with"
1660            "~n   Vsns:         ~p"
1661            "~n   MgrDir:       ~p"
1662            "~n   AgentConfDir: ~p"
1663            "~n   MIp:          ~p"
1664            "~n   AIp:          ~p"
1665            "~n   IpFamily:     ~p",
1666            [Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily]),
1667    ?line {Domain, ManagerAddr} =
1668	case IpFamily of
1669	    inet6 ->
1670		TransportDomain6 = transportDomainUdpIpv6,
1671		AgentAddr6       = {AIp, 4000},
1672		ManagerAddr6     = {MIp, ?TRAP_UDP},
1673		?line ok =
1674		    snmp_config:write_agent_snmp_files(
1675		      AgentConfDir, Vsns,
1676		      TransportDomain6, ManagerAddr6, AgentAddr6, "test"),
1677		{TransportDomain6, ManagerAddr6};
1678	    inet ->
1679		TransportDomain4 = transportDomainUdpIpv4,
1680                AIp2 = maybe_fix_addr(AIp),
1681		ManagerAddr4     = {MIp, ?TRAP_UDP},
1682                %% AgentPreTransport  =
1683                %%     [#{addr => {AIp2, 4000}, kind => req_responder},
1684                %%      #{addr => {AIp2, 4001}, kind => trap_sender}],
1685                AgentPreTransport  = [#{addr => {AIp2, 4000}}],
1686		?line ok =
1687		    snmp_config:write_agent_snmp_files(
1688		      AgentConfDir, Vsns,
1689		      TransportDomain4, ManagerAddr4, AgentPreTransport,
1690                      "test"),
1691		{TransportDomain4, ManagerAddr4};
1692	    _ ->
1693		?line ok =
1694		    snmp_config:write_agent_snmp_files(
1695		      AgentConfDir, Vsns, MIp, ?TRAP_UDP, AIp, 4000, "test"),
1696		{snmpUDPDomain, {MIp, ?TRAP_UDP}}
1697	  end,
1698
1699    ?line case update_usm(Vsns, AgentConfDir) of
1700	      true ->
1701		  ?line copy_file(join(AgentConfDir, "usm.conf"),
1702				  join(MgrDir, "usm.conf")),
1703		  ?line update_usm_mgr(Vsns, MgrDir);
1704	      false ->
1705		  ?line ok
1706	  end,
1707    ?line update_community(Vsns, AgentConfDir),
1708    ?line update_vacm(Vsns, AgentConfDir),
1709    ?line write_target_addr_conf(AgentConfDir, Domain, ManagerAddr, Vsns),
1710    ?line write_target_params_conf(AgentConfDir, Vsns),
1711    ?line write_notify_conf(AgentConfDir),
1712    ok.
1713
1714maybe_fix_addr(Addr) when is_list(Addr) ->
1715    list_to_tuple(Addr);
1716maybe_fix_addr(Addr) when is_tuple(Addr) ->
1717    Addr.
1718
1719
1720delete_files(Config) ->
1721    AgentDir = ?config(agent_dir, Config),
1722    delete_files(AgentDir, [db, conf]).
1723
1724delete_files(_AgentFiles, []) ->
1725    ok;
1726delete_files(AgentDir, [DirName|DirNames]) ->
1727    Dir = join(AgentDir, DirName),
1728    {ok, Files} = file:list_dir(Dir),
1729    lists:foreach(fun(FName) -> file:delete(join(Dir, FName)) end,
1730		  Files),
1731    delete_files(AgentDir, DirNames).
1732
1733update_usm(Vsns, Dir) ->
1734    case lists:member(v3, Vsns) of
1735	true ->
1736	    Conf = [{"agentEngine", "all-rights", "all-rights", zeroDotZero,
1737		     usmNoAuthProtocol, "", "",
1738		     usmNoPrivProtocol, "", "", "", "", ""},
1739
1740		    {"agentEngine", "no-rights", "no-rights", zeroDotZero,
1741		     usmNoAuthProtocol, "", "",
1742		     usmNoPrivProtocol, "", "", "", "", ""},
1743
1744		    {"agentEngine", "authMD5", "authMD5", zeroDotZero,
1745		     usmHMACMD5AuthProtocol, "", "",
1746		     usmNoPrivProtocol, "", "", "", "passwd_md5xxxxxx", ""},
1747
1748		    {"agentEngine", "authSHA", "authSHA", zeroDotZero,
1749		     usmHMACSHAAuthProtocol, "", "",
1750		     usmNoPrivProtocol, "", "", "",
1751		     "passwd_shaxxxxxxxxxx", ""},
1752
1753		    {"agentEngine", "privDES", "privDES", zeroDotZero,
1754		     usmHMACSHAAuthProtocol, "", "",
1755		     usmDESPrivProtocol, "", "", "",
1756		     "passwd_shaxxxxxxxxxx", "passwd_desxxxxxx"},
1757
1758		    {"mgrEngine", "all-rights", "all-rights", zeroDotZero,
1759		     usmNoAuthProtocol, "", "",
1760		     usmNoPrivProtocol, "", "", "", "", ""},
1761
1762		    {"mgrEngine", "no-rights", "no-rights", zeroDotZero,
1763		     usmNoAuthProtocol, "", "",
1764		     usmNoPrivProtocol, "", "", "", "", ""},
1765
1766		    {"mgrEngine", "authMD5", "authMD5", zeroDotZero,
1767		     usmHMACMD5AuthProtocol, "", "",
1768		     usmNoPrivProtocol, "", "", "", "passwd_md5xxxxxx", ""},
1769
1770		    {"mgrEngine", "authSHA", "authSHA", zeroDotZero,
1771		     usmHMACSHAAuthProtocol, "", "",
1772		     usmNoPrivProtocol, "", "", "",
1773		     "passwd_shaxxxxxxxxxx", ""},
1774
1775		    {"mgrEngine", "privDES", "privDES", zeroDotZero,
1776		     usmHMACSHAAuthProtocol, "", "",
1777		     usmDESPrivProtocol, "", "", "",
1778		     "passwd_shaxxxxxxxxxx", "passwd_desxxxxxx"}],
1779	    ?line ok = snmp_config:update_agent_usm_config(Dir, Conf),
1780	    true;
1781	false ->
1782	    false
1783    end.
1784
1785update_usm_mgr(Vsns, Dir) ->
1786    case lists:member(v3, Vsns) of
1787	true ->
1788	    Conf = [{"agentEngine", "newUser", "newUser", zeroDotZero,
1789		     usmHMACSHAAuthProtocol, "", "",
1790		     usmDESPrivProtocol, "", "", "",
1791		     "passwd_shaxxxxxxxxxx", "passwd_desxxxxxx"},
1792
1793		    {"mgrEngine", "newUser", "newUser", zeroDotZero,
1794		     usmHMACSHAAuthProtocol, "", "",
1795		     usmDESPrivProtocol, "", "", "",
1796		     "passwd_shaxxxxxxxxxx", "passwd_desxxxxxx"}],
1797
1798	    ?line ok = snmp_config:update_agent_usm_config(Dir, Conf),
1799	    true;
1800	false ->
1801	    false
1802    end.
1803
1804rewrite_usm_mgr(Dir, ShaKey, DesKey) ->
1805    ?line ok = file:rename(join(Dir,"usm.conf"),
1806			   join(Dir,"usm.old")),
1807    Conf = [{"agentEngine", "newUser", "newUser", zeroDotZero,
1808	     usmHMACSHAAuthProtocol, "", "",
1809	     usmDESPrivProtocol, "", "", "", ShaKey, DesKey},
1810	    {"mgrEngine", "newUser", "newUser", zeroDotZero,
1811	     usmHMACSHAAuthProtocol, "", "",
1812	     usmDESPrivProtocol, "", "", "", ShaKey, DesKey}],
1813    ?line ok = snmp_config:write_agent_usm_config(Dir, "", Conf).
1814
1815reset_usm_mgr(Dir) ->
1816    ?line ok = file:rename(join(Dir,"usm.old"),
1817			   join(Dir,"usm.conf")).
1818
1819
1820update_community([v3], _Dir) ->
1821    ok;
1822update_community(_, Dir) ->
1823    Conf = [{"no-rights", "no-rights", "no-rights", "", ""}],
1824    ?line ok = snmp_config:update_agent_community_config(Dir, Conf).
1825
1826
1827-define(tDescr_instance, [1,3,6,1,2,1,16,1,0]).
1828update_vacm(_Vsn, Dir) ->
1829    Conf = [{vacmSecurityToGroup, usm, "authMD5", "initial"},
1830	    {vacmSecurityToGroup, usm, "authSHA", "initial"},
1831	    {vacmSecurityToGroup, usm, "privDES", "initial"},
1832	    {vacmSecurityToGroup, usm, "newUser", "initial"},
1833	    {vacmViewTreeFamily, "internet", ?tDescr_instance,
1834	     excluded, null}],
1835    ?line ok = snmp_config:update_agent_vacm_config(Dir, Conf).
1836
1837
1838write_community_conf(Dir, Conf) ->
1839    ?line ok = snmp_config:write_agent_community_config(Dir, "", Conf).
1840
1841write_target_addr_conf(Dir, Conf) ->
1842    ?line ok = snmp_config:write_agent_target_addr_config(Dir, "", Conf).
1843
1844write_target_addr_conf(Dir, Ip_or_Domain, Port_or_Addr, Vsns) ->
1845    ?line ok =
1846	snmp_config:write_agent_snmp_target_addr_conf(
1847	  Dir, Ip_or_Domain, Port_or_Addr, Vsns).
1848
1849rewrite_target_addr_conf(Dir, NewPort) ->
1850    ?DBG("rewrite_target_addr_conf -> entry with"
1851	 "~n   NewPort: ~p", [NewPort]),
1852    TAFile = join(Dir, "target_addr.conf"),
1853    case file:read_file_info(TAFile) of
1854	{ok, _} ->
1855	    ok;
1856	{error, _R} ->
1857	    ?WPRINT("failure reading file info of "
1858                    "target address config file: ~p", [_R]),
1859	    ok
1860    end,
1861
1862    ?line [TrapAddr|Addrs] =
1863	snmp_conf:read(TAFile, fun rewrite_target_addr_conf_check/1),
1864
1865    ?DBG("rewrite_target_addr_conf -> TrapAddr: ~p",[TrapAddr]),
1866
1867    NewAddrs = [rewrite_target_addr_conf2(NewPort,TrapAddr)|Addrs],
1868
1869    ?DBG("rewrite_target_addr_conf -> NewAddrs: ~p",[NewAddrs]),
1870
1871    ?line ok = file:rename(join(Dir,"target_addr.conf"),
1872			   join(Dir,"target_addr.old")),
1873
1874    ?line ok = snmp_config:write_agent_target_addr_config(Dir, "", NewAddrs).
1875
1876rewrite_target_addr_conf_check(O) ->
1877    {ok,O}.
1878
1879rewrite_target_addr_conf2(NewPort,
1880			  {Name, Ip, _Port, Timeout, Retry,
1881			   "std_trap", EngineId}) ->
1882    ?IPRINT("rewrite_target_addr_conf2 -> entry with std_trap",[]),
1883    {Name,Ip,NewPort,Timeout,Retry,"std_trap",EngineId};
1884rewrite_target_addr_conf2(_NewPort,O) ->
1885    ?IPRINT("rewrite_target_addr_conf2 -> entry with "
1886	 "~n   O: ~p",[O]),
1887    O.
1888
1889reset_target_addr_conf(Dir) ->
1890    ?line ok = file:rename(join(Dir, "target_addr.old"),
1891			   join(Dir, "target_addr.conf")).
1892
1893write_target_params_conf(Dir, Vsns) ->
1894    F = fun(v1) -> {"target_v1", v1,  v1,  "all-rights", noAuthNoPriv};
1895	   (v2) -> {"target_v2", v2c, v2c, "all-rights", noAuthNoPriv};
1896	   (v3) -> {"target_v3", v3,  usm, "all-rights", noAuthNoPriv}
1897	end,
1898    Conf = [F(Vsn) || Vsn <- Vsns],
1899    ?line ok = snmp_config:write_agent_target_params_config(Dir, "", Conf).
1900
1901rewrite_target_params_conf(Dir, SecName, SecLevel)
1902  when is_list(SecName) andalso is_atom(SecLevel) ->
1903    ?line ok = file:rename(join(Dir,"target_params.conf"),
1904			   join(Dir,"target_params.old")),
1905    Conf = [{"target_v3", v3, usm, SecName, SecLevel}],
1906    ?line ok = snmp_config:write_agent_target_params_config(Dir, "", Conf).
1907
1908reset_target_params_conf(Dir) ->
1909    ?line ok = file:rename(join(Dir,"target_params.old"),
1910			   join(Dir,"target_params.conf")).
1911
1912write_notify_conf(Dir) ->
1913    Conf = [{"standard trap",   "std_trap",   trap},
1914	    {"standard inform", "std_inform", inform}],
1915    ?line ok = snmp_config:write_agent_notify_config(Dir, "", Conf).
1916
1917write_view_conf(Dir) ->
1918    Conf = [{2, [1,3,6], included, null},
1919	    {2, ?tDescr_instance, excluded, null}],
1920    ?line ok = snmp_config:write_agent_view_config(Dir, "", Conf).
1921
1922
1923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1924
1925copy_file(From, To) ->
1926    {ok, Bin} = file:read_file(From),
1927    ok = file:write_file(To, Bin).
1928
1929
1930%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1931
1932display_memory_usage() ->
1933    Info       = snmpa:info(snmp_master_agent),
1934    TreeSize   = key1search(tree_size_bytes,  Info),
1935    ProcMem    = key1search(process_memory,   Info),
1936    MibDbSize  = key1search([db_memory,mib],  Info),
1937    NodeDbSize = key1search([db_memory,node], Info),
1938    TreeDbSize = key1search([db_memory,tree], Info),
1939    ?IPRINT("Memory usage: "
1940            "~n   Tree size:           ~p"
1941            "~n   Process memory size: ~p"
1942            "~n   Mib db size:         ~p"
1943            "~n   Node db size:        ~p"
1944            "~n   Tree db size:        ~p",
1945    [TreeSize, ProcMem, MibDbSize, NodeDbSize, TreeDbSize]).
1946
1947key1search([], Res) ->
1948    Res;
1949key1search([Key|Keys], List) when is_atom(Key) andalso is_list(List) ->
1950    case lists:keysearch(Key, 1, List) of
1951	{value, {Key, Val}} ->
1952	    key1search(Keys, Val);
1953	false ->
1954	    undefined
1955    end;
1956key1search(Key, List) when is_atom(Key) ->
1957    case lists:keysearch(Key, 1, List) of
1958	{value, {Key, Val}} ->
1959	    Val;
1960	false ->
1961	    undefined
1962    end.
1963
1964
1965regs() ->
1966    lists:sort(registered()).
1967
1968
1969rpc(Node, F, A) ->
1970    rpc:call(Node, snmpa, F, A).
1971
1972
1973join(Dir, File) ->
1974    filename:join(Dir, File).
1975
1976
1977skip(R) ->
1978    exit({skip, R}).
1979
1980