%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(snmp_test_mgr).
%%----------------------------------------------------------------------
%% This module implements a simple SNMP manager for Erlang. Its used
%% during by the agent test suite.
%%----------------------------------------------------------------------
%% 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").
-include("snmp_test_lib.hrl").
-include_lib("snmp/src/app/snmp_internal.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(_) -> 10000. % Trying to improve test results % 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),
?SNMP_RAND_SEED(),
%% rand:seed(exrop,
%% {erlang:phash2([node()]),
%% erlang:monotonic_time(),
%% erlang:unique_integer()}),
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),
print("[~w] ~p -> PacksDbg: ~p~n", [?MODULE, self(), PacksDbg]),
RecBufSz = get_value(recbuf, Options, 1024),
print("[~w] ~p -> RecBufSz: ~p~n", [?MODULE, self(), RecBufSz]),
Mibs = get_value(mibs, Options, []),
print("[~w] ~p -> Mibs: ~p~n", [?MODULE, self(), Mibs]),
Udp = get_value(agent_udp, Options, 4000),
print("[~w] ~p -> Udp: ~p~n", [?MODULE, self(), Udp]),
User = get_value(user, Options, "initial"),
print("[~w] ~p -> User: ~p~n", [?MODULE, self(), User]),
EngineId = get_value(engine_id, Options, "agentEngine"),
print("[~w] ~p -> EngineId: ~p~n", [?MODULE, self(), EngineId]),
CtxEngineId = get_value(context_engine_id, Options, EngineId),
print("[~w] ~p -> CtxEngineId: ~p~n", [?MODULE, self(), CtxEngineId]),
TrapUdp = get_value(trap_udp, Options, 5000),
print("[~w] ~p -> TrapUdp: ~p~n", [?MODULE, self(), TrapUdp]),
Dir = get_value(dir, Options, "."),
print("[~w] ~p -> Dir: ~p~n", [?MODULE, self(), Dir]),
SecLevel = get_value(sec_level, Options, noAuthNoPriv),
print("[~w] ~p -> SecLevel: ~p~n", [?MODULE, self(), SecLevel]),
MiniMIB = snmp_mini_mib:create(Mibs),
d("[~w] ~p -> MiniMIB: "
"~n ~p", [?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,
print("[~w] ~p -> Version: ~p~n", [?MODULE, self(), Version]),
Com = case Version of
'version-3' ->
get_value(context, Options, "");
_ ->
get_value(community, Options, "public")
end,
print("[~w] ~p -> Com: ~p~n", [?MODULE, self(), Com]),
VsnHdrD =
{Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)},
print("[~w] ~p -> VsnHdrD: ~p~n", [?MODULE, self(), VsnHdrD]),
IpFamily = get_value(ipfamily, Options, inet),
print("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]),
AgIp = case snmp_misc:assq(agent, Options) of
{value, Addr} when is_tuple(Addr) andalso
(size(Addr) =:= 4) andalso
(IpFamily =:= inet) ->
print("[~w] ~p -> Addr: ~p~n",
[?MODULE, self(), Addr]),
Addr;
{value, Addr} when is_tuple(Addr) andalso
(size(Addr) =:= 8) andalso
(IpFamily =:= inet6) ->
print("[~w] ~p -> Addr: ~p~n",
[?MODULE, self(), Addr]),
Addr;
{value, Host} when is_list(Host) ->
print("[~w] ~p -> Host: ~p~n",
[?MODULE, self(), Host]),
{ok, Ip} = snmp_misc:ip(Host, IpFamily),
Ip
end,
print("[~w] ~p -> AgIp: ~p~n", [?MODULE, self(), AgIp]),
Quiet = lists:member(quiet, Options),
print("[~w] ~p -> Quiet: ~p~n", [?MODULE, self(), Quiet]),
PackServ =
start_packet_server(
Quiet, Options, CallerPid, AgIp, Udp, TrapUdp,
VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily),
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, IpFamily) ->
d("start_packet_server -> entry", []),
?PACK_SERV:start_link_packet(
{msg, self()}, AgIp, Udp, TrapUdp,
VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily);
start_packet_server(true, Options, CallerPid, AgIp, Udp, TrapUdp,
VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily) ->
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, IpFamily).
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([{ipfamily,IpFamily}|Opts])
when IpFamily =:= inet;
IpFamily =:= inet6 ->
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 "
"~n ~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() ->
snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647).
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) ->
print(F, A);
d(_,_F,_A) ->
ok.
print(F, A) ->
?PRINT2("MGR " ++ F, A).
%% formated_timestamp() ->
%% snmp_test_lib:formated_timestamp().