[Fwd: RE: implementing a large table over a database]
Martin Janzen
janzen____pixelmetrix.com
Thu Mar 27 09:44:33 CET 2003
> Gopi Krishna Bhavaraju wrote:
>> I came across this old posting which talks about adding sub-table rows
>> dynamically. In this posting whenever a last row is hit in a table, it
>> suggests to register rows for the next sub-table. But with this
>> approach if the NMS directly queries (does only sub-table walk) the next
>> sub-table, no
>> rows corresponding to it will be shown. So, is this is correct way of
>> registering sub-table rows dynamically?
Frank Fock wrote:
> 1. What are sub-tables? How do you define a
> "sub-table"?
> 2. Where in the original postings you quoted
> is stated that a table should only be updated
> when a search on a previous table has failed?
> (The opposite is true, the MibTable::update
> method has to be overridden for each table
> with dynamic row allocation)
I think that by "sub-tables", we're talking about the case in which an
NMS doesn't walk the agent's entire MIB tree starting from the
beginning, but instead begins at some arbitrary point -- at some
arbitrary row in a table which is to be filled dynamically. Is that
about right, Gopi?
If that's so, then the first request from the NMS to the agent will be a
Get, not a GetNext, and so your MibTable's (overridden) update() method
will be called. For each subrequest, this method must load the
requested row, extracting the index for the row from the OID, something
like this:
void
MyMibTableSubclass::update(Agentpp::Request* pReq)
{
// (Could check for changes to pReq and pReq->get_transaction_id(),
// returning if unchanged in order to avoid unnecessary work.)
// Throw away all previously loaded rows for this table.
clear();
mPreviousIndexOID.clear(); // see below
for (u_int sub = 0; sub < pReq->subrequests(); sub++)
{
// Extract the index value for this subrequest.
Agentpp::Oidx subrequestOID(pReq->get_oid(sub));
Agentpp::Oidx indexOID(index(subrequestOID));
switch (pReq->get_type())
{
case sNMP_PDU_GET:
case sNMP_PDU_SET:
// See whether the requested OID is in this table.
if (base(subrequestOID) != *key())
break;
// See whether we have a valid index OID.
if (!is_index_valid(indexOID))
break;
// Create a row for this index OID, if found in the database.
myDatabase.loadRow(indexOID);
break;
case sNMP_PDU_GETNEXT:
case sNMP_PDU_GETBULK:
// If this table's update() method was called in response
// to a GETNEXT request on the last object in a previous
// table, try to load the first row of the table, so that
// Agent++ knows it needs to descend into this subtree.
if (base(subrequestOID) < *key())
{
// As an optimization, see whether we just filled in
// the row corresponding to this index OID; see below.
if ((mPreviousIndexOID.len() == 0) ||
(indexOID != mPreviousIndexOID))
{
mPreviousIndexOID = indexOID;
loadFirstRow();
}
}
// Now the overridden MibTable::find_succ() method will call
// loadNextRow() as Agent++ traverses the table's subtree.
break;
default:
break;
}
}
Subsequent requests from the NMS will be GetNext requests, so your
MibTable's (also overridden) find_succ() method will be called instead.
This method must make sure that the "next" row of the table is loaded
(depending on what "next" means for that table).
Also, there are a couple of special cases to watch out for. First,
because GetNext walks tables by column, if you hit the end of one column
then you need to load the first row of the table again. Second, because
an SNMPv2 GetBulk request frequently asks for several columns of the
same row, it'll save you a bunch of time if you check for this case as well:
Agentpp::Oidx
MyMibTableSubclass::find_succ(
const Agentpp::Oidx& oid, Agentpp::Request* pReq)
{
find_succ_impl(oid);
return MibTable::find_succ(oid, pReq);
}
bool
MyMibTableSubclass::find_succ_impl(
const Agentpp::Oidx& oid, Agentpp::Request* pReq)
{
// As an optimization, see whether we just filled in the row
// corresponding to this index OID. (This will frequently be
// the case when processing GETBULK requests.)
Agentpp::Oidx indexOID(index(oid));
if ((mPreviousIndexOID.len() > 0) && (indexOID == mPreviousIndexOID))
return true;
mPreviousIndexOID = indexOID;
// If we are at the start of the table, load the first row.
if (indexOID.len() == 0)
return myDatabase.loadFirstRow();
// Find the row following the given index OID; returns true if found.
if (myDatabase.loadNextRow(indexOID))
return true;
// If we hit the end of the table, try again to load the first row,
// in case we need to wrap around to the top of the next column.
return myDatabase.loadFirstRow();
}
Note that when you hit the last row of the table, you do _not_ need to
do anything special in order to load the first row of the next table in
your MIB. As it traverses back up the MIB tree and then down into the
next table's subtree, Agent++ will call that table's update() method,
giving it an OID whose index value is empty (ie. it will pass the OID of
the next table itself, not of a specific columnar object within that
table). So, the loadRow() method should be trivial, and loadNextRow()
is not much more difficult. For example, assuming that the table has a
key consisting of a single integer (here, a "slot number"), it might
look like this:
bool MyDatabaseTable::loadFirstRow() {return loadNextRow(1);}
bool
MyDatabaseTable::loadNextRow(const Agentpp::Oidx& indexOID)
{
// If we're starting at the base of the table, find the first slot
// for which we have an entry.
if (indexOID.len() < 1) // ie. < number of indices for this table
return loadFirstRow();
// Extract the slot number, which is the last indexOID subidentifier.
// If we want the row after this slot number, check it for errors.
// In case it's higher than the maximum allowed slot number, load the
// first row of the table, if any, so that Agent++ can wrap around to
// the top of the next column if necessary.
int slot;
if (!checkSlotIndex(slot, indexOID, 0))
return loadFirstRow();
// See whether there is an entry for a higher slot number.
// (If this fails, then our caller, onFindSucc(), will wrap around
// and call loadFirstRow() instead.)
return loadNextRow(slot + 1);
}
bool
MyDatabaseTable::loadNextRow(int slot)
{
// Create a row for the next higher slot number.
for (; slot <= MAX_NUM_OF_SLOTS; slot++)
if (fillRow(slot))
return true;
return false;
}
Of course, if you're using any sort of half-decent database, you'll
probably have a more elegant way to find the "next" row than this ugly
'for' loop; and if your table has a key consisting of more than one
part, your loadNextRow() becomes a bit more complicated; but you get the
idea.
HTH...
--
Martin Janzen
janzen at pixelmetrix dot com
More information about the AGENTPP
mailing list