1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2003-2015. 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%% Purpose: Simple (snmp) manager used when performing appup tests. 23%%---------------------------------------------------------------------- 24-module(snmp_appup_mgr). 25 26-behaviour(snmpm_user). 27 28-include_lib("snmp/include/STANDARD-MIB.hrl"). 29-include_lib("snmp/include/snmp_types.hrl"). 30 31-export([start/0, start/1, start/2]). 32-export([handle_error/3, 33 handle_agent/4, 34 handle_pdu/5, 35 handle_trap/4, 36 handle_inform/4, 37 handle_report/4]). 38-export([main/2]). 39 40-record(agent, {host, port, conf}). 41-record(state, {timer, reqs, ids, agent}). 42 43-define(USER_ID, ?MODULE). 44-define(REQ_TIMEOUT, 10000). 45-define(POLL_TIMEOUT, 5000). 46-define(DEFAULT_PORT, 4000). 47%% -define(DEFAULT_PORT, 161). 48 49-define(v1_2(V1,V2), 50 case get(vsn) of 51 v1 -> V1; 52 _ -> V2 53 end). 54 55 56start() -> 57 {ok, AgentHost} = inet:gethostname(), 58 AgentPort = ?DEFAULT_PORT, 59 start(AgentHost, AgentPort). 60 61start(AgentPort) when is_integer(AgentPort) -> 62 {ok, AgentHost} = inet:gethostname(), 63 start(AgentHost, AgentPort); 64start(AgentHost) when is_list(AgentHost) -> 65 AgentPort = 161, 66 start(AgentHost, AgentPort). 67 68start(AgentHost, AgentPort) 69 when is_list(AgentHost) and is_integer(AgentPort) -> 70 ensure_started(snmp), 71 Pid = erlang:spawn_link(?MODULE, main, [AgentHost, AgentPort]), 72 receive 73 {'EXIT', Pid, normal} -> 74 ok; 75 {'EXIT', Pid, Reason} -> 76 {error, {unexpected_exit, Reason}} 77 end. 78 79ensure_started(App) -> 80 case application:start(App) of 81 ok -> 82 ok; 83 {error, {already_started, _}} -> 84 ok; 85 {error, Reason} -> 86 exit(Reason) 87 end. 88 89poll_timer() -> 90 poll_timer(first). 91 92poll_timer(How) -> 93 erlang:send_after(?POLL_TIMEOUT, self(), {poll_timeout, How}). 94 95next_poll_type(first) -> 96 all; 97next_poll_type(all) -> 98 first. 99 100main(AgentHost, AgentPort) -> 101 ok = snmpm:register_user_monitor(?USER_ID, ?MODULE, self()), 102 AgentConf = [{community, "all-rights"}, 103 {engine_id, "agentEngine"}, 104 {sec_level, noAuthNoPriv}, 105 {version, v1}], 106 ok = snmpm:register_agent(?USER_ID, AgentHost, AgentPort, AgentConf), 107 Reqs = [{"sysDescr", get, ?sysDescr_instance}, 108 {"sysObjectID", get, ?sysObjectID_instance}, 109 {"sysUpTime", get, ?sysUpTime_instance}], 110 Agent = #agent{host = AgentHost, port = AgentPort, conf = AgentConf}, 111 State = #state{timer = poll_timer(), reqs = Reqs, agent = Agent}, 112 loop(State). 113 114loop(State) -> 115 receive 116 {poll_timeout, How} -> 117 NewState = handle_poll_timeout(State, How), 118 loop(NewState#state{timer = poll_timer(next_poll_type(How))}); 119 120 {req_timeout, ReqId} -> 121 NewState = handle_req_timeout(State, ReqId), 122 loop(NewState); 123 124 {snmp_callback, Info} -> 125 NewState = handle_snmp(State, Info), 126 loop(NewState) 127 end. 128 129 130handle_poll_timeout(#state{agent = Agent, reqs = [Req|Reqs], ids = IDs} = S, 131 first) -> 132 ReqId = handle_req(Agent, [Req]), 133 S#state{reqs = Reqs ++ [Req], ids = [ReqId|IDs]}; 134handle_poll_timeout(#state{agent = Agent, reqs = Reqs, ids = IDs} = S, all) -> 135 ReqId = handle_req(Agent, Reqs), 136 S#state{ids = [ReqId|IDs]}. 137 138handle_req(#agent{host = Host, port = Port}, Reqs) -> 139 Oids = [Oid || {_Desc, Op, Oid} <- Reqs, Op == get], 140 Descs = [Desc || {Desc, Op, _Oid} <- Reqs, Op == get], 141 {ok, ReqId} = snmpm:ag(?USER_ID, Host, Port, Oids), 142 p("issued get-request (~w) for: ~s", [ReqId, oid_descs(Descs)]), 143 ReqTimer = erlang:send_after(?REQ_TIMEOUT, self(), {req_timeout, ReqId}), 144 {ReqId, erlang:monotonic_time(micro_seconds), ReqTimer}. 145 146oid_descs([]) -> 147 []; 148oid_descs([Desc]) -> 149 lists:flatten(io_lib:format("~s", [Desc])); 150oid_descs([Desc|Descs]) -> 151 lists:flatten(io_lib:format("~s, ", [Desc])) ++ oid_descs(Descs). 152 153handle_req_timeout(#state{ids = IDs0} = State, ReqId) -> 154 case lists:keysearch(ReqId, 1, IDs0) of 155 {value, {ReqId, _T, _Ref}} -> 156 e("Request timeout for request ~w", [ReqId]), 157 IDs = lists:keydelete(ReqId, 1, IDs0), 158 State#state{ids = IDs}; 159 false -> 160 w("Did not find request corresponding to id ~w", [ReqId]), 161 State 162 end. 163 164handle_snmp(#state{ids = IDs0} = S, {error, ReqId, Reason}) -> 165 case lists:keysearch(ReqId, 1, IDs0) of 166 {value, {ReqId, T, Ref}} -> 167 Diff = erlang:monotonic_time(micro_seconds) - T, 168 p("SNMP error regarding outstanding request after ~w microsec:" 169 "~n ReqId: ~w" 170 "~n Reason: ~w", [Diff, ReqId, Reason]), 171 IDs = lists:keydelete(ReqId, 1, IDs0), 172 erlang:cancel_timer(Ref), 173 S#state{ids = IDs}; 174 false -> 175 w("SNMP error regarding unknown request:" 176 "~n ReqId: ~w" 177 "~n Reason: ~w", [ReqId, Reason]), 178 S 179 end; 180 181handle_snmp(State, {agent, Addr, Port, SnmpInfo}) -> 182 p("Received agent info:" 183 "~n Addr: ~w" 184 "~n Port: ~w" 185 "~n SnmpInfo: ~w", [Addr, Port, SnmpInfo]), 186 State; 187 188handle_snmp(#state{ids = IDs0} = S, {pdu, Addr, Port, ReqId, SnmpResponse}) -> 189 case lists:keysearch(ReqId, 1, IDs0) of 190 {value, {ReqId, T, Ref}} -> 191 Diff = erlang:monotonic_time(micro_seconds) - T, 192 p("SNMP pdu regarding outstanding request after ~w microsec:" 193 "~n ReqId: ~w" 194 "~n Addr: ~w" 195 "~n Port: ~w" 196 "~n SnmpResponse: ~w", 197 [Diff, ReqId, Addr, Port, SnmpResponse]), 198 IDs = lists:keydelete(ReqId, 1, IDs0), 199 erlang:cancel_timer(Ref), 200 S#state{ids = IDs}; 201 false -> 202 w("SNMP pdu regarding unknown request:" 203 "~n ReqId: ~w" 204 "~n Addr: ~w" 205 "~n Port: ~w" 206 "~n SnmpResponse: ~w", [ReqId, Addr, Port, SnmpResponse]), 207 S 208 end; 209 210handle_snmp(State, {trap, Addr, Port, SnmpTrapInfo}) -> 211 p("Received trap:" 212 "~n Addr: ~w" 213 "~n Port: ~w" 214 "~n SnmpTrapInfo: ~w", [Addr, Port, SnmpTrapInfo]), 215 State; 216 217handle_snmp(State, {inform, Addr, Port, SnmpInform}) -> 218 p("Received inform:" 219 "~n Addr: ~w" 220 "~n Port: ~w" 221 "~n SnmpInform: ~w", [Addr, Port, SnmpInform]), 222 State; 223 224handle_snmp(State, {report, Addr, Port, SnmpReport}) -> 225 p("Received report:" 226 "~n Addr: ~w" 227 "~n Port: ~w" 228 "~n SnmpReport: ~w", [Addr, Port, SnmpReport]), 229 State; 230 231handle_snmp(State, Unknown) -> 232 p("Received unknown snmp info:" 233 "~n Unknown: ~w", [Unknown]), 234 State. 235 236 237%% ----------------------------------------------------------------------- 238%% 239%% Manager user callback API 240%% 241%% ----------------------------------------------------------------------- 242 243 244handle_error(ReqId, Reason, Pid) -> 245 Pid ! {snmp_callback, {error, ReqId, Reason}}, 246 ignore. 247 248handle_agent(Addr, Port, SnmpInfo, Pid) -> 249 Pid ! {snmp_callback, {agent, Addr, Port, SnmpInfo}}, 250 ignore. 251 252handle_pdu(Addr, Port, ReqId, SnmpResponse, Pid) -> 253 Pid ! {snmp_callback, {pdu, Addr, Port, ReqId, SnmpResponse}}, 254 ignore. 255 256handle_trap(Addr, Port, SnmpTrapInfo, Pid) -> 257 Pid ! {snmp_callback, {trap, Addr, Port, SnmpTrapInfo}}, 258 ignore. 259 260handle_inform(Addr, Port, SnmpInform, Pid) -> 261 Pid ! {snmp_callback, {inform, Addr, Port, SnmpInform}}, 262 ignore. 263 264handle_report(Addr, Port, SnmpReport, Pid) -> 265 Pid ! {snmp_callback, {report, Addr, Port, SnmpReport}}, 266 ignore. 267 268 269%% ----------------------------------------------------------------------- 270 271e(F, A) -> 272 p("*** ERROR ***", F, A). 273 274w(F, A) -> 275 p("*** WARNING ***", F, A). 276 277p(F, A) -> 278 p("*** INFO ***", F, A). 279 280p(P, F, A) -> 281 io:format("~s~nMGR: " ++ F ++ "~n~n", [P|A]). 282