Welcome to the SNMP Table!

... or the Hitchhiker's Guide to SNMP Part IV. In this article, I will try to provide a brief overview of multi-indexed SNMP tables.

This article comes as a follow-up on a series of articles I have written about SNMP. The series starts with [Simple Is Not Easy] and [What is the JVM SNMP Agent?] and continues with [The Hitchhiker's Guide to SNMP - part I], [II], and [III]. Readers who are not familiar with SNMP are invited to browse these articles first.

In Understanding The Structure Of Management Information (SMI), I have already explained how SNMP tables are defined in SMI and introduced the concepts of Scalar Objects and Columnar Objects, Conceptual Rows and Indexes. I have also shown in an example based on the JVM-MANAGEMENT-MIB how to get a columnar object on a simple SNMP table indexed by a single integer.

In this article, I want to explain how all of this works with multi-indexed tables.

Defining a multi-index table

Defining a multi indexed table is simple: it looks just like a single index table, except that the definition of the conceptual row has a coma-separated list of indexes defined in its INDEX clause. Here is an example taken from the JVM-MANAGEMENT-MIB:

jvmMemMgrPoolRelEntry OBJECT-TYPE
    SYNTAX      JvmMemMgrPoolRelEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"A row in this table indicates that the Memory Manager identified
         by jvmMemManagerIndex manages the Memory Pool identified by
         jvmMemPoolIndex. Note that a pool may be managed by several 
         memory managers, and a memory manager can manage several 
         memory pool.

	 See java.lang.management.MemoryManagerMXBean.getMemoryPoolNames(),
	     java.lang.management.MemoryPoolMXBean.getMemoryManagerNames()
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.MemoryPoolMXBean,
	      java.lang.management.MemoryManagerMXBean"
    INDEX { jvmMemManagerIndex, jvmMemPoolIndex }
    ::= { jvmMemMgrPoolRelTable 1 }
                        

This table has two indexes: jvmMemManagerIndex and jvmMemPoolIndex.

Contrarily to single-indexed tables, multi-index tables can take different forms and can be used to model different things. The table shown just above is in fact a relationship table that relates two conceptual rows defined in two other tables. We will discuss this kind of tables later in this document. However, no matter what the type of the table is, addressing of elements within a table is always done in the same way: by appending the value of the indexes to the OBJECT IDENTIFIER of the columnar object that you want to get. Simply, instead of appending a single index value, we're going to append several.

So given that jvmMemManagerIndex and jvmMemPoolIndex are two integer indexes, if we want to get the value of jvmMemMgrRelManagerName (which is the first readable columnar object in that table) for the row indexed by (jvmMemManagerIndex=3, jvmMemPoolIndex=5), we're simply going to append 3.5 to the OBJECT IDENTIFIER of the columnar object which we want to get. In our case:

    SNMP GET jvmMemMgrRelManagerName.3.5

A perhaps more interesting perspective in this case and for this table, which is a relation table, is that if I simply walk the table using GET NEXT, and get back an instance whose OBJECT IDENTIFIER is jvmMemMgrRelManagerName.3.5, then I can deduce that the JVM Memory Manager that corresponds to jvmMemManagerIndex=3 and the JVM Memory Pool that corresponds to jvmMemPoolIndex=5 are related. We will come back to this later.

The next section will discuss a few common type of indexes and show how to append their values to an OBJECT IDENTIFIER.

Appending Index Values

We have seen how to compose the OID of a columnar object instance when the values of the indexes are INTEGER based types. But what happens if the syntax of the indexes are based on OCTET STRING (e.g. the index is a name and uses the DisplayString TEXTUAL CONVENTION - see RFC 2579), or are based on OBJECT IDENTIFIER (e.g. the value of the index is itself an OBJECT IDENTIFIER)? What happens if the value of the index can be negative, or is a 64 bits quantity? These are the questions we are going to discuss in this section.

Appending integer-valued indexes

This concerns all syntaxes based on INTEGER or Gauge32

Numbers are the simplest case: simply append the value of that number to the OBJECT IDENTIFIER:

1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2.5
\^---sun-----\^              \^     \^  \^ \^ \^
\^------jvmMgtMIB-----------\^     \^  \^ \^ \^
\^--------jvmRTInputArgsTable-----\^  \^ \^ \^
\^--------jvmRTInputArgsEntry--------\^ \^ \^ 
\^----------jvmRTInputArgsItem---------\^ \^
\^------value of jvmRTInputArgsItem------\^
         at jvmRTInputArgsIndex=5 
                        

There is however one restriction: the index must be a positive integer comprised between 0 and 231-1 (0 to 2147483647 decimal). If the value of the index object can exceed these limits then you cannot use that object directly as an index. We will see later what workarounds can be used. Also you are not allowed to use objects whose syntax is Counter32 or Counter64 as indexes.

Here is an extract from RFC 2578 (SMIv2), section 7.7. Mapping of the INDEX clause, which explains how integer-valued indexes are appended to the OID of the columnar object:

(1)  integer-valued (i.e., having INTEGER as its underlying primitive
     type):  a single sub-identifier taking the integer value (this
     works only for non-negative integers);
     
[...]
 
   Since a single value of a Counter has (in general) no information
   content (see section 7.1.6 and 7.1.10), objects defined using the
   syntax, Counter32 or Counter64, must not be specified in an INDEX
     
[...]
 
   Instances identified by use of integer-valued objects should be
   numbered starting from one (i.e., not from zero).  The use of zero as
   a value for an integer-valued index object should be avoided, except
   in special cases.    
                        

Appending string-valued indexes

This concerns all syntaxes based on OCTET STRING. There are two cases to be considered:

  • Fixed length strings: e.g. OCTET STRING (SIZE(4))
  • Variable length strings: e.g. OCTET STRING (SIZE(8..16))

For fixed length strings, we will append one sub-identifier per octet in the string. The value of that subidentifier is the numerical value contained in that octet i.e., a number comprised in [0..255].

So if I had an index object defined with a syntax of OCTET STRING (SIZE(3)), and if I wanted to access an object in a row where that index value were "foo", I would need to append 102.111.111 (val("f").val("o").val("o")) when composing my instance Object Identifier.

For variable length strings, we first append the length of the string, and then the value of each octet in the string. So with my example above, assuming that my index object was defined with a syntax of OCTET STRING (SIZE(0..10)), for accessing an object in a row where that index value is "foo", I would now need to append 3.102.111.111 (length("foo"=3).val("f").val("o").val("o")) when composing my instance Object Identifier.

Note that in some cases, when an OCTET STRING based object is used as last index in a table, if you use the IMPLIED keyword, you no longer need to append the length of the OCTET STRING first. This can work only for the last index defined in the INDEX clause. The IMPLIED keyword means that the length of the string is implied by the length of the OID, and equals to the number of sub-identifiers remaining in the OID: you need only to count all the remaining sub-identifiers to figure out the length of the string. I will not go deeper in details about the IMPLIED keyword, but will strongly advise not to use it when defining your own tables. Readers who would like more information about IMPLIED lengths are invited to refer directly to RFC 2578 (SMIv2).

Index Objects which have a BITS syntax are also handled like variable length OCTET STRING.

Here is the extract from RFC 2578 which explains how to append the value of a string index:

(2)  string-valued, fixed-length strings (or variable-length preceded by
     the IMPLIED keyword):  `n' sub-identifiers, where `n' is the length
     of the string (each octet of the string is encoded in a separate
     sub-identifier);

(3)  string-valued, variable-length strings (not preceded by the IMPLIED
     keyword):  `n+1' sub-identifiers, where `n' is the length of the
     string (the first sub-identifier is `n' itself, following this,
     each octet of the string is encoded in a separate sub-identifier);

[...]

   Note that the IMPLIED keyword can only be present for an object
   having a variable-length syntax (e.g., variable-length strings or
   object identifier-valued objects), Further, the IMPLIED keyword can
   only be associated with the last object in the INDEX clause.
   Finally, the IMPLIED keyword may not be used on a variable-length
   string object if that string might have a value of zero-length.

[...]

   If an object defined using the BITS construct is used in an
   INDEX clause, it is considered a variable-length string.
                        

Note that using variable length OCTET STRINGs as indexes (in particular names) can present some difficulties:

  • First, OBJECT IDENTIFIERs can have at most 128 sub identifiers. Since we need one sub-identifier per octet in the string, plus one for the length, we must guarantee that all index values we use will remain short enough to fit within these limits.
  • Then, there could be LOCALE issues. If the string is a displayable name, you may have to know which encoding is used in order to encode it properly. This information should be defined in the MIB, either in the description of the index object, or in the description of its syntax (if it is using a TEXTUAL CONVENTION.
  • Finally, appending the length of the string as first sub√®identifier in the OID can have some counter intuitive effects: instances in a table are sorted lexicographically whith respect to their object identifier. This means that an instance at index "yy" will appear after an instance at index "x", but before an instance at index "aaa". This is because although "aaa" < "x" < "yyy", 1.(x) < 2.(y).(y) < 3.(a).(a).(a). So if your index uses a variable length, your rows will be sorted as "x","yy","aaa".

For all these reasons we have preferred not to use names as indexes in the JVM-MANAGEMENT-MIB.

Appending object identifier-valued indexes

OBJECT IDENTIFIER values are quite simple to append, and are very similar to strings. Here is what RFC 2578 says:

(4)  object identifier-valued (when preceded by the IMPLIED keyword):
     `n' sub-identifiers, where `n' is the number of sub-identifiers in
     the value (each sub-identifier of the value is copied into a
     separate sub-identifier);

(5)  object identifier-valued (when not preceded by the IMPLIED
     keyword):  `n+1' sub-identifiers, where `n' is the number of sub-
     identifiers in the value (the first sub-identifier is `n' itself,
     following this, each sub-identifier in the value is copied);
                        

You will note that OBJECT IDENTIFIERs present the same limitation than strings with respect to size limits. However, you will very rarely need to use OBJECT IDENTIFIER valued objects as indexes.

Appending IpAddress-valued indexes

Finally, there's a last case for objects whose syntax is IpAddress. IpAddress is now rarely use because it can only contain IP V4 addresses. Here is what RFC 2578 says:

(6)  IpAddress-valued:  4 sub-identifiers, in the familiar a.b.c.d
     notation.
                        

What about negative or 64 bits quantities?

Well, as we have seen, you just can't use an object that can be negative or can exceeds 231-1 directly as an index. However, you can still define a new object of type OCTET STRING in which you will encode the value that you wanted to use as index. In the description of that new object, you must then explain how the value is encoded. Then you can use that new object itself as an index.

For instance, this is what we did in the JVM-MANAGEMENT-MIB for the jvmThreadInstanceTable. We wanted to use the Thread ID, which is a long (i.e. 64 bits) as index in the table. So we defined the following objects:

----------------------------------------------------------------------------
-- TEXTUAL CONVENTION

JvmUnsigned64TC ::= TEXTUAL-CONVENTION
    STATUS       current
    DESCRIPTION
           "A non-negative 64-bit bit integer, without counter
            semantics."
    -- We have cloned the Unsigned64TC defined in RFC 2564 rather
    -- than importing it because the JVM-MANAGEMENT-MIB and the
    -- APPLICATION-MIB are not related.
    --
    REFERENCE "RFC 2564 - APPLICATION-MIB, Unsigned64TC."
    SYNTAX Counter64
    
JvmIndex64TC ::= TEXTUAL-CONVENTION
    STATUS current
    DESCRIPTION
	"A 64 bits string mapping an unsigned 64 bits integer value
         in big-endian ordering (i.e: 1 is encoded as 0x0000000000000001).

	 This type can be used when an unsigned 64 bits integer needs
	 to be used inside a table index.
	"
    SYNTAX OCTET STRING (SIZE(8))
          
----------------------------------------------------------------------------

[...]

----------------------------------------------------------------------------

jvmThreadInstanceTable OBJECT-TYPE
    SYNTAX      SEQUENCE OF JvmThreadInstanceEntry
    [...]
    ::= { jvmThreading 10 }

jvmThreadInstanceEntry OBJECT-TYPE
    SYNTAX      JvmThreadInstanceEntry
    [...]
    INDEX { jvmThreadInstIndex }
    ::= { jvmThreadInstanceTable 1 }

JvmThreadInstanceEntry ::= SEQUENCE {
        jvmThreadInstIndex            JvmIndex64TC,
	jvmThreadInstId               JvmUnsigned64TC,
        [...]
}

jvmThreadInstIndex OBJECT-TYPE
    SYNTAX      JvmIndex64TC
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"An index uniquely identifying a live thread, and directly
         derived from the value of jvmThreadInstId. The jvmThreadInstId 
	 cannot be used directly as index in the table, because integer 
	 indexes cannot exceed an unsigned 32 int. 
	 
	 The jvmThreadInstIndex index is an 8 byte octet string as
         defined by the JvmIndex64TC TEXTUAL-CONVENTION. Its value is 
         directly derived from the value of the corresponding ThreadID
         returned by jvmThreadInstId.
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.ThreadMXBean, java.lang.Thread"
    ::= { jvmThreadInstanceEntry 1 }

jvmThreadInstId OBJECT-TYPE
    SYNTAX      JvmUnsigned64TC
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
	"The thread ID, as returned by Thread.getId().

	 See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).
	                          getThreadId()
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.ThreadMXBean, java.lang.Thread"
    ::= { jvmThreadInstanceEntry 2 }
                        

We see in this example how we have replaced jvmThreadInstId which we couldn't use because it was a 64bit quantity, by jvmThreadInstIndex which uses a syntax (JvmIndex64TC) based on OCTET STRING.

Tricks of the same nature can also be performed to acomodate objects which can have negative values.

Yet, I would recommend to carefully think everything over before resorting to such tricks. In our example above, using the thread ID as index in the table was the natural thing to do, and there wasn't really anything else we could have used as index without introducing more complexity.

Modeling with tables

In the sections above, I have detailed how SNMP tables are indexed. In this section, I will focus on table semantics and design issues.

In fact there are two kinds of tables: tables which stand on their own, and tables which relate to other tables. In general this distinction is reflected in INDEX clauses of all these different tables

Tables which stand on their own, like for example the jvmRTInputArgsTable, have indexes which are completely defined within that table. On the other hand, tables which relate to other table have at least part of their indexes which a) are imported from other tables, or b) have indexes whose values are imported from other table indexes.

In the JVM-MANAGEMENT-MIB we have two such tables: the jvmMemGCTable and the jvmMemMgrPoolRelTable

There is also a third kind of table, which are the augmenting tables. This third kind is mostly related to MIB extension and MIB evolution issues, and we will therefore leave them alone for now. Eager readers are invited to refer directly to RFC 2578 section 7.8. Mapping of the AUGMENTS clause.

Among the various types of patterns modeled by inter-related table indexes we can distinguish three common use cases: Containment, Relations, and Subclassing or inheritance.

Structural Containment

Containment is usually modeled by importing the index of the containing table in the contained table. For instance, if I wanted to model a system where "Modules" are always contained in "Products", I may define a productTable indexed by productIndex, and a moduleTable indexed by {productIndex, moduleIndex}.

    productTable OBJECT-TYPE
        SYNTAX SEQUENCE OF ProductEntry 
        ...
        ::= {somewhere 2}
    productEntry OBJECT-TYPE
        SYNTAX ProductEntry
        ...
        INDEX {productIndex}
        ::= {productTable 1}
    ProductEntry SEQUENCE {
        productIndex ....,
        ...
    }
    moduleTable OBJECT-TYPE
        SYNTAX SEQUENCE OF ModuleEntry 
        ...
        ::= {somewhere 3}
    moduleEntry OBJECT-TYPE
        SYNTAX ProductEntry
        ...
        INDEX {productIndex, moduleIndex}
        ::= {moduleTable 1}
    ModuleEntry SEQUENCE {
        moduleIndex ....,
        ...
    }

There are a few things worth noting in this example.

  • Modules are logically contained in Products, but the moduleTable is registered in the OID tree next to the productTable (::= {somewhere 3}), and not underneath it (::= {productTable \*}). Indeed, SMIv2 does not allow to register a table underneath another table. It is still possible to define tables which are logically contained in other tables (and the moduleTable is such an example), but this is done purely by designing properly their respective index clauses.
  • Modules cannot exists without products. This is implied by the reference to the productIndex object in the index clause of the moduleTable. To have a module instance, you must first have a productIndex, and hence a product instance. The moduleTable is called a dependent table.
  • Two different rows in the moduleTable define two different modules, even if they have the same module index. In our case, the scope of the moduleIndex is completely enclosed by the containing productIndex. Had we wanted to model modules that could be "shared" between products, we would have defined a separate relation table to model possible containment of modules within products - see below.
  • In our case, containment of modules within products is a relation implied by the structure (indexing) of these two tables. If modules could have existed outside of products, we would have defined a separate relation table to model possible containment of modules within products - see below.

Relation Tables

Another common type of tables are relation tables. Relation tables are tables whose principal interest reside in the possible existence of a row, rather than in the content of that row itself.

The JVM-MANAGEMENT-MIB has an example of such a table: the jvmMemMgrPoolRelTable.

jvmMemManagerTable OBJECT-TYPE
    SYNTAX SEQUENCE OF JvmMemManagerEntry
    ...
    ::= { jvmMemory 100 }
jvmMemManagerEntry OBJECT-TYPE
    SYNTAX JvmMemManagerEntry
    ...
    INDEX { jvmMemManagerIndex }
    ::= { jvmMemManagerTable 1 }
JvmMemManagerEntry ::= SEQUENCE {
        jvmMemManagerIndex JvmPositive32TC,
        ...
}
jvmMemPoolTable OBJECT-TYPE
    SYNTAX SEQUENCE OF JvmMemPoolEntry
    ...
    ::= { jvmMemory 110 }
jvmMemPoolEntry OBJECT-TYPE
    SYNTAX JvmMemPoolEntry
    ...
    INDEX { jvmMemPoolIndex }
    ::= { jvmMemPoolTable 1 }
JvmMemPoolEntry ::= SEQUENCE {
        jvmMemPoolIndex JvmPositive32TC,
        ...
}
jvmMemMgrPoolRelTable OBJECT-TYPE
    SYNTAX SEQUENCE OF JvmMemMgrPoolRelEntry
    ...
    ::= { jvmMemory 120 }
jvmMemMgrPoolRelEntry OBJECT-TYPE
    SYNTAX JvmMemMgrPoolRelEntry
    ...
    INDEX { jvmMemManagerIndex, jvmMemPoolIndex }
    ::= { jvmMemMgrPoolRelTable 1 }
JvmMemMgrPoolRelEntry ::= SEQUENCE {
        ...
}
        

As explained earlier in this document, the interest of this table resides only in the indexes of its rows: if I simply walk the table using GET NEXT, and get back an instance whose OBJECT IDENTIFIER is jvmMemMgrRelManagerName.3.5, then I can deduce that the JVM Memory Manager that corresponds to jvmMemManagerIndex=3 and the JVM Memory Pool that corresponds to jvmMemPoolIndex=5 are related. What exactly is the nature of that relation is explained in the various DESCRIPTION clauses of the objects that define the jvmMemMgrPoolRelTable. You can't just guess the nature of the relationship by looking at the structure of the table: you have to read the DESCRIPTIONS within the MIB. It is therefore very important to take time writing tables, entries, and indexes DESCRIPTIONS within a MIB file. If you don't, people who will want to use the MIB will be unable to guess what were your real intents.

In our "Product/Module" example above, if we had wanted to define modules that could exist without products, or to define modules that could be shared by products, this is what we would have defined:

    productTable OBJECT-TYPE
        SYNTAX SEQUENCE OF ProductEntry 
        ...
        ::= {somewhere 2}
    productEntry OBJECT-TYPE
        SYNTAX ProductEntry
        ...
        INDEX {productIndex}
        ::= {productTable 1}
    ProductEntry SEQUENCE {
        productIndex ....,
        ...
    }
    moduleTable OBJECT-TYPE
        SYNTAX SEQUENCE OF ModuleEntry 
        ...
        ::= {somewhere 3}
    moduleEntry OBJECT-TYPE
        SYNTAX ProductEntry
        ...
        INDEX {moduleIndex}
        ::= {moduleTable 1}
    ModuleEntry SEQUENCE {
        moduleIndex ....,
        ...
    }
productModulRelTable OBJECT-TYPE
    SYNTAX SEQUENCE OF ProductModuleRelEntry
    ...
    ::= { somewhere 7 }
productModuleRelEntry OBJECT-TYPE
    SYNTAX ProductModuleRelEntry
    ...
    INDEX { productIndex, moduleIndex }
    ::= { productModulRelTable 1 }
ProductModuleRelEntry ::= SEQUENCE {
        ...
}
        

You will notice the differences in the indexing scheme: now the moduleTable stands on its own, and a third relations table makes the links between products and modules. Using SNMP tables, you will be able to model all kinds of relationships. Eager readers are invited to read David Perkins' very good memo on inter-table relationships.

Modeling inheritence

Finally, another common pattern when modeling with tables involves modeling of inheritence. The JVM-MANAGEMENT-MIB has one such example: the jvmMemGCTable. Rows in the jvmMemGCTable represent Garbage Collectors, and Garbage Collector is a subclass of Memory Manager. This has been modeled as follows:

--
-- Memory managers are represented by the jvmMemManagerTable, which contains
-- one row per Memory manager. 
-- The garbage collector is one type of memory  manager responsible for 
-- reclaiming memory occupied by unreachable objects.
-- The jvmMemGCTable is an extension of the jvmMemManagerTable, which contains
-- the attribute specific to garbage collectors. A garbage collector entity
-- is thus represented by one row in the jvmMemManagerTable, and one 
-- extension row in the jvmMemGCTable.
--
        
jvmMemManagerTable OBJECT-TYPE
    SYNTAX      SEQUENCE OF JvmMemManagerEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"The Memory Manager Table contains the whole list of Memory 
	 Managers  as returned by ManagementFactory.getMemoryManagerMXBeans(). 

	 When a MemoryManagerMXBean object is an instance of 
	 GarbageCollectorMXBean, then additional information specific to 
	 the GarbageCollectorMXBean class will be found in the 
	 jvmGCTable, at the same index. 

	 Relationships between MemoryManagers and MemoryPools are shown 
	 by the Memory Manager-Pool Relation table (jvmMemMgrPoolRelTable). 
	"
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.MemoryManagerMXBean"
    ::= { jvmMemory 100 }

jvmMemManagerEntry OBJECT-TYPE
    SYNTAX      JvmMemManagerEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"A jvmMemManagerEntry conceptual row represent an instance of the
	 java.lang.management.MemoryManagerMXBean interface. If that instance 
	 is also an instance of java.lang.management.GarbageCollectorMXBean, 
	 then additional information will be found in the jvmGCTable, at the 
         same index.

	 Columnar objects in this table are mapped from attributes of
	 the MemoryManagerMXBean interface.

	 See java.lang.management.MemoryManagerMXBean
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.MemoryManagerMXBean"
    INDEX { jvmMemManagerIndex }
    ::= { jvmMemManagerTable 1 }

JvmMemManagerEntry ::= SEQUENCE {
        jvmMemManagerIndex JvmPositive32TC,
        jvmMemManagerName  JvmJavaObjectNameTC,
	jvmMemManagerState JvmValidityStateTC
}
jvmMemGCTable OBJECT-TYPE
    SYNTAX      SEQUENCE OF JvmMemGCEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"The Garbage Collector table provides additional information 
	 on those MemoryManagers which are also GarbageCollectors. 
	 This table extends the  jvmMemManagerTable table. The index 
	 used in the jvmMemGCTable table is imported from the 
	 jvmMemManagerTable table. If a row from the jvmMemManagerTable 
	 table is deleted, and if it has an extension in the jvmMemGCTable 
	 table, then the extension row will also be deleted.
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.GarbageCollectorMXBean"
    ::= { jvmMemory 101 }

jvmMemGCEntry OBJECT-TYPE
    SYNTAX      JvmMemGCEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
	"Provide additional information on Garbage Collectors.
	 
	 Columnar objects in this table are mapped from the
	 GarbageCollectorMXBean interface.

	 See java.lang.management.GarbageCollectorMXBean
        "
    REFERENCE "J2SE 5.0 API Specification,
              java.lang.management.GarbageCollectorMXBean"
    INDEX   { jvmMemManagerIndex }
    ::= {jvmMemGCTable 1 }

JvmMemGCEntry ::= SEQUENCE {
        jvmMemGCCount  Counter64,
        jvmMemGCTimeMs JvmTimeMillis64TC
}








Readers who are eager to learn more about modeling with tables in SNMP are invited to refer to the excellent book:

Understanding SNMP MIBs by Perkins and McGinnis, published by Prentice Hall.

Well, it's been a long journey in the world of SNMP, and I hope you did enjoy it. Next time, we will maybe speak of GET NEXT, for which I had some recent request.

Cheers, but now, it's time to sit down to table!
-- daniel

Comments:

This is great.

Do you have any thoughts about using deterministic table ids in the context of mapping SNMP to MBeans?

As an example: having a custom table where the entries map to "java.lang:type=MemoryPool" and each row is an MBean and the row ids would be: "Code Cache", "Eden Space", etc...

Posted by Erik Earle on June 01, 2007 at 12:57 PM CEST #

Hi Erik,

Deterministic table ids are very difficult to achieve in the context of mapping SNMP to MBeans.
The reason is that ObjectNames can be long, the way to map a String to an OID is to map each character to a subidentifier node, and an Object Identifier should not have more than 128 subidentifiers.

Even if you break up the ObjectName in parts, you will only delay the problem. For instance you could attempt to have one index per key in the ObjectName, plus one for the domain, but you still can't guarantee that none of the keys will go over the 128 subidentifiers limit.
What's more it wouldn't work for a global MBean table, since the number of indexes in a table is static (you have to write it in the MIB), whereas the number of keys that can be used in an ObjectName is unlimited (even when the recommendation is to stick to the scheme type= name=).

If you are interested in SNMP tables representing MBeans you can have a look at the SNMP MBean table example that comes with Java DMK 5.1.

There I am using "undeterministic ids" to map MBeans to table rows.

Hope this helps,
-- daniel

Posted by daniel on June 04, 2007 at 03:13 AM CEST #

DF >> ...the number of indexes in a table is static

I'm not clear on what you mean by 'the number of indexes'. Are you referring to the upper limit of the number of rows or the range of acceptable index values?

Posted by Erik Earle on June 04, 2007 at 02:29 PM CEST #

What I mean is that the number of columnar objects that serve as indexes in a given table is fixed by that table definition in the MIB.

On the other hand you cannot predict how many keys will be used in an ObjectName, unless you refer to MBeans of a particular type registered by a particular application.

So generically mapping an ObjectName keys to SNMP table indexes will not work.

It could have worked for Sun Memory Pool MBeans, but would have put a constraint on the accepatable length of memory pool names, which I prefered to avoid.

As it stands now, the MIB depends on the java.lang.management API, but the java.lang.management API doesn't depend on the MIB.

The cost to pay is that you have to walk through the memory pool table to discover the arbitrary integer indexes assigned by the SNMP agent, but IMHO, it is safer that way.

regards,
--daniel

Posted by daniel on June 04, 2007 at 03:39 PM CEST #

Really Good

Posted by Swamy on February 29, 2008 at 01:03 PM CET #

Hi Daniel,

thank you very much for this overview!

I currently have a project in which I need to implement a base table and two "subtables". Each subtable refers to a column/cell of the base table. As far as I understand, the subtables have containment relation to the base table. My idea now is to "import" the index and the corresponding columns of the base table as index into the subtables, such as:

Base table: Product
productIndex | bookIndex | cdIndex | Other
INDEX { productIndex, bookIndex, cdIndex }
----------------------------------

Subtable Book:
productIndex | bookIndex | book | price
INDEX {productIndex, bookIndex, book}
----------------------------------

Subtable CD:
productIndex | cdIndex | cd | price
INDEX {productIndex, cdIndex, cd}

Do you also think that it should be done this way? My questions are:
1) Do I have to define both "book" and "cd" columns as Index of the base table?
2) Should I access the "book price" column using:
baseTable.productIndex.bookIndex.book.price?

Thank you very much in advance.

Have a nice day.

Shuyin

Posted by Shuyin on August 01, 2008 at 03:48 AM CEST #

Hi Shuyin,

No, you shouldn't have bookIndex and cdIndex in your base table. Also, unless a product can contain several books and cd's - you'll probably want to have bookIndex=productIndex and cdIndex=productIndex - that is - if a product is either a book or a cd.

Finally to access book price you would have to do:

bookTable.1.price.(productIndex).(bookIndex) - in the case were a product can contain several books:

Subtable Book:
bookIndex | book | price
INDEX {productIndex, bookIndex}
-------------------------------

or
bookTable.1.price.(productIndex) - in the case were a book is a product (and bookIndex=productIndex)

Subtable Book:
book | price
INDEX {productIndex}
--------------------

[You should see SNMP tables as relational DB tables]

Hope this helps,

-- daniel

Posted by daniel on August 03, 2008 at 05:03 AM CEST #

really great entry!
this helped me a lot! thank you very much

Posted by felix schmutz on October 13, 2008 at 11:05 AM CEST #

Thanks so much for writing this up. Best explanation of SNMP tables I've found!

Posted by Ryan on December 14, 2009 at 07:00 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Daniel Fuchs blogs on Scene Builder, JMX, SNMP, Java, etc...

The views expressed on this blog are those of the author and do not necessarily reflect the views of Oracle.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
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
   
       
Today