aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/test/snmp_test_mgr.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/test/snmp_test_mgr.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/test/snmp_test_mgr.erl')
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
new file mode 100644
index 0000000000..085dc8600f
--- /dev/null
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -0,0 +1,1139 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_test_mgr).
+
+%%----------------------------------------------------------------------
+%% This module implements a simple SNMP manager for Erlang.
+%%----------------------------------------------------------------------
+
+%% c(snmp_test_mgr).
+%% snmp_test_mgr:start().
+%% snmp_test_mgr:g([[sysContact,0]]).
+
+%% snmp_test_mgr:start([{engine_id, "mbjk's engine"}, v3, {agent, "clip"}, {mibs, ["../mibs/SNMPv2-MIB"]}]).
+
+%% snmp_test_mgr:start([{engine_id, "agentEngine"}, {user, "iwl_test"}, {dir, "mgr_conf"}, {sec_level, authPriv}, v3, {agent, "clip"}]).
+
+%% User interface
+-export([start_link/1, start/1, stop/0,
+ d/0, discovery/0,
+ g/1, s/1, gn/1, gn/0, r/0, gb/3, rpl/1,
+ send_bytes/1,
+ expect/2,expect/3,expect/4,expect/6,get_response/2,
+ receive_response/0,
+ purify_oid/1,
+ oid_to_name/1, name_to_oid/1]).
+
+%% Internal exports
+-export([get_oid_from_varbind/1,
+ var_and_value_to_varbind/2, flatten_oid/2, make_vb/1]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
+
+-include_lib("snmp/include/snmp_types.hrl").
+-include_lib("snmp/include/STANDARD-MIB.hrl").
+
+-record(state,{dbg = true,
+ quiet,
+ parent,
+ timeout = 3500,
+ print_traps = true,
+ mini_mib,
+ packet_server,
+ last_sent_pdu,
+ last_received_pdu}).
+
+-define(SERVER, ?MODULE).
+-define(PACK_SERV, snmp_test_mgr_misc).
+
+start_link(Options) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, {Options, self()}, []).
+
+start(Options) ->
+ gen_server:start({local, ?SERVER}, ?MODULE, {Options, self()}, []).
+
+stop() ->
+ call(stop).
+
+d() ->
+ discovery().
+
+discovery() ->
+ call(discovery).
+
+g(Oids) ->
+ cast({get, Oids}).
+
+%% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value}
+s(VarsAndValues) ->
+ cast({set, VarsAndValues}).
+
+gn(Oids) when is_list(Oids) ->
+ cast({get_next, Oids});
+gn(N) when is_integer(N) ->
+ cast({iter_get_next, N}).
+gn() ->
+ cast(iter_get_next).
+
+r() ->
+ cast(resend_pdu).
+
+gb(NonRepeaters, MaxRepetitions, Oids) ->
+ cast({bulk, {NonRepeaters, MaxRepetitions, Oids}}).
+
+rpl(RespPdu) ->
+ cast({response, RespPdu}).
+
+send_bytes(Bytes) ->
+ cast({send_bytes, Bytes}).
+
+purify_oid(Oid) ->
+ call({purify_oid, Oid}, 5000).
+
+oid_to_name(Oid) ->
+ call({oid_to_name, Oid}, 5000).
+
+name_to_oid(Name) ->
+ call({name_to_oid, Name}, 5000).
+
+
+%%----------------------------------------------------------------------
+%% Purpose: For writing test sequences
+%% Args: Y=any (varbinds) | trap | timeout | VarBinds | ErrStatus
+%% Returns: ok|{error, Id, Reason}
+%%----------------------------------------------------------------------
+expect(Id,Y) -> echo_errors(expect_impl(Id,Y)).
+expect(Id,v2trap,VBs) -> echo_errors(expect_impl(Id,v2trap,VBs));
+expect(Id,report,VBs) -> echo_errors(expect_impl(Id,report,VBs));
+expect(Id,{inform, Reply},VBs) ->
+ echo_errors(expect_impl(Id,{inform,Reply},VBs)).
+expect(Id,Err,Idx,VBs) -> echo_errors(expect_impl(Id,Err,Idx,VBs)).
+expect(Id,trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
+ echo_errors(expect_impl(Id,trap,Enterp,Generic,
+ Specific,ExpectedVarbinds)).
+
+%%-----------------------------------------------------------------
+%% Purpose: For writing test sequences
+%%-----------------------------------------------------------------
+get_response(Id, Vars) -> echo_errors(get_response_impl(Id, Vars)).
+
+%%----------------------------------------------------------------------
+%% Receives a response from the agent.
+%% Returns: a PDU or {error, Reason}.
+%% It doesn't receive traps though.
+%%----------------------------------------------------------------------
+receive_response() ->
+ receive_response(get_timeout()).
+
+receive_response(Timeout) ->
+ d("await response within ~w ms",[Timeout]),
+ receive
+ {snmp_pdu, PDU} when is_record(PDU, pdu) ->
+ d("received PDU: ~n\t~p",[PDU]),
+ PDU
+ after Timeout ->
+ d("response timeout",[]),
+ {error, timeout}
+ end.
+
+
+get_timeout() ->
+ case get(receive_response_timeout) of
+ Int when is_integer(Int) and (Int > 0) ->
+ Int;
+ _ ->
+ get_timeout(os:type())
+ end.
+
+get_timeout(vxworks) -> 7000;
+get_timeout(_) -> 3500.
+
+%%----------------------------------------------------------------------
+%% Receives a trap from the agent.
+%% Returns: TrapPdu|{error, Reason}
+%%----------------------------------------------------------------------
+receive_trap(Timeout) ->
+ d("await trap within ~w ms",[Timeout]),
+ receive
+ {snmp_pdu, PDU} when is_record(PDU, trappdu) ->
+ d("received trap-PDU: ~n\t~p",[PDU]),
+ PDU
+ after Timeout ->
+ d("trap timeout",[]),
+ {error, timeout}
+ end.
+
+%%----------------------------------------------------------------------
+%% Options: List of
+%% {agent_udp, UDPPort}, {agent, Agent}
+%% Optional:
+%% {community, String ("public" is default}, quiet,
+%% {mibs, List of Filenames}, {trap_udp, UDPPort (default 5000)},
+%%----------------------------------------------------------------------
+init({Options, CallerPid}) ->
+ put(sname, mgr),
+ put(verbosity, debug),
+ {A1,A2,A3} = erlang:now(),
+ random:seed(A1,A2,A3),
+ case (catch is_options_ok(Options)) of
+ true ->
+ put(debug, get_value(debug, Options, false)),
+ d("init -> (~p) extract options",[self()]),
+ PacksDbg = get_value(packet_server_debug, Options, false),
+ io:format("[~w] ~p -> PacksDbg: ~p~n", [?MODULE, self(), PacksDbg]),
+ RecBufSz = get_value(recbuf, Options, 1024),
+ io:format("[~w] ~p -> RecBufSz: ~p~n", [?MODULE, self(), RecBufSz]),
+ Mibs = get_value(mibs, Options, []),
+ io:format("[~w] ~p -> Mibs: ~p~n", [?MODULE, self(), Mibs]),
+ Udp = get_value(agent_udp, Options, 4000),
+ io:format("[~w] ~p -> Udp: ~p~n", [?MODULE, self(), Udp]),
+ User = get_value(user, Options, "initial"),
+ io:format("[~w] ~p -> User: ~p~n", [?MODULE, self(), User]),
+ EngineId = get_value(engine_id, Options, "agentEngine"),
+ io:format("[~w] ~p -> EngineId: ~p~n", [?MODULE, self(), EngineId]),
+ CtxEngineId = get_value(context_engine_id, Options, EngineId),
+ io:format("[~w] ~p -> CtxEngineId: ~p~n", [?MODULE, self(), CtxEngineId]),
+ TrapUdp = get_value(trap_udp, Options, 5000),
+ io:format("[~w] ~p -> TrapUdp: ~p~n", [?MODULE, self(), TrapUdp]),
+ Dir = get_value(dir, Options, "."),
+ io:format("[~w] ~p -> Dir: ~p~n", [?MODULE, self(), Dir]),
+ SecLevel = get_value(sec_level, Options, noAuthNoPriv),
+ io:format("[~w] ~p -> SecLevel: ~p~n", [?MODULE, self(), SecLevel]),
+ MiniMIB = snmp_mini_mib:create(Mibs),
+ io:format("[~w] ~p -> MiniMIB: ~p~n", [?MODULE, self(), MiniMIB]),
+ Version = case lists:member(v2, Options) of
+ true -> 'version-2';
+ false ->
+ case lists:member(v3, Options) of
+ true -> 'version-3';
+ false -> 'version-1'
+ end
+ end,
+ io:format("[~w] ~p -> Version: ~p~n", [?MODULE, self(), Version]),
+ Com = case Version of
+ 'version-3' ->
+ get_value(context, Options, "");
+ _ ->
+ get_value(community, Options, "public")
+ end,
+ io:format("[~w] ~p -> Com: ~p~n", [?MODULE, self(), Com]),
+ VsnHdrD =
+ {Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)},
+ io:format("[~w] ~p -> VsnHdrD: ~p~n", [?MODULE, self(), VsnHdrD]),
+ AgIp = case snmp_misc:assq(agent, Options) of
+ {value, Tuple4} when is_tuple(Tuple4) andalso
+ (size(Tuple4) =:= 4) ->
+ Tuple4;
+ {value, Host} when is_list(Host) ->
+ {ok, Ip} = snmp_misc:ip(Host),
+ Ip
+ end,
+ io:format("[~w] ~p -> AgIp: ~p~n", [?MODULE, self(), AgIp]),
+ Quiet = lists:member(quiet, Options),
+ io:format("[~w] ~p -> Quiet: ~p~n", [?MODULE, self(), Quiet]),
+ PackServ = start_packet_server(Quiet, Options, CallerPid,
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg),
+ d("init -> packet server: ~p",[PackServ]),
+ State = #state{parent = CallerPid,
+ quiet = Quiet,
+ mini_mib = MiniMIB,
+ packet_server = PackServ},
+ d("init -> done",[]),
+ {ok, State};
+
+ {error, Reason} ->
+ {stop,Reason}
+ end.
+
+start_packet_server(false, _Options, _CallerPid, AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz, PacksDbg) ->
+ d("start_packet_server -> entry", []),
+ ?PACK_SERV:start_link_packet({msg, self()},
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg);
+start_packet_server(true, Options, CallerPid, AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz, PacksDbg) ->
+ Type = get_value(receive_type, Options, pdu),
+ d("start_packet_server -> entry with"
+ "~n CallerPid: ~p"
+ "~n when"
+ "~n Type: ~p",[CallerPid, Type]),
+ ?PACK_SERV:start_link_packet({Type, CallerPid},
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg).
+
+is_options_ok([{mibs,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([quiet|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{agent,_}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{agent_udp,Int}|Opts]) when is_integer(Int) ->
+ is_options_ok(Opts);
+is_options_ok([{trap_udp,Int}|Opts]) when is_integer(Int) ->
+ is_options_ok(Opts);
+is_options_ok([{community,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{dir,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,noAuthNoPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,authNoPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,authPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{context,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{user,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{engine_id,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{context_engine_id,List}|Opts]) when is_list(List) ->
+ is_options_ok(Opts);
+is_options_ok([v1|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([v2|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([v3|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{debug,Bool}|Opts]) ->
+ case is_bool(Bool) of
+ ok ->
+ is_options_ok(Opts);
+ error ->
+ {error, {bad_option, debug, Bool}}
+ end;
+is_options_ok([{packet_server_debug,Bool}|Opts]) ->
+ case is_bool(Bool) of
+ ok ->
+ is_options_ok(Opts);
+ error ->
+ {error, {bad_option, packet_server_debug, Bool}}
+ end;
+is_options_ok([{recbuf,Sz}|Opts]) when (0 < Sz) and (Sz =< 65535) ->
+ is_options_ok(Opts);
+is_options_ok([InvOpt|_]) ->
+ {error,{invalid_option,InvOpt}};
+is_options_ok([]) -> true.
+
+is_bool(true) -> ok;
+is_bool(false) -> ok;
+is_bool(_) -> error.
+
+mk_seclevel(noAuthNoPriv) -> 0;
+mk_seclevel(authNoPriv) -> 1;
+mk_seclevel(authPriv) -> 3.
+
+
+handle_call({purify_oid, Oid}, _From, #state{mini_mib = MiniMib} = State) ->
+ d("handle_call -> purify_oid for ~p",[Oid]),
+ Reply = (catch purify_oid(Oid, MiniMib)),
+ {reply, Reply, State};
+
+handle_call({find_pure_oid, XOid}, _From, State) ->
+ d("handle_call -> find_pure_oid for ~p",[XOid]),
+ {reply, catch flatten_oid(XOid, State#state.mini_mib), State};
+
+handle_call({oid_to_name, Oid}, _From, State) ->
+ d("handle_call -> oid_to_name for Oid: ~p",[Oid]),
+ Reply =
+ case lists:keysearch(Oid, 1, State#state.mini_mib) of
+ {value, {_Oid, Name, _Type}} ->
+ {ok, Name};
+ false ->
+ {error, {no_such_oid, Oid}}
+ end,
+ {reply, Reply, State};
+
+handle_call({name_to_oid, Name}, _From, State) ->
+ d("handle_call -> name_to_oid for Name: ~p",[Name]),
+ Reply =
+ case lists:keysearch(Name, 2, State#state.mini_mib) of
+ {value, {Oid, _Name, _Type}} ->
+ {ok, Oid};
+ false ->
+ {error, {no_such_name, Name}}
+ end,
+ {reply, Reply, State};
+
+handle_call(stop, _From, #state{mini_mib = MiniMIB} = State) ->
+ d("handle_call -> stop request",[]),
+ snmp_mini_mib:delete(MiniMIB),
+ {stop, normal, ok, State#state{mini_mib = undefined}};
+
+handle_call(discovery, _From, State) ->
+ d("handle_call -> discovery",[]),
+ {Reply, NewState} = execute_discovery(State),
+ {reply, Reply, NewState}.
+
+
+handle_cast({get, Oids}, State) ->
+ d("handle_cast -> get request for ~p", [Oids]),
+ {noreply, execute_request(get, Oids, State)};
+
+handle_cast({set, VariablesAndValues}, State) ->
+ d("handle_cast -> set request for ~p", [VariablesAndValues]),
+ {noreply, execute_request(set, VariablesAndValues, State)};
+
+handle_cast({get_next, Oids}, State) ->
+ d("handle_cast -> get-next request for ~p", [Oids]),
+ {noreply, execute_request(get_next, Oids, State)};
+
+handle_cast(iter_get_next, State)
+ when is_record(State#state.last_received_pdu, pdu) ->
+ d("handle_cast -> iter_get_next request", []),
+ PrevPDU = State#state.last_received_pdu,
+ Oids = [get_oid_from_varbind(Vb) || Vb <- PrevPDU#pdu.varbinds],
+ {noreply, execute_request(get_next, Oids, State)};
+
+handle_cast(iter_get_next, State) ->
+ ?PACK_SERV:error("[Iterated get-next] No Response PDU to "
+ "start iterating from.", []),
+ {noreply, State};
+
+handle_cast({iter_get_next, N}, State) ->
+ d("handle_cast -> iter_get_next(~p) request",[N]),
+ if
+ is_record(State#state.last_received_pdu, pdu) ->
+ PDU = get_next_iter_impl(N, State#state.last_received_pdu,
+ State#state.mini_mib,
+ State#state.packet_server),
+ {noreply, State#state{last_received_pdu = PDU}};
+ true ->
+ ?PACK_SERV:error("[Iterated get-next] No Response PDU to "
+ "start iterating from.", []),
+ {noreply, State}
+ end;
+
+handle_cast(resend_pdu, #state{last_sent_pdu = PDU} = State) ->
+ d("handle_cast -> resend_pdu request when"
+ "~n PDU = ~p", [PDU]),
+ send_pdu(PDU#pdu{request_id = make_request_id()},
+ State#state.mini_mib,
+ State#state.packet_server),
+ {noreply, State};
+
+handle_cast({bulk, Args}, State) ->
+ d("handle_bulk -> bulk request for ~p", [Args]),
+ {noreply, execute_request(bulk, Args, State)};
+
+handle_cast({response, RespPdu}, State) ->
+ d("handle_cast -> response request with ~p", [RespPdu]),
+ ?PACK_SERV:send_pdu(RespPdu, State#state.packet_server),
+ {noreply, State};
+
+handle_cast({send_bytes, Bytes}, State) ->
+ d("handle_cast -> send-bytes request for ~p bytes", [sizeOf(Bytes)]),
+ ?PACK_SERV:send_bytes(Bytes, State#state.packet_server),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ d("handle_cast -> unknown message: "
+ "~n ~p", [Msg]),
+ {noreply, State}.
+
+
+handle_info({snmp_msg, Msg, Ip, Udp}, State) ->
+ io:format("* Got PDU: ~s", [?PACK_SERV:format_hdr(Msg)]),
+ PDU = ?PACK_SERV:get_pdu(Msg),
+ echo_pdu(PDU, State#state.mini_mib),
+ case PDU#pdu.type of
+ 'inform-request' ->
+ %% Generate a response...
+ RespPDU = PDU#pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0},
+ RespMsg = ?PACK_SERV:set_pdu(Msg, RespPDU),
+ ?PACK_SERV:send_msg(RespMsg, State#state.packet_server, Ip, Udp);
+ _Else ->
+ ok
+ end,
+ {noreply, State#state{last_received_pdu = PDU}};
+
+handle_info(Info, State) ->
+ d("handle_info -> unknown info: "
+ "~n ~p", [Info]),
+ {noreply, State}.
+
+
+terminate(Reason, State) ->
+ d("terminate -> with Reason: ~n\t~p",[Reason]),
+ ?PACK_SERV:stop(State#state.packet_server).
+
+
+%%----------------------------------------------------------------------
+%% Returns: A new State
+%%----------------------------------------------------------------------
+execute_discovery(State) ->
+ Pdu = make_discovery_pdu(),
+ Reply = ?PACK_SERV:send_discovery_pdu(Pdu, State#state.packet_server),
+ {Reply, State#state{last_sent_pdu = Pdu}}.
+
+
+execute_request(Operation, Data, State) ->
+ case (catch make_pdu(Operation, Data, State#state.mini_mib)) of
+ {error, {Format, Data2}} ->
+ report_error(State, Format, Data2),
+ State;
+ {error, _Reason} ->
+ State;
+ PDU when is_record(PDU, pdu) ->
+ send_pdu(PDU, State#state.mini_mib, State#state.packet_server),
+ State#state{last_sent_pdu = PDU}
+ end.
+
+report_error(#state{quiet = true, parent = Pid}, Format, Args) ->
+ Reason = lists:flatten(io_lib:format(Format, Args)),
+ Pid ! {oid_error, Reason};
+report_error(_, Format, Args) ->
+ ?PACK_SERV:error(Format, Args).
+
+
+get_oid_from_varbind(#varbind{oid = Oid}) -> Oid.
+
+send_pdu(PDU, _MiniMIB, PackServ) ->
+ ?PACK_SERV:send_pdu(PDU, PackServ).
+
+%%----------------------------------------------------------------------
+%% Purpose: Unnesting of oids like [myTable, 3, 4, "hej", 45] to
+%% [1,2,3,3,4,104,101,106,45]
+%%----------------------------------------------------------------------
+
+purify_oid([A|T], MiniMib) when is_atom(A) ->
+ Oid2 =
+ case snmp_mini_mib:oid(MiniMib, A) of
+ false ->
+ throw({error, {unknown_aliasname, A}});
+ Oid ->
+ lists:flatten([Oid|T])
+ end,
+ {ok, verify_pure_oid(Oid2)};
+purify_oid(L, _) when is_list(L) ->
+ {ok, verify_pure_oid(lists:flatten(L))};
+purify_oid(X, _) ->
+ {error, {invalid_oid, X}}.
+
+verify_pure_oid([]) ->
+ [];
+verify_pure_oid([H | T]) when is_integer(H) and (H >= 0) ->
+ [H | verify_pure_oid(T)];
+verify_pure_oid([H | _]) ->
+ throw({error, {not_pure_oid, H}}).
+
+flatten_oid(XOid, DB) ->
+ Oid = case XOid of
+ [A|T] when is_atom(A) ->
+ [remove_atom(A, DB)|T];
+ L when is_list(L) ->
+ XOid;
+ Shit ->
+ throw({error,
+ {"Invalid oid, not a list of integers: ~w", [Shit]}})
+ end,
+ check_is_pure_oid(lists:flatten(Oid)).
+
+remove_atom(AliasName, DB) when is_atom(AliasName) ->
+ case snmp_mini_mib:oid(DB, AliasName) of
+ false ->
+ throw({error, {"Unknown aliasname in oid: ~w", [AliasName]}});
+ Oid ->
+ Oid
+ end;
+remove_atom(X, _DB) ->
+ X.
+
+%%----------------------------------------------------------------------
+%% Throws if not a list of integers
+%%----------------------------------------------------------------------
+check_is_pure_oid([]) -> [];
+check_is_pure_oid([X | T]) when is_integer(X) and (X >= 0) ->
+ [X | check_is_pure_oid(T)];
+check_is_pure_oid([X | _T]) ->
+ throw({error, {"Invalid oid, it contains a non-integer: ~w", [X]}}).
+
+get_next_iter_impl(0, PrevPDU, _MiniMIB, _PackServ) ->
+ PrevPDU;
+get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ) ->
+ Oids = [get_oid_from_varbind(Vb) || Vb <- PrevPDU#pdu.varbinds],
+ PDU = make_pdu(get_next, Oids, MiniMIB),
+ send_pdu(PDU, MiniMIB, PackServ),
+ case receive_response() of
+ {error, timeout} ->
+ io:format("(timeout)~n"),
+ get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ);
+ {error, _Reason} ->
+ PrevPDU;
+ RPDU when is_record(RPDU, pdu) ->
+ io:format("(~w)", [N]),
+ echo_pdu(RPDU, MiniMIB),
+ get_next_iter_impl(N-1, RPDU, MiniMIB, PackServ)
+ end.
+
+%%--------------------------------------------------
+%% Used to resend a PDU. Takes the old PDU and
+%% generates a fresh one (with a new requestID).
+%%--------------------------------------------------
+
+make_pdu(set, VarsAndValues, MiniMIB) ->
+ VBs = [var_and_value_to_varbind(VAV, MiniMIB) || VAV <- VarsAndValues],
+ make_pdu_impl(set, VBs);
+make_pdu(bulk, {NonRepeaters, MaxRepetitions, Oids}, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ #pdu{type = 'get-bulk-request',
+ request_id = make_request_id(),
+ error_status = NonRepeaters,
+ error_index = MaxRepetitions,
+ varbinds = [make_vb(Oid) || Oid <- Foids]};
+make_pdu(Operation, Oids, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ make_pdu_impl(Operation, Foids).
+
+make_pdu_impl(get, Oids) ->
+ #pdu{type = 'get-request',
+ request_id = make_request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(get_next, Oids) ->
+ #pdu{type = 'get-next-request',
+ request_id = make_request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(set, Varbinds) ->
+ #pdu{type = 'set-request',
+ request_id = make_request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = Varbinds}.
+
+make_discovery_pdu() ->
+ make_pdu_impl(get, []).
+
+var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2,
+ variabletype = char_to_type(Type),
+ value = Value};
+var_and_value_to_varbind({XOid, Value}, MiniMIB) ->
+ Oid = flatten_oid(XOid, MiniMIB),
+ #varbind{oid = Oid,
+ variabletype = snmp_mini_mib:type(MiniMIB, Oid),
+ value = Value}.
+
+char_to_type(o) ->
+ 'OBJECT IDENTIFIER';
+char_to_type(i) ->
+ 'INTEGER';
+char_to_type(u) ->
+ 'Unsigned32';
+char_to_type(g) -> % Gauge, Gauge32
+ 'Unsigned32';
+char_to_type(s) ->
+ 'OCTET STRING'.
+
+make_vb(Oid) ->
+ #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
+
+make_request_id() ->
+ random:uniform(16#FFFFFFF-1).
+
+echo_pdu(PDU, MiniMIB) ->
+ io:format("~s", [snmp_misc:format_pdu(PDU, MiniMIB)]).
+
+
+%%----------------------------------------------------------------------
+%% Test Sequence
+%%----------------------------------------------------------------------
+echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})->
+ io:format("* Unexpected Behaviour * Id: ~w.~n"
+ " Expected: " ++ ExpectedFormat ++ "~n"
+ " Got: " ++ Format ++ "~n",
+ [Id] ++ ExpectedData ++ Data),
+ {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}};
+echo_errors(ok) -> ok;
+echo_errors({ok, Val}) -> {ok, Val}.
+
+get_response_impl(Id, Vars) ->
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ match_vars(Id, find_pure_oids2(Vars), VBs, []);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error,
+ Id,
+ {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w",
+ [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+
+
+%%----------------------------------------------------------------------
+%% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}
+%%----------------------------------------------------------------------
+expect_impl(Id, any) ->
+ io:format("expect_impl(~w, any) -> entry ~n", [Id]),
+ case receive_response() of
+ PDU when is_record(PDU, pdu) -> ok;
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, return) ->
+ io:format("expect_impl(~w, return) -> entry ~n", [Id]),
+ case receive_response() of
+ PDU when is_record(PDU, pdu) -> {ok, PDU};
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, trap) ->
+ io:format("expect_impl(~w, trap) -> entry ~n", [Id]),
+ case receive_trap(3500) of
+ PDU when is_record(PDU, trappdu) -> ok;
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, timeout) ->
+ io:format("expect_impl(~w, timeout) -> entry ~n", [Id]),
+ receive
+ X ->
+ io:format("expect_impl(~w, timeout) -> "
+ "received unexpected message: ~n~p~n", [Id, X]),
+ {error, Id, {"Timeout", []}, {"Message ~w", [X]}}
+ after 3500 ->
+ ok
+ end;
+
+expect_impl(Id, Err) when is_atom(Err) ->
+ io:format("expect_impl(~w, ~w) -> entry ~n", [Id, Err]),
+ case receive_response() of
+ #pdu{error_status = Err} ->
+ ok;
+
+ #pdu{request_id = ReqId,
+ error_status = OtherErr} ->
+ io:format("expect_impl(~w, ~w) -> "
+ "received pdu (~w) with unexpected error-status: "
+ "~n~p~n", [Id, Err, ReqId, OtherErr]),
+ {error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]},
+ {"ErrorStatus: ~w", [OtherErr]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
+ io:format("expect_impl(~w) -> entry with"
+ "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ io:format("expect_impl(~w) -> received pdu with"
+ "~n VBs: ~p~n", [Id, VBs]),
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ io:format("expect_impl(~w) -> received unexpected pdu with"
+ "~n Type2: ~p"
+ "~n ReqId: ~p"
+ "~n Err2: ~p"
+ "~n Index2: ~p"
+ "~n", [Id, Type2, ReqId, Err2, Index2]),
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
+ io:format("expect_impl(~w, v2trap) -> entry with"
+ "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ case receive_response() of
+ #pdu{type = 'snmpv2-trap',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ io:format("expect_impl(~w, v2trap) -> received pdu with"
+ "~n VBs: ~p~n", [Id, VBs]),
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ io:format("expect_impl(~w, v2trap) -> received unexpected pdu with"
+ "~n Type2: ~p"
+ "~n ReqId: ~p"
+ "~n Err2: ~p"
+ "~n Index2: ~p"
+ "~n", [Id, Type2, ReqId, Err2, Index2]),
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['snmpv2-trap', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
+ io:format("expect_impl(~w, report) -> entry with"
+ "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ case receive_response() of
+ #pdu{type = 'report',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ io:format("expect_impl(~w, report) -> received pdu with"
+ "~n VBs: ~p~n", [Id, VBs]),
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ io:format("expect_impl(~w, report) -> received unexpected pdu with"
+ "~n Type2: ~p"
+ "~n ReqId: ~p"
+ "~n Err2: ~p"
+ "~n Index2: ~p"
+ "~n", [Id, Type2, ReqId, Err2, Index2]),
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ [report, noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
+ when is_list(ExpectedVarbinds) ->
+ io:format("expect_impl(~w, inform) -> entry with"
+ "~n Reply: ~p"
+ "~n ExpectedVarbinds: ~p"
+ "~n", [Id, Reply, ExpectedVarbinds]),
+ Resp = receive_response(),
+ case Resp of
+ #pdu{type = 'inform-request',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ io:format("expect_impl(~w, inform) -> received pdu with"
+ "~n VBs: ~p~n", [Id, VBs]),
+ case check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs) of
+ ok when (Reply == true) ->
+ io:format("expect_impl(~w, inform) -> send ok response"
+ "~n", [Id]),
+ RespPDU = Resp#pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0},
+ ?MODULE:rpl(RespPDU),
+ ok;
+ ok when (element(1, Reply) == error) ->
+ io:format("expect_impl(~w, inform) -> send error response"
+ "~n", [Id]),
+ {error, Status, Index} = Reply,
+ RespPDU = Resp#pdu{type = 'get-response',
+ error_status = Status,
+ error_index = Index},
+ ?MODULE:rpl(RespPDU),
+ ok;
+ ok when (Reply == false) ->
+ io:format("expect_impl(~w, inform) -> no response sent"
+ "~n", [Id]),
+ ok;
+ Else ->
+ io:format("expect_impl(~w, inform) -> "
+ "~n Else: ~p"
+ "~n", [Id, Else]),
+ Else
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ io:format("expect_impl(~w, inform) -> received unexpected pdu with"
+ "~n Type2: ~p"
+ "~n ReqId: ~p"
+ "~n Err2: ~p"
+ "~n Index2: ~p"
+ "~n", [Id, Type2, ReqId, Err2, Index2]),
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['inform-request', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ io:format("expect_impl(~w, inform) -> receive failed"
+ "~n Reason: ~p"
+ "~n", [Id, Reason]),
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, Err, Index, any) ->
+ io:format("expect_impl(~w, any) -> entry with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n", [Id, Err, Index]),
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = Err,
+ error_index = Index} ->
+ io:format("expect_impl(~w, any) -> received expected pdu"
+ "~n", [Id]),
+ ok;
+
+ #pdu{type = 'get-response', error_status = Err} when (Index == any) ->
+ io:format("expect_impl(~w, any) -> received expected pdu (any)"
+ "~n", [Id]),
+ ok;
+
+ #pdu{type = 'get-response',
+ request_id = ReqId,
+ error_status = Err,
+ error_index = Idx} when is_list(Index) ->
+ io:format("expect_impl(~w, any) -> received pdu: "
+ "~n ReqId: ~p"
+ "~n Err: ~p"
+ "~n Idx: ~p"
+ "~n", [Id, ReqId, Err, Idx]),
+ case lists:member(Idx, Index) of
+ true ->
+ ok;
+ false ->
+ {error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ [Err, Index, ReqId]},
+ {"ErrStat: ~w, Idx: ~w", [Err, Idx]}}
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ io:format("expect_impl(~w, any) -> received unexpected pdu: "
+ "~n Type2: ~p"
+ "~n ReqId: ~p"
+ "~n Err2: ~p"
+ "~n Index2: ~p"
+ "~n", [Id, Type2, ReqId, Err2, Index2]),
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', Err, Index, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, Err, Index, ExpectedVarbinds) ->
+ io:format("expect_impl(~w) -> entry with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n ExpectedVarbinds: ~p"
+ "~n", [Id, Err, Index, ExpectedVarbinds]),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = Err,
+ error_index = Index,
+ varbinds = VBs} ->
+ check_vars(Id, PureVBs, VBs);
+
+ #pdu{type = 'get-response',
+ error_status = Err,
+ varbinds = VBs} when (Index == any) ->
+ check_vars(Id, PureVBs, VBs);
+
+ #pdu{type = 'get-response',
+ request_id = ReqId,
+ error_status = Err,
+ error_index = Idx,
+ varbinds = VBs} when is_list(Index) ->
+ case lists:member(Idx, Index) of
+ true ->
+ check_vars(Id, PureVBs, VBs);
+ false ->
+ {error,Id,
+ {"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
+ [Err,Index,PureVBs,ReqId]},
+ {"ErrStat: ~w, Idx: ~w, Varbinds: ~w",
+ [Err,Idx,VBs]}}
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2,
+ varbinds = VBs} ->
+ {error,Id,
+ {"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
+ ['get-response',Err,Index,PureVBs,ReqId]},
+ {"Type: ~w, ErrStat: ~w Idx: ~w Varbinds: ~w",
+ [Type2,Err2,Index2,VBs]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
+ PureE = find_pure_oid(Enterp),
+ case receive_trap(3500) of
+ #trappdu{enterprise = PureE,
+ generic_trap = Generic,
+ specific_trap = Specific,
+ varbinds = VBs} ->
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #trappdu{enterprise = Ent2,
+ generic_trap = G2,
+ specific_trap = Spec2,
+ varbinds = VBs} ->
+ {error, Id,
+ {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
+ [PureE, Generic, Specific, ExpectedVarbinds]},
+ {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
+ [Ent2, G2, Spec2, VBs]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+format_reason(Id, Reason) ->
+ {error, Id, {"?", []}, {"~w", [Reason]}}.
+
+%%----------------------------------------------------------------------
+%% Args: Id, ExpectedVarbinds, GotVarbinds
+%% Returns: ok
+%% Fails: if not ok
+%%----------------------------------------------------------------------
+check_vars(_Id,[], []) ->
+ ok;
+check_vars(Id,Vars, []) ->
+ {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
+check_vars(Id,[], Varbinds) ->
+ {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}};
+check_vars(Id,[{_XOid, any} | Vars], [#varbind{oid = _Oid} |Vbs]) ->
+ check_vars(Id,Vars, Vbs);
+check_vars(Id,[{Oid, Val} | Vars], [#varbind{oid = Oid, value = Val} |Vbs]) ->
+ check_vars(Id,Vars, Vbs);
+check_vars(Id,[{Oid, Val} | _], [#varbind{oid = Oid, value = Val2} |_]) ->
+ {error, Id, {" Varbind: ~w = ~w", [Oid, Val]}, {"Value: ~w", [Val2]}};
+check_vars(Id,[{Oid, _Val} | _], [#varbind{oid = Oid2, value = _Val2} |_]) ->
+ {error, Id, {"Oid: ~w", [Oid]}, {"Oid: ~w", [Oid2]}}.
+
+match_vars(Id, [Oid|T], [#varbind{oid = Oid, value = Value} | Vbs], Res) ->
+ match_vars(Id, T, Vbs, [Value | Res]);
+match_vars(_Id, [], [], Res) ->
+ {ok, lists:reverse(Res)};
+match_vars(Id, [Oid | _], [#varbind{oid = Oid2}], _Res) ->
+ {error, Id, {" Oid: ~w", [Oid]}, {"Oid2: ~w", [Oid2]}};
+match_vars(Id, Vars, [], _Res) ->
+ {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
+match_vars(Id, [], Varbinds, _Res) ->
+ {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}}.
+
+
+
+find_pure_oids([]) -> [];
+find_pure_oids([{XOid, Q}|T]) ->
+ [{find_pure_oid(XOid), Q} | find_pure_oids(T)].
+
+find_pure_oids2([]) -> [];
+find_pure_oids2([XOid|T]) ->
+ [find_pure_oid(XOid) | find_pure_oids2(T)].
+
+
+%%----------------------------------------------------------------------
+%% Returns: Oid
+%% Fails: malformed oids
+%%----------------------------------------------------------------------
+find_pure_oid(XOid) ->
+ case gen_server:call(?MODULE, {find_pure_oid, XOid}, infinity) of
+ {error, {Format, Data}} ->
+ ok = io:format(Format, Data),
+ exit(malformed_oid);
+ Oid when is_list(Oid) -> Oid
+ end.
+
+get_value(Opt, Opts, Default) ->
+ case snmp_misc:assq(Opt,Opts) of
+ {value, C} -> C;
+ false -> Default
+ end.
+
+
+%%----------------------------------------------------------------------
+
+call(Req) ->
+ call(Req, infinity).
+
+call(Req, To) ->
+ gen_server:call(?SERVER, Req, To).
+
+cast(Msg) ->
+ gen_server:cast(?SERVER, Msg).
+
+
+%%----------------------------------------------------------------------
+%% Debug
+%%----------------------------------------------------------------------
+
+sizeOf(L) when is_list(L) ->
+ length(lists:flatten(L));
+sizeOf(B) when is_binary(B) ->
+ size(B).
+
+d(F,A) -> d(get(debug),F,A).
+
+d(true,F,A) ->
+ io:format("*** [~s] MGR_DBG *** " ++ F ++ "~n",
+ [format_timestamp(now())|A]);
+d(_,_F,_A) ->
+ ok.
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
+