Dependency Monitor

If you’ve been following the series, you should have two classes and a unit monitor. As discussed earlier in the series, the base class types used are Local Application and Application Component, and these do not automatically rollup health to its hosting class.

If you want unit monitors targeting the application component class to rollup health to its hosting class (local application), you can add a dependency monitor

I prefer to add dependency monitors to the fragment that contains the class in which I want to rollup. So, this fragment is a repeat of the application component class fragment posted earlier, with the exception of including the dependency monitor.

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TypeDefinitions>
<EntityTypes>
<ClassTypes>
<ClassType ID="SCOMskills.Demo.ApplicationComponent.Class" Abstract="false" Accessibility="Public" Base="Windows!Microsoft.Windows.ApplicationComponent" Hosted="true" Singleton="false">
<Property ID="CsvField1" Key="false" Type="string"/>
<Property ID="CsvField2" Key="false" Type="string"/>
</ClassType>
</ClassTypes>
<RelationshipTypes>
<RelationshipType ID="LocalApplicationHostsApplicationComponent" Base="System!System.Hosting" Accessibility="Public">
<Source ID="LocalApplication" Type="SCOMskills.Demo.LocalApplication.Class"/>
<Target ID="ApplicationComponent" Type="SCOMskills.Demo.ApplicationComponent.Class"/>
</RelationshipType>
</RelationshipTypes>
</EntityTypes>
</TypeDefinitions>
<Monitoring>
<Monitors>
<DependencyMonitor ID="ApplicationComponentAvailabilityRollup" Accessibility=“InternalEnabled=“trueMemberMonitor=“Health!System.Health.AvailabilityStateParentMonitorID=“Health!System.Health.AvailabilityStateRelationshipType=“LocalApplicationHostsApplicationComponentPriority=“NormalRemotable=“falseTarget=“SCOMskills.Demo.LocalApplication.Class>
<Category>AvailabilityHealth</Category>
<Algorithm>WorstOf</Algorithm>
</DependencyMonitor>
</Monitors>
</Monitoring>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class">
<Name>SCOMskills Application Component Class</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class" SubElementID="CsvField1">
<Name>CSV Field 1</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class" SubElementID="CsvField2">
<Name>CSV Field 2</Name>
</DisplayString>
<DisplayString ElementID="LocalApplicationHostsApplicationComponent">
<Name>Local Application class hosts Application Component class</Name>
</DisplayString>
<DisplayString ElementID="ApplicationComponentAvailabilityRollup">
<Name>Application Component Health</Name>
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>

Two-State Event Detection

This fragment demonstrates how you can use one of the built-in, two-state Windows event modules to compose a unit monitor. This fragment is the next example in the VSAE Fragment category; the xml fragment below should work as described if you have been following the series.

Module Type used in this post: Microsoft.Windows.2SingleEventLog2StateMonitorType

Scenario

Change state to critical and raise a critical alert when event Id 101, from  source TEST, and event level is Error is detected in the Application log. Resolve alert when same event is detected with event level Information.

A note about event level:

1 = Error

4 = Information

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Monitoring>
    <Monitors>
      <UnitMonitor ID="SCOMskills.Demo.Event101.2State" Accessibility="Public" ConfirmDelivery="false" Enabled="true" ParentMonitorID="Health!System.Health.AvailabilityState" Priority="Normal" Remotable="false" Target="SCOMskills.Demo.ApplicationComponent.Class" TypeID="Windows!Microsoft.Windows.2SingleEventLog2StateMonitorType">
        <Category>AvailabilityHealth</Category>
        <AlertSettings AlertMessage="SCOMskills.Demo.Event101.2State.AlertMessage">
          <AlertOnState>Error</AlertOnState>
          <AutoResolve>true</AutoResolve>
          <AlertPriority>Normal</AlertPriority>
          <AlertSeverity>Error</AlertSeverity>
          <AlertParameters>
            <AlertParameter1>$Data/Context/EventDescription$</AlertParameter1>
          </AlertParameters>
        </AlertSettings>
        <OperationalStates>
          <OperationalState ID="Unhealthy" HealthState="Error" MonitorTypeStateID="FirstEventRaised"/>
          <OperationalState ID="Healthy" HealthState="Success" MonitorTypeStateID="SecondEventRaised"/>
        </OperationalStates>
        <Configuration>
          <FirstComputerName>$Target/Host/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</FirstComputerName>
          <FirstLogName>Application</FirstLogName>
          <FirstExpression>
            <And>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventSourceName</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>TEST</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventDisplayNumber</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>101</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventLevel</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>1</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
            </And>
          </FirstExpression>
          <SecondComputerName>$Target/Host/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</SecondComputerName>
          <SecondLogName>Application</SecondLogName>
          <SecondExpression>
            <And>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventSourceName</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>TEST</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventDisplayNumber</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>101</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
              <Expression>
                <SimpleExpression>
                  <ValueExpression>
                    <XPathQuery>EventLevel</XPathQuery>
                  </ValueExpression>
                  <Operator>Equal</Operator>
                  <ValueExpression>
                    <Value>4</Value>
                  </ValueExpression>
                </SimpleExpression>
              </Expression>
            </And>
          </SecondExpression>
        </Configuration>
      </UnitMonitor>
    </Monitors>
  </Monitoring>
  <Presentation>
    <StringResources>
      <StringResource ID="SCOMskills.Demo.Event101.2State.AlertMessage"/>
    </StringResources>
  </Presentation>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SCOMskills.Demo.Event101.2State">
          <Name>Event 101 Monitor</Name>
          <Description>Detect event Id 101. Raise alert if error, resolve alert if informational.</Description>
        </DisplayString>
        <DisplayString ElementID="SCOMskills.Demo.Event101.2State.AlertMessage">
          <Name>Detected Event Id 101</Name>
          <Description>{0}</Description>
        </DisplayString>
        <DisplayString ElementID="SCOMskills.Demo.Event101.2State" SubElementID="Healthy">
          <Name>Healthy</Name>
        </DisplayString>
        <DisplayString ElementID="SCOMskills.Demo.Event101.2State" SubElementID="Unhealthy">
          <Name>Unhealthy</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 
 
Logo_Main_LinkedIn[4]

_

Timed Script Discovery (vbscript part 2)

Referring back to the previous VSAE Fragment post regarding the Windows Timed Script Discovery Provider, you can see this module can be implemented directly in a new discovery. In most cases, this works very well. In some cases, you may want to create a custom discovery provider in order to expose configuration elements as separate parameters – particularly override parameters.

Example of override parameters by implementing the built-in module type in your discovery

image

Zooming in on the script arguments, you can see this:

$MPElement$ $Target/Id$ $Target/Host/Property[Type=”Windows!Microsoft.Windows.Computer”]/PrincipalName$ c:\ScomSkillsDemo.csv

This isn’t very friendly to an operator that is setting a discovery override, and presents greater opportunity for override mistakes. The only parameter an operator might need to change is the CSV File Location.

So, how can this be improved from an operator perspective? One option is to create a new module type. If you have multiple parameters you want separated in the Operations console, this is probably the best option, as the built-in provider only allows for a single string of arguments.

This example is purely to offer a better experience for the operator creating overrides – there is really no difference in how instances will be discovered under the hood. The result will be new override parameters available in the Operations console.

Example of override parameters by implementing a custom discovery provider

image

 

What does this do?

This performs the same discovery as the previous fragment, but creates custom override parameters in the Operations console.

 

Fragment 1: Custom discovery module type

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <ModuleTypes>
      <DataSourceModuleType ID="SCOMskills.Demo.TimedScript.DiscoveryProvider.Custom" Accessibility="Public">
        <Configuration>
          <xsd:element minOccurs="1" name="Interval" type="xsd:integer" />
          <xsd:element minOccurs="1" name="CsvFileLocation" type="xsd:string" />
          <xsd:element minOccurs="1" name="PrincipalName" type="xsd:string" />
          <xsd:element minOccurs="1" name="Timeout" type="xsd:integer" />
        </Configuration>
        <OverrideableParameters>
          <OverrideableParameter ID="Interval" ParameterType="int" Selector="$Config/Interval$"/>
          <OverrideableParameter ID="CsvFileLocation" ParameterType="string" Selector="$Config/CsvFileLocation$"/>
          <OverrideableParameter ID="Timeout" ParameterType="int" Selector="$Config/Timeout$"/>
        </OverrideableParameters>
        <ModuleImplementation Isolation="Any">
          <Composite>
            <MemberModules>
              <DataSource ID="Scheduler" TypeID="System!System.Scheduler">
                <Scheduler>
                  <SimpleReccuringSchedule>
                    <Interval>$Config/Interval$</Interval>
                  </SimpleReccuringSchedule>
                  <ExcludeDates />
                </Scheduler>
              </DataSource>
              <ProbeAction ID="Script" TypeID="Windows!Microsoft.Windows.ScriptDiscoveryProbe">
                <ScriptName>SCOMskills.Demo.Discovery.vbs</ScriptName>
                <Arguments>$MPElement$ $Target/Id$ $Config/PrincipalName$ $Config/CsvFileLocation$</Arguments>
                <ScriptBody>
                  <![CDATA[
Option Explicit

Dim oArgs
Set oArgs = WScript.Arguments
if oArgs.Count < 4 Then
   Wscript.Quit -1
End If

Dim SourceID, ManagedEntityId, TargetComputer, SourceFile

SourceId = oArgs(0)
ManagedEntityId = oArgs(1)
TargetComputer = oArgs(2)
SourceFile = oArgs(3)

Dim oAPI, oDiscoveryData, oInst
Set oAPI = CreateObject("MOM.ScriptAPI")
set oDiscoveryData = oAPI.CreateDiscoveryData(0, SourceId, ManagedEntityId)

Dim oFso, oFile, aField
Set oFso = CreateObject("Scripting.FileSystemObject")

If (oFso.FileExists(SourceFile)) Then
Set oFile = oFso.OpenTextFile(SourceFile)
aField = split(oFile.ReadLine,",")

set oInst = oDiscoveryData.CreateClassInstance("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']$")
call oInst.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", TargetComputer)
call oInst.AddProperty("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']/CsvField1$", aField(0))
call oInst.AddProperty("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']/CsvField2$", aField(1))
call oInst.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", TargetComputer)
call oDiscoveryData.AddInstance(oInst)

Else

Wscript.Quit -1

End If

Call oAPI.Return(oDiscoveryData) 
                ]]>
                </ScriptBody>
                <TimeoutSeconds>$Config/Timeout$</TimeoutSeconds>
              </ProbeAction>
            </MemberModules>
            <Composition>
              <Node ID="Script">
                <Node ID="Scheduler" />
              </Node>
            </Composition>
          </Composite>
        </ModuleImplementation>
        <OutputType>System!System.Discovery.Data</OutputType>
      </DataSourceModuleType>
    </ModuleTypes>
  </TypeDefinitions>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SCOMskills.Demo.TimedScript.DiscoveryProvider.Custom">
          <Name>SCOMskills Custom Timed Script Discovery</Name>
        </DisplayString>
        <DisplayString ElementID="SCOMskills.Demo.TimedScript.DiscoveryProvider.Custom" SubElementID="CsvFileLocation">
          <Name>CSV File Location</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

Fragment 2: Implementing the custom discovery module

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Monitoring>
    <Discoveries>
      <Discovery ID="SCOMskills.Demo.TimedScript.Custom.Discovery" ConfirmDelivery="false" Enabled="true" Priority="Normal" Remotable="true" Target="SCOMskills.Demo.LocalApplication.Class">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="SCOMskills.Demo.ApplicationComponent.Class">
            <Property TypeID="SCOMskills.Demo.ApplicationComponent.Class" PropertyID="CsvField1"/>
            <Property TypeID="SCOMskills.Demo.ApplicationComponent.Class" PropertyID="CsvField2"/>
            <Property TypeID="System!System.Entity" PropertyID="DisplayName"/>
          </DiscoveryClass>
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="SCOMskills.Demo.TimedScript.DiscoveryProvider.Custom">
          <Interval>60</Interval>
          <CsvFileLocation>c:\ScomSkillsDemo.csv</CsvFileLocation>
          <PrincipalName>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</PrincipalName>
          <Timeout>60</Timeout>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SCOMskills.Demo.TimedScript.Custom.Discovery">
          <Name>SCOMskills Timed Script Custom Discovery</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>
 
 
 
 
Logo_Main_LinkedIn

_

Logical Disk Free Space monitor doesn’t cookdown

I haven’t looked at the internals of this monitor in a long time. But recently I was working on a modified disk free space monitoring solution for a customer, and to my surprise uncovered the fact that the built-in logical disk space monitor in the Windows management packs do not utilize cookdown. This had changed at some point in time, because I know these monitors  took advantage of cookdown in previous versions.

Fixing this wasn’t officially a part of the disk monitoring project I was tasked with, but just knowing the fact it didn’t utilize cookdown was enough to compel me to rewrite the entire workflow. After I fixed the cookdown problem and finished other additions to the disk space monitor, I was going to post a management pack here with an updated logical disk free space monitor…until I stumbled across a blog entry Kevin Holman posted over a year ago.

I’m not sure if Kevin realized at the time that his addendum MP fixed the cookdown problem, but it does. Hot smile Head over to his blog to download the addendum MP, especially if your company uses a lot of instance level disk space overrides.

 

For those that need a refresher on cookdown, this is a programming technique that (if implemented properly) will execute only one instance of a script data source even if there are multiple monitoring instances that consume the output. The key to cookdown is passing no (or only necessary) configuration elements into the script execution, and processing instance and state filtering after script execution by using expression filters.

If the script expects configuration input, and the monitor configuration is changed by an override, the script will need to run once for each instance override. In the case of the logical disk free space monitor, this leaves a huge door of opportunity to run multiple copies of the data source script, and this will lead to resource consumption on the monitored computer.

 

The built-in logical disk free space data source module (cookdown broken with overrides)

 

<DataSourceModuleType ID="Microsoft.Windows.Server.2008.FreeSpace.Moduletype" Accessibility="Internal" Batching="false">
  <Configuration>
    <xsd:element minOccurs="1" name="ComputerName" type="xsd:string" />
    <xsd:element minOccurs="1" name="DiskLabel" type="xsd:string" />
    <xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:integer" />
    <xsd:element minOccurs="1" name="SystemDriveWarningMBytesThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="SystemDriveWarningPercentThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="SystemDriveErrorMBytesThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="SystemDriveErrorPercentThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="NonSystemDriveWarningMBytesThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="NonSystemDriveWarningPercentThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="NonSystemDriveErrorMBytesThreshold" type="xsd:double" />
    <xsd:element minOccurs="1" name="NonSystemDriveErrorPercentThreshold" type="xsd:double" />
  </Configuration>
  <OverrideableParameters>
    <OverrideableParameter ID="Interval" Selector="$Config/IntervalSeconds$" ParameterType="int" />
    <OverrideableParameter ID="SystemDriveWarningMBytesThreshold" Selector="$Config/SystemDriveWarningMBytesThreshold$" ParameterType="double" />
    <OverrideableParameter ID="SystemDriveWarningPercentThreshold" Selector="$Config/SystemDriveWarningPercentThreshold$" ParameterType="double" />
    <OverrideableParameter ID="SystemDriveErrorMBytesThreshold" Selector="$Config/SystemDriveErrorMBytesThreshold$" ParameterType="double" />
    <OverrideableParameter ID="SystemDriveErrorPercentThreshold" Selector="$Config/SystemDriveErrorPercentThreshold$" ParameterType="double" />
    <OverrideableParameter ID="NonSystemDriveWarningMBytesThreshold" Selector="$Config/NonSystemDriveWarningMBytesThreshold$" ParameterType="double" />
    <OverrideableParameter ID="NonSystemDriveWarningPercentThreshold" Selector="$Config/NonSystemDriveWarningPercentThreshold$" ParameterType="double" />
    <OverrideableParameter ID="NonSystemDriveErrorMBytesThreshold" Selector="$Config/NonSystemDriveErrorMBytesThreshold$" ParameterType="double" />
    <OverrideableParameter ID="NonSystemDriveErrorPercentThreshold" Selector="$Config/NonSystemDriveErrorPercentThreshold$" ParameterType="double" />
  </OverrideableParameters>

 

The proper way to implement a script data source to utilize cookdown (configuration and state filtering should happen at the monitor type, not the data source module)

 

<DataSourceModuleType ID="Microsoft.Windows.Server.2008.Monitoring.Addendum.FreeSpace.ModuleType" Accessibility="Internal" Batching="false">
  <Configuration>
    <xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:int" />
    <xsd:element minOccurs="1" name="TargetComputerName" type="xsd:string" />
  </Configuration>

 

I wonder if Microsoft will plan to fix this in the future. Good thing these modules are flagged internal, so it shouldn’t be much to ask and an update wouldn’t break upgrade compatibility for customers.

This post applies to Windows MP versions 6.0.6958 – 6.0.6989.0.

_

Health Explorer – Scope is only unhealthy child monitors

This is misleading, as it’s only true sometimes. Let me show you a clear example with pictures.

 

The examples below can be reproduced with OpsMgr 2012 SP1 by opening Health Explorer for the Windows Computer object.

Example 1

Two unhealthy instances; one critical and the other warning.

Health Explorer default behavior

Instance in warning state isn’t listed with the filter applied.

image

 

Remove the unhealthy filter

Instance in warning state is listed when filter is removed (as expected).

image

 

Example 2

Two unhealthy instances; both critical.

Health Explorer default behavior

Both critical instances are listed.

image.

 

Remove the unhealthy filter

Both critical instances are listed (as expected).

image

 

Example 2

Two unhealthy instances; both warning.

Health Explorer default behavior

Both warning instances are listed. Also notice that HEALTHY instances of the SAME TYPE are also listed.

image

 

Remove the unhealthy filter

Both warning instances are listed, as well as everything else (as expected).

image

 

The main takeaway with this post is to beware of the default behavior in Health Explorer – it might only show you half the truth. I’m not sure if I’d go as far as lodging a bug or calling into Microsoft support services for this, but I certainly didn’t’ expect this behavior with the filtering option.