Device components discovery [SNMP MP Chap 4] #scom #sysctr


Now that we have discovered and monitored CPU and memory we have all the basic plumbing to discover other device components at least all of the component that are single instanced. There are several components that are exposed via SNMP in multiple instances, one such example are interfaces. In many cases an SNMP table is used to return these components and their properties.

This article will go through the complex process of discovering SNMP tables using the native OpsMgr SNMP modules.

SNMP Tables

What are SNMP tables and how are they structured? On a formal point of view SNMP tables are structured on a series of row identified by an index, each row is a different device and contains the properties (or column) for the device. On a practical side tables have a base OID, followed by a property (or column) ID, followed by an index.

Relative OID / index

1

2

3

1

Base OID.1.1

Base OID.1.2

Base OID.1.3

2

Base OID.2.1

Base OID.2.2

Base OID.2.3

3

Base OID.3.1

Base OID.3.2

Base OID.3.3

4

Base OID.4.1

Base OID.4.2

Base OID.4.3

 

Let’s take as an example the interface table (ifTable) defined in RFC1213. The base OID is 1.3.6.1.2.1.2.2. The table is composed of rows of ifEntry with relative ID 1. So the base OID is actually 1.3.6.1.2.1.2.2.1 or ifEntry. The first column of the table is the index, so ifEntry.1 is the OID of the index property. To get the index of all the interfaces (rows or ifEntry) we must WALK ifEntry.1 and get something like:

ifEntry .1 .1 = 1

ifEntry .1 .2 = 2

ifEntry .1 .3 = 3

This means the device has 3 interfaces with index 1, 2 and 3.

The interface description property is identified by the relative OID 2, so following the above rules we have:

ifEntry .2 .1 to get the description (.2) of the interface with index .1

The interface type is identified by relative OID 3, so if I want to get the interface type for interface index 2 I must GET ifEntry.3.2 or 1.3.6.1.2.1.2.2.1.3.2

Discovering SNMP tables entries

If all this makes sense let’s try to define the challenge of discovering table based components with the standard OpsMgr SNMP modules. Before blaming the team remember it is assumed those modules are not used for discovery, since discovery is performed outside the standard OpsMgr modules.

The first challenge is tables are dynamic, we don’t know how many rows are going to be there, nor we know the indexes, since they are not forced to be sequential as in my example above. I can have 3 interfaces with index 1, 2 and 3 as well as indexes 3,5,6.

The second challenge is tables can contain similar objects and there are cases where we don’t want to monitor all the objects in a table, so it is useful to be able to filter by some table property (or column).

The third challenge is tables are constructed with the index as the last ID, so we cannot walk all the properties for device index x. On the other hand we can walk all the table and we can walk all the values for property n for all devices.

Remember that for tables the index is the key.

The OpsMgr SNMP modules don’t have any concept of tables, they just WALK or GET specific OIDs and they can return a single data item for the whole WALK or a data item for each OID, this translates in a single class instance created in OpsMgr or in indexes * properties class instances. So, no you cannot just walk the table.

If we don’t need to filter the table objects we can just walk the index property and create one OpsMgr class instance for every index. Then we can walk each and every property we need to discover for each device: no chance to cookdown here we will have one GET for every device we want to discover.

If we do want to filter the returned devices by one of the properties things get more complicated, since we cannot get the index from the SNMP modules.

Let’s set our stake to the more complex scenario, we have a table from which we want just specific elements based on one of the table row properties.

To set up our example let’s say QND.MyNetwork.Device has some internal storage and it implements RFC 2790 for storage resources: 1.3.6.1.2.1.25.2.3 or hrStorageTable. Our base OID will be hrStorageEntry or hrStorageTable.1 or 1.3.6.1.2.1.25.2.3.1.

hrStorageEntry defines the following properties:

          hrStorageIndex(1)

          hrStorageType(2)

          hrStorageDescr(3)

          hrStorageAllocationUnits(4)

          hrStorageSize(5)

          hrStorgeUsed(6)

          hrStorageAllocationFailures(7)

We just want to monitor storage type hrStorageFixedDisk (.1.3.6.1.2.1.25.2.1.4)

The logic can be:

          Walk the table and return the storage types (hrStorageEntry.hrStorageType)

          Filter the value for fixed disks (hrStorageFixedDisk)

          Extract the device index from the returned entry, the index is the last ID

          Get the storage details

          Populate and create the instances in OpsMgr

This logic cannot work in OpsMgr we have several issues here, dissecting them all can be an argument for another article, but I think no one is interested in what could be, but it is not.

The logic I implemented is the following:

          Root discovery, targeted at the device

o   Walk the table and return the storage types, one entry per OID

o   Filter the entries for fixed disks              

o   Create the device instances and set the key to the returned OID

          Index discovery, targeted at the storage entry

o   Enumerate the just discovered devices and extract the index from the device key we populated in root discovery

o   Set the device index and update the device

          Properties discovery, targeted at the storage entry

o   Check if the index has been populated, if not skip the device

o   GET specific properties OID using the device index

o   Set the device properties and update the device

I know it’s pretty complex and requires several SNMP enquiry to complete, but this is the only way I found to discover just the entries that I need from an SNMP table using the OpsMgr standard SNMP modules.

As usual let’s go through the code one step at time.

First of all let’s define our disk class and the relationships with the QND.MyNetwork.Device. We leverage the predefined System.NetworkManagement.Disk class that already have a hosting relationship defined for the NetworkNode class. We just add a property for the AllocationUnitSIze that can be useful to convert the disk size from allocation units to bytes.

XML

       <ClassType ID="QND.MyNetwork.Device.Disk" Accessibility="Public" Abstract="false" Base="Network!System.NetworkManagement.Disk" Hosted="true" Singleton="false" Extension="false">

        <Property ID=AllocationUnitSize Type=int AutoIncrement=false Key=false CaseSensitive=false MaxLength=256 MinLength=0 Required=false Scale=0 />

      </ClassType>

 

As you know every discovery rule needs to have its own data source module. In this sample we’re going to have 3 discovery rules and 3 corresponding data source modules. Taking a bottom down approach, let’s start with the root discovery. Since the code it’s pretty verbose this is the main logic:

          Use the QND.NetLibrary.SNMPDevice.SearchOID.DS

o   Walk the hrStorageType tree

o   Filter the entries based on hrStorageFixedDisk

          Using a ClassSnapshotDataMapper discover the disks

o   Set the device key to Disk – [OID returned by the walk]. This is important since we’re going to use the OID to extract the device index in subsequent discoveries

XML

      <DataSourceModuleType ID=QND.NetLibrary.SNMPDevice.SearchOID.MT Accessibility=Public Batching=false>

        <Configuration>

          <IncludeSchemaTypes>

            <SchemaType>System!System.ExpressionEvaluatorSchema</SchemaType>

            <SchemaType>Snmp!System.SnmpVarBindsSchema</SchemaType>

          </IncludeSchemaTypes>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=NoOfRetries type=xsd:unsignedInt default=3 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 />

          <xsd:element name=IP type=xsd:string />

          <xsd:element name=CommunityString type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string />

          <xsd:element minOccurs=1 maxOccurs=1 name=StartOID type=xsd:string />

          <xsd:element minOccurs=1 maxOccurs=1 name=MatchValue type=xsd:string />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <DataSource ID=Scheduler TypeID=System!System.Discovery.Scheduler>

                <Scheduler>

                  <SimpleReccuringSchedule>

                    <Interval>$Config/IntervalSeconds$</Interval>

                    <SyncTime>$Config/SyncTime$</SyncTime>

                  </SimpleReccuringSchedule>

                  <ExcludeDates />

                </Scheduler>

              </DataSource>

              <ProbeAction ID=Probe TypeID=Network!System.NetworkManagement.SnmpProbe>

                <Walk>true</Walk>

                <WalkReturnMultipleItems>true</WalkReturnMultipleItems>

                <NoOfRetries>3</NoOfRetries>

                <Timeout>$Config/Timeout$</Timeout>

                <Port>$Config/Port$</Port>

                <SNMPv3UserName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/UserName$</SNMPv3UserName>

                <SNMPv3AuthProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationProtocol$</SNMPv3AuthProtocol>

                <SNMPv3AuthPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationKey$</SNMPv3AuthPassword>

                <SNMPv3PrivProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyProtocol$</SNMPv3PrivProtocol>

                <SNMPv3PrivPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyKey$</SNMPv3PrivPassword>

                <SNMPv3ContextName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/ContextName$</SNMPv3ContextName>

                <IsWriteAction>false</IsWriteAction>

                <IP>$Config/IP$</IP>

                <CommunityString>$Config/CommunityString$</CommunityString>

                <Version>$Config/Version$</Version>

                <SnmpVarBinds>

                  <SnmpVarBind>

                    <OID>$Config/StartOID$</OID>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                </SnmpVarBinds>

                <OutputOnError>false</OutputOnError>

              </ProbeAction>

              <ConditionDetection ID=FilterOnObjectValue TypeID=System!System.ExpressionFilter>

                <Expression>

                  <SimpleExpression>

                    <ValueExpression>

                      <XPathQuery Type=String>SnmpVarBinds/SnmpVarBind/Value</XPathQuery>

                    </ValueExpression>

                    <Operator>Equal</Operator>

                    <ValueExpression>

                      <Value Type=String>$Config/MatchValue$</Value>

                    </ValueExpression>

                  </SimpleExpression>

                </Expression>

              </ConditionDetection>

            </MemberModules>

            <Composition>

              <Node ID=FilterOnObjectValue>

                <Node ID=Probe>

                  <Node ID=Scheduler />

                </Node>

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>Snmp!System.SnmpData</OutputType>

      </DataSourceModuleType>

 

     

<DataSourceModuleType ID=Progel.LibraEsva.Disk.Discovery.MT Accessibility=Public Batching=false>

        <Configuration>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 />

          <xsd:element name=IP type=xsd:string />

          <xsd:element name=CommunityString type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string />

          <xsd:element minOccurs=1 name=DeviceKey type=xsd:string />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <DataSource ID=MatchOID TypeID=Progel.Library.SNMPDevice.SearchOID.MT>

                <IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>

                <SyncTime>$Config/SyncTime$</SyncTime>

                <NoOfRetries>3</NoOfRetries>

                <Timeout>$Config/Timeout$</Timeout>

                <Port>$Config/Port$</Port>

                <IP>$Config/IP$</IP>

                <CommunityString>$Config/CommunityString$</CommunityString>

                <Version>$Config/Version$</Version>

                <StartOID>.1.3.6.1.2.1.25.2.3.1.2</StartOID>

                <!– .iso.org.dod.internet.mgmt.mib-2.host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageType –>

                <MatchValue>.1.3.6.1.2.1.25.2.1.4</MatchValue>

                <!– .iso.org.dod.internet.mgmt.mib-2.host.hrStorage.hrStorage Types.hrStorageFixedDisk –>

              </DataSource>

             

              <ConditionDetection ID=MapToDiscovery TypeID=System!System.Discovery.ClassSnapshotDataMapper>

                <ClassId>$MPElement[Name=’Progel.LibraEsva.Disk’]$</ClassId>

                <InstanceSettings>

                  <Settings>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.Node’]/DeviceKey$</Name>

                      <Value>$Config/DeviceKey$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.LogicalDevice’]/Key$</Name>

                      <Value>Disk-$Data/SnmpVarBinds/SnmpVarBind/OID$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.Disk’]/Removable$</Name>

                      <Value>false</Value>

                    </Setting>

                  </Settings>

                </InstanceSettings>

              </ConditionDetection>

            </MemberModules>

            <Composition>

              <Node ID=MapToDiscovery>

                <Node ID=MatchOID />

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>System!System.Discovery.Data</OutputType>

      </DataSourceModuleType>

… in the monitoring section

<Discovery ID=Progel.LibraEsva.Disk.Discovery Enabled=true ConfirmDelivery=false Remotable=true Priority=Normal Target=Progel.LibraEsva.Device>

        <Category>Discovery</Category>

        <DiscoveryTypes>

          <DiscoveryClass TypeID=Progel.LibraEsva.Disk />

        </DiscoveryTypes>

        <DataSource ID=DS TypeID=Progel.LibraEsva.Disk.Discovery.MT>

          <IntervalSeconds>43200</IntervalSeconds>

          <SyncTime />

          <Timeout>3500</Timeout>

          <Port>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/PortNumber$</Port>

          <IP>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPAddress$</IP>

          <CommunityString>$RunAs[Name=”Network!System.NetworkManagement.Snmp.MonitoringAccount”]/CommunityString$$Target/Property[Type=”Network!System.NetworkManagement.Node”]/VirtualCommunitySuffix$</CommunityString>

          <Version>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPVersion$</Version>

          <DeviceKey>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/DeviceKey$</DeviceKey>

        </DataSource>

      </Discovery>

 

 

The second discovery is very simple and uses a powershell script to extract the index from the previously discovered disk OID.

XML

      <DataSourceModuleType ID=QND.MyNetwork.Device.DiskIndex.Discovery.MT Accessibility=Public Batching=false>

        <Configuration>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string />

          <xsd:element minOccurs=1 name=DeviceKey type=xsd:string />

          <xsd:element minOccurs=1 name=LogicalDeviceKey type=xsd:string />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <DataSource ID=Scheduler TypeID=System!System.Discovery.Scheduler>

                <Scheduler>

                  <SimpleReccuringSchedule>

                    <Interval>$Config/IntervalSeconds$</Interval>

                    <SyncTime>$Config/SyncTime$</SyncTime>

                  </SimpleReccuringSchedule>

                  <ExcludeDates />

                </Scheduler>

              </DataSource>

              <ProbeAction ID=PS TypeID=Windows!Microsoft.Windows.PowerShellPropertyBagProbe>

                <ScriptName>GetIndex.ps1</ScriptName>

                <ScriptBody>

                  <![CDATA[

param($OID)

$index=$oid.Substring($oid.LastIndexOf(‘.’)+1)

$oAPI = new-object -comObject “MOM.ScriptAPI”

$pb = $oAPI.CreatePropertyBag()

# Populate the property bag with data from the registy

$pb.AddValue(“Index”, $index)

$pb

               

                ]]></ScriptBody>

                <Parameters>

                  <Parameter>

                    <Name>OID</Name>

                    <Value>$Config/LogicalDeviceKey$</Value>

                  </Parameter>

                </Parameters>

                <TimeoutSeconds>60</TimeoutSeconds>

              </ProbeAction>

              <ConditionDetection ID=MapToDiscovery TypeID=System!System.Discovery.ClassSnapshotDataMapper>

                <ClassId>$MPElement[Name=’QND.MyNetwork.Device.Disk’]$</ClassId>

                <InstanceSettings>

                  <Settings>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.Node’]/DeviceKey$</Name>

                      <Value>$Config/DeviceKey$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.LogicalDevice’]/Key$</Name>

                      <Value>$Config/LogicalDeviceKey$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.LogicalDevice’]/Index$</Name>

                      <Value>$Data/Property[@Name=”Index”]$</Value>

                    </Setting>

                  </Settings>

                </InstanceSettings>

              </ConditionDetection>

            </MemberModules>

            <Composition>

              <Node ID=MapToDiscovery>

                <Node ID=PS>

                  <Node ID=Scheduler />

                </Node>

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>System!System.Discovery.Data</OutputType>

      </DataSourceModuleType>

 

… in the monitoring section

      <Discovery ID=QND.MyNetwork.Device.Disk.Discovery Enabled=true ConfirmDelivery=false Remotable=true Priority=Normal Target=QND.MyNetwork.Device.Device>

        <Category>Discovery</Category>

        <DiscoveryTypes>

          <DiscoveryClass TypeID=QND.MyNetwork.Device.Disk />

        </DiscoveryTypes>

        <DataSource ID=DS TypeID=QND.MyNetwork.Device.Disk.Discovery.MT>

          <IntervalSeconds>43200</IntervalSeconds>

          <SyncTime />

          <Timeout>3500</Timeout>

          <Port>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/PortNumber$</Port>

          <IP>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPAddress$</IP>

          <CommunityString>$RunAs[Name=”Network!System.NetworkManagement.Snmp.MonitoringAccount”]/CommunityString$$Target/Property[Type=”Network!System.NetworkManagement.Node”]/VirtualCommunitySuffix$</CommunityString>

          <Version>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPVersion$</Version>

          <DeviceKey>$Target/Property[Type=”Network!System.NetworkManagement.Node”]/DeviceKey$</DeviceKey>

        </DataSource>

      </Discovery>

 

 

The latest discovery will leverage the previous two to get some more info on the disks, to keep the code clean we will use a specific probe. In this case the code is straightforward, just note the way to get multiple OIDs from the same probe without using a walk (since the index is last ID  we cannot use a walk) and the filter to check if the disk index has already being discovered. The latter is to avoid errors in eventlog and to provide some sort of sync between the two disocveries.

XML

      <DataSourceModuleType ID=QND.MyNetwork.Device.DiskProperty.Discovery.MT Accessibility=Public Batching=false>

        <Configuration>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 />

          <xsd:element name=IP type=xsd:string />

          <xsd:element name=CommunityString type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string />

          <xsd:element minOccurs=1 name=DeviceKey type=xsd:string />

          <xsd:element minOccurs=1 name=LogicalDeviceKey type=xsd:string />

          <xsd:element minOccurs=1 name=LogicalDeviceIndex type=xsd:string />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <DataSource ID=Scheduler TypeID=System!System.Discovery.Scheduler>

                <Scheduler>

                  <SimpleReccuringSchedule>

                    <Interval>$Config/IntervalSeconds$</Interval>

                    <SyncTime>$Config/SyncTime$</SyncTime>

                  </SimpleReccuringSchedule>

                  <ExcludeDates />

                </Scheduler>

              </DataSource>

              <ProbeAction ID=GetDetail TypeID=QND.MyNetwork.Device.GetStorageInfo.PT>

                <Timeout>$Config/Timeout$</Timeout>

                <Port>$Config/Port$</Port>

                <IP>$Config/IP$</IP>

                <CommunityString>$Config/CommunityString$</CommunityString>

                <Version>$Config/Version$</Version>

                <Index>$Config/LogicalDeviceIndex$</Index>

              </ProbeAction>

              <ConditionDetection ID=CheckIndex TypeID=System!System.ExpressionFilter>

                <Expression>

                  <RegExExpression>

                    <ValueExpression>

                      <Value Type=String>$Config/LogicalDeviceIndex$</Value>

                    </ValueExpression>

                    <Operator>MatchesRegularExpression</Operator>

                    <Pattern>^[0-9]{1,}$</Pattern>

                  </RegExExpression>

                </Expression>

              </ConditionDetection>

              <ConditionDetection ID=MapToDiscovery TypeID=System!System.Discovery.ClassSnapshotDataMapper>

                <ClassId>$MPElement[Name=’QND.MyNetwork.Device.Disk’]$</ClassId>

                <InstanceSettings>

                  <Settings>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.Node’]/DeviceKey$</Name>

                      <Value>$Config/DeviceKey$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.LogicalDevice’]/Key$</Name>

                      <Value>$Config/LogicalDeviceKey$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.LogicalDevice’]/Description$</Name>

                      <Value>$Data/SnmpVarBinds/SnmpVarBind[2]/Value$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’System!System.Entity’]/DisplayName$</Name>

                      <Value>Disk – $Data/SnmpVarBinds/SnmpVarBind[2]/Value$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’Network!System.NetworkManagement.Disk’]/Capacity$</Name>

                      <Value>$Data/SnmpVarBinds/SnmpVarBind[4]/Value$</Value>

                    </Setting>

                    <Setting>

                      <Name>$MPElement[Name=’QND.MyNetwork.Device.Disk’]/AllocationUnitSize$</Name>

                      <Value>$Data/SnmpVarBinds/SnmpVarBind[3]/Value$</Value>

                    </Setting>

                  </Settings>

                </InstanceSettings>

              </ConditionDetection>

            </MemberModules>

            <Composition>

              <Node ID=MapToDiscovery>

                <Node ID=GetDetail>

                  <Node ID=CheckIndex>

                    <Node ID=Scheduler />

                  </Node>

                </Node>

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>System!System.Discovery.Data</OutputType>

      </DataSourceModuleType>

      <ProbeActionModuleType ID=QND.MyNetwork.Device.GetStorageInfo.PT Accessibility=Internal Batching=false PassThrough=false>

        <Configuration>

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element name=IP type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element name=CommunityString type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Index type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <ProbeAction ID=SnmpProbe TypeID=Network!System.NetworkManagement.SnmpProbe>

                <Walk>false</Walk>

                <WalkReturnMultipleItems>false</WalkReturnMultipleItems>

                <NoOfRetries>3</NoOfRetries>

                <Timeout>$Config/Timeout$</Timeout>

                <Port>$Config/Port$</Port>

                <SNMPv3UserName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/UserName$</SNMPv3UserName>

                <SNMPv3AuthProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationProtocol$</SNMPv3AuthProtocol>

                <SNMPv3AuthPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationKey$</SNMPv3AuthPassword>

                <SNMPv3PrivProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyProtocol$</SNMPv3PrivProtocol>

                <SNMPv3PrivPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyKey$</SNMPv3PrivPassword>

                <SNMPv3ContextName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/ContextName$</SNMPv3ContextName>

                <IsWriteAction>false</IsWriteAction>

                <IP>$Config/IP$</IP>

                <CommunityString>$Config/CommunityString$</CommunityString>

                <Version>$Config/Version$</Version>

                <SnmpVarBinds>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1.1.$Config/Index$</OID>

                    <!– hrStorageIndex –>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1.3.$Config/Index$</OID>

                    <!– hrStorageDescr –>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1.4.$Config/Index$</OID>

                    <!– hrStorageAllocationUnits –>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1.5.$Config/Index$</OID>

                    <!– hrStorageSize –>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1.6.$Config/Index$</OID>

                    <!– hrStorageUsed –>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                </SnmpVarBinds>

                <OutputOnError>false</OutputOnError>

              </ProbeAction>

            </MemberModules>

            <Composition>

              <Node ID=SnmpProbe />

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>Snmp!System.SnmpData</OutputType>

        <InputType>System!System.BaseData</InputType>

      </ProbeActionModuleType>

… in the monitoring section

      <Discovery ID="QND.MyNetwork.Device.DiskProperty.Discovery" Enabled="true" ConfirmDelivery="false" Remotable="true" Priority="Normal" Target="QND.MyNetwork.Device.Disk">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="QND.MyNetwork.Device.Disk" />
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="QND.MyNetwork.Device.DiskProperty.Discovery.MT">
          <IntervalSeconds>43400</IntervalSeconds>
          <SyncTime />
          <Timeout>3500</Timeout>
          <Port>$Target/Host/Property[Type="Network!System.NetworkManagement.Node"]/PortNumber$</Port>
          <IP>$Target/Host/Property[Type="Network!System.NetworkManagement.Node"]/SNMPAddress$</IP>
          <CommunityString>$RunAs[Name="Network!System.NetworkManagement.Snmp.MonitoringAccount"]/CommunityString$$Target/Host/Property[Type="Network!System.NetworkManagement.Node"]/VirtualCommunitySuffix$</CommunityString>
          <Version>$Target/Host/Property[Type="Network!System.NetworkManagement.Node"]/SNMPVersion$</Version>
          <DeviceKey>$Target/Host/Property[Type="Network!System.NetworkManagement.Node"]/DeviceKey$</DeviceKey>
          <LogicalDeviceKey>$Target/Property[Type="Network!System.NetworkManagement.LogicalDevice"]/Key$</LogicalDeviceKey>
          <LogicalDeviceIndex>$Target/Property[Type="Network!System.NetworkManagement.LogicalDevice"]/Index$</LogicalDeviceIndex>
        </DataSource>
      </Discovery>

 

 

Monitoring the disks

Now that our storage (disks) has been discovered it time to close the loop and add some monitoring. Remember: I won’t add the code for strings and display strings, I leave it up to my readers.

The monitor I’m going to present will check for disk free space using a threshold on the percent free space. To get the percentage of free space we need to calculate: (hrStorageSize-hrStorageUsed)/HrStorageSize. It’s easy to calculate with a module we already met System.NetworkManagement.Computation in chapter 3. Also to note is the datasource used by the monitor will be executed once for each device (and not for each disk, here we’re leveraging cookdown).

XML

      <DataSourceModuleType ID=QND.MyNetwork.Device.WalkStorageTable.DS Accessibility=Internal Batching=false>

        <Configuration>

          <IncludeSchemaTypes>

            <SchemaType>System!System.ExpressionEvaluatorSchema</SchemaType>

            <SchemaType>Snmp!System.SnmpVarBindsSchema</SchemaType>

          </IncludeSchemaTypes>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=NoOfRetries type=xsd:unsignedInt default=3 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 />

          <xsd:element name=IP type=xsd:string />

          <xsd:element name=CommunityString type=xsd:string />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string />

        </Configuration>

        <ModuleImplementation Isolation=Any>

          <Composite>

            <MemberModules>

              <DataSource ID=Scheduler TypeID=System!System.Scheduler>

                <Scheduler>

                  <SimpleReccuringSchedule>

                    <Interval>$Config/IntervalSeconds$</Interval>

                    <SyncTime>$Config/SyncTime$</SyncTime>

                  </SimpleReccuringSchedule>

                  <ExcludeDates />

                </Scheduler>

              </DataSource>

              <ProbeAction ID=Probe TypeID=Network!System.NetworkManagement.SnmpProbe>

                <Walk>true</Walk>

                <WalkReturnMultipleItems>false</WalkReturnMultipleItems>

                <NoOfRetries>3</NoOfRetries>

                <Timeout>$Config/Timeout$</Timeout>

                <Port>$Config/Port$</Port>

                <SNMPv3UserName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/UserName$</SNMPv3UserName>

                <SNMPv3AuthProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationProtocol$</SNMPv3AuthProtocol>

                <SNMPv3AuthPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/AuthenticationKey$</SNMPv3AuthPassword>

                <SNMPv3PrivProtocol>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyProtocol$</SNMPv3PrivProtocol>

                <SNMPv3PrivPassword>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/PrivacyKey$</SNMPv3PrivPassword>

                <SNMPv3ContextName>$RunAs[Name=”Network!System.NetworkManagement.SnmpV3.MonitoringAccount”]/ContextName$</SNMPv3ContextName>

                <IsWriteAction>false</IsWriteAction>

                <IP>$Config/IP$</IP>

                <CommunityString>$Config/CommunityString$</CommunityString>

                <Version>$Config/Version$</Version>

                <SnmpVarBinds>

                  <SnmpVarBind>

                    <OID>.1.3.6.1.2.1.25.2.3.1</OID>

                    <Syntax>0</Syntax>

                    <Value VariantType=8 />

                  </SnmpVarBind>

                </SnmpVarBinds>

                <OutputOnError>false</OutputOnError>

              </ProbeAction>

            </MemberModules>

            <Composition>

              <Node ID=Probe>

                <Node ID=Scheduler />

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>Snmp!System.SnmpData</OutputType>

      </DataSourceModuleType>

 

      <UnitMonitorType ID=QND.MyNetwork.Device.DiskFreeSpace.MT Accessibility=Internal>

        <MonitorTypeStates>

          <MonitorTypeState ID=AboveThreshold NoDetection=false />

          <MonitorTypeState ID=UnderThreshold NoDetection=false />

        </MonitorTypeStates>

        <Configuration>

          <xsd:element minOccurs=1 name=IntervalSeconds type=xsd:integer xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 name=SyncTime type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Timeout  type=xsd:unsignedInt default=500 xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Port type=xsd:unsignedInt default=161 xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element name=IP type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element name=CommunityString type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=0 maxOccurs=1 name=Version type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=1 name=LogicalDeviceIndex type=xsd:string xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=1 name=Threshold type=xsd:integer xmlns:xsd=http://www.w3.org/2001/XMLSchema />

          <xsd:element minOccurs=1 name=NumberOfSamples type=xsd:integer xmlns:xsd=http://www.w3.org/2001/XMLSchema />

        </Configuration>

        <OverrideableParameters>

          <OverrideableParameter ID=IntervalSeconds Selector=$Config/IntervalSeconds$ ParameterType=int />

          <OverrideableParameter ID=SyncTime Selector=$Config/SyncTime$ ParameterType=string />

          <OverrideableParameter ID=Timeout Selector=$Config/IntervalSeconds$ ParameterType=int />

          <OverrideableParameter ID=Threshold Selector=$Config/Threshold$ ParameterType=int />

          <OverrideableParameter ID=NumberOfSamples Selector=$Config/NumberOfSamples$ ParameterType=int />

        </OverrideableParameters>

        <MonitorImplementation>

          <MemberModules>

            <DataSource ID=DS TypeID=QND.MyNetwork.Device.WalkStorageTable.DS>

              <IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>

              <SyncTime>$Config/SyncTime$</SyncTime>

              <Timeout>$Config/Timeout$</Timeout>

              <Port>$Config/Port$</Port>

              <IP>$Config/IP$</IP>

              <CommunityString>$Config/CommunityString$</CommunityString>

              <Version>$Config/Version$</Version>

            </DataSource>

            <ConditionDetection ID=CheckIndex TypeID=System!System.ExpressionFilter>

              <Expression>

                <RegExExpression>

                  <ValueExpression>

                    <Value Type=String>$Config/LogicalDeviceIndex$</Value>

                  </ValueExpression>

                  <Operator>MatchesRegularExpression</Operator>

                  <Pattern>^[0-9]*$</Pattern>

                </RegExExpression>

              </Expression>

            </ConditionDetection>

            <ConditionDetection ID=Compute TypeID=NetworkMonitor!System.NetworkManagement.Computation>

              <NumericValue>

                <Product>

                  <NumericValue>

                    <Division>

                      <NumericValue>

                        <Subtraction>

                          <NumericValue>

                            <XPathQuery Type=Double>SnmpVarBinds/SnmpVarBind[OID=”.1.3.6.1.2.1.25.2.3.1.5.$Config/LogicalDeviceIndex$”]/Value</XPathQuery>

                          </NumericValue>

                          <NumericValue>

                            <XPathQuery Type=Double>SnmpVarBinds/SnmpVarBind[OID=”.1.3.6.1.2.1.25.2.3.1.6.$Config/LogicalDeviceIndex$”]/Value</XPathQuery>

                          </NumericValue>

                        </Subtraction>

                      </NumericValue>

                      <NumericValue>

                        <XPathQuery Type=Double>SnmpVarBinds/SnmpVarBind[OID=”.1.3.6.1.2.1.25.2.3.1.5.$Config/LogicalDeviceIndex$”]/Value</XPathQuery>

                      </NumericValue>

                    </Division>

                  </NumericValue>

                  <NumericValue>

                    <Value Type=Double>100.0</Value>

                  </NumericValue>

                </Product>

              </NumericValue>

            </ConditionDetection>

            <ConditionDetection ID=AboveThresholdCD TypeID=NetworkMonitor!System.NetworkManagement.ExpressionFilter>

              <Expression>

                <SimpleExpression>

                  <ValueExpression>

                    <XPathQuery Type=Double>Value</XPathQuery>

                  </ValueExpression>

                  <Operator>GreaterEqual</Operator>

                  <ValueExpression>

                    <Value Type=Integer>$Config/Threshold$</Value>

                  </ValueExpression>

                </SimpleExpression>

              </Expression>

              <SuppressionSettings>

                <MatchCount>$Config/NumberOfSamples$</MatchCount>

                <SampleCount>$Config/NumberOfSamples$</SampleCount>

              </SuppressionSettings>

            </ConditionDetection>

            <ConditionDetection ID=UnderThresholdCD TypeID=NetworkMonitor!System.NetworkManagement.ExpressionFilter>

              <Expression>

                <SimpleExpression>

                  <ValueExpression>

                    <XPathQuery Type=Double>Value</XPathQuery>

                  </ValueExpression>

                  <Operator>Less</Operator>

                  <ValueExpression>

                    <Value Type=Integer>$Config/Threshold$</Value>

                  </ValueExpression>

                </SimpleExpression>

              </Expression>

              <SuppressionSettings>

                <MatchCount>$Config/NumberOfSamples$</MatchCount>

                <SampleCount>$Config/NumberOfSamples$</SampleCount>

              </SuppressionSettings>

            </ConditionDetection>

          </MemberModules>

          <RegularDetections>

            <RegularDetection MonitorTypeStateID=AboveThreshold>

              <Node ID=AboveThresholdCD>

                <Node ID=Compute>

                  <Node ID=CheckIndex>

                    <Node ID=DS />

                  </Node>

                </Node>

              </Node>

            </RegularDetection>

            <RegularDetection MonitorTypeStateID=UnderThreshold>

              <Node ID=UnderThresholdCD>

                <Node ID=Compute>

                  <Node ID=CheckIndex>

                    <Node ID=DS />

                  </Node>

                </Node>

              </Node>

            </RegularDetection>

          </RegularDetections>

        </MonitorImplementation>

      </UnitMonitorType>

 

      <UnitMonitor ID=QND.MyNetwork.Device.DiskFreeSpace.Monitor Accessibility=Public Enabled=true Target=QND.MyNetwork.Device.Disk ParentMonitorID=Health!System.Health.AvailabilityState Remotable=true Priority=Normal

             TypeID=QND.MyNetwork.Device.DiskFreeSpace.MT ConfirmDelivery=false>

        <Category>AvailabilityHealth</Category>

        <AlertSettings AlertMessage=QND.MyNetwork.Device.DiskFreeSpace.AlertMessage>

          <AlertOnState>Error</AlertOnState>

          <AutoResolve>true</AutoResolve>

          <AlertPriority>Normal</AlertPriority>

          <AlertSeverity>MatchMonitorHealth</AlertSeverity>

          <AlertParameters>

            <AlertParameter1>$Target/Host/Property[Type=”System!System.Entity”]/DisplayName$</AlertParameter1>

            <AlertParameter2>$Data/Context/Value$</AlertParameter2>

          </AlertParameters>

        </AlertSettings>

        <OperationalStates>

          <OperationalState ID=OverThreshold MonitorTypeStateID=AboveThreshold HealthState=Success />

          <OperationalState ID=UnderThreshold MonitorTypeStateID=UnderThreshold HealthState=Error />

        </OperationalStates>

        <Configuration>

          <IntervalSeconds>900</IntervalSeconds>

          <Timeout>3500</Timeout>

          <Port>$Target/Host/Property[Type=”Network!System.NetworkManagement.Node”]/PortNumber$</Port>

          <IP>$Target/Host/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPAddress$</IP>

          <CommunityString>$RunAs[Name=”Network!System.NetworkManagement.Snmp.MonitoringAccount”]/CommunityString$$Target/Host/Property[Type=”Network!System.NetworkManagement.Node”]/VirtualCommunitySuffix$</CommunityString>

          <Version>$Target/Host/Property[Type=”Network!System.NetworkManagement.Node”]/SNMPVersion$</Version>

          <LogicalDeviceIndex>$Target/Property[Type=”Network!System.NetworkManagement.LogicalDevice”]/Index$</LogicalDeviceIndex>

          <Threshold>10</Threshold>

          <NumberOfSamples>3</NumberOfSamples>

        </Configuration>

      </UnitMonitor>

 

Conclusions

After this long article, you should have all the primitives to build your own network device discovery and monitor in OpsMgr. As I anticipated at the start of this series if I get community interest I will move some steps further:

– defining a custom powershell module to make easier to discover tables (see https://nocentdocent.wordpress.com/2014/06/20/discovering-and-monitoring-snmp-tables-snmp-mp-chap-5-scom-sysctr/)

– translate some xSNMP devices to OpsMgr 2012

I will check in a month or so for hits and comments and see if it’s worth or I should move to more hot arguments.

– Daniele

This posting is provided “AS IS” with no warranties, and confers no rights.

  1. #1 by Hector on March 23, 2016 - 11:57 pm

    Hi Daniele,

    Upgrading the x SNMP suite to SCOM 2012 would be fantastic.

    I second that motion.

  2. #2 by Peter Svensson on January 14, 2016 - 12:41 pm

    Hi!

    We are getting this in the Event log of the Management Server that are running the Network Discovery.

    QND-Translate-SnmpTable : Main Cannot convert argument “1”, with value: “SCRIPT_STATUS”, for “LogScriptEvent” to type “System.UInt16”: “Cannot convert value “SCRIPT_STATUS” to type “System.UInt16”. Error: “Input string was not in a correct format.””
    Version :1.0

    Any thoughts on what this means?

    • #3 by Daniele Grandini on January 25, 2016 - 3:39 pm

      Hi Peter,
      sorry for the late reply, this is a bug when the script tries to log an error. The point is something gets wrong and script tries to log an error, but I made a mistake on the logging function parameters. Since it is only called when an error arises it skipped my checks. Can you post the version of the MP you are using?

      • #4 by Peter Svensson on January 27, 2016 - 6:55 am

        Hi

        Sorry, didnt see this post so I posted a similiar question on the chapter 5 page.

        The MP versions:
        QND SNMP Library – 1.0.0.105
        QND DataItem Encoder Library – 1.0.0.9

Leave a reply to Daniele Grandini Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.