1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2016. 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%% This module implements an SNMP manager used in the test suite
23%%----------------------------------------------------------------------
24%%
25
26-module(snmp_test_manager).
27
28-behaviour(gen_server).
29-behaviour(snmpm_user).
30
31
32%% External exports
33-export([
34	 start_link/0, start_link/1,
35	 stop/0,
36
37	 sync_get/1,      sync_get/2,
38	 sync_get_next/1, sync_get_next/2,
39	 sync_get_bulk/3,
40	 sync_set/1,      sync_set/2
41	]).
42
43
44%% Manager callback API:
45-export([
46	 handle_error/3,
47         handle_agent/5,
48         handle_pdu/4,
49         handle_trap/3,
50         handle_inform/3,
51         handle_report/3,
52	 handle_invalid_result/3
53	]).
54
55
56%% gen_server callbacks
57-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
58	 code_change/3, terminate/2]).
59
60-record(state, {parent, req, agent_target_name}).
61
62-define(SERVER, ?MODULE).
63-define(USER,   ?MODULE).
64
65
66%%%-------------------------------------------------------------------
67%%% API
68%%%-------------------------------------------------------------------
69
70start_link() ->
71    start_link([]).
72
73start_link(Opts) ->
74    gen_server:start_link({local, ?SERVER}, ?MODULE, [self(), Opts], []).
75
76stop() ->
77    call(stop).
78
79
80sync_get(Oids) ->
81    sync_get(Oids, fun(X) -> {ok, X} end).
82
83sync_get(Oids, Verify) when is_list(Oids) and is_function(Verify) ->
84    Verify(call({sync_get, Oids})).
85
86
87sync_get_next(Oids) ->
88    sync_get_next(Oids, fun(X) -> {ok, X} end).
89
90sync_get_next(Oids, Verify) when is_list(Oids) and is_function(Verify) ->
91    Verify(call({sync_get_next, Oids})).
92
93
94sync_get_bulk(NR, MR, Oids) ->
95    sync_get_bulk(NR, MR, Oids, fun(X) -> {ok, X} end).
96
97sync_get_bulk(NR, MR, Oids, Verify)
98  when is_integer(NR) and is_integer(MR) and
99       is_list(Oids) and is_function(Verify) ->
100    Verify(call({sync_get_bulk, NR, MR, Oids})).
101
102
103sync_set(VarsAndVals) ->
104    sync_set(VarsAndVals, fun(X) -> {ok, X} end).
105
106sync_set(VarsAndVals, Verify)
107  when is_list(VarsAndVals) and is_function(Verify) ->
108    Verify(call({sync_set, VarsAndVals})).
109
110
111%%%-------------------------------------------------------------------
112%%% Callback functions from gen_server
113%%%-------------------------------------------------------------------
114
115%%--------------------------------------------------------------------
116%% Func: init/1
117%% Returns: {ok, State}          |
118%%          {ok, State, Timeout} |
119%%          ignore               |
120%%          {stop, Reason}
121%%--------------------------------------------------------------------
122init([Parent, Opts]) ->
123    process_flag(trap_exit, true),
124    case (catch do_init(Opts)) of
125	{ok, State} ->
126	    {ok, State#state{parent = Parent}};
127	{error, Reason} ->
128	    {stop, Reason}
129    end.
130
131do_init(Opts) ->
132    {MgrDir, MgrConf, MgrOpts, AgentTargetName, AgentConf} = parse_opts(Opts),
133    ok = snmp_config:write_manager_config(MgrDir, "", MgrConf),
134    ok = snmpm:start_link(MgrOpts),
135    ok = snmpm:register_user(?USER, ?MODULE, self()),
136    ok = snmpm:register_agent(?USER, AgentTargetName, AgentConf),
137    {ok, #state{agent_target_name = AgentTargetName}}.
138
139
140parse_opts(Opts) ->
141    %% Manager config (written to the manager.conf file)
142    %% Addr     = get_opt(addr,      Opts, ?HOSTNAME()),
143    Port     = get_opt(port,      Opts, 5000),
144    EngineId = get_opt(engine_id, Opts, "mgrEngine"),
145    MMS      = get_opt(max_message_size, Opts, 484),
146
147    MgrConf = [%% {address,          Addr},
148	       {port,             Port},
149	       {engine_id,        EngineId},
150	       {max_message_size, MMS}],
151
152
153    %% Manager options
154    MgrOpts = get_opt(options, Opts),
155    MgrDir  = get_opt(dir, get_opt(config,  MgrOpts, [])),
156
157
158    %% Retreive the agent configuration
159    AgentConf   = get_opt(agent_config, Opts),
160    AgentTarget = get_opt(agent_target, Opts),
161    {MgrDir, MgrConf, MgrOpts, AgentTarget, AgentConf}.
162
163
164get_opt(Key, Opts) ->
165    case lists:keysearch(Key, 1, Opts) of
166	{value, {Key, Val}} ->
167	    Val;
168	false ->
169	    throw({error, {missing_mandatory, Key}})
170    end.
171
172get_opt(Key, Opts, Def) ->
173    case lists:keysearch(Key, 1, Opts) of
174	{value, {Key, Val}} ->
175	    Val;
176	false ->
177	    Def
178    end.
179
180
181%%--------------------------------------------------------------------
182%% Func: handle_call/3
183%% Returns: {reply, Reply, State}          |
184%%          {reply, Reply, State, Timeout} |
185%%          {noreply, State}               |
186%%          {noreply, State, Timeout}      |
187%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
188%%          {stop, Reason, State}            (terminate/2 is called)
189%%--------------------------------------------------------------------
190handle_call(stop, _From, S) ->
191    (catch snmpm:stop()),
192    {stop, normal, S};
193
194handle_call({sync_get, Oids}, _From,
195	    #state{agent_target_name = TargetName} = S) ->
196    Reply = (catch snmpm:sync_get2(?USER, TargetName, Oids)),
197    {reply, Reply, S};
198
199handle_call({sync_get_next, Oids}, _From,
200	    #state{agent_target_name = TargetName} = S) ->
201    Reply = (catch snmpm:sync_get_next2(?USER, TargetName, Oids)),
202    {reply, Reply, S};
203
204handle_call({sync_get_bulk, NR, MR, Oids}, _From,
205	    #state{agent_target_name = TargetName} = S) ->
206    Reply = (catch snmpm:sync_get_bulk2(?USER, TargetName, NR, MR, Oids)),
207    {reply, Reply, S};
208
209handle_call({sync_set, VarsAndVals}, _From,
210	    #state{agent_target_name = TargetName} = S) ->
211    Reply = (catch snmpm:sync_set2(?USER, TargetName, VarsAndVals)),
212    {reply, Reply, S};
213
214handle_call(Req, From, State) ->
215    error_msg("received unknown request ~n~p~nFrom ~p", [Req, From]),
216    {reply, {error, unknown_request}, State}.
217
218
219%%--------------------------------------------------------------------
220%% Func: handle_cast/2
221%% Returns: {noreply, State}          |
222%%          {noreply, State, Timeout} |
223%%          {stop, Reason, State}            (terminate/2 is called)
224%%--------------------------------------------------------------------
225handle_cast(Msg, State) ->
226    error_msg("received unknown message ~n~p", [Msg]),
227    {noreply, State}.
228
229
230%%--------------------------------------------------------------------
231%% Func: handle_info/2
232%% Returns: {noreply, State}          |
233%%          {noreply, State, Timeout} |
234%%          {stop, Reason, State}            (terminate/2 is called)
235%%--------------------------------------------------------------------
236handle_info({snmp_error, ReqId, Reason},
237	    #state{parent = P} = State) ->
238    info_msg("received snmp error: "
239	      "~n   ReqId:  ~w"
240	      "~n   Reason: ~p", [ReqId, Reason]),
241    P ! {snmp_error, ReqId, Reason},
242    {noreply, State};
243
244handle_info({snmp_agent, Addr, Port, Info, Pid, _UserData},
245	    #state{parent = P} = State) ->
246    error_msg("detected new agent: "
247	      "~n   Addr: ~w"
248	      "~n   Port: ~p"
249	      "~n   Info: ~p", [Addr, Port, Info]),
250    Pid ! {snmp_agent_reply, ignore, self()},
251    P ! {snmp_agent, Addr, Port, Info},
252    {noreply, State};
253
254handle_info({snmp_pdu, TargetName, ReqId, Resp},
255	    #state{parent = P} = State) ->
256    info_msg("received snmp pdu: "
257	      "~n   TargetName: ~p"
258	      "~n   ReqId:      ~w"
259	      "~n   Resp:       ~p", [TargetName, ReqId, Resp]),
260    P ! {snmp_pdu, TargetName, ReqId, Resp},
261    {noreply, State};
262
263handle_info({snmp_trap, TargetName, Info, Pid},
264	    #state{parent = P} = State) ->
265    info_msg("received snmp trap: "
266	      "~n   TargetName: ~p"
267	      "~n   Info:       ~p", [TargetName, Info]),
268    Pid ! {snmp_trap_reply, ignore, self()},
269    P ! {snmp_trap, TargetName, Info},
270    {noreply, State};
271
272handle_info({snmp_inform, TargetName, Info, Pid},
273	    #state{parent = P} = State) ->
274    info_msg("received snmp inform: "
275	      "~n   TargetName: ~p"
276	      "~n   Info:       ~p", [TargetName, Info]),
277    Pid ! {snmp_inform_reply, ignore, self()},
278    P ! {snmp_inform, TargetName, Info},
279    {noreply, State};
280
281handle_info({snmp_report, TargetName, Info, Pid},
282	    #state{parent = P} = State) ->
283    info_msg("received snmp report: "
284	     "~n   TargetName: ~p"
285	     "~n   Info:       ~p", [TargetName, Info]),
286    Pid ! {snmp_report_reply, ignore, self()},
287    P ! {snmp_report, TargetName, Info},
288    {noreply, State};
289
290handle_info({snmp_invalid_result, In, Out}, State) ->
291    error_msg("Callback failure: "
292	      "~n   In:  ~p"
293	      "~n   Out: ~p", [In, Out]),
294    {noreply, State};
295
296handle_info(Info, State) ->
297    error_msg("received unknown info: "
298	      "~n   Info: ~p", [Info]),
299    {noreply, State}.
300
301
302%%--------------------------------------------------------------------
303%% Func: terminate/2
304%% Purpose: Shutdown the server
305%% Returns: any (ignored by gen_server)
306%%--------------------------------------------------------------------
307terminate(_Reason, _State) ->
308    ok.
309
310
311code_change({down, _Vsn}, State, _Extra) ->
312    {ok, State};
313
314% upgrade
315code_change(_Vsn, State, _Extra) ->
316    {ok, State}.
317
318
319%%%-------------------------------------------------------------------
320%%% Internal functions
321%%%-------------------------------------------------------------------
322
323
324
325
326%% --------------------------------------------------------------------------
327%%
328%%                   SNMP manager callback functions
329%%
330%% --------------------------------------------------------------------------
331
332handle_error(ReqId, Reason, Pid) ->
333    Pid ! {snmp_error, ReqId, Reason},
334    ignore.
335
336
337handle_agent(Addr, Port, SnmpInfo, Pid, UserData) ->
338    Pid ! {snmp_agent, Addr, Port, SnmpInfo, self(), UserData},
339    receive
340	{snmp_agent_reply, Reply, Pid} ->
341	    Reply
342    after 10000 ->
343	    ignore
344    end.
345
346
347handle_pdu(TargetName, ReqId, SnmpResponse, Pid) ->
348    Pid ! {snmp_pdu, TargetName, ReqId, SnmpResponse},
349    ignore.
350
351
352handle_trap(TargetName, SnmpTrapInfo, Pid) ->
353    Pid ! {snmp_trap, TargetName, SnmpTrapInfo, self()},
354    receive
355	{snmp_trap_reply, Reply, Pid} ->
356	    Reply
357    after 10000 ->
358	    ignore
359    end.
360
361
362handle_inform(TargetName, SnmpInfo, Pid) ->
363    Pid ! {snmp_inform, TargetName, SnmpInfo, self()},
364    receive
365	{snmp_inform_reply, Reply, Pid} ->
366	    Reply
367    after 10000 ->
368	    ignore
369    end.
370
371
372handle_report(TargetName, SnmpInfo, Pid) ->
373    Pid ! {snmp_report, TargetName, SnmpInfo, self()},
374    receive
375	{snmp_report_reply, Reply, Pid} ->
376	    Reply
377    after 10000 ->
378	    ignore
379    end.
380
381
382handle_invalid_result(In, Out, Pid) ->
383    Pid ! {snmp_invalid_result, In, Out},
384    ignore.
385
386
387%%----------------------------------------------------------------------
388
389call(Req) ->
390    gen_server:call(?SERVER, Req, infinity).
391
392% cast(Msg) ->
393%     gen_server:cast(?SERVER, Msg).
394
395info_msg(F, A) ->
396    catch error_logger:info_msg("*** TEST-MANAGER: " ++ F ++ "~n", A).
397
398error_msg(F, A) ->
399    catch error_logger:error_msg("*** TEST-MANAGER: " ++ F ++ "~n", A).
400
401
402