Setting the State of an Instance Programmatically

There has been a lot of interest in a simple way to set the state of an instance. Since we don't allow this to happen directly via the SDK, I've had many requests for a simple way to do this so here it is. One caveat: if the instance's state is Error, for instance, from a different monitor, this will not override that state. The monitor this implementation rolls up to has a "Worst Of" algorithm, so even though this monitor as set with the code below is Healthy, there may be another monitor that contributes state that is not. Hope that makes sense.

First, import the attached management pack, Manual.EntityState.Helper.xml. I've left it as unsealed so you can make changes to it if you want. Next you will need this class:

 using System;
using System.Collections.ObjectModel;
using System.Text;
using System.Xml;
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Monitoring;
using Microsoft.EnterpriseManagement.Configuration;

namespace Jakub_WorkSamples
{
    /// <summary>
    /// A class to be used in conjunction with the Manual Entity State Helper management pack to quickly set the state of any instance.
    /// </summary>
    public class EntityStateHelper
    {
        /// <summary>
        /// Sets the state of an instance.
        /// </summary>
        /// <param name="monitoringObject">The object to set the state of.</param>
        /// <param name="healthState">The state to set to.</param>
        public static void SetState(MonitoringObject monitoringObject, HealthState healthState)
        {
            int eventNumber;

            // Set the proper event id based on the state
            switch (healthState)
            {
                case HealthState.Error:
                    eventNumber = 1;
                    break;
                case HealthState.Warning:
                    eventNumber = 2;
                    break;
                case HealthState.Success:
                    eventNumber = 3;
                    break;
                default:
                    return;
            }

            // Insert the event
            CustomMonitoringEvent stateEvent = new CustomMonitoringEvent("EntityStateHelper", eventNumber);
            monitoringObject.InsertCustomMonitoringEvent(stateEvent);
        }
    }
}

Usage is pretty simple:

 // Connect to the local management group
ManagementGroup mg = new ManagementGroup("localhost");

// Get the agent class
MonitoringClass hs = mg.GetMonitoringClass(SystemMonitoringClass.HealthService);

// Get an agent
ReadOnlyCollection<MonitoringObject> monitoringObjects = mg.GetMonitoringObjects(hs);

// Set its state
EntityStateHelper.SetState(monitoringObjects[0], HealthState.Warning);

I have tested this a bit, but there may be places where this doesn't work as this was meant to work for instances hosted by the Root Management Server. If you do run into issues with a scenario, please let me know so I can try to get it figured out.

Update: You cannot use the method described above to set the state of any instance not hosted on the Root Management Server.

Manual.EntityState.Helper.xml

Comments

  • Anonymous
    August 06, 2008
    Would it be possible to compile this into a powershell cmdlet? PS>SetState agetn? I would love to use this in a loop to set state based on the outcome of a script (If...else). Thoughts? Suggestions?

  • Anonymous
    August 07, 2008
    I believe you could do it. Power shell supports .Net so, this should be straightforward to port.

  • Anonymous
    March 30, 2009
    Jakub could you please expand a bit on your update about this only working on the Root Management Server. Specifically, is it the case generally that the SDK modules (like TargetEntitySdkEventProvider) can only be used against monitoring objects hosted on the root management server? In a previous post you seemed to be suggesting this would work if you got the db permissions right: http://blogs.msdn.com/jakuboleksy/archive/2006/09/05/Inserting-Operational-Data.aspx Does that essentially mean that connector-inserted objects in general can only really appear to be hosted on the root management server? Cheers,

  • Anonymous
    March 30, 2009
    Objects inserted by the SDK can be hosted anywhere, as long as you tell the system where you want them to be managed, either by entering them as hosted by something hosted on said machine or by inserting the 'should manage' relationship directly. That being said, if it is hosted somewhere else, the module requires DB permissions to pull data, hence it's only supported on RMS, but also we've never tested it elsewhere and there may be issues trying to use the write action to set the state.  

  • Anonymous
    March 31, 2009
    The comment has been removed

  • Anonymous
    March 31, 2009
    You can't have your monitor run on the RMS and set state for an instance managed elsewhere; there is no way around that. In terms of getting the SDK module to run not on the RMS, I am never actually tried it so it only works "in theory".

  • Anonymous
    March 31, 2009
    I was confused originally as to why you were so clear that the method above would only work against the RMS, whereas you'd said previously (and repeated above) that the 'monitoring SDK events' method could work remotely (accepting you didn't say it would, just that it might). Looking at the MP above I realise that the native class you use as the implementation for the Manual.EntityState.Helper.SetStateAction module is probably only registered on the RMS server, and not on the agent managed machines (though I can't actually find that GUID anywhere when I look - it's not an R2 ClassID is it?). I'm guessing that’s why you're so clear that this is a technique that can only be used on the RMS. But that got me thinking about the TargetEntitySdkEventProvider. Ultimately it's implementation relies on SdkEventDataSource from Microsoft.Mom.SdkModules, and that assembly is does not appear to be on my agent managed host anywhere. It looks to me like just because you reference a management pack doesn't mean that any assemblies that MP is implemented against get deployed to the agent along with the monitor definition. I can see the Microsoft.SystemCenter.Library MP in the 'Management Packs' folder on the agent machine, but I can't find Microsoft.Mom.SdkModules anywhere on that machine. [and that's got a fair few dependencies itself, so bringing it over to get it working might be a bit hit and miss] Have I hit the wall and should stop wasting my time now, or did I miss something? Cheers,

  • Anonymous
    April 01, 2009
    You'll have to distribute th SDK dll yourself since we only install that on the RMS. The SetStateAction should be available on that machine (it's just copied from a system library).

  • Anonymous
    April 01, 2009
    It's working now. Just to clarify for anyone else reading, this is for the 'monitoring based on SDK events' scenario, not the 'forcing the state of an aggregate monitor' scenario, which is what Jakub was actually discussing in the post above. Like I said, the Microsoft.Mom.SdkModules has a fair few dependencies, so it's not just a case of copying the SDK dlls (which don't include that assembly anyway) over. I had to copy over the following: Microsoft.Mom.SdkModules Microsoft.Mom.DataAccessLayer Microsoft.EnterpriseManagement.OperationsManager.Common Microsoft.EnterpriseManagement.HealthService.Modules.Instrumentation Additionally the following assemblies appear as dependencies from Microsoft.Mom.SdkModules, but don't appear to be required (obviously those call paths are not exercised in this case): Microsoft.EnterpriseManagement.HealthService Microsoft.Mom.AlertSubscriptionDataSourceModule I also had to set the following registry key, which the DataAccessLayer (used by the SdkEventDataSource class) uses to locate the MOM database: HKLMSOFTWAREMicrosoftMicrosoft Operations Manager3.0SetupDatabaseServerName Then I had to configure a Run As Account against the Run As Profile 'Operational Database Account', limiting this to the machine I need to run the SDK data source on. Finally, since I configured the RunAs account as Network Service, I had to go and grant the Network Service account on the remote machine access to the OperationsManager database. I gave it sdk_users role, and it somehow also got db_datareader, db_datawriter, db_ddladmin. Then it all worked. Yes, it really was that easy.

  • Anonymous
    April 01, 2009
    Wow =) Nice work and thanks for following up.