1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- This document was generated using DocBuilder 3.3.3 -->
<HTML>
<HEAD>
<TITLE>Advanced Agent Topics</TITLE>
<SCRIPT type="text/javascript" src="../../../../doc/erlresolvelinks.js">
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF"
ALINK="#FF0000">
<CENTER>
<A HREF="http://www.erlang.se"><IMG BORDER=0 ALT="[Ericsson AB]" SRC="min_head.gif"></A>
</CENTER>
<A NAME="15"><!-- Empty --></A>
<H2>15 Advanced Agent Topics</H2>
<P>The chapter <STRONG>Advanced Agent Topics</STRONG> describes the more advanced
agent related features of the SNMP development tool. The following topics
are covered:
<P>
<UL>
<LI>
When to use a Subagent
</LI>
<LI>
Agent semantics
</LI>
<LI>
Subagents and dependencies
</LI>
<LI>
Distributed tables
</LI>
<LI>
Fault tolerance
</LI>
<LI>
Using Mnesia tables as SNMP tables
</LI>
<LI>
Audit Trail Logging
</LI>
<LI>
Deviations from the standard
</LI>
</UL>
<A NAME="15.1"><!-- Empty --></A>
<H3>15.1 When to use a Subagent</H3>
<P>The section <STRONG>When to use a Subagent</STRONG> describes situations
where the mechanism of loading and unloading MIBs is insufficient.
In these cases a subagent is needed.
<A NAME="15.1.1"><!-- Empty --></A>
<H4>15.1.1 Special Set Transaction Mechanism</H4>
<P>Each subagent can implement its own mechanisms for
<CODE>set</CODE>, <CODE>get</CODE> and <CODE>get-next</CODE>. For example, if the
application requires the <CODE>get</CODE> mechanism to be
asynchronous, or needs a N-phase <CODE>set</CODE> mechanism, a
specialized subagent should be used.
<P>The toolkit allows different kinds of subagents at the same
time. Accordingly, different MIBs can have different <CODE>set</CODE>
or <CODE>get</CODE> mechanisms.
<A NAME="15.1.2"><!-- Empty --></A>
<H4>15.1.2 Process Communication</H4>
<P>A simple distributed agent can be managed without subagents.
The instrumentation functions can use distributed Erlang to
communicate with other parts of the application. However, a
subagent can be used on each node if this generates too much
unnecessary traffic. A subagent processes requests per
incoming SNMP request, not per variable. Therefore the network
traffic is minimized.
<P>If the instrumentation functions communicate with UNIX
processes, it might be a good idea to use a special
subagent. This subagent sends the SNMP request to the other
process in one packet in order to minimize context switches. For
example, if a whole MIB is implemented on the C level in UNIX,
but you still want to use the Erlang SNMP tool, then you may
have one special subagent, which sends the variables in the
request as a single operation down to C.
<A NAME="15.1.3"><!-- Empty --></A>
<H4>15.1.3 Frequent Loading of MIBs</H4>
<P>Loading and unloading of MIBs are quite cheap
operations. However, if the application does this very often,
perhaps several times per minute, it should load the MIBs once
and for all in a subagent. This subagent only registers and
de-registers itself under another agent instead of loading the
MIBs each time. This is cheaper than loading an MIB.
<A NAME="15.1.4"><!-- Empty --></A>
<H4>15.1.4 Interaction With Other SNMP Agent Toolkits</H4>
<P>If the SNMP agent needs to interact with subagents
constructed in another package, a special subagent should be
used, which communicates through a protocol specified by the
other package.
<A NAME="15.2"><!-- Empty --></A>
<H3>15.2 Agent Semantics</H3>
<P>The agent can be configured to be multi-threaded, to process
one incoming request at a time, or to have a request limit
enabled (this can be used for load control or to limit the effect
of DoS attacs). If it is multi-threaded, read requests (<CODE>get</CODE>,
<CODE>get-next</CODE> and <CODE>get-bulk</CODE>) and traps are processed in
parallel with each other and <CODE>set</CODE> requests. However, all
<CODE>set</CODE> requests are serialized, which means that if the agent
is waiting for the application to complete a complicated write
operation, it will not process any new write requests until this
operation is finished. It processes read requests and sends traps,
concurrently. The reason for not parallelize write requests is that
a complex locking mechanism would be needed even in the simplest
cases. Even with the scheme described above, the user must be
careful not to violate that the <CODE>set</CODE> requests are atoms.
If this is hard to do, do not use the multi-threaded feature.
<P>The order within an request is undefined and variables are not
processed in a defined order. Do not assume that the first
variable in the PDU will be processed before the second, even if
the agent processes variables in this order. It
cannot even be assumed that requests belonging to different
subagents have any order.
<P>If the manager tries to set the same variable many times in the
same PDU, the agent is free to improvise. There is no definition
which determines if the instrumentation will be called once or
twice. If called once only, there is no definition that determines
which of the new values is going to be supplied.
<P>When the agent receives a request, it keeps the request ID for
one second after the response is sent. If the agent receives
another request with the same request ID during this time, from
the same IP address and UDP port, that request will be
discarded. This mechanism has nothing to do with the function
<CODE>snmpa:current_request_id/0</CODE>.
<A NAME="15.3"><!-- Empty --></A>
<H3>15.3 Subagents and Dependencies </H3>
<P>The toolkit supports the use of different types of subagents,
but not the construction of subagents.
<P>Also, the toolkit does not support dependencies between
subagents. A subagent should by definition be stand alone and it is
therefore not good design to create dependencies between them.
<A NAME="15.4"><!-- Empty --></A>
<H3>15.4 Distributed Tables</H3>
<P>A common situation in more complex systems is that the data in
a table is distributed. Different table rows are implemented in
different places. Some SNMP toolkits dedicate an SNMP subagent for
each part of the table and load the corresponding MIB into all
subagents. The Master Agent is responsible for presenting the
distributed table as a single table to the manager. The toolkit
supplied uses a different method.
<P>The method used to implement distributed tables with this SNMP
tool is to implement a table coordinator process responsible for
coordinating the processes, which hold the table data and they
are called table holders. All table holders must in some way be
known by the coordinator; the structure of the table data
determines how this is achieved. The coordinator may require
that the table holders explicitly register themselves and specify
their information. In other cases, the table holders can be
determined once at compile time.
<P>When the instrumentation function for the distributed table is
called, the request should be forwarded to the table
coordinator. The coordinator finds the requested information among
the table holders and then returns the answer to the
instrumentation function. The SNMP toolkit contains no support for
coordination of tables since this must be independent of the
implementation.
<P>The advantages of separating the table coordinator from the
SNMP tool are:
<P>
<UL>
<LI>
We do not need a subagent for each table holder. Normally,
the subagent is needed to take care of communication, but in
Distributed Erlang we use ordinary message passing.
</LI>
<LI>
Most likely, some type of table coordinator already
exists. This process should take care of the instrumentation for
the table.
</LI>
<LI>
The method used to present a distributed table is strongly
application dependent. The use of different masking techniques
is only valid for a small subset of problems and registering
every row in a distributed table makes it non-distributed.
</LI>
</UL>
<A NAME="15.5"><!-- Empty --></A>
<H3>15.5 Fault Tolerance</H3>
<P>The SNMP agent toolkit gets input from three different sources:
<P>
<UL>
<LI>
UDP packets from the network
</LI>
<LI>
return values from the user defined instrumentation functions
</LI>
<LI>
return values from the MIB.
</LI>
</UL>
<P>The agent is highly fault tolerant. If the manager gets an
unexpected response from the agent, it is possible that some
instrumentation function has returned an erroneous value. The
agent will not crash even if the instrumentation does. It should
be noted that if an instrumentation function enters an infinite
loop, the agent will also be blocked forever. The supervisor ,or
the application, specifies how to restart the agent.
<A NAME="15.5.1"><!-- Empty --></A>
<H4>15.5.1 Using the SNMP Agent in a Distributed Environment</H4>
<P>The normal way to use the agent in a distributed
environment is to use one master agent located at one node,
and zero or more subagents located on other nodes. However,
this configuration makes the master agent node a single point
of failure. If that node goes down, the agent will not work.
<P>One solution to this problem is to make the snmp application
a distributed Erlang application, and that means, the agent
may be configured to run on one of several nodes. If the node
where it runs goes down, another node restarts the agent.
This is called <STRONG>failover</STRONG>. When the node starts again,
it may <STRONG>takeover</STRONG> the application. This solution to
the problem adds another problem. Generally, the new node has
another IP address than the first one, which may cause
problems in the communication between the SNMP managers and
the agent.
<P>If the snmp agent is configured as a distributed Erlang
application, it will during takeover try to load the same MIBs
that were loaded at the old node. It uses the same filenames
as the old node. If the MIBs are not located in the same
paths at the different nodes, the MIBs must be loaded
explicitly after takeover.
<A NAME="15.6"><!-- Empty --></A>
<H3>15.6 Using Mnesia Tables as SNMP Tables</H3>
<P>The Mnesia DBMS can be used for storing data of SNMP
tables. This means that an SNMP table can be implemented as a
Mnesia table, and that a Mnesia table can be made visible via
SNMP. This mapping is largely automated.
<P>There are three main reasons for using this mapping:
<P>
<UL>
<LI>
We get all features of Mnesia, such as fault tolerance,
persistent data storage, replication, and so on.
</LI>
<LI>
Much of the work involved is automated. This includes
<CODE>get-next</CODE> processing and <CODE>RowStatus</CODE> handling.
</LI>
<LI>
The table may be used as an ordinary Mnesia table, using
the Mnesia API internally in the application at the same time as
it is visible through SNMP.
</LI>
</UL>
<P>When this mapping is used, insertion and deletion in the
original Mnesia table is slower, with a factor O(log n). The read
access is not affected.
<P>A drawback with implementing an SNMP table as a Mnesia table is
that the internal resource is forced to use the table definition
from the MIB, which means that the external data model must be
used internally. Actually, this is only partially true. The Mnesia
table may extend the SNMP table, which means that the Mnesia table
may have columns which are use internally and are not seen by
SNMP. Still, the data model from SNMP must be maintained. Although
this is undesirable, it is a pragmatic compromise in many
situations where simple and efficient implementation is preferable
to abstraction.
<A NAME="15.6.1"><!-- Empty --></A>
<H4>15.6.1 Creating the Mnesia Table</H4>
<P>The table must be created in Mnesia before the manager can
use it. The table must be declared as type <CODE>snmp</CODE>. This
makes the table ordered in accordance with the lexicographical
ordering rules of SNMP. The name of the Mnesia table must be
identical to the SNMP table name. The types of the INDEX fields
in the corresponding SNMP table must be specified.
<P>If the SNMP table has more than one INDEX column, the
corresponding Mnesia row is a tuple, where the first element
is a tuple with the INDEX columns. Generally, if the SNMP table
has <STRONG>N</STRONG> INDEX columns and <STRONG>C</STRONG> data columns, the
Mnesia table is of arity <STRONG>(C-N)+1</STRONG>, where the key is a
tuple of arity <STRONG>N</STRONG> if <STRONG>N > 1</STRONG>, or a single term
if <STRONG>N = 1</STRONG>.
<P>Refer to the Mnesia User's Guide for information on how to
declare a Mnesia table as an SNMP table.
<P>The following example illustrates a situation in which we
have an SNMP table that we wish to implement as a Mnesia
table. The table stores information about employees at a
company. Each employee is indexed with the department number and
the name.
<PRE>
empTable OBJECT-TYPE
SYNTAX SEQUENCE OF EmpEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
"A table with information about employees."
::= { emp 1}
empEntry OBJECT-TYPE
SYNTAX EmpEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
""
INDEX { empDepNo, empName }
::= { empTable 1 }
EmpEntry ::=
SEQUENCE {
empDepNo INTEGER,
empName DisplayString,
empTelNo DisplayString
empStatus RowStatus
}
</PRE>
<P>The corresponding Mnesia table is specified as follows:
<PRE>
mnesia:create_table([{name, employees},
{snmp, [{key, {integer, string}}]},
{attributes, [key, telno, row_status]}]).
</PRE>
<P>
<TABLE CELLPADDING=4>
<TR>
<TD VALIGN=TOP><IMG ALT="Note!" SRC="note.gif"></TD>
<TD>
<P>In the Mnesia tables, the two key columns are stored as a
tuple with two elements. Therefore, the arity of the table is
3.
</TD>
</TR>
</TABLE>
<A NAME="15.6.2"><!-- Empty --></A>
<H4>15.6.2 Instrumentation Functions</H4>
<P>The MIB table shown in the previous section can be compiled
as follows:
<PRE>
1> <STRONG>snmpc:compile("EmpMIB", [{db, mnesia}]).</STRONG>
</PRE>
<P>This is all that has to be done! Now the manager can read,
add, and modify rows. Also, you can use the ordinary Mnesia API
to access the table from your programs. The only explicit action
is to create the Mnesia table, an action the user has to perform
in order to create the required table schemas.
<A NAME="15.6.3"><!-- Empty --></A>
<H4>15.6.3 Adding Own Actions</H4>
<P>It is often necessary to take some specific action when a
table is modified. This is accomplished with an instrumentation
function. It executes some specific code when the table is set,
and passes all other requests down to the pre-defined function.
<P>The following example illustrates this idea:
<PRE>
emp_table(set, RowIndex, Cols) ->
notify_internal_resources(RowIndex, Cols),
snmp_generic:table_func(set, RowIndex, Cols, {empTable, mnesia});
emp_table(Op, RowIndex, Cols) ->
snmp_generic:table_func(Op, RowIndex, Cols, {empTable, mnesia}).
</PRE>
<P>The default instrumentation functions are defined in the
module <CODE>snmp_generic</CODE>. Refer to the Reference Manual,
section SNMP, module <CODE>snmp_generic</CODE> for details.
<A NAME="15.6.4"><!-- Empty --></A>
<H4>15.6.4 Extending the Mnesia Table</H4>
<P>A table may contain columns that are used internally, but
should not be visible to a manager. These internal columns must
be the last columns in the table. The <CODE>set</CODE> operation will
not work with this arrangement, because there are columns that
the agent does not know about. This situation is handled by
adding values for the internal columns in the <CODE>set</CODE>
function.
<P>To illustrate this, suppose we extend our Mnesia
<CODE>empTable</CODE> with one internal column. We create it as
before, but with an arity of 4, by adding another attribute.
<PRE>
mnesia:create_table([{name, employees},
{snmp, [{key, {integer, string}}]},
{attributes, {key, telno, row_status, internal_col}}]).
</PRE>
<P>The last column is the internal column. When performing a
<CODE>set</CODE> operation, which creates a row, we must give a
value to the internal column. The instrumentation functions will now
look as follows:
<PRE>
-define(createAndGo, 4).
-define(createAndWait, 5).
emp_table(set, RowIndex, Cols) ->
notify_internal_resources(RowIndex, Cols),
NewCols =
case is_row_created(empTable, Cols) of
true -> Cols ++ [{4, "internal"}]; % add internal column
false -> Cols % keep original cols
end,
snmp_generic:table_func(set, RowIndex, NewCols, {empTable, mnesia});
emp_table(Op, RowIndex, Cols) ->
snmp_generic:table_func(Op, RowIndex, Cols, {empTable, mnesia}).
is_row_created(Name, Cols) ->
case snmp_generic:get_status_col(Name, Cols) of
{ok, ?createAndGo} -> true;
{ok, ?createAndWait} -> true;
_ -> false
end.
</PRE>
<P> If a row is created, we always set the internal column to
<CODE>"internal"</CODE>.
<A NAME="15.7"><!-- Empty --></A>
<H3>15.7 Deviations from the Standard</H3>
<P>In some aspects the agent does not implement SNMP fully. Here
are the differences:
<P>
<UL>
<LI>
The default functions and <CODE>snmp_generic</CODE> cannot
handle an object of type <CODE>NetworkAddress</CODE> as INDEX
(SNMPv1 only!). Use <CODE>IpAddress</CODE> instead.
</LI>
<LI>
The agent does not check complex ranges specified for
INTEGER objects. In these cases it just checks that the value
lies within the minimum and maximum values specified. For
example, if the range is specified as <CODE>1..10 | 12..20</CODE>
the agent would let 11 through, but not 0 or 21. The
instrumentation functions must check the complex ranges
itself.
</LI>
<LI>
The agent will never generate the <CODE>wrongEncoding</CODE>
error. If a variable binding is erroneous encoded, the
<CODE>asn1ParseError</CODE> counter will be incremented.
</LI>
<LI>
A <CODE>tooBig</CODE> error in an SNMPv1 packet will always use
the <CODE>'NULL'</CODE> value in all variable bindings.
</LI>
<LI>
The default functions and <CODE>snmp_generic</CODE> do not check
the range of each OCTET in textual conventions derived from
OCTET STRING, e.g. <CODE>DisplayString</CODE> and
<CODE>DateAndTime</CODE>. This must be checked in an overloaded
<CODE>is_set_ok</CODE> function.
</LI>
</UL>
<CENTER>
<HR>
<SMALL>
Copyright © 1991-2006
<A HREF="http://www.erlang.se">Ericsson AB</A><BR>
</SMALL>
</CENTER>
</BODY>
</HTML>
|