The aim of this posting is to explain with examples how discovery data can be created out of any other data-item produced by modules and be submitted to OpsMgr.

Let us assume that we have an application that acts as a server for various agents deployed in our system. We would like to collect information about various agents that contact the server and run monitoring rules against them. These agents need to be modeled as a class whose instances/objects identify various agents.

The class could be defined in Management Pack (MP) as follows:

<ClassType ID="MyAgent" Accessibility="Public" Base="System!System.Entity" Hosted="false" Abstract="false">

  <Property ID="AgentID" Type="int" Key="true" />

  <Property ID="AgentName" Type="string" Key="false" CaseSensitive="false" />

  <Property ID="AgentVersion" Type="string" Key="false" CaseSensitive="false" />

  <Property ID="AgentOwner" Type="string" Key="false" CaseSensitive="false" />

  <Property ID="LastMessageReceived" Type="string" Key="false" CaseSensitive="false" />

  <Property ID="Comments" Type="string" Key="false" CaseSensitive="false" />

</ClassType>

Table 1: Sample Class

 

As can be seen above, class MyAgent has one key Property ‘AgentID’ of type int and 5 non-key properties of type string: AgentName, AgentVersion, AgentOwner, LastMessageReceived and Comments.

To submit instances (discovery data) for this class, we would have the following section in MP:

<Monitoring>

  <Discoveries>

    <Discovery ID="AgentDiscoverer" Target="System!System.Computer" Enabled="true" ConfirmDelivery="true">

      <Category>Discovery</Category>

      <DiscoveryTypes>

        <DiscoveryClass TypeID="MyAgent" />

      </DiscoveryTypes>

      <DataSource ID="AgentDiscovererDS" TypeID="AgentDataGenerator" />

    </Discovery>

  </Discoveries>

</Monitoring>

Table 2: Sample Data Generator

 

The above text in MP tells that there would be a discovery rule running targeted towards System.Computer that would generate instances of type ‘MyAgent’. These instances would be submitted to the system by an instance of AgentDataGenerator module.

A discovery rule expects DiscoveryData. Therefore, output of AgentDataGenerator module should be System!System.Discovery.Data and nothing else. If we have a module that outputs a different type, how do we convert it to DiscoveryData/instance of MyAgent?

Let’s look at our module first.

<DataSourceModuleType ID="AgentExplorer" Accessibility="Public">

  <Configuration />

  <ModuleImplementation>

    <Managed>

      <Assembly>ServerAssembly</Assembly>

      <Type>ServerNamespace.MyServer</Type>

    </Managed>

  </ModuleImplementation>

  <OutputType>AgentData</OutputType>

</DataSourceModuleType>

Table 3: Sample Module

 

From the above definition looks like AgentExplorer is implemented as class MyServer in namespace ServerNamespace in ServerAssembly and it outputs AgentData. AgentData being defined as follows:

<DataType ID="AgentData" Base="System!System.BaseData" Accessibility="Public">

  <Implementation>

    <Assembly>ServerAssembly</Assembly>

    <Type>ServerNamespace.AgentData</Type>

  </Implementation>

</DataType>

Table 4: Sample Data

 

AgentData is implemented as AgentData in ServerAssembly. AgentData would definitely be inheriting from DataItemBase defined in Microsoft.EnterpriseManagement.HealthService.dll. As a result of this inheritance, AgentData would also provide serialization/deserialization of the contents into/from xml.

Let’s assume the xml representation (serialized version of the datatype) looks like below:

<AgentData>

  <ID>0</ID>

  <Name>Sample Agent</Name>

  <MajorVersion>1.0</MajorVersion>

  <MinorVersion>0.0</MinorVersion>

  <OwnerInfo>

    <Name>AgentOwner1</Name>

    <Name>AgentOwner2</Name>

  </OwnerInfo>

  <Message>Client 0 pinging server.</Message>

</AgentData>

Table 5: Sample Data (Serialized)

 

Now, let us look as to how we can connect the above module to a managed discovery data mapper module and generate discovery data. In short, we would be defining the module from table 2.

<DataSourceModuleType ID="AgentDataGenerator" Accessibility="Public">

  <Configuration /

  <ModuleImplementation>

    <Composite>

      <MemberModules>

        <DataSource ID="AgentExplorerDS" TypeID="AgentExplorer" />

        <ProbeAction ID="DiscoveryMapper" TypeID="MapperLibrary!Microsoft.SystemCenter.CM.DiscoveryMapperPA">

          <!-- To Add More Here Later -->

        </ProbeAction>

      </MemberModules>

      <Composition>

        <Node ID="DiscoveryMapper">

          <Node ID="AgentExplorerDS" />

        </Node>

      </Composition>

    </Composite>

  </ModuleImplementation>

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

</DataSourceModuleType>

Table 6: Sample Discovery Data Generator

 

Now, let us look at configuration for DiscoveryMapper module (defined in above table) closely. (Schema is defined in a previous blog posting).

<ManagedEntity><Detailed>

  <TimeGenerated UseCurrent="true" />

  <DiscoveryType>AddUpdate</DiscoveryType>

  <DiscoverySourceType>Rule</DiscoverySourceType>

  <SourceObjectId>$MPElement$</SourceObjectId>

  <SourceManagedEntityId>$Target/Id$</SourceManagedEntityId>

  <Properties>

    <Evaluate If=".">

      <Property ID="IDValue">//AgentData/ID</Property>

      <Property ID="NameValue">//AgentData/Name</Property>

      <Property ID="VersionValue">concat(//AgentData/MajorVersion, “.”, //AgentData/MinorVersion)</Property>

      <Property ID="OwnerValue">//AgentData/OwnerInfo/Name</Property>

      <Property ID="MessageValue">//AgentData/Message</Property>

    </Evaluate>

  </Properties>

  <PropertySets />

  <ClassInstances>

    <ClassInstance If=".">

      <TypeId>$MPElement[Name="MyAgent"]$</TypeId>

      <Properties>

        <Property>

          <Name>$MPElement[Name="MyAgent"]/AgentID$</Name>

          <Value>IDValue</Value>

        </Property>

        <Property>

          <Name>$MPElement[Name="MyAgent"]/AgentName$</Name>

          <Value>NameValue</Value>

        </Property>

        <Property>

          <Name>$MPElement[Name="MyAgent"]/AgentVersion$</Name>

          <Value>VersionValue</Value>

        </Property>

        <Property>

          <Name>$MPElement[Name="MyAgent"]/AgentOwner$</Name>

          <Value>OwnerValue[0]</Value>

        </Property>

        <Property>

          <Name>$MPElement[Name="MyAgent"]/LastMessageReceived$</Name>

          <Value>MessageValue</Value>

        </Property>

        <Property Optional="true">

          <Name>$MPElement[Name="MyAgent"]/Comments$</Name>

          <Value>No Comments!</Value>

        </Property>

      </Properties>

    </ClassInstance>

  </ClassInstances>

  <RelationshipInstances />

</Detailed></ManagedEntity>

Table 7: Configuration for Generic Data Mapper

 

The TimeGenerated element specifies the time stamp associcated with discovery data.

DiscoveryType element can be AddUpdate/Snapshot/Remove based on kind of discovery operations performed.

DiscoverySourceType element specified source of discovery data submitted to the system. In the sample case, it’s a rule. However, discovery data can be submitted via task/user/system as well.

SourceId is also required besides the DiscoverySourceType. $MPElement$ retrieves the Id of rule in which it is being used.

SourceManagedEntityId is managed entity Id of the target in this case, which is System!System.Computer.

The Properties element section in configuration allows to evaluate a bunch of xpath expressions from serialized data item (e.g., see table 5). The evaluation can be conditional as well. The ‘If’ attribute requires an xpath expression whose evaluates to a non-empty string will cause the included properties to be evaluated. There can be multiple Evaluate element sections. The mapper would build a list of property elements (as permitted by the ‘Evaluate/if’ attribute) referenced by an ID associated with xpath expressions. Xpath functions are permitted. (See VersionValue). ID can refer to a list as well here (See OwnverValue).

Explanation for PropertySets element is not required for this sample case.

The ClassInstances section allows creation of multiple class instances based on evaluation of ‘If’ attribute. If the ‘If’ attribute permits, then instance for specified ‘TypeId’ will be created. The Properties associated with it are defined as name-value pairs. Name takes an Id to the property of the class whose value needs to be populated. The Value element can take values either from configuration, values from propertyID’s defined earlier (see IDValue, NameValue, etc), values hard-coded (See Comments) and values from a member of ID referencing a list (See AgentOwner).

Also, make note of Optional attribute defined on Property element (See  Comments). When set to true, the mapper would not include that particular property in submission list if the value evaluates to an empty string.

RelationshipInstances can be discovered similarly. See schema for more info.

See all the pieces combined together in attached MP.