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