%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2018. 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(ct_snmp).
-include("snmp_types.hrl").
-include("inet.hrl").
-include("ct.hrl").
%%% API
-export([start/2, start/3, stop/1, get_values/3, get_next_values/3, set_values/4,
set_info/1, register_users/2, register_agents/2, register_usm_users/2,
unregister_users/1, unregister_users/2, unregister_agents/1,
unregister_agents/2, unregister_usm_users/1, unregister_usm_users/2,
load_mibs/1, unload_mibs/1]).
%% Manager values
-define(CT_SNMP_LOG_FILE, "ct_snmp_set.log").
-define(MGR_PORT, 5000).
-define(MAX_MSG_SIZE, 484).
-define(ENGINE_ID, "mgrEngine").
%% Agent values
-define(AGENT_ENGINE_ID, "agentEngine").
-define(TRAP_UDP, 5000).
-define(AGENT_UDP, 4000).
-define(CONF_FILE_VER, [v2]).
-define(AGENT_MAX_MSG_SIZE, 484).
-define(AGENT_NOTIFY_TYPE, trap).
-define(AGENT_SEC_TYPE, none).
-define(AGENT_PASSWD, "").
%%%=========================================================================
%%% API
%%%=========================================================================
start(Config, MgrAgentConfName) ->
start(Config, MgrAgentConfName, undefined).
start(Config, MgrAgentConfName, SnmpAppConfName) ->
StartManager= ct:get_config({MgrAgentConfName, start_manager}, true),
StartAgent = ct:get_config({MgrAgentConfName, start_agent}, false),
SysName = ct:get_config({MgrAgentConfName, agent_sysname}, "ct_test"),
{ok, HostName} = inet:gethostname(),
{ok, Addr} = inet:getaddr(HostName, inet),
IP = tuple_to_list(Addr),
AgentManagerIP = ct:get_config({MgrAgentConfName, agent_manager_ip}, IP),
prepare_snmp_env(),
setup_agent(StartAgent, MgrAgentConfName, SnmpAppConfName,
Config, SysName, AgentManagerIP, IP),
setup_manager(StartManager, MgrAgentConfName, SnmpAppConfName,
Config, AgentManagerIP),
ok = start_application(snmp),
manager_register(StartManager, MgrAgentConfName).
start_application(App) ->
case application:start(App) of
{error, {already_started, App}} ->
ok;
Else ->
Else
end.
stop(Config) ->
PrivDir = ?config(priv_dir, Config),
ok = application:stop(snmp),
ok = application:stop(mnesia),
MgrDir = filename:join(PrivDir,"mgr"),
ConfDir = filename:join(PrivDir, "conf"),
DbDir = filename:join(PrivDir,"db"),
catch del_dir(MgrDir),
catch del_dir(ConfDir),
catch del_dir(DbDir).
get_values(Agent, Oids, MgrAgentConfName) ->
[Uid | _] = agent_conf(Agent, MgrAgentConfName),
{ok, SnmpReply, _} = snmpm:sync_get2(Uid, target_name(Agent), Oids),
SnmpReply.
get_next_values(Agent, Oids, MgrAgentConfName) ->
[Uid | _] = agent_conf(Agent, MgrAgentConfName),
{ok, SnmpReply, _} = snmpm:sync_get_next2(Uid, target_name(Agent), Oids),
SnmpReply.
set_values(Agent, VarsAndVals, MgrAgentConfName, Config) ->
PrivDir = ?config(priv_dir, Config),
[Uid | _] = agent_conf(Agent, MgrAgentConfName),
Oids = lists:map(fun({Oid, _, _}) -> Oid end, VarsAndVals),
TargetName = target_name(Agent),
{ok, SnmpGetReply, _} = snmpm:sync_get2(Uid, TargetName, Oids),
{ok, SnmpSetReply, _} = snmpm:sync_set2(Uid, TargetName, VarsAndVals),
case SnmpSetReply of
{noError, 0, _} when PrivDir /= false ->
log(PrivDir, Agent, SnmpGetReply, VarsAndVals);
_ ->
set_failed_or_user_did_not_want_to_log
end,
SnmpSetReply.
set_info(Config) ->
PrivDir = ?config(priv_dir, Config),
SetLogFile = filename:join(PrivDir, ?CT_SNMP_LOG_FILE),
case file:consult(SetLogFile) of
{ok, SetInfo} ->
ok = delete_file(SetLogFile),
lists:reverse(SetInfo);
_ ->
[]
end.
register_users(MgrAgentConfName, Users) ->
case setup_users(Users) of
ok ->
SnmpVals = ct:get_config(MgrAgentConfName),
OldUsers = ct:get_config({MgrAgentConfName,users},[]),
NewSnmpVals = lists:keystore(users, 1, SnmpVals,
{users, Users ++ OldUsers}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok;
Error ->
Error
end.
register_agents(MgrAgentConfName, ManagedAgents) ->
case setup_managed_agents(MgrAgentConfName,ManagedAgents) of
ok ->
SnmpVals = ct:get_config(MgrAgentConfName),
OldAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
NewSnmpVals = lists:keystore(managed_agents, 1, SnmpVals,
{managed_agents,
ManagedAgents ++ OldAgents}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok;
Error ->
Error
end.
register_usm_users(MgrAgentConfName, UsmUsers) ->
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
case setup_usm_users(UsmUsers, EngineID) of
ok ->
SnmpVals = ct:get_config(MgrAgentConfName),
OldUsmUsers = ct:get_config({MgrAgentConfName,usm_users},[]),
NewSnmpVals = lists:keystore(usm_users, 1, SnmpVals,
{usm_users, UsmUsers ++ OldUsmUsers}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok;
Error ->
Error
end.
unregister_users(MgrAgentConfName) ->
Users = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, users},[])],
unregister_users(MgrAgentConfName,Users).
unregister_users(MgrAgentConfName,Users) ->
takedown_users(Users),
SnmpVals = ct:get_config(MgrAgentConfName),
AllUsers = ct:get_config({MgrAgentConfName, users},[]),
RemainingUsers = lists:filter(fun({Id,_}) ->
not lists:member(Id,Users)
end,
AllUsers),
NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users,RemainingUsers}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok.
unregister_agents(MgrAgentConfName) ->
ManagedAgents = [AgentName ||
{AgentName, _} <-
ct:get_config({MgrAgentConfName,managed_agents},[])],
unregister_agents(MgrAgentConfName,ManagedAgents).
unregister_agents(MgrAgentConfName,ManagedAgents) ->
takedown_managed_agents(MgrAgentConfName, ManagedAgents),
SnmpVals = ct:get_config(MgrAgentConfName),
AllAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
RemainingAgents = lists:filter(fun({Name,_}) ->
not lists:member(Name,ManagedAgents)
end,
AllAgents),
NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
{managed_agents,RemainingAgents}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok.
unregister_usm_users(MgrAgentConfName) ->
UsmUsers = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, usm_users},[])],
unregister_usm_users(MgrAgentConfName,UsmUsers).
unregister_usm_users(MgrAgentConfName,UsmUsers) ->
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
takedown_usm_users(UsmUsers,EngineID),
SnmpVals = ct:get_config(MgrAgentConfName),
AllUsmUsers = ct:get_config({MgrAgentConfName, usm_users},[]),
RemainingUsmUsers = lists:filter(fun({Id,_}) ->
not lists:member(Id,UsmUsers)
end,
AllUsmUsers),
NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
{usm_users,RemainingUsmUsers}),
ct_config:update_config(MgrAgentConfName, NewSnmpVals),
ok.
load_mibs(Mibs) ->
snmpa:load_mibs(snmp_master_agent, Mibs).
unload_mibs(Mibs) ->
snmpa:unload_mibs(snmp_master_agent, Mibs).
%%%========================================================================
%%% Internal functions
%%%========================================================================
prepare_snmp_env() ->
%% To make sure application:set_env is not overwritten by any
%% app-file settings.
_ = application:load(snmp),
%% Fix for older versions of snmp where there are some
%% inappropriate default values for alway starting an
%% agent.
application:unset_env(snmp, agent).
%%%---------------------------------------------------------------------------
setup_manager(false, _, _, _, _) ->
ok;
setup_manager(true, MgrConfName, SnmpConfName, Config, IP) ->
PrivDir = ?config(priv_dir, Config),
MaxMsgSize = ct:get_config({MgrConfName,max_msg_size}, ?MAX_MSG_SIZE),
Port = ct:get_config({MgrConfName,mgr_port}, ?MGR_PORT),
EngineID = ct:get_config({MgrConfName,engine_id}, ?ENGINE_ID),
MgrDir = filename:join(PrivDir,"mgr"),
%%% Users, Agents and Usms are in test suites register after the
%%% snmp application is started.
Users = [],
Agents = [],
Usms = [],
ok = make_dir(MgrDir),
snmp_config:write_manager_snmp_files(MgrDir, IP, Port, MaxMsgSize,
EngineID, Users, Agents, Usms),
SnmpEnv = merge_snmp_conf([{config, [{dir, MgrDir},{db_dir, MgrDir},
{verbosity, trace}]},
{server, [{verbosity, trace}]},
{net_if, [{verbosity, trace}]},
{versions, [v1, v2, v3]}],
ct:get_config({SnmpConfName,manager})),
application:set_env(snmp, manager, SnmpEnv).
%%%---------------------------------------------------------------------------
setup_agent(false,_, _, _, _, _, _) ->
ok;
setup_agent(true, AgentConfName, SnmpConfName,
Config, SysName, ManagerIP, AgentIP) ->
ok = start_application(mnesia),
PrivDir = ?config(priv_dir, Config),
Vsns = ct:get_config({AgentConfName, agent_vsns}, ?CONF_FILE_VER),
TrapUdp = ct:get_config({AgentConfName, agent_trap_udp}, ?TRAP_UDP),
AgentUdp = ct:get_config({AgentConfName, agent_udp}, ?AGENT_UDP),
NotifType = ct:get_config({AgentConfName, agent_notify_type},
?AGENT_NOTIFY_TYPE),
SecType = ct:get_config({AgentConfName, agent_sec_type}, ?AGENT_SEC_TYPE),
Passwd = ct:get_config({AgentConfName, agent_passwd}, ?AGENT_PASSWD),
AgentEngineID = ct:get_config({AgentConfName, agent_engine_id},
?AGENT_ENGINE_ID),
AgentMaxMsgSize = ct:get_config({AgentConfName, agent_max_msg_size},
?MAX_MSG_SIZE),
ConfDir = filename:join(PrivDir, "conf"),
DbDir = filename:join(PrivDir,"db"),
ok = make_dir(ConfDir),
ok = make_dir(DbDir),
snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp,
AgentIP, AgentUdp, SysName,
NotifType, SecType, Passwd,
AgentEngineID, AgentMaxMsgSize),
override_default_configuration(Config, AgentConfName),
SnmpEnv = merge_snmp_conf([{db_dir, DbDir},
{config, [{dir, ConfDir},
{verbosity, trace}]},
{agent_type, master},
{agent_verbosity, trace},
{net_if, [{verbosity, trace}]},
{versions, Vsns}],
ct:get_config({SnmpConfName,agent})),
application:set_env(snmp, agent, SnmpEnv).
%%%---------------------------------------------------------------------------
merge_snmp_conf(Defaults, undefined) ->
Defaults;
merge_snmp_conf([Def={Key,DefList=[P|_]}|DefParams], UserParams) when is_tuple(P) ->
case lists:keysearch(Key, 1, UserParams) of
false ->
[Def | merge_snmp_conf(DefParams, UserParams)];
{value,{Key,UserList}} ->
DefList1 = [{SubKey,Val} || {SubKey,Val} <- DefList,
lists:keysearch(SubKey, 1, UserList) == false],
[{Key,DefList1++UserList} | merge_snmp_conf(DefParams,
lists:keydelete(Key, 1, UserParams))]
end;
merge_snmp_conf([Def={Key,_}|DefParams], UserParams) ->
case lists:keysearch(Key, 1, UserParams) of
false ->
[Def | merge_snmp_conf(DefParams, UserParams)];
{value,_} ->
merge_snmp_conf(DefParams, UserParams)
end;
merge_snmp_conf([], UserParams) ->
UserParams.
%%%---------------------------------------------------------------------------
manager_register(false, _) ->
ok;
manager_register(true, MgrAgentConfName) ->
Agents = ct:get_config({MgrAgentConfName, managed_agents}, []),
Users = ct:get_config({MgrAgentConfName, users}, []),
UsmUsers = ct:get_config({MgrAgentConfName, usm_users}, []),
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
setup_usm_users(UsmUsers, EngineID),
setup_users(Users),
setup_managed_agents(MgrAgentConfName,Agents).
%%%---------------------------------------------------------------------------
setup_users(Users) ->
while_ok(fun({Id, [Module, Data]}) ->
snmpm:register_user(Id, Module, Data)
end, Users).
%%%---------------------------------------------------------------------------
setup_managed_agents(AgentConfName,Agents) ->
Fun =
fun({AgentName, [Uid, AgentIp, AgentUdpPort, AgentConf0]}) ->
NewAgentIp = case AgentIp of
IpTuple when is_tuple(IpTuple) ->
IpTuple;
HostName when is_list(HostName) ->
{ok,Hostent} = inet:gethostbyname(HostName),
[IpTuple|_] = Hostent#hostent.h_addr_list,
IpTuple
end,
AgentConf =
case lists:keymember(engine_id,1,AgentConf0) of
true ->
AgentConf0;
false ->
DefaultEngineID =
ct:get_config({AgentConfName,agent_engine_id},
?AGENT_ENGINE_ID),
[{engine_id,DefaultEngineID}|AgentConf0]
end,
snmpm:register_agent(Uid, target_name(AgentName),
[{address,NewAgentIp},{port,AgentUdpPort} |
AgentConf])
end,
while_ok(Fun,Agents).
%%%---------------------------------------------------------------------------
setup_usm_users(UsmUsers, EngineID)->
while_ok(fun({UsmUser, Conf}) ->
snmpm:register_usm_user(EngineID, UsmUser, Conf)
end, UsmUsers).
%%%---------------------------------------------------------------------------
takedown_users(Users) ->
lists:foreach(fun(Id) ->
snmpm:unregister_user(Id)
end, Users).
%%%---------------------------------------------------------------------------
takedown_managed_agents(MgrAgentConfName,ManagedAgents) ->
lists:foreach(fun(AgentName) ->
[Uid | _] = agent_conf(AgentName, MgrAgentConfName),
snmpm:unregister_agent(Uid, target_name(AgentName))
end, ManagedAgents).
%%%---------------------------------------------------------------------------
takedown_usm_users(UsmUsers, EngineID) ->
lists:foreach(fun(Id) ->
snmpm:unregister_usm_user(EngineID, Id)
end, UsmUsers).
%%%---------------------------------------------------------------------------
log(PrivDir, Agent, {_, _, Varbinds}, NewVarsAndVals) ->
Fun = fun(#varbind{oid = Oid, variabletype = Type, value = Value}) ->
{Oid, Type, Value}
end,
OldVarsAndVals = lists:map(Fun, Varbinds),
File = filename:join(PrivDir, ?CT_SNMP_LOG_FILE),
{ok, Fd} = file:open(File, [write, append]),
io:format(Fd, "~p.~n", [{Agent, OldVarsAndVals, NewVarsAndVals}]),
ok = file:close(Fd),
ok.
%%%---------------------------------------------------------------------------
del_dir(Dir) ->
{ok, Files} = file:list_dir(Dir),
FullPathFiles = lists:map(fun(File) -> filename:join(Dir, File) end,
Files),
lists:foreach(fun file:delete/1, FullPathFiles),
ok = delete_dir(Dir),
ok.
%%%---------------------------------------------------------------------------
agent_conf(Agent, MgrAgentConfName) ->
Agents = ct:get_config({MgrAgentConfName, managed_agents}),
case lists:keysearch(Agent, 1, Agents) of
{value, {Agent, AgentConf}} ->
AgentConf;
_ ->
exit({error, {unknown_agent, Agent, Agents}})
end.
%%%---------------------------------------------------------------------------
override_default_configuration(Config, MgrAgentConfName) ->
override_contexts(Config,
ct:get_config({MgrAgentConfName, agent_contexts}, undefined)),
override_community(Config,
ct:get_config({MgrAgentConfName, agent_community}, undefined)),
override_sysinfo(Config,
ct:get_config({MgrAgentConfName, agent_sysinfo}, undefined)),
override_vacm(Config,
ct:get_config({MgrAgentConfName, agent_vacm}, undefined)),
override_usm(Config,
ct:get_config({MgrAgentConfName, agent_usm}, undefined)),
override_notify(Config,
ct:get_config({MgrAgentConfName, agent_notify_def}, undefined)),
override_target_address(Config,
ct:get_config({MgrAgentConfName,
agent_target_address_def},
undefined)),
override_target_params(Config,
ct:get_config({MgrAgentConfName, agent_target_param_def},
undefined)).
%%%---------------------------------------------------------------------------
override_contexts(_, undefined) ->
ok;
override_contexts(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, ContextInfo} = file:consult(FullPathFile),
override_contexts(Config, ContextInfo);
override_contexts(Config, Contexts) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"context.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_context_config(Dir, "", Contexts).
%%%---------------------------------------------------------------------------
override_sysinfo(_, undefined) ->
ok;
override_sysinfo(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, SysInfo} = file:consult(FullPathFile),
override_sysinfo(Config, SysInfo);
override_sysinfo(Config, SysInfo) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"standard.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_standard_config(Dir, "", SysInfo).
%%%---------------------------------------------------------------------------
override_target_address(_, undefined) ->
ok;
override_target_address(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, TargetAddressConf} = file:consult(FullPathFile),
override_target_address(Config, TargetAddressConf);
override_target_address(Config, TargetAddressConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_addr.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf).
%%%---------------------------------------------------------------------------
override_target_params(_, undefined) ->
ok;
override_target_params(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, TargetParamsConf} = file:consult(FullPathFile),
override_target_params(Config, TargetParamsConf);
override_target_params(Config, TargetParamsConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_params.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf).
%%%---------------------------------------------------------------------------
override_notify(_, undefined) ->
ok;
override_notify(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, NotifyConf} = file:consult(FullPathFile),
override_notify(Config, NotifyConf);
override_notify(Config, NotifyConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"notify.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_notify_config(Dir, "", NotifyConf).
%%%---------------------------------------------------------------------------
override_usm(_, undefined) ->
ok;
override_usm(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, UsmConf} = file:consult(FullPathFile),
override_usm(Config, UsmConf);
override_usm(Config, UsmConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"usm.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_usm_config(Dir, "", UsmConf).
%%%--------------------------------------------------------------------------
override_community(_, undefined) ->
ok;
override_community(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, CommunityConf} = file:consult(FullPathFile),
override_community(Config, CommunityConf);
override_community(Config, CommunityConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"community.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_community_config(Dir, "", CommunityConf).
%%%---------------------------------------------------------------------------
override_vacm(_, undefined) ->
ok;
override_vacm(Config, {data_dir_file, File}) ->
Dir = ?config(data_dir, Config),
FullPathFile = filename:join(Dir, File),
{ok, VacmConf} = file:consult(FullPathFile),
override_vacm(Config, VacmConf);
override_vacm(Config, VacmConf) ->
Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"vacm.conf"),
ok = delete_file(File),
ok = snmp_config:write_agent_vacm_config(Dir, "", VacmConf).
%%%---------------------------------------------------------------------------
target_name(Agent) ->
atom_to_list(Agent).
while_ok(Fun,[H|T]) ->
case Fun(H) of
ok -> while_ok(Fun,T);
Error -> Error
end;
while_ok(_Fun,[]) ->
ok.
delete_file(FileName) ->
case file:delete(FileName) of
{error, enoent} -> ok;
Else -> Else
end.
make_dir(Dir) ->
case file:make_dir(Dir) of
{error, eexist} -> ok;
Else -> Else
end.
delete_dir(Dir) ->
case file:del_dir(Dir) of
{error, enoent} -> ok;
Else -> Else
end.