%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-2011. 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_notification_mib). %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). -export([configure/1, reconfigure/1, invalidate_cache/0, snmpNotifyTable/1, snmpNotifyTable/3, snmpNotifyFilterTable/3, snmpNotifyFilterProfileTable/3, get_targets/0, get_targets/1]). -export([add_notify/3, delete_notify/1]). -export([check_notify/1]). -include("SNMP-NOTIFICATION-MIB.hrl"). -include("SNMPv2-TC.hrl"). -include("snmp_tables.hrl"). -define(VMODULE,"NOTIFICATION-MIB"). -include("snmp_verbosity.hrl"). -ifndef(default_verbosity). -define(default_verbosity,silence). -endif. %%----------------------------------------------------------------- %% Func: configure/1 %% Args: Dir is the directory where the configuration files are found. %% Purpose: If the tables doesn't exist, this function reads %% the config-files for the notify tables, and %% inserts the data. This means that the data in the tables %% survive a reboot. However, the StorageType column is %% checked for each row. If volatile, the row is deleted. %% Returns: ok %% Fails: exit(configuration_error) %%----------------------------------------------------------------- configure(Dir) -> set_sname(), case db(snmpNotifyTable) of {_, mnesia} -> ?vlog("notification table in mnesia: cleanup",[]), gc_tabs(); TabDb -> case snmpa_local_db:table_exists(TabDb) of true -> ?vlog("notification table exist: cleanup",[]), gc_tabs(); false -> ?vlog("notification table does not exist: reconfigure",[]), reconfigure(Dir) end end. %%----------------------------------------------------------------- %% Func: reconfigure/1 %% Args: Dir is the directory where the configuration files are found. %% Purpose: Reads the config-files for the notify tables, and %% inserts the data. Makes sure that all old data in %% the tables are deleted, and the new data inserted. %% This function makes sure that all (and only) %% config-file-data are in the tables. %% Returns: ok %% Fails: exit(configuration_error) %%----------------------------------------------------------------- reconfigure(Dir) -> set_sname(), case (catch do_reconfigure(Dir)) of ok -> ok; {error, Reason} -> ?vinfo("reconfigure error: ~p", [Reason]), config_err("reconfigure failed: ~p", [Reason]), exit(configuration_error); Error -> ?vinfo("reconfigure failed: ~p", [Error]), config_err("reconfigure failed: ~p", [Error]), exit(configuration_error) end. do_reconfigure(Dir) -> ?vdebug("read notify config files",[]), Notifs = read_notify_config_files(Dir), init_tabs(Notifs), ?vdebug("invalidate cache",[]), invalidate_cache(), ok. read_notify_config_files(Dir) -> ?vdebug("read notify config file",[]), Gen = fun(_) -> ok end, Filter = fun(Notifs) -> Notifs end, Check = fun(Entry) -> check_notify(Entry) end, [Notifs] = snmp_conf:read_files(Dir, [{Gen, Filter, Check, "notify.conf"}]), Notifs. check_notify({Name, Tag, Type}) -> snmp_conf:check_string(Name,{gt,0}), snmp_conf:check_string(Tag), {ok, Val} = snmp_conf:check_atom(Type, [{trap, 1}, {inform, 2}]), Notify = {Name, Tag, Val, ?'StorageType_nonVolatile', ?'RowStatus_active'}, {ok, Notify}; check_notify(X) -> error({invalid_notify, X}). init_tabs(Notifs) -> ?vdebug("create notify table",[]), snmpa_local_db:table_delete(db(snmpNotifyTable)), snmpa_local_db:table_create(db(snmpNotifyTable)), ?vdebug("initiate notify table",[]), init_notify_table(Notifs). init_notify_table([Row | T]) -> Key = element(1, Row), snmpa_local_db:table_create_row(db(snmpNotifyTable), Key, Row), init_notify_table(T); init_notify_table([]) -> true. table_cre_row(Tab, Key, Row) -> snmpa_mib_lib:table_cre_row(db(Tab), Key, Row). table_del_row(Tab, Key) -> snmpa_mib_lib:table_del_row(db(Tab), Key). %% FIXME: does not work with mnesia add_notify(Name, Tag, Type) -> Notif = {Name, Tag, Type}, case (catch check_notify(Notif)) of {ok, Row} -> Key = element(1, Row), case table_cre_row(snmpNotifyTable, Key, Row) of true -> {ok, Key}; false -> {error, create_failed} end; {error, Reason} -> {error, Reason}; Error -> {error, Error} end. %% FIXME: does not work with mnesia delete_notify(Key) -> case table_del_row(snmpNotifyTable, Key) of true -> ok; false -> {error, delete_failed} end. gc_tabs() -> DB = db(snmpNotifyTable), STC = stc(snmpNotifyTable), FOI = foi(snmpNotifyTable), snmpa_mib_lib:gc_tab(DB, STC, FOI), ok. %%----------------------------------------------------------------- %% Func: get_targets() %% get_targets(NotifyName) -> [Target] %% Types: Target = {DestAddr, TargetName, TargetParams, NotifyType} %% NotifyName = string() - the INDEX %% DestAddr = {TDomain, TAddr} %% TagrgetName = string() %% TargetParams = {MpModel, SecModel, SecName, SecLevel} %% NotifyType = trap | {inform, Timeout, Retry} %% Purpose: Returns a list of all targets. Called by snmpa_trap %% when a trap should be sent. %% If a NotifyName is specified, the targets for that %% name is returned. %%----------------------------------------------------------------- get_targets() -> TargetsFun = fun find_targets/0, snmpa_target_cache:targets(TargetsFun). get_targets(NotifyName) -> TargetsFun = fun find_targets/0, snmpa_target_cache:targets(TargetsFun, NotifyName). %%----------------------------------------------------------------- %% We use a cache of targets to avoid searching the tables each %% time a trap is sent. When some of the 3 tables (notify, %% targetAddr, targetParams) is modified, the cache is invalidated. %%----------------------------------------------------------------- invalidate_cache() -> snmpa_target_cache:invalidate(). %% Ret: [{NotifyName, {DestAddr, TargetName, TargetParams, NotifyType}}] %% NotifyType = trap | {inform, Timeout. Retry} %% DestAddr = {Domain, Addr} ; e.g. {snmpUDPDomain, {IPasList, UdpPort}} find_targets() -> TargAddrs = snmp_target_mib:get_target_addrs(), %% TargAddrs = [{TagList,DestAddr,TargetName,TargetParams,Timeout,Retry}] {_, Db} = db(snmpNotifyTable), find_targets([], TargAddrs, Db, []). find_targets(Key, TargAddrs, Db, Res) -> case table_next(snmpNotifyTable, Key) of endOfTable -> Res; NextKey when Db == mnesia -> case mnesia:snmp_get_row(snmpNotifyTable, NextKey) of {ok, #snmpNotifyTable{ snmpNotifyTag = Tag, snmpNotifyType = Type, snmpNotifyRowStatus = ?'RowStatus_active'}} -> ?vtrace("found notify entry for ~w" "~n Tag: ~w" "~n Type: ~w", [NextKey, Tag, Type]), Targs = get_targets(TargAddrs, Tag, Type, NextKey), find_targets(NextKey, TargAddrs, Db, Targs ++ Res); {ok, #snmpNotifyTable{ snmpNotifyTag = Tag, snmpNotifyType = Type, snmpNotifyRowStatus = RowStatus}} -> ?vtrace("found invalid notify entry for ~w" "~n Tag: ~w" "~n Type: ~w" "~n RowStatus: ~p", [NextKey, Tag, Type, RowStatus]), find_targets(NextKey, TargAddrs, Db, Res); _ -> ?vtrace("notify entry not found for ~w", [NextKey]), find_targets(NextKey, TargAddrs, Db, Res) end; NextKey -> Elements = [?snmpNotifyTag, ?snmpNotifyType, ?snmpNotifyRowStatus], case snmpNotifyTable(get, NextKey, Elements) of [{value, Tag}, {value, Type}, {value, ?'RowStatus_active'}] -> ?vtrace("found notify entry for ~w" "~n Tag: ~w" "~n Type: ~w", [NextKey, Tag, Type]), Targs = get_targets(TargAddrs, Tag, Type, NextKey), find_targets(NextKey, TargAddrs, Db, Targs ++ Res); [{value, Tag1}, {value, Type1}, {value, RowStatus}] -> ?vtrace("found invalid notify entry for ~w" "~n Tag: ~w" "~n Type: ~w" "~n RowStatus: ~w", [NextKey, Tag1, Type1, RowStatus]), find_targets(NextKey, TargAddrs, Db, Res); _ -> ?vtrace("notify entry not found for ~w", [NextKey]), find_targets(NextKey, TargAddrs, Db, Res) end end. get_targets([{TagList, Addr, TargetName, Params, Timeout, Retry}|T], Tag, Type, Name) -> case snmp_misc:is_tag_member(Tag, TagList) of true -> ?vtrace("tag ~w *is* member", [Tag]), [{Name, {Addr, TargetName, Params, type(Type, Timeout, Retry)}}| get_targets(T, Tag, Type, Name)]; false -> ?vtrace("tag ~w is *not* member", [Tag]), get_targets(T, Tag, Type, Name) end; get_targets([], _Tag, _Type, _Name) -> []. type(trap, _, _) -> trap; type(1, _, _) -> trap; %% OTP-4329 type(inform, Timeout, Retry) -> {inform, Timeout, Retry}; type(2, Timeout, Retry) -> {inform, Timeout, Retry}. %% OTP-4329 %%----------------------------------------------------------------- %% Instrumentation Functions %%----------------------------------------------------------------- %% Op = print - Used for debugging purposes snmpNotifyTable(print) -> Table = snmpNotifyTable, DB = db(Table), FOI = foi(Table), PrintRow = fun(Prefix, Row) -> lists:flatten( io_lib:format("~sName: ~p" "~n~sTag: ~p" "~n~sType: ~p (~w)" "~n~sStorageType: ~p (~w)" "~n~sStatus: ~p (~w)", [Prefix, element(?snmpNotifyName, Row), Prefix, element(?snmpNotifyTag, Row), Prefix, element(?snmpNotifyType, Row), case element(?snmpNotifyType, Row) of ?snmpNotifyType_inform -> inform; ?snmpNotifyType_trap -> trap; _ -> undefined end, Prefix, element(?snmpNotifyStorageType, Row), case element(?snmpNotifyStorageType, Row) of ?'snmpNotifyStorageType_readOnly' -> readOnly; ?'snmpNotifyStorageType_permanent' -> permanent; ?'snmpNotifyStorageType_nonVolatile' -> nonVolatile; ?'snmpNotifyStorageType_volatile' -> volatile; ?'snmpNotifyStorageType_other' -> other; _ -> undefined end, Prefix, element(?snmpNotifyRowStatus, Row), case element(?snmpNotifyRowStatus, Row) of ?'snmpNotifyRowStatus_destroy' -> destroy; ?'snmpNotifyRowStatus_createAndWait' -> createAndWait; ?'snmpNotifyRowStatus_createAndGo' -> createAndGo; ?'snmpNotifyRowStatus_notReady' -> notReady; ?'snmpNotifyRowStatus_notInService' -> notInService; ?'snmpNotifyRowStatus_active' -> active; _ -> undefined end])) end, snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow); %% Op == new | delete snmpNotifyTable(Op) -> snmp_generic:table_func(Op, db(snmpNotifyTable)). %% Op == get | is_set_ok | set | get_next snmpNotifyTable(get, RowIndex, Cols) -> %% BMK BMK BMK BMK get(snmpNotifyTable, RowIndex, Cols); snmpNotifyTable(get_next, RowIndex, Cols) -> %% BMK BMK BMK BMK next(snmpNotifyTable, RowIndex, Cols); snmpNotifyTable(set, RowIndex, Cols0) -> %% BMK BMK BMK BMK case (catch verify_snmpNotifyTable_cols(Cols0, [])) of {ok, Cols} -> invalidate_cache(), %% invalidate_cache(RowIndex), Db = db(snmpNotifyTable), snmp_generic:table_func(set, RowIndex, Cols, Db); Error -> Error end; snmpNotifyTable(is_set_ok, RowIndex, Cols0) -> case (catch verify_snmpNotifyTable_cols(Cols0, [])) of {ok, Cols} -> Db = db(snmpNotifyTable), snmp_generic:table_func(is_set_ok, RowIndex, Cols, Db); Error -> Error end; snmpNotifyTable(Op, Arg1, Arg2) -> snmp_generic:table_func(Op, Arg1, Arg2, db(snmpNotifyTable)). verify_snmpNotifyTable_cols([], Cols) -> {ok, lists:reverse(Cols)}; verify_snmpNotifyTable_cols([{Col, Val0}|Cols], Acc) -> Val = verify_snmpNotifyTable_col(Col, Val0), verify_snmpNotifyTable_cols(Cols, [{Col, Val}|Acc]). verify_snmpNotifyTable_col(?snmpNotifyName, Name) -> case (catch snmp_conf:check_string(Name, {gt, 0})) of ok -> Name; _ -> wrongValue(?snmpNotifyName) end; verify_snmpNotifyTable_col(?snmpNotifyTag, Tag) -> case (catch snmp_conf:check_string(Tag)) of ok -> Tag; _ -> wrongValue(?snmpNotifyTag) end; verify_snmpNotifyTable_col(?snmpNotifyType, Type) -> case Type of trap -> 1; inform -> 2; 1 -> 1; 2 -> 2; _ -> wrongValue(?snmpNotifyType) end; verify_snmpNotifyTable_col(_, Val) -> Val. %%----------------------------------------------------------------- %% In this version of the agent, we don't support notification %% filters. %%----------------------------------------------------------------- snmpNotifyFilterTable(get, _RowIndex, Cols) -> lists:map(fun(_Col) -> {noValue, noSuchObject} end, Cols); snmpNotifyFilterTable(get_next, _RowIndex, Cols) -> lists:map(fun(_Col) -> endOfTable end, Cols); snmpNotifyFilterTable(is_set_ok, _RowIndex, Cols) -> {notWritable, element(1, hd(Cols))}. snmpNotifyFilterProfileTable(get, _RowIndex, Cols) -> lists:map(fun(_Col) -> {noValue, noSuchObject} end, Cols); snmpNotifyFilterProfileTable(get_next, _RowIndex, Cols) -> lists:map(fun(_Col) -> endOfTable end, Cols); snmpNotifyFilterProfileTable(is_set_ok, _RowIndex, Cols) -> {notWritable, element(1, hd(Cols))}. db(X) -> snmpa_agent:db(X). fa(snmpNotifyTable) -> ?snmpNotifyTag. foi(snmpNotifyTable) -> ?snmpNotifyName. noc(snmpNotifyTable) -> 5. stc(snmpNotifyTable) -> ?snmpNotifyStorageType. next(Name, RowIndex, Cols) -> snmp_generic:handle_table_next(db(Name), RowIndex, Cols, fa(Name), foi(Name), noc(Name)). table_next(Name, RestOid) -> snmp_generic:table_next(db(Name), RestOid). get(Name, RowIndex, Cols) -> snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)). wrongValue(V) -> throw({wrongValue, V}). %% ----- set_sname() -> set_sname(get(sname)). set_sname(undefined) -> put(sname,conf); set_sname(_) -> %% Keep it, if already set. ok. error(Reason) -> throw({error, Reason}). config_err(F, A) -> snmpa_error:config_err("[NOTIFICATION-MIB]: " ++ F, A).