查看源代码 代理实现示例
本实现示例章节描述了如何使用 SNMP 开发工具包实现 MIB。
示例可以在工具包分发中找到。
该代理使用配置工具进行配置,除了管理器节点之外,所有内容都使用默认建议。
MIB
本示例中使用的 MIB 称为 EX1-MIB。它包含两个对象,一个带有名称的变量和一个带有朋友的表。
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
experimental FROM RFC1155-SMI
RowStatus FROM STANDARD-MIB
DisplayString FROM RFC1213-MIB
OBJECT-TYPE FROM RFC-1212
;
example1 OBJECT IDENTIFIER ::= { experimental 7 }
myName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
STATUS mandatory
DESCRIPTION
"My own name"
::= { example1 1 }
friendsTable OBJECT-TYPE
SYNTAX SEQUENCE OF FriendsEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
"A list of friends."
::= { example1 4 }
friendsEntry OBJECT-TYPE
SYNTAX FriendsEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
""
INDEX { fIndex }
::= { friendsTable 1 }
FriendsEntry ::=
SEQUENCE {
fIndex
INTEGER,
fName
DisplayString,
fAddress
DisplayString,
fStatus
RowStatus }
fIndex OBJECT-TYPE
SYNTAX INTEGER
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
"number of friend"
::= { friendsEntry 1 }
fName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
STATUS mandatory
DESCRIPTION
"Name of friend"
::= { friendsEntry 2 }
fAddress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
STATUS mandatory
DESCRIPTION
"Address of friend"
::= { friendsEntry 3 }
fStatus OBJECT-TYPE
SYNTAX RowStatus
ACCESS read-write
STATUS mandatory
DESCRIPTION
"The status of this conceptual row."
::= { friendsEntry 4 }
fTrap TRAP-TYPE
ENTERPRISE example1
VARIABLES { myName, fIndex }
DESCRIPTION
"This trap is sent when something happens to
the friend specified by fIndex."
::= 1
END
默认实现
无需编写任何检测函数,我们可以编译 MIB 并使用其默认实现。请注意,编译时,由 "EX1-MIB.mib" 导入的 MIB 必须存在并编译在当前目录中 ("./STANDARD-MIB.bin","./RFC1213-MIB.bin")。
unix> erl -config ./sys
1> application:start(snmp).
ok
2> snmpc:compile("EX1-MIB").
No accessfunction for 'friendsTable', using default.
No accessfunction for 'myName', using default.
{ok, "EX1-MIB.bin"}
3> snmpa:load_mibs(snmp_master_agent, ["EX1-MIB"]).
ok
现在,此 MIB 已加载到代理中,管理器可以提出问题。例如,我们启动另一个 Erlang 系统和工具包中的简单 Erlang 管理器
1> snmp_test_mgr:start_link([{agent,"dront.ericsson.se"},{community,"all-rights"},
%% making it understand symbolic names: {mibs,["EX1-MIB","STANDARD-MIB"]}]).
{ok, <0.89.0>}
%% a get-next request with one OID.
2> snmp_test_mgr:gn([[1,3,6,1,3,7]]).
ok
* Got PDU:
[myName,0] = []
%% A set-request (now using symbolic names for convenience)
3> snmp_test_mgr:s([{[myName,0], "Martin"}]).
ok
* Got PDU:
[myName,0] = "Martin"
%% Try the same get-next request again
4> snmp_test_mgr:gn([[1,3,6,1,3,7]]).
ok
* Got PDU:
[myName,0] = "Martin"
%% ... and we got the new value.
%% you can event do row operations. How to add a row:
5> snmp_test_mgr:s([{[fName,0], "Martin"}, {[fAddress,0],"home"}, {[fStatus,0],4}]).
%% createAndGo
ok
* Got PDU:
[fName,0] = "Martin"
[fAddress,0] = "home"
[fStatus,0] = 4
6> snmp_test_mgr:gn([[myName,0]]).
ok
* Got PDU:
[fName,0] = "Martin"
7> snmp_test_mgr:gn().
ok
* Got PDU:
[fAddress,0] = "home"
8> snmp_test_mgr:gn().
ok
* Got PDU:
[fStatus,0] = 1
9>
手动实现
以下示例展示了 EX1-MIB 在 Erlang 中的“手动”实现。在此示例中,对象的值存储在 Erlang 服务器中。服务器有一个 2 元组作为循环数据,其中第一个元素是变量 myName
的值,第二个元素是表 friendsTable
中行的排序列表。每一行都是一个 4 元组。
注意
还有更有效的手动创建表的方法,即使用模块
snmp_index
。
代码
-module(ex1).
-author('[email protected]').
%% External exports
-export([start/0, my_name/1, my_name/2, friends_table/3]).
%% Internal exports
-export([init/0]).
-define(status_col, 4).
-define(active, 1).
-define(notInService, 2).
-define(notReady, 3).
-define(createAndGo, 4). % Action; written, not read
-define(createAndWait, 5). % Action; written, not read
-define(destroy, 6). % Action; written, not read
start() ->
spawn(ex1, init, []).
%%----------------------------------------------------------------
%% Instrumentation function for variable myName.
%% Returns: (get) {value, Name}
%% (set) noError
%%----------------------------------------------------------------
my_name(get) ->
ex1_server ! {self(), get_my_name},
Name = wait_answer(),
{value, Name}.
my_name(set, NewName) ->
ex1_server ! {self(), {set_my_name, NewName}},
noError.
%%----------------------------------------------------------------
%% Instrumentation function for table friendsTable.
%%----------------------------------------------------------------
friends_table(get, RowIndex, Cols) ->
case get_row(RowIndex) of
{ok, Row} ->
get_cols(Cols, Row);
_ ->
{noValue, noSuchInstance}
end;
friends_table(get_next, RowIndex, Cols) ->
case get_next_row(RowIndex) of
{ok, Row} ->
get_next_cols(Cols, Row);
_ ->
case get_next_row([]) of
{ok, Row} ->
% Get next cols from first row.
NewCols = add_one_to_cols(Cols),
get_next_cols(NewCols, Row);
_ ->
end_of_table(Cols)
end
end;
%%----------------------------------------------------------------
%% If RowStatus is set, then:
%% *) If set to destroy, check that row does exist
%% *) If set to createAndGo, check that row does not exist AND
%% that all columns are given values.
%% *) Otherwise, error (for simplicity).
%% Otherwise, row is modified; check that row exists.
%%----------------------------------------------------------------
friends_table(is_set_ok, RowIndex, Cols) ->
RowExists =
case get_row(RowIndex) of
{ok, _Row} -> true;
_ -> false
end,
case is_row_status_col_changed(Cols) of
{true, ?destroy} when RowExists == true ->
{noError, 0};
{true, ?createAndGo} when RowExists == false,
length(Cols) == 3 ->
{noError, 0};
{true, _} ->
{inconsistentValue, ?status_col};
false when RowExists == true ->
{noError, 0};
_ ->
[{Col, _NewVal} | _Cols] = Cols,
{inconsistentName, Col}
end;
friends_table(set, RowIndex, Cols) ->
case is_row_status_col_changed(Cols) of
{true, ?destroy} ->
ex1_server ! {self(), {delete_row, RowIndex}};
{true, ?createAndGo} ->
NewRow = make_row(RowIndex, Cols),
ex1_server ! {self(), {add_row, NewRow}};
false ->
{ok, Row} = get_row(RowIndex),
NewRow = merge_rows(Row, Cols),
ex1_server ! {self(), {delete_row, RowIndex}},
ex1_server ! {self(), {add_row, NewRow}}
end,
{noError, 0}.
%%----------------------------------------------------------------
%% Make a list of {value, Val} of the Row and Cols list.
%%----------------------------------------------------------------
get_cols([Col | Cols], Row) ->
[{value, element(Col, Row)} | get_cols(Cols, Row)];
get_cols([], _Row) ->
[].
%%----------------------------------------------------------------
%% As get_cols, but the Cols list may contain invalid column
%% numbers. If it does, we must find the next valid column,
%% or return endOfTable.
%%----------------------------------------------------------------
get_next_cols([Col | Cols], Row) when Col < 2 ->
[{[2, element(1, Row)], element(2, Row)} |
get_next_cols(Cols, Row)];
get_next_cols([Col | Cols], Row) when Col > 4 ->
[endOfTable |
get_next_cols(Cols, Row)];
get_next_cols([Col | Cols], Row) ->
[{[Col, element(1, Row)], element(Col, Row)} |
get_next_cols(Cols, Row)];
get_next_cols([], _Row) ->
[].
%%----------------------------------------------------------------
%% Make a list of endOfTable with as many elems as Cols list.
%%----------------------------------------------------------------
end_of_table([Col | Cols]) ->
[endOfTable | end_of_table(Cols)];
end_of_table([]) ->
[].
add_one_to_cols([Col | Cols]) ->
[Col + 1 | add_one_to_cols(Cols)];
add_one_to_cols([]) ->
[].
is_row_status_col_changed(Cols) ->
case lists:keysearch(?status_col, 1, Cols) of
{value, {?status_col, StatusVal}} ->
{true, StatusVal};
_ -> false
end.
get_row(RowIndex) ->
ex1_server ! {self(), {get_row, RowIndex}},
wait_answer().
get_next_row(RowIndex) ->
ex1_server ! {self(), {get_next_row, RowIndex}},
wait_answer().
wait_answer() ->
receive
{ex1_server, Answer} ->
Answer
end.
%%%---------------------------------------------------------------
%%% Server code follows
%%%---------------------------------------------------------------
init() ->
register(ex1_server, self()),
loop("", []).
loop(MyName, Table) ->
receive
{From, get_my_name} ->
From ! {ex1_server, MyName},
loop(MyName, Table);
{From, {set_my_name, NewName}} ->
loop(NewName, Table);
{From, {get_row, RowIndex}} ->
Res = table_get_row(Table, RowIndex),
From ! {ex1_server, Res},
loop(MyName, Table);
{From, {get_next_row, RowIndex}} ->
Res = table_get_next_row(Table, RowIndex),
From ! {ex1_server, Res},
loop(MyName, Table);
{From, {delete_row, RowIndex}} ->
NewTable = table_delete_row(Table, RowIndex),
loop(MyName, NewTable);
{From, {add_row, NewRow}} ->
NewTable = table_add_row(Table, NewRow),
loop(MyName, NewTable)
end.
%%%---------------------------------------------------------------
%%% Functions for table operations. The table is represented as
%%% a list of rows.
%%%---------------------------------------------------------------
table_get_row([{Index, Name, Address, Status} | _], [Index]) ->
{ok, {Index, Name, Address, Status}};
table_get_row([H | T], RowIndex) ->
table_get_row(T, RowIndex);
table_get_row([], _RowIndex) ->
no_such_row.
table_get_next_row([Row | T], []) ->
{ok, Row};
table_get_next_row([Row | T], [Index | _])
when element(1, Row) > Index ->
{ok, Row};
table_get_next_row([Row | T], RowIndex) ->
table_get_next_row(T, RowIndex);
table_get_next_row([], RowIndex) ->
endOfTable.
table_delete_row([{Index, _, _, _} | T], [Index]) ->
T;
table_delete_row([H | T], RowIndex) ->
[H | table_delete_row(T, RowIndex)];
table_delete_row([], _RowIndex) ->
[].
table_add_row([Row | T], NewRow)
when element(1, Row) > element(1, NewRow) ->
[NewRow, Row | T];
table_add_row([H | T], NewRow) ->
[H | table_add_row(T, NewRow)];
table_add_row([], NewRow) ->
[NewRow].
make_row([Index], [{2, Name}, {3, Address} | _]) ->
{Index, Name, Address, ?active}.
merge_rows(Row, [{Col, NewVal} | T]) ->
merge_rows(setelement(Col, Row, NewVal), T);
merge_rows(Row, []) ->
Row.
关联文件
实际实现的关联文件 EX1-MIB.funcs
如下所示
{myName, {ex1, my_name, []}}.
{friendsTable, {ex1, friends_table, []}}.
记录
要使用实际实现,我们必须重新编译 MIB 并将其加载到代理中。
1> application:start(snmp).
ok
2> snmpc:compile("EX1-MIB").
{ok,"EX1-MIB.bin"}
3> snmpa:load_mibs(snmp_master_agent, ["EX1-MIB"]).
ok
4> ex1:start().
<0.115.0>
%% Now all requests operates on this "real" implementation.
%% The output from the manager requests will *look* exactly the
%% same as for the default implementation.
Trap 发送
本节展示了如何通过从主代理发送 fTrap
来发送 trap。主代理已加载 MIB EX1-MIB
,其中定义了 trap。此 trap 指定应随 trap 一起发送两个变量,myName
和 fIndex
。fIndex
是一个表列,因此我们必须在调用 snmpa:send_notification2/3
时提供其值和行的索引。在下面的示例中,我们假设所讨论的行由 2 索引(fIndex
为 2 的行)。
我们使用一个简单的 Erlang SNMP 管理器,它可以接收 trap。
[MANAGER]
1> snmp_test_mgr:start_link([{agent,"dront.ericsson.se"},{community,"public"}
%% does not have write-access
1>{mibs,["EX1-MIB","STANDARD-MIB"]}]).
{ok, <0.100.0>}
2> snmp_test_mgr:s([{[myName,0], "Klas"}]).
ok
* Got PDU:
Received a trap:
Generic: 4 %% authenticationFailure
Enterprise: [iso,2,3]
Specific: 0
Agent addr: [123,12,12,21]
TimeStamp: 42993
2>
[AGENT]
3> SendOpts = [{receiver, no_receiver}, {varbinds, [{fIndex,[2],2}]}, {name, "standard trap"}, {context, ""}],
4> snmpa:send_notification2(snmp_master_agent, fTrap, SendOpts).
[MANAGER]
2>
* Got PDU:
Received a trap:
Generic: 6
Enterprise: [example1]
Specific: 1
Agent addr: [123,12,12,21]
TimeStamp: 69649
[myName,0] = "Martin"
[fIndex,2] = 2
2>