1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: Test suite of the agent mib-server.
24%%          Some of these tests should really be in a mib-storage suite.
25%%----------------------------------------------------------------------
26
27-module(snmp_agent_mibs_SUITE).
28
29
30%%----------------------------------------------------------------------
31%% Include files
32%%----------------------------------------------------------------------
33
34-include_lib("common_test/include/ct.hrl").
35-include("snmp_test_lib.hrl").
36-include_lib("snmp/include/snmp_types.hrl").
37-include_lib("snmp/include/SNMP-COMMUNITY-MIB.hrl").
38-include_lib("snmp/include/SNMP-VIEW-BASED-ACM-MIB.hrl").
39-include_lib("snmp/include/SNMP-USER-BASED-SM-MIB.hrl").
40-include("snmp_test_data/Test2.hrl").
41
42
43%%----------------------------------------------------------------------
44%% External exports
45%%----------------------------------------------------------------------
46
47-export([
48         suite/0, all/0, groups/0,
49         init_per_suite/1,    end_per_suite/1,
50         init_per_group/2,    end_per_group/2,
51         init_per_testcase/2, end_per_testcase/2,
52
53	 start_and_stop/1,
54
55	 size_check_ets1/1,
56	 size_check_ets2/1,
57	 size_check_ets2_bad_file1/1,
58	 size_check_ets3/1,
59	 size_check_ets3_bad_file1/1,
60	 size_check_dets/1,
61	 size_check_mnesia/1,
62	 load_unload/1,
63	 me_lookup/1,
64	 which_mib/1,
65	 cache_test/1
66
67	]).
68
69
70-define(ALIB, snmp_agent_test_lib).
71
72
73%%======================================================================
74%% Common Test interface functions
75%%======================================================================
76
77suite() ->
78    [{ct_hooks, [ts_install_cth]}].
79
80all() ->
81    [
82     start_and_stop,
83     load_unload,
84     {group, size_check},
85     me_lookup,
86     which_mib,
87     cache_test
88    ].
89
90groups() ->
91    [
92     {size_check, [], size_check_cases()}
93    ].
94
95size_check_cases() ->
96    [
97     size_check_ets1,            % Plain ets
98     size_check_ets2,            % ets with a file
99     size_check_ets2_bad_file1,  % ets with a bad file
100     size_check_ets3,            % ets with a checksummed file
101     size_check_ets3_bad_file1,  % ets with bad file (checksummed)
102     size_check_dets,            % Plain dets
103     size_check_mnesia           % Plain mnesia
104    ].
105
106
107
108%%
109%% -----
110%%
111
112init_per_suite(Config0) when is_list(Config0) ->
113
114    ?DBG("init_per_suite -> entry with"
115         "~n   Config0: ~p", [Config0]),
116
117    case snmp_test_lib:init_per_suite(Config0) of
118        {skip, _} = SKIP ->
119            SKIP;
120
121        Config1 when is_list(Config1) ->
122            Config2 = ?LIB:init_suite_top_dir(?MODULE, Config1),
123
124            %% We need a monitor on this node also
125            snmp_test_sys_monitor:start(),
126
127            ?DBG("init_per_suite -> done when"
128                 "~n   Config: ~p", [Config]),
129
130            Config2
131    end.
132
133end_per_suite(Config) when is_list(Config) ->
134
135    ?DBG("end_per_suite -> entry with"
136         "~n   Config: ~p", [Config]),
137
138    snmp_test_sys_monitor:stop(),
139
140    ?LIB:end_per_suite(Config).
141
142
143
144%%
145%% -----
146%%
147
148init_per_group(GroupName, Config) ->
149    ?LIB:init_group_top_dir(GroupName, Config).
150
151end_per_group(_GroupName, Config) ->
152    Config.
153
154
155
156
157%%
158%% -----
159%%
160
161init_per_testcase(Case, Config0) when is_list(Config0) ->
162    snmp_test_global_sys_monitor:reset_events(),
163    Config1    = ?LIB:fix_data_dir(Config0),
164    CaseTopDir = ?LIB:init_testcase_top_dir(Case, Config1),
165    DbDir      = join(CaseTopDir, "db_dir/"),
166    ?line ok   = file:make_dir(DbDir),
167    init_per_testcase2(Case, [{db_dir,       DbDir},
168			      {case_top_dir, CaseTopDir} | Config1]).
169
170init_per_testcase2(size_check_ets2_bad_file1, Config) when is_list(Config) ->
171    DbDir = ?config(db_dir, Config),
172    %% Create a bad file
173    ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"),
174			 "calvin and hoppes play chess"),
175    Config;
176init_per_testcase2(size_check_ets3_bad_file1, Config) when is_list(Config) ->
177    DbDir = ?config(db_dir, Config),
178    %% Create a bad file
179    ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"),
180			 "calvin and hoppes play chess"),
181    Config;
182init_per_testcase2(size_check_mnesia, Config) when is_list(Config) ->
183    Config;
184init_per_testcase2(cache_test, Config) when is_list(Config) ->
185    Min = ?MINS(10),
186    Timeout =
187	case lists:keysearch(tc_timeout, 1, Config) of
188	    {value, {tc_timeout, TcTimeout}} when TcTimeout < Min ->
189		Min;
190	    {value, {tc_timeout, TcTimeout}} ->
191		TcTimeout;
192	    _ ->
193		Min
194	end,
195    Dog = test_server:timetrap(Timeout),
196    [{watchdog, Dog} | Config];
197init_per_testcase2(_Case, Config) when is_list(Config) ->
198    Config.
199
200
201end_per_testcase(Case, Config) when is_list(Config) ->
202    ?IPRINT("system events during test: "
203            "~n   ~p", [snmp_test_global_sys_monitor:events()]),
204    end_per_testcase1(Case, Config).
205
206end_per_testcase1(size_check_mnesia, Config) when is_list(Config) ->
207    mnesia_stop(),
208    Config;
209end_per_testcase1(cache_test, Config) when is_list(Config) ->
210    Dog = ?config(watchdog, Config),
211    test_server:timetrap_cancel(Dog),
212    Config;
213end_per_testcase1(_Case, Config) when is_list(Config) ->
214    Config.
215
216
217%%======================================================================
218%% Test functions
219%%======================================================================
220
221start_and_stop(suite) -> [];
222start_and_stop(Config) when is_list(Config) ->
223    Prio      = normal,
224    Verbosity = trace,
225
226    ?line sym_start(Prio, Verbosity),
227    ?line MibsPid = mibs_start(Prio, Verbosity),
228
229    ?line mibs_info(MibsPid),
230
231    ?line mibs_stop(MibsPid),
232    ?line sym_stop(),
233
234    ok.
235
236
237%% ---------------------------------------------------------------------
238
239load_unload(suite) -> [];
240load_unload(Config) when is_list(Config) ->
241    ?DBG("load_unload -> start", []),
242
243    Prio       = normal,
244    Verbosity  = log,
245    MibDir     = ?config(data_dir, Config),
246
247    ?DBG("load_unload -> start symbolic store", []),
248    ?line sym_start(Prio, Verbosity),
249
250    ?DBG("load_unload -> start mib server", []),
251    ?line MibsPid = mibs_start(Prio, Verbosity),
252
253    ?DBG("load_unload -> load one not already loaded mib", []),
254    ?line ok = verify_loaded_mibs(MibsPid, MibDir, []),
255    ?line ok = load_mibs(MibsPid, MibDir, ["Test2"]),
256    ?line ok = verify_loaded_mibs(MibsPid, MibDir, ["Test2"]),
257
258    ?DBG("load_unload -> try load one *already loaded* mib", []),
259    EMib = join(MibDir, "Test2"),
260    ?line {error, {'load aborted at', EMib, already_loaded}} =
261	load_mibs(MibsPid, MibDir, ["Test2"]),
262
263    ?DBG("load_unload -> load 2 not already loaded mibs", []),
264    ?line ok = load_mibs(MibsPid, MibDir, ["TestTrap", "TestTrapv2"]),
265    ?line ok = verify_loaded_mibs(MibsPid, MibDir,
266				  ["Test2", "TestTrap", "TestTrapv2"]),
267
268    ?DBG("load_unload -> unload one loaded mib", []),
269    ?line ok = unload_mibs(MibsPid, ["Test2"]),
270    ?line ok = verify_loaded_mibs(MibsPid, MibDir, ["TestTrap", "TestTrapv2"]),
271
272    ?DBG("load_unload -> try unload two loaded mibs and one not loaded", []),
273    ?line {error, {'unload aborted at', "Test2", not_loaded}} =
274	   unload_mibs(MibsPid, ["TestTrap","Test2","TestTrapv2"]),
275    ?line ok = verify_loaded_mibs(MibsPid, MibDir, ["TestTrapv2"]),
276
277    ?DBG("load_unload -> unload the remaining loaded mib", []),
278    ?line ok = unload_mibs(MibsPid, ["TestTrapv2"]),
279    ?line ok = verify_loaded_mibs(MibsPid, MibDir, []),
280
281    ?DBG("load_unload -> stop mib server", []),
282    ?line mibs_stop(MibsPid),
283
284    ?DBG("load_unload -> stop symbolic store", []),
285    ?line sym_stop(),
286
287    ?DBG("load_unload -> done", []),
288    ok.
289
290
291%% ---------------------------------------------------------------------
292
293
294size_check_ets1(suite) ->
295    [];
296size_check_ets1(Config) when is_list(Config) ->
297    MibStorage = [{module, snmpa_mib_storage_ets}],
298    do_size_check(size_check_ets1,
299                  [{mib_storage, MibStorage}|Config]).
300
301size_check_ets2(suite) ->
302    [];
303size_check_ets2(Config) when is_list(Config) ->
304    Dir = ?config(db_dir, Config),
305    MibStorage = [{module,  snmpa_mib_storage_ets},
306		  {options, [{dir, Dir}]}],
307    do_size_check(size_check_ets2,
308                  [{mib_storage, MibStorage}|Config]).
309
310size_check_ets2_bad_file1(suite) ->
311    [];
312size_check_ets2_bad_file1(Config) when is_list(Config) ->
313    Dir = ?config(db_dir, Config),
314    %% Ensure that the bad file does not cause any problems (action = clear)
315    MibStorage = [{module,  snmpa_mib_storage_ets},
316		  {options, [{dir,    Dir},
317			     {action, clear}]}],
318    do_size_check(size_check_ets2_bad_file1,
319                  [{mib_storage, MibStorage}|Config]).
320
321size_check_ets3(suite) ->
322    [];
323size_check_ets3(Config) when is_list(Config) ->
324    Dir = ?config(db_dir, Config),
325    MibStorage = [{module,  snmpa_mib_storage_ets},
326		  {options, [{dir,      Dir},
327			     {checksum, true}]}],
328    do_size_check(size_check_ets3,
329                  [{mib_storage, MibStorage}|Config]).
330
331size_check_ets3_bad_file1(suite) ->
332    [];
333size_check_ets3_bad_file1(Config) when is_list(Config) ->
334    Dir = ?config(db_dir, Config),
335    %% Ensure that the bad file does not cause any problems (action = clear)
336    MibStorage = [{module,  snmpa_mib_storage_ets},
337		  {options, [{dir,      Dir},
338			     {action,   clear},
339			     {checksum, true}]}],
340    do_size_check(size_check_ets3_bad_file1,
341                  [{mib_storage, MibStorage}|Config]).
342
343size_check_dets(suite) ->
344    [];
345size_check_dets(Config) when is_list(Config) ->
346    Dir = ?config(db_dir, Config),
347    MibStorage = [{module,  snmpa_mib_storage_dets},
348		  {options, [{dir, Dir}]}],
349    do_size_check(size_check_dets,
350                  [{mib_storage, MibStorage}|Config]).
351
352size_check_mnesia(suite) ->
353    [];
354size_check_mnesia(Config) when is_list(Config) ->
355    MibStorage = [{module,  snmpa_mib_storage_mnesia},
356                  {options, [{nodes, []}]}],
357    DbDir      = ?config(db_dir, Config),
358    Init       = fun() -> mnesia_start([{dir, DbDir}]), ok end,
359    do_size_check(size_check_mnesia,
360                  Init,
361                  [{mib_storage, MibStorage}|Config]).
362
363do_size_check(Name, Config) ->
364    Init = fun() -> ok end,
365    do_size_check(Name, Init, Config).
366
367do_size_check(Name, Init, Config) ->
368    Pre = fun() ->
369                  {ok, Node} = ?ALIB:start_node(unique(Name)),
370                  ok = run_on(Node, Init),
371                  Node
372          end,
373    Case = fun(Node) ->
374                   monitor_node(Node, true),
375                   Pid = spawn_link(Node, fun() -> do_size_check(Config) end),
376                   receive
377                       {nodedown, Node} = N ->
378                           exit(N);
379                       {'EXIT', Pid, normal} ->
380                           monitor_node(Node, false),
381                           ok;
382                       {'EXIT', Pid, ok} ->
383                           monitor_node(Node, false),
384                           ok;
385                       {'EXIT', Pid, Reason} ->
386                           monitor_node(Node, false),
387                           exit(Reason)
388                   end
389           end,
390    Post = fun({Node, _}) ->
391                   ?STOP_NODE(Node)
392           end,
393    ?TC_TRY(Name, Pre, Case, Post).
394
395run_on(Node, F) when is_atom(Node) andalso is_function(F, 0) ->
396    monitor_node(Node, true),
397    Pid = spawn_link(Node, F),
398    receive
399        {nodedown, Node} = N ->
400            exit(N);
401        {'EXIT', Pid, normal} ->
402            monitor_node(Node, false),
403            ok;
404        {'EXIT', Pid, Reason} ->
405            monitor_node(Node, false),
406            Reason
407    end.
408
409unique(PreName) ->
410    list_to_atom(?F("~w_~w", [PreName, erlang:system_time(millisecond)])).
411
412do_size_check(Config) ->
413    ?IPRINT("do_size_check -> start with"
414            "~n   Config: ~p", [Config]),
415    Prio      = normal,
416    Verbosity = trace,
417
418    MibStorage = ?config(mib_storage, Config),
419    ?DBG("do_size_check -> MibStorage: ~p", [MibStorage]),
420    MibDir     = ?config(data_dir, Config),
421    StdMibDir  = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
422
423    ?DBG("do_size_check -> start symbolic store", []),
424    ?line sym_start(Prio, MibStorage, Verbosity),
425    ?DBG("do_size_check -> start mib server", []),
426    ?line MibsPid = mibs_start(Prio, MibStorage, Verbosity),
427
428    Mibs    = ["Test2", "TestTrap", "TestTrapv2"],
429    StdMibs = ["OTP-SNMPEA-MIB",
430	       "SNMP-COMMUNITY-MIB",
431	       "SNMP-FRAMEWORK-MIB",
432	       "SNMP-MPD-MIB",
433	       "SNMP-NOTIFICATION-MIB",
434	       "SNMP-TARGET-MIB",
435	       "SNMP-USER-BASED-SM-MIB",
436	       "SNMP-VIEW-BASED-ACM-MIB",
437	       "SNMPv2-MIB",
438	       "SNMPv2-TC",
439	       "SNMPv2-TM"],
440
441    ?DBG("do_size_check -> load mibs", []),
442    ?line load_mibs(MibsPid, MibDir, Mibs),
443    ?DBG("do_size_check -> load std mibs", []),
444    ?line load_mibs(MibsPid, StdMibDir, StdMibs),
445
446    ?SLEEP(2000),
447    ?DBG("do_size_check -> display mem usage", []),
448    ?line display_memory_usage(MibsPid),
449
450    ?DBG("do_size_check -> unload std mibs", []),
451    ?line unload_mibs(MibsPid, StdMibs),
452    ?DBG("do_size_check -> unload mibs", []),
453    ?line unload_mibs(MibsPid, Mibs),
454
455    ?DBG("do_size_check -> stop mib server", []),
456    ?line mibs_stop(MibsPid),
457    ?DBG("do_size_check -> stop symbolic store", []),
458    ?line sym_stop(),
459
460    ?IPRINT("do_size_check -> done", []),
461    ok.
462
463
464%% ---------------------------------------------------------------------
465
466me_lookup(suite) -> [];
467me_lookup(Config) when is_list(Config) ->
468    Prio       = normal,
469    Verbosity  = trace,
470    MibDir     = ?config(data_dir, Config),
471    StdMibDir  = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
472    Mibs    = ["Test2", "TestTrap", "TestTrapv2"],
473    StdMibs = ["OTP-SNMPEA-MIB",
474	       "SNMP-COMMUNITY-MIB",
475	       "SNMP-FRAMEWORK-MIB",
476	       "SNMP-MPD-MIB",
477	       "SNMP-NOTIFICATION-MIB",
478	       "SNMP-TARGET-MIB",
479	       %% "SNMP-USER-BASED-SM-MIB",
480	       "SNMP-VIEW-BASED-ACM-MIB",
481	       "SNMPv2-MIB",
482	       "SNMPv2-TC",
483	       "SNMPv2-TM"],
484
485    ?DBG("me_lookup -> start symbolic store", []),
486    ?line sym_start(Prio, Verbosity),
487
488    ?DBG("me_lookup -> start mib server", []),
489    ?line MibsPid = mibs_start(Prio, Verbosity),
490
491    ?DBG("me_lookup -> load mibs", []),
492    ?line load_mibs(MibsPid, MibDir, Mibs),
493    ?DBG("me_lookup -> load std mibs", []),
494    ?line load_mibs(MibsPid, StdMibDir, StdMibs),
495
496    ?DBG("me_lookup -> find ~w from SNMP-COMMUNITY-MIB",
497	 [?snmpTrapCommunity_instance]),
498    ?line ok = me_lookup(MibsPid, ?snmpTrapCommunity_instance),
499
500    ?DBG("me_lookup -> find ~w from SNMP-VIEW-BASED-ACM-MIB",
501	 [?vacmViewSpinLock_instance]),
502    ?line ok = me_lookup(MibsPid, ?vacmViewSpinLock_instance),
503
504    ?DBG("me_lookup -> find ~w from SNMP-USER-BASED-SM-MIB",
505	 [?usmStatsNotInTimeWindows_instance]),
506    ?line {error, _} = me_lookup(MibsPid, ?usmStatsNotInTimeWindows_instance),
507
508    ?DBG("me_lookup -> stop mib server", []),
509    ?line mibs_stop(MibsPid),
510
511    ?DBG("me_lookup -> stop symbolic store", []),
512    ?line sym_stop(),
513
514    ok.
515
516
517%% ---------------------------------------------------------------------
518
519which_mib(suite) -> [];
520which_mib(Config) when is_list(Config) ->
521    Prio       = normal,
522    Verbosity  = trace,
523    MibDir     = ?config(data_dir, Config),
524    StdMibDir  = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
525    Mibs    = ["Test2", "TestTrap", "TestTrapv2"],
526    StdMibs = ["OTP-SNMPEA-MIB",
527	       "SNMP-COMMUNITY-MIB",
528	       "SNMP-FRAMEWORK-MIB",
529	       "SNMP-MPD-MIB",
530	       "SNMP-NOTIFICATION-MIB",
531	       "SNMP-TARGET-MIB",
532	       %% "SNMP-USER-BASED-SM-MIB",
533	       "SNMP-VIEW-BASED-ACM-MIB",
534	       "SNMPv2-MIB",
535	       "SNMPv2-TC",
536	       "SNMPv2-TM"],
537
538    ?DBG("which_mib -> start symbolic store", []),
539    ?line sym_start(Prio, Verbosity),
540
541    ?DBG("which_mib -> start mib server", []),
542    ?line MibsPid = mibs_start(Prio, Verbosity),
543
544    ?DBG("which_mib -> load mibs", []),
545    ?line load_mibs(MibsPid, MibDir, Mibs),
546    ?DBG("which_mib -> load std mibs", []),
547    ?line load_mibs(MibsPid, StdMibDir, StdMibs),
548
549    ?DBG("which_mib -> find ~w from SNMP-COMMUNITY-MIB",
550	 [?snmpTrapCommunity_instance]),
551    ?line ok = which_mib(MibsPid, ?snmpTrapCommunity_instance,
552			 "SNMP-COMMUNITY-MIB"),
553
554    ?DBG("which_mib -> find ~w from SNMP-VIEW-BASED-ACM-MIB",
555	 [?vacmViewSpinLock_instance]),
556    ?line ok = which_mib(MibsPid, ?vacmViewSpinLock_instance,
557			 "SNMP-VIEW-BASED-ACM-MIB"),
558
559    ?DBG("which_mib -> find ~w from SNMP-USER-BASED-SM-MIB (not loaded)",
560	 [?usmStatsNotInTimeWindows_instance]),
561    ?line {error, _} = which_mib(MibsPid, ?usmStatsNotInTimeWindows_instance,
562				"SNMP-USER-BASED-SM-MIB"),
563
564    ?DBG("which_mib -> stop mib server", []),
565    ?line mibs_stop(MibsPid),
566
567    ?DBG("which_mib -> stop symbolic store", []),
568    ?line sym_stop(),
569
570    ok.
571
572
573%% ---------------------------------------------------------------------
574
575cache_test(suite) -> [];
576cache_test(Config) when is_list(Config) ->
577    ?IPRINT("cache_test -> start"),
578    Prio       = normal,
579    %% Verbosity  = trace,
580    Verbosity  = info,
581    MibStorage = [{module, snmpa_mib_storage_ets}],
582    MibDir     = ?config(data_dir, Config),
583    StdMibDir  = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
584    Mibs       = ["Test2", "TestTrap", "TestTrapv2"],
585    StdMibs    = ["OTP-SNMPEA-MIB",
586		  "SNMP-COMMUNITY-MIB",
587		  "SNMP-FRAMEWORK-MIB",
588		  "SNMP-MPD-MIB",
589		  "SNMP-NOTIFICATION-MIB",
590		  "SNMP-TARGET-MIB",
591		  "SNMP-USER-BASED-SM-MIB",
592		  "SNMP-VIEW-BASED-ACM-MIB",
593		  "SNMPv2-MIB",
594		  "SNMPv2-TC",
595		  "SNMPv2-TM"],
596
597    ?IPRINT("cache_test -> start symbolic store"),
598    ?line sym_start(Prio, MibStorage, silence), % Verbosity),
599
600    ?IPRINT("cache_test -> start mib server"),
601    GcLimit   = 3,
602    Age       = timer:seconds(10),
603    CacheOpts = [{autogc,    false},
604                 {age,       Age},
605                 {gclimit,   GcLimit},
606                 {gcverbose, true}],
607    ?line MibsPid = mibs_start(Prio, MibStorage, [], Verbosity, CacheOpts),
608
609    ?NPRINT("Info before load mibs: "
610            "~n      ~p", [snmpa_mib:info(MibsPid)]),
611
612    ?IPRINT("cache_test -> load mibs"),
613    ?line load_mibs(MibsPid, MibDir, Mibs),
614
615    ?NPRINT("Info before load std mibs: "
616            "~n      ~p", [snmpa_mib:info(MibsPid)]),
617
618    ?IPRINT("cache_test -> load std mibs"),
619    ?line load_mibs(MibsPid, StdMibDir, StdMibs),
620
621    ?NPRINT("Info (after mibs load but) before populate: "
622            "~n      ~p", [snmpa_mib:info(MibsPid)]),
623
624    ?IPRINT("cache_test -> populate the cache"),
625    ?line ok = populate(MibsPid),
626
627    ?NPRINT("Info after populate: "
628            "~n      ~p", [snmpa_mib:info(MibsPid)]),
629
630    Sz1 = cache_sz_verify(1, MibsPid, any),
631
632    ?IPRINT("cache_test -> sleep 5 secs"),
633    ?SLEEP(timer:seconds(5)),
634
635    _ = cache_gc_verify(1, MibsPid),
636
637    ?NPRINT("Info after 5 sec sleep: "
638            "~n      ~p", [snmpa_mib:info(MibsPid)]),
639
640    ?IPRINT("cache_test -> sleep 10 secs"),
641    ?SLEEP(timer:seconds(10)),
642
643    ?NPRINT("Info after 10 sec sleep: "
644            "~n      ~p", [snmpa_mib:info(MibsPid)]),
645
646    GcLimit1 = cache_gc_verify(2, MibsPid, Age, GcLimit + 1),
647
648    Sz2 = cache_sz_verify(2, MibsPid, Sz1 - GcLimit1),
649
650
651    ?IPRINT("cache_test -> subscribe to GC events"),
652    ?line ok = snmpa_mib:subscribe_gc_events(MibsPid),
653
654    ?IPRINT("cache_test -> enable cache autogc"),
655    ?line ok = snmpa_mib:enable_cache_autogc(MibsPid),
656
657    ?IPRINT("cache_test -> wait 65 seconds to allow gc to happen"),
658    ?SLEEP(timer:seconds(65)),
659
660    ?NPRINT("Info after 65 sec sleep: "
661            "~n      ~p", [snmpa_mib:info(MibsPid)]),
662
663    ?IPRINT("cache_test -> [1] flush expected GC events"),
664    {NumEvents1, TotGC1} = cache_flush_gc_events(MibsPid),
665    ?IPRINT("cache_test -> GC events: "
666            "~n      Number of Events:    ~p"
667            "~n      Total elements GCed: ~p", [NumEvents1, TotGC1]),
668
669    _ = cache_sz_verify(3, MibsPid, Sz2 - GcLimit),
670
671    ?IPRINT("cache_test -> "
672            "wait 2 minutes to allow gc to happen, expect empty cache"),
673    ?SLEEP(timer:minutes(2)),
674
675    ?NPRINT("Info after 2 min sleep: "
676            "~n      ~p", [snmpa_mib:info(MibsPid)]),
677
678    _ = cache_sz_verify(4, MibsPid, 0),
679
680
681    ?IPRINT("cache_test -> change gclimit to infinity"),
682    snmpa_mib:update_cache_gclimit(MibsPid, infinity),
683
684    ?IPRINT("cache_test -> change age to ~w mins", [3]),
685    snmpa_mib:update_cache_age(MibsPid, ?MINS(3)),
686
687    ?IPRINT("cache_test -> [2] flush expected GC events"),
688    {NumEvents2, TotGC2} = cache_flush_gc_events(MibsPid),
689    ?IPRINT("cache_test -> GC events: "
690            "~n      Number of Events:    ~p"
691            "~n      Total elements GCed: ~p", [NumEvents2, TotGC2]),
692
693    ?IPRINT("cache_test -> populate the cache again"),
694    populate(MibsPid),
695
696    ?IPRINT("cache_test -> validate cache size"),
697    {ok, Sz4} = snmpa_mib:which_cache_size(MibsPid),
698    if (Sz4 > 0) ->
699            ?IPRINT("cache_test -> expected cache size: ~w > 0", [Sz4]);
700       true      ->
701            ?EPRINT("cache_test -> cache *not* populated"),
702            ?FAIL(cache_not_populated)
703    end,
704
705    ?NPRINT("Info after poulated: "
706            "~n      ~p", [snmpa_mib:info(MibsPid)]),
707
708    ?IPRINT("cache_test -> wait 2 mins - before tuching some entries"),
709    ?SLEEP(?MINS(2)),
710
711    %% There should not be anything GC:ed
712
713    receive
714        {MibsPid, gc_result, {ok, NGC1}} ->
715            ?EPRINT("cache_test -> unexpected GC of ~w elements", [NGC1]),
716            exit({unexpected_gc_result, NGC1})
717    after 0 ->
718            ok
719    end,
720
721    ?IPRINT("cache_test -> touch some elements again (update the cache)"),
722    populate_lookup(MibsPid),
723
724    ?IPRINT("cache_test -> await partial GC"),
725    NumGC2 =
726        receive
727            {MibsPid, gc_result, {ok, NGC2}}
728              when (NGC2 > 0) andalso (Sz4 > NGC2) ->
729                ?NPRINT("cache_test -> "
730                        "received partial GC result of ~w elements", [NGC2]),
731                NGC2
732        end,
733
734    ?NPRINT("Info after partial GC: "
735            "~n      ~p", [snmpa_mib:info(MibsPid)]),
736
737
738    ?IPRINT("cache_test -> await final GC"),
739    receive
740        {MibsPid, gc_result, {ok, NGC3}}
741          when (NGC3 > 0) andalso ((Sz4 - NumGC2) =:= NGC3) ->
742            ?NPRINT("cache_test -> "
743                    "received final GC result of ~w elements", [NGC3]),
744            NGC3;
745        Any ->
746            ?EPRINT("cache_test -> unexpected message: "
747                    "~n      ~p", [Any]),
748            ?FAIL({unexpected, Any})
749    end,
750
751    ?NPRINT("Info after final GC: "
752            "~n      ~p", [snmpa_mib:info(MibsPid)]),
753
754    ?IPRINT("cache_test -> validate cache size (expect empty)"),
755    {ok, Sz5} = snmpa_mib:which_cache_size(MibsPid),
756    if (Sz5 =:= 0) ->
757            ?IPRINT("cache_test -> expected cache size: 0");
758       true      ->
759            ?EPRINT("cache_test -> cache *not* empty (~w)", [Sz5]),
760            ?FAIL({cache_populated, Sz5})
761    end,
762
763
764    ?IPRINT("cache_test -> stop mib server"),
765    ?line mibs_stop(MibsPid),
766
767    ?IPRINT("cache_test -> stop symbolic store"),
768    ?line sym_stop(),
769
770    ?IPRINT("cache_test -> end"),
771    ok.
772
773populate(MibsPid) ->
774    %% Make some lookups
775    populate_lookup(MibsPid),
776    %% Make some walk's
777    populate_walk(MibsPid).
778
779populate_lookup(MibsPid) ->
780    {variable, _} = snmpa_mib:lookup(MibsPid, ?snmpTrapCommunity_instance),
781    {variable, _} = snmpa_mib:lookup(MibsPid, ?vacmViewSpinLock_instance),
782    {variable, _} = snmpa_mib:lookup(MibsPid, ?usmStatsNotInTimeWindows_instance),
783    {variable, _} = snmpa_mib:lookup(MibsPid, ?tDescr_instance),
784    ok.
785
786populate_walk(MibsPid) ->
787    MibView = snmpa_acm:get_root_mib_view(),
788    walk(MibsPid, ?snmpTrapCommunity_instance,        MibView),
789    walk(MibsPid, ?vacmViewSpinLock_instance,         MibView),
790    walk(MibsPid, ?usmStatsNotInTimeWindows_instance, MibView),
791    walk(MibsPid, ?tDescr_instance,                   MibView),
792    ok.
793
794walk(MibsPid, Oid, MibView) ->
795    ?IPRINT("walk -> entry with"
796            "~n      Oid: ~p", [Oid]),
797    do_walk(MibsPid, Oid, MibView).
798
799do_walk(MibsPid, Oid, MibView) ->
800    case snmpa_mib:next(MibsPid, Oid, MibView) of
801	{table, _, _, #me{oid = Oid}} ->
802            ?IPRINT("do_walk -> table done"),
803	    ok;
804	{table, _, _, #me{oid = Next}} ->
805            ?IPRINT("do_walk -> table next ~p", [Next]),
806	    do_walk(MibsPid, Next, MibView);
807	{variable, #me{oid = Oid}, _} ->
808            ?IPRINT("do_walk -> variable done"),
809	    ok;
810	{variable, #me{oid = Next}, _} ->
811            ?IPRINT("do_walk -> variable next ~p", [Next]),
812	    do_walk(MibsPid, Next, MibView)
813    end.
814
815
816cache_gc_verify(ID, MibsPid) ->
817    GC = fun() -> snmpa_mib:gc_cache(MibsPid) end,
818    cache_gc_verify(ID, GC, 0).
819
820cache_gc_verify(ID, MibsPid, Age, ExpectedGcLimit) ->
821    GC = fun() -> snmpa_mib:gc_cache(MibsPid, Age, ExpectedGcLimit) end,
822    cache_gc_verify(ID, GC, ExpectedGcLimit).
823
824cache_gc_verify(ID, GC, ExpectedGc) ->
825    ?IPRINT("cache_gc_verify -> [~w] perform gc, expect ~w", [ID, ExpectedGc]),
826    case GC() of
827        {ok, ExpectedGc} ->
828            ?IPRINT("cache_gc_verify -> [~w] gc => ok", [ID]),
829            ExpectedGc;
830        {ok, OtherGc} ->
831            ?IPRINT("cache_gc_verify -> [~w] invalid GC limit: "
832                    "~n      Expected: ~p"
833                    "~n      Got:      ~p"
834                    "~n      ~p",
835                    [ID, 0, OtherGc]),
836            exit({ID, invalid_gc_limit, {ExpectedGc, OtherGc}});
837        Unexpected ->
838            ?IPRINT("cache_gc_verify -> [~w] unexpected: "
839                    "~n      ~p",
840                    [ID, Unexpected]),
841            exit({ID, unexpected, Unexpected})
842    end.
843
844
845cache_sz_verify(ID, MibsPid, ExpectedSz) ->
846    ?IPRINT("cache_sz_verify -> [~w] expect size ~w", [ID, ExpectedSz]),
847    case snmpa_mib:which_cache_size(MibsPid) of
848        {ok, ExpectedSz} ->
849            ?IPRINT("cache_sz_verify -> [~w] sz => ok", [ID]),
850            ExpectedSz;
851        {ok, UnexpectedSz} when (ExpectedSz =:= any) ->
852            ?IPRINT("cache_sz_verify -> [~w] sz => ok (~w)", [ID, UnexpectedSz]),
853            UnexpectedSz;
854        {ok, UnexpectedSz} ->
855            ?IPRINT("cache_sz_verify -> [~w] invalid size: "
856                    "~n      Expected: ~p"
857                    "~n      Got:      ~p",
858                    [ID, ExpectedSz, UnexpectedSz]),
859            exit({ID, invalid_size, {ExpectedSz, UnexpectedSz}});
860        Unexpected ->
861            ?IPRINT("cache_sz_verify -> [~w] unexpected: "
862                    "~n      ~p",
863                    [ID, Unexpected]),
864            exit({ID, unexpected, Unexpected})
865    end.
866
867
868cache_flush_gc_events(MibServer) ->
869    cache_flush_gc_events(MibServer, 0, 0).
870
871cache_flush_gc_events(MibServer, NumEvents, TotGC) ->
872    receive
873        {MibServer, gc_result, {ok, NumGC}} ->
874            ?IPRINT("cache_flush_gc_events -> GC event ~w (~w)",
875                    [NumGC, NumEvents]),
876            cache_flush_gc_events(MibServer, NumEvents+1, TotGC+NumGC)
877    after 0 ->
878            if
879                (NumEvents =:= 0) andalso (TotGC =:= 0) ->
880                    ?IPRINT("cache_flush_gc_events -> no GC events"),
881                    exit(no_gc_events);
882                true ->
883                    {NumEvents, TotGC}
884            end
885    end.
886
887
888%%======================================================================
889%% Internal functions
890%%======================================================================
891
892%% -- Mnesia functions
893
894mnesia_start(Opts) ->
895    mnesia_start(Opts, [node()]).
896
897mnesia_start(Opts, Nodes) ->
898    %% We can accept mnesia beeing loaded but *not* started.
899    %% If its started it *may* contain data that will invalidate
900    %% this test case.
901    ?IPRINT("mnesia_start -> try load mnesia when:"
902            "~n   Loaded:  ~p"
903            "~n   Running: ~p", [apps_loaded(), apps_running()]),
904    ?line ok = case application:load(mnesia) of
905                   ok ->
906                       ok;
907                   {error, {already_loaded, mnesia}} ->
908                       ok;
909                   {error, _} = ERROR ->
910                       ERROR
911               end,
912    F = fun({Key, Val}) ->
913		?IPRINT("mnesia_start -> try set mnesia env: "
914                        "~n   ~p -> ~p", [Key, Val]),
915		?line application_controller:set_env(mnesia, Key, Val)
916	end,
917    lists:foreach(F, Opts),
918    ?IPRINT("mnesia_start -> create mnesia schema on ~p", [Nodes]),
919    ?line case mnesia:create_schema(Nodes) of
920              ok ->
921                  ok;
922              {error, {_, {already_exist, _}}} ->
923                  throw({skip, "Mnesia schema already exists"});
924              {error, SchemaReason} ->
925                  throw({skip,
926                         ?F("Failed create mnesia schema: ~p", [SchemaReason])})
927          end,
928    ?IPRINT("mnesia_start -> start mnesia", []),
929    ?line case application:start(mnesia) of
930              ok ->
931                  ok;
932              {error, {already_started, mnesia}} ->
933                  throw({skip, "Mnesia already started"});
934              {error, StartReason} ->
935                  throw({skip,
936                         ?F("Failed starting mnesia: ~p", [StartReason])})
937          end,
938    ?IPRINT("mnesia_start -> mnesia started", []),
939    ok.
940
941mnesia_stop() ->
942    ?IPRINT("mnesia_stop -> try stop mnesia when:"
943            "~n   Loaded:  ~p"
944            "~n   Running: ~p", [apps_loaded(), apps_running()]),
945    application:stop(mnesia),
946    ?IPRINT("mnesia_stop -> try unload mnesia when"
947            "~n   Loaded:  ~p"
948            "~n   Running: ~p", [apps_loaded(), apps_running()]),
949    application:unload(mnesia),
950    ?IPRINT("mnesia_stop -> done when:"
951            "~n   Loaded:  ~p"
952            "~n   Running: ~p", [apps_loaded(), apps_running()]),
953    ok.
954
955apps_loaded() ->
956    [App || {App, _, _} <- application:loaded_applications()].
957
958apps_running() ->
959    [App || {App, _, _} <- application:which_applications()].
960
961
962%% - Symbolic Store mini interface
963
964sym_start(Prio, Verbosity) ->
965    sym_start(Prio, mib_storage(), Verbosity).
966
967sym_start(Prio, MibStorage, Verbosity) ->
968    Opts = [{mib_storage, MibStorage}, {verbosity,Verbosity}],
969    {ok, _Pid} = snmpa_symbolic_store:start_link(Prio, Opts),
970    ok.
971
972sym_stop() ->
973    ok = snmpa_symbolic_store:stop().
974
975sym_info() ->
976    snmpa_symbolic_store:info().
977
978
979%% -- MIB server mini interface
980
981mibs_start(Prio, Verbosity) when is_atom(Prio) andalso is_atom(Verbosity) ->
982    mibs_start(Prio, mib_storage(), [], Verbosity).
983
984mibs_start(Prio, MibStorage, Verbosity)
985  when is_atom(Prio) andalso is_atom(Verbosity) ->
986    mibs_start(Prio, MibStorage, [], Verbosity).
987
988mibs_start(Prio, MibStorage, Mibs, Verbosity)
989  when is_atom(Prio)       andalso
990       is_list(Mibs)       andalso
991       is_atom(Verbosity) ->
992    mibs_start(Prio, MibStorage, Mibs, Verbosity, []).
993
994mibs_start(Prio, MibStorage, Mibs, Verbosity, CacheOpts)
995  when is_atom(Prio)       andalso
996       is_list(Mibs)       andalso
997       is_atom(Verbosity)  andalso
998       is_list(CacheOpts) ->
999    Opts = [{mib_storage, MibStorage},
1000	    {verbosity,   Verbosity},
1001	    {cache,       CacheOpts}],
1002    {ok, Pid} = snmpa_mib:start_link(Prio, Mibs, Opts),
1003    Pid.
1004
1005mibs_stop(Pid) ->
1006    ok = snmpa_mib:stop(Pid).
1007
1008mibs_info(Pid) ->
1009    snmpa_mib:info(Pid).
1010
1011load_mibs(Pid, Dir, Mibs0) ->
1012    Mibs = [join(Dir, Mib) || Mib <- Mibs0],
1013    Res = snmpa_mib:load_mibs(Pid, Mibs),
1014    %% ?DBG("load_mibs -> "
1015    %% 	 "~n   Res: ~p", [Res]),
1016    Res.
1017
1018unload_mibs(Pid, Mibs) ->
1019    Res = snmpa_mib:unload_mibs(Pid, Mibs),
1020    %% ?DBG("unload_mibs -> "
1021    %% 	 "~n   Res: ~p", [Res]),
1022    Res.
1023
1024verify_loaded_mibs(Pid, Dir, ExpectedMibs0) ->
1025    ExpectedMibs = [join(Dir, Mib) || Mib <- ExpectedMibs0],
1026    case snmpa_mib:info(Pid, loaded_mibs) of
1027	ExpectedMibs ->
1028	    ok;
1029	LoadedMibs0 ->
1030	    ?DBG("verify_loaded_mibs -> LoadedMibs0: ~p", [LoadedMibs0]),
1031	    LoadedMibs = [filename:rootname(FN, ".bin") || FN <- LoadedMibs0],
1032	    ?DBG("verify_loaded_mibs -> LoadedMibs: ~p", [LoadedMibs]),
1033	    ExpNotLoadedMibs = ExpectedMibs -- LoadedMibs,
1034	    LoadedNotExpMibs = LoadedMibs -- ExpectedMibs,
1035	    ?DBG("verify_loaded_mibs -> "
1036		 "~n   ExpNotLoadedMibs: ~p"
1037		 "~n   LoadedNotExpMibs: ~p",
1038		 [ExpNotLoadedMibs, LoadedNotExpMibs]),
1039	    case ExpNotLoadedMibs of
1040		[] ->
1041		    case LoadedNotExpMibs of
1042			[] ->
1043			    ok;
1044			_ ->
1045			    {error, {unexpected_loaded_mibs, LoadedNotExpMibs}}
1046		    end;
1047		_ ->
1048		    case LoadedNotExpMibs of
1049			[] ->
1050			    {error, {not_loaded_mibs, ExpNotLoadedMibs}};
1051			_ ->
1052			    {error, {unexpected_mibs,
1053				     ExpNotLoadedMibs, LoadedNotExpMibs}}
1054		    end
1055	    end
1056
1057    end.
1058
1059me_lookup(Pid, Oid) ->
1060    case snmpa_mib:lookup(Pid, Oid) of
1061	{variable, #me{oid = Oid}} ->
1062	    ok;
1063	{variable, #me{oid = OtherOid}} ->
1064	    case lists:reverse(Oid) of
1065		[0|Rest] ->
1066		    case lists:reverse(Rest) of
1067			OtherOid ->
1068			    ok;
1069			AnotherOid ->
1070			    {error, {invalid_oid, Oid, AnotherOid}}
1071		    end;
1072		_ ->
1073		    {error, {invalid_oid, Oid, OtherOid}}
1074	    end;
1075	{table_column, _ME, _TableEntryOid} ->
1076            ok;
1077        {subagent, SubAgentPid, _SANextOid} ->
1078            {error, {subagent, SubAgentPid}};
1079        {false, Reason} ->
1080            {error, Reason};
1081        Else ->
1082            {error, Else}
1083    end.
1084
1085
1086which_mib(Pid, Oid, Mib1) ->
1087    case snmpa_mib:which_mib(Pid, Oid) of
1088	{ok, Mib2} when is_atom(Mib2) ->
1089	    Mib3 = atom_to_list(Mib2),
1090	    which_mib(Mib1, Mib3);
1091	{ok, Mib2} ->
1092	    which_mib(Mib1, Mib2);
1093        {error, Reason} ->
1094            {error, Reason};
1095        Else ->
1096            {error, Else}
1097    end.
1098
1099which_mib(M, M) ->
1100    ok;
1101which_mib(M1, M2) ->
1102    {error, {invalid_mib, M1, M2}}.
1103
1104
1105%% Default mib-storage
1106mib_storage() ->
1107    [{module, snmpa_mib_storage_ets}].
1108
1109
1110%% --
1111
1112display_memory_usage(MibsPid) ->
1113    SymInfo     = sym_info(),
1114    SymProcSize = key1search(process_memory, SymInfo),
1115    DbSize      = key1search(db_memory,      SymInfo),
1116    MibsInfo    = mibs_info(MibsPid),
1117    TreeSize    = key1search(tree_size_bytes,  MibsInfo),
1118    MibsProcMem = key1search(process_memory,   MibsInfo),
1119    MibDbSize   = key1search([db_memory,mib],  MibsInfo),
1120    NodeDbSize  = key1search([db_memory,node], MibsInfo),
1121    TreeDbSize  = key1search([db_memory,tree], MibsInfo),
1122    ?IPRINT("Symbolic store memory usage: "
1123            "~n   Process memory size: ~p"
1124            "~n   Db size:             ~p"
1125            "~n"
1126            "~nMib server memory usage: "
1127            "~n   Tree size:           ~p"
1128            "~n   Process memory size: ~p"
1129            "~n   Mib db size:         ~p"
1130            "~n   Node db size:        ~p"
1131            "~n   Tree db size:        ~p"
1132            "~n",
1133            [SymProcSize, DbSize,
1134             TreeSize, MibsProcMem, MibDbSize, NodeDbSize, TreeDbSize]).
1135
1136key1search([], Res) ->
1137    Res;
1138key1search([Key|Keys], List) when is_atom(Key) andalso is_list(List) ->
1139    case lists:keysearch(Key, 1, List) of
1140	{value, {Key, Val}} ->
1141	    key1search(Keys, Val);
1142	false ->
1143	    undefined
1144    end;
1145key1search(Key, List) when is_atom(Key) ->
1146    case lists:keysearch(Key, 1, List) of
1147	{value, {Key, Val}} ->
1148	    Val;
1149	false ->
1150	    undefined
1151    end.
1152
1153join(Dir, File) ->
1154    filename:join(Dir, File).
1155