diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/doc/src/snmp_impl_example_agent.xml | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/doc/src/snmp_impl_example_agent.xml')
-rw-r--r-- | lib/snmp/doc/src/snmp_impl_example_agent.xml | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/lib/snmp/doc/src/snmp_impl_example_agent.xml b/lib/snmp/doc/src/snmp_impl_example_agent.xml new file mode 100644 index 0000000000..3bba6467ed --- /dev/null +++ b/lib/snmp/doc/src/snmp_impl_example_agent.xml @@ -0,0 +1,510 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>Agent Implementation Example</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>snmp_impl_example_agent.xml</file> + </header> + <p>This <em>Implementation Example</em> section describes how an + MIB can be implemented with the SNMP Development Toolkit. </p> + <p>The example shown can be found in the toolkit distribution. </p> + <p>The agent is configured with the configuration tool, using + default suggestions for everything but the manager node. </p> + + <section> + <title>MIB</title> + <p>The MIB used in this example is called EX1-MIB. It contains two + objects, a variable with a name and a table with friends. + </p> + <code type="none"> +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 + </code> + </section> + + <section> + <title>Default Implementation</title> + <p>Without writing any instrumentation functions, we can compile + the MIB and use the default implementation of it. Recall that MIBs + imported by "EX1-MIB.mib" must be present and compiled in the + current directory ("./STANDARD-MIB.bin","./RFC1213-MIB.bin") when + compiling. + </p> + <pre> +unix> <input>erl -config ./sys</input> +1> <input>application:start(snmp).</input> +ok +2> <input>snmpc:compile("EX1-MIB").</input> +No accessfunction for 'friendsTable', using default. +No accessfunction for 'myName', using default. +{ok, "EX1-MIB.bin"} +3> <input>snmpa:load_mibs(snmp_master_agent, ["EX1-MIB"]).</input> +ok + </pre> + <p>This MIB is now loaded into the agent, and a manager can ask + questions. As an example of this, we start another Erlang system + and the simple Erlang manager in the toolkit: + </p> + <pre> +1> <input>snmp_test_mgr:start_link([{agent,"dront.ericsson.se"},{community,"all-rights"},</input> + %% making it understand symbolic names: {mibs,["EX1-MIB","STANDARD-MIB"]}]). +{ok, <0.89.0>} +%% a get-next request with one OID. +2> <input>snmp_test_mgr:gn([[1,3,6,1,3,7]]).</input> +ok +* Got PDU: +[myName,0] = [] +%% A set-request (now using symbolic names for convenience) +3> <input>snmp_test_mgr:s([{[myName,0], "Martin"}]).</input> +ok +* Got PDU: +[myName,0] = "Martin" +%% Try the same get-next request again +4> <input>snmp_test_mgr:gn([[1,3,6,1,3,7]]).</input> +ok +* Got PDU: +[myName,0] = "Martin" +%% ... and we got the new value. +%% you can event do row operations. How to add a row: +5> <input>snmp_test_mgr:s([{[fName,0], "Martin"}, {[fAddress,0],"home"}, {[fStatus,0],4}]).</input> + %% createAndGo +ok +* Got PDU: +[fName,0] = "Martin" +[fAddress,0] = "home" +[fStatus,0] = 4 +6> <input>snmp_test_mgr:gn([[myName,0]]).</input> +ok +* Got PDU: +[fName,0] = "Martin" +7> <input>snmp_test_mgr:gn().</input> +ok +* Got PDU: +[fAddress,0] = "home" +8> <input>snmp_test_mgr:gn().</input> +ok +* Got PDU: +[fStatus,0] = 1 +9> + </pre> + </section> + + <section> + <title>Manual Implementation</title> + <p>The following example shows a "manual" implementation of the + EX1-MIB in Erlang. In this example, the values of the objects are + stored in an Erlang server. The server has a 2-tuple as loop + data, where the first element is the value of variable + <c>myName</c>, and the second is a sorted list of rows in the + table <c>friendsTable</c>. Each row is a 4-tuple. + </p> + <note> + <p>There are more efficient ways to create tables manually, i.e. + to use the module <c>snmp_index</c>.</p> + </note> + + <section> + <title>Code</title> + <code type="none"><![CDATA[ +-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. + ]]></code> + </section> + + <section> + <title>Association File</title> + <p>The association file <c>EX1-MIB.funcs</c> for the real + implementation looks as follows: + </p> + <code type="none"> +{myName, {ex1, my_name, []}}. +{friendsTable, {ex1, friends_table, []}}. + </code> + </section> + + <section> + <title>Transcript</title> + <p>To use the real implementation, we must recompile the MIB and + load it into the agent. + </p> + <pre> +1> <input>application:start(snmp).</input> +ok +2> <input>snmpc:compile("EX1-MIB").</input> +{ok,"EX1-MIB.bin"} +3> <input>snmpa:load_mibs(snmp_master_agent, ["EX1-MIB"]).</input> +ok +4> <input>ex1:start().</input> +<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. + </pre> + </section> + + <section> + <title>Trap Sending</title> + <p>How to send a trap by sending the + <c>fTrap</c> from the master agent is shown in this section. + The master agent has the MIB <c>EX1-MIB</c> loaded, where the + trap is defined. This trap specifies that two variables should + be sent along with the trap, <c>myName</c> and <c>fIndex</c>. + <c>fIndex</c> is a table column, so we must provide its value + and the index for the row in the call to <c>snmpa:send_trap/4</c>. + In the example below, we assume that the row in question is + indexed by 2 (the row with <c>fIndex</c> 2). + </p> + <p>we use a simple Erlang SNMP manager, which can receive traps. + </p> + <pre> +[MANAGER] +1> <input>snmp_test_mgr:start_link([{agent,"dront.ericsson.se"},{community,"public"}</input> + %% does not have write-access +1><input>{mibs,["EX1-MIB","STANDARD-MIB"]}]).</input> +{ok, <0.100.0>} +2> <input>snmp_test_mgr:s([{[myName,0], "Klas"}]).</input> +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> <input>snmpa:send_trap(snmp_master_agent, fTrap,"standard trap", [{fIndex,[2],2}]).</input> +[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> + </pre> + </section> + </section> +</chapter> + |