I'm writing an agent for one of our C# apps that exposes various application properties via SNMP GET using SharpSnmpLib, which I've successfully sent traps and retrieved GETs with in the past. I'm able to expose single properties, but I'm having trouble figuring out how to define a table in my application.
This table should exposes a set of GET parameters once for each instance of a stream concept within my application (the number of streams is fixed on startup), each stream should be represented as a row in the table.
It's easy enough to define this in the MIB, and I've successfully defined and implemented such tables in the past with C++ libraries, so I know it's possible.
Here's how I define a simple GET of the app version in the MIB:
dtStream OBJECT IDENTIFIER ::= {dtApps 3}
dtVersion OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The version number of the application"
::= {dtStream 1}
And in code:
public class StringVar : ScalarObject
{
private ISnmpData _data;
private string _value;
public StringVar(ObjectIdentifier oid, string value) : base(oid)
{
Value = value;
}
public override ISnmpData Data
{
get { return _data; }
set { throw new AccessFailureException(); }
}
public string Value
{
set { _data = new OctetString(_value = value); }
get { return _value; }
}
}
...
ObjectStore os = new ObjectStore();
os.Add(Version = new StringVar(OID_STREAM_VERSION, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()));
Here's an example MIB definition for the table I'm trying to create:
dtStreamTable OBJECT-TYPE
SYNTAX SEQUENCE OF DtStreamEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Statistics on the status and performance of the streams"
::= {dtStream 5}
DtStreamEntry OBJECT-TYPE
SYNTAX DtStreamEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Status of a particular stream."
INDEX {dtStreamID}
::= {dtStreamTable 1}
DtStreamEntry ::= SEQUENCE {
dtmStreamID Integer32,
dtStreamName DisplayString
dtStreamHost DisplayString
dtStreamType Integer32
}
dtStreamID OBJECT-TYPE
SYNTAX Integer32 (0..255)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Index of the stream entry in the table."
::= {DtStreamEntry 1}
dtStreamName OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Name of the stream."
::= {DtStreamEntry 2}
dtStreamHost OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Hostname that the stream is connected to."
::= {DtStreamEntry 3}
dtStreamType OBJECT-TYPE
SYNTAX Integer32(1..2)
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Type of stream"
::= {DtStreamEntry 4}
It looks like this in iReasoning MIB Browser:
I've got as far as defining what I'd consider to be the "row", as a class derived from TableObject
:
class StreamEntry : TableObject
{
private readonly IList<ScalarObject> _elements = new List<ScalarObject>();
public StreamEntry(int id, string name, string host, int type)
: base()
{
_elements.Add(StreamId = new IntVar(OID_STREAM_ID, id));
_elements.Add(Name = new StringVar(OID_STREAM_NAME, name));
_elements.Add(Host = new StringVar(OID_STREAM_HOST, host));
_elements.Add(Type = new IntVar(OID_STREAM_TYPE, type));
}
public IntVar StreamId { get; private set; }
public StringVar Name { get; private set; }
public StringVar Host { get; private set; }
public IntVar Type { get; private set; }
protected override IEnumerable<ScalarObject> Objects
{
get { return _elements; }
}
}
After all that, I guess my question is, how do I associate these into a table that is returned with the OID for the table or entry?
I've tried adding multiple StreamEntry
objects into the ObjectStore:
foreach(Stream s in Streams)
{
os.Add(new StreamEntry(s.ID, s.Name, s.HostName, s.Type));
}
With this I can only retrieve the data for the first one (using individual GETs, or WALK on the dtStreamEntry in the MIB Browser).
Ok, I figured it out. All ScalarObjects
need to be added to the ObjectStore
in OID order - otherwise everything breaks.
So I needed to create multiple StreamEntry
objects (no longer having TableObject
as a base class) within an enclosing StreamTable
object which was derived from TableObject
, and I needed to extract the ScalarObjects
from each StreamEntry
and sort them all by OID before returning them as the Objects collection override.
I now have a working SNMP table. :)