8.代理实现示例 | 8. Agent Implementation Example
8 代理实现示例
本实现示例
部分描述了如何使用SNMP开发工具包实现MIB。
在工具包发行版中可以找到所示的示例。
代理是使用配置工具配置的,它使用默认的建议来处理除管理器节点之外的所有内容。
8.1 MIB
本例中使用的MIB称为EX1-MIB。它包含两个对象,一个带有名称的变量和一个带朋友的表。
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
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
8.2默认实现
在不编写任何仪器功能的情况下,我们可以编译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>
8.3手册执行
以下示例显示了Erlang中EX1-MIB的“手动”实现。 在这个例子中,对象的值存储在Erlang服务器中。 服务器有一个2元组作为循环数据,其中第一个元素是变量myName的值,第二个元素是表friendsTable中的行的排序列表。 每行是一个4元组。
注
有更高效的手动创建表的方法,即使用模块snmp_index
。
代码
-module(ex1).
-author('dummy@flop.org').
%% 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.
陷阱发送
本节介绍如何通过从主代理发送fTrap发送陷阱。 主代理已加载MIB EX1-MIB,其中定义了陷阱。 此陷阱指定应将两个变量与陷阱myName和fIndex一起发送。 fIndex是一个表列,所以我们必须为snmpa:send_trap / 4的调用中的行提供其值和索引。 在下面的例子中,我们假设有问题的行索引2(fIndex 2的行)。
我们使用一个简单的ErlangSNMP管理器,它可以接收陷阱。
[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> snmpa:send_trap(snmp_master_agent, fTrap,"standard trap", [{fIndex,[2],2}]).
[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>