Welcome to MSDN Blogs Sign in | Join | Help

On Demand Detection

 A really useful feature of Health Explorer in Operations Manager is the ability to force the agent recalculate health of a particular monitor. One the most common uses for this is when you want to confirm that the action that you took actually fixed the problem. If you have implemented your management pack correctly all you need to do is select the unit monitor in health explorer and click on the "Recalculate Health" button.

In order to make this button actually force your monitor to recalculate health state, you must do some extra work in your management pack, or else nothing will happen. This is especially important if you are either creating a new monitor type or creating a script based monitor. Basically when creating a new monitor type or creating a script based monitor, unless you do some extra work "Recalculate Health" will not do much for you and the users of your MP.

Before we dive into the details of what needs to be done, let's first review a standard two state script based monitor. This monitor uses a script to determine the health state of a service (in a shipping MP I would use a different monitor type, but this was but this was convenient for demonstration purposes).

<UnitMonitor ID="Sample.ServiceStatus.Monitor2" Accessibility="Public" Enabled="true" Target="Sample.ServerRole" ParentMonitorID="Health!System.Health.AvailabilityState" Remotable="true" Priority="Normal" TypeID="Windows!Microsoft.Windows.TimedScript.TwoStateMonitorType" ConfirmDelivery="false">

        <Category>Custom</Category>

        <OperationalStates>

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

          <OperationalState ID="Success" MonitorTypeStateID="Success" HealthState="Success" />

        </OperationalStates>

        <Configuration>

          <IntervalSeconds>180</IntervalSeconds>

          <SyncTime />

          <ScriptName>GetServiceStatus2.vbs</ScriptName>

          <Arguments>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$ dhcp</Arguments>

          <ScriptBody>

            <![CDATA[

                  call Main()

 

                  Sub Main()

 

                  Dim oAPI

                  Dim oBag

                  Dim serviceName

                  Dim computerName

                  Dim statusColl

                  Dim serviceStatus

 

                  computerName  = WScript.Arguments(0)

                  serviceName   = WScript.Arguments(1)

                                   

                  Set wmi = GetObject("winmgmts:\\" & computerName  & "\root\cimv2")

 

                  wql = "SELECT * FROM Win32_Service WHERE Name='" & serviceName & "'"

 

                  Set statusColl = wmi.ExecQuery(wql)

 

                  If statusColl.Count = 1 Then

                    Dim service

                    Set service = statusColl.ItemIndex(0)

 

                    serviceStatus = service.State

                  End If

 

                  Set oAPI = CreateObject("MOM.ScriptAPI")

                  Set oBag = oAPI.CreatePropertyBag()

                  oBag.AddValue "ServiceStatus", serviceStatus

 

                  oAPI.Return(oBag)

                  End Sub

                  ]]>

          </ScriptBody>

          <TimeoutSeconds>120</TimeoutSeconds>

          <ErrorExpression>

            <SimpleExpression>

              <ValueExpression>

                <XPathQuery Type="String">Property[@Name='ServiceStatus']</XPathQuery>

              </ValueExpression>

              <Operator>NotEqual</Operator>

              <ValueExpression>

                <Value Type="String">Running</Value>

              </ValueExpression>

            </SimpleExpression>

          </ErrorExpression>

          <SuccessExpression>

            <SimpleExpression>

              <ValueExpression>

                <XPathQuery Type="String">Property[@Name='ServiceStatus']</XPathQuery>

              </ValueExpression>

              <Operator>Equal</Operator>

              <ValueExpression>

                <Value Type="String">Running</Value>

              </ValueExpression>

            </SimpleExpression>

          </SuccessExpression>

        </Configuration>

      </UnitMonitor>

This monitor looks totally fine and will work properly by running the script and interpreting the results of the script every 180 seconds. The problem is that if you try to recalculate the health state of this monitor using health explorer, nothing will happen. This is because the underlying monitor type which in essence acts as the foundation for this monitor does not implement an "On Demand Detection". The "On Demand Detection" section of the monitor type provides additional information to OpsMgr in order to allow it to recalculate the health state at any point in time rather than waiting for the 180 seconds interval. Now lets look at the steps that need to be taken in order to implement a monitor which will react to a request to recalculate its health state:

1 - Implement a probe which is just responsible for retrieving the particular piece of instrumentation

<ProbeActionModuleType ID="Sample.ServiceStatus.Probe" Accessibility="Public" Batching="false" PassThrough="false">

        <Configuration>

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

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

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

        </Configuration>

        <ModuleImplementation Isolation="Any">

          <Composite>

            <MemberModules>

              <ProbeAction ID="PassThrough" TypeID="System!System.PassThroughProbe" />

              <ProbeAction ID="Script" TypeID="Windows!Microsoft.Windows.ScriptPropertyBagProbe">

                <ScriptName>GetServiceStatus.vbs</ScriptName>

                <Arguments>$Config/ComputerPrincipalName$ $Config/ServiceName$</Arguments>

                <ScriptBody>

                  <![CDATA[

                  call Main()

 

                  Sub Main()

 

                  Dim oAPI

                  Dim oBag

                  Dim serviceName

                  Dim computerName

                  Dim statusColl

                  Dim serviceStatus

 

                  computerName  = WScript.Arguments(0)

                  serviceName   = WScript.Arguments(1)

                                   

                  Set wmi = GetObject("winmgmts:\\" & computerName  & "\root\cimv2")

 

                  wql = "SELECT * FROM Win32_Service WHERE Name='" & serviceName & "'"

 

                  Set statusColl = wmi.ExecQuery(wql)

 

                  If statusColl.Count = 1 Then

                    Dim service

                    Set service = statusColl.ItemIndex(0)

 

                    serviceStatus = service.State

                  End If

 

                  Set oAPI = CreateObject("MOM.ScriptAPI")

                  Set oBag = oAPI.CreatePropertyBag()

                  oBag.AddValue "ServiceStatus", serviceStatus

 

                  oAPI.Return(oBag)

                  End Sub

                  ]]>

                </ScriptBody>

                <TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>

              </ProbeAction>

            </MemberModules>

            <Composition>

              <Node ID="Script">

                <Node ID="PassThrough" />

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <OutputType>System!System.PropertyBagData</OutputType>

        <TriggerOnly>true</TriggerOnly>

      </ProbeActionModuleType>

2 - Implement a composite data source module which would combine the probe with a scheduler. Its not a must, but I would recommend you do this to simplify the implementation of the monitor type in step 3:

<DataSourceModuleType ID="Sample.ServiceStatus.DataSource" Accessibility="Internal" Batching="false">

        <Configuration>

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

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

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

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

        </Configuration>

        <OverrideableParameters>

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

          <OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int" />

        </OverrideableParameters>

        <ModuleImplementation Isolation="Any">

          <Composite>

            <MemberModules>

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

                <Scheduler>

                  <SimpleReccuringSchedule>

                    <Interval Unit="Seconds">$Config/IntervalSeconds$</Interval>

                  </SimpleReccuringSchedule>

                  <ExcludeDates />

                </Scheduler>

              </DataSource>

              <ProbeAction ID="Probe" TypeID="Sample.ServiceStatus.Probe">

                <TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>

                <ComputerPrincipalName>$Config/ComputerPrincipalName$</ComputerPrincipalName>

                <ServiceName>$Config/ServiceName$</ServiceName>

              </ProbeAction>

            </MemberModules>

            <Composition>

              <Node ID="Probe">

                <Node ID="Scheduler" />

              </Node>

            </Composition>

          </Composite>

        </ModuleImplementation>

        <Outp