[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