Monitoring Objects

Monitoring objects are the instances that are discovered in SCOM. They are identified by unique values for key properties as defined by the class(es) that they are as well as the key properties of their host, if applicable. Their identity is solidified by the first non-abstract class in the objects class hierarchy, even though there may still exist a hierarchy of non-abstract classes that derive from this class. For instance, a computer is defined by its System.Computer class, even though it is also a System.Windows.Computer and potentially a MyCompany.SpecialComputer, both of which derive from System.Computer. It is important to note that these additional non-abstract classes do not in any way change the identity of the object itself and can be thought of as roles.

Since a monitoring object is a representation of an instantiated class, it really represents a meta-object in a way. Early on, we were trying to figure out if we want strongly typed monitoring objects, such as Computer or Server, but for the most part decided against it, with a few exceptions to SCOM specific classes like ManagementServer. Given that, the MonitoringObject, in it's most basic form, is a property bag of values for all the class(es) properties of the object as well as the objects host(s).

When we store monitoring objects in the database, they are stored in two places. We have a general store where common attributes of all monitoring objects are maintained, such as Name, Id and class list (what classes the object is) and a dynamically generated view per identifying class that gets populated with class values. As such, all SDK methods that return MonitoringObject must make at least two queries to construct the object. If the response is heterogeneous in object class, there are even more queries performed. This fact and it's performance implications led to the SDK exposing a full MonitoringObject that represents the property bag as mentioned above as well as a stripped PartialMonitoringClass that includes all the same functionality as MonitoringObject, but does not include any property values or property value related methods. Any method returning PartialMonitoringObject will always perform a single database query and thus is the recommended approach when working with these objects, unless the properties are necessary. The only additional method on MonitoringObject that is not on PartialMonitoringObject is:

 public object GetMonitoringPropertyValue(MonitoringClassProperty property)

Please note that this method can be used properties of the monitoring object itself, or any key properties of any host class.

Before diving into all the various ways to query for monitoring objects, I wanted to go through some of features of the object (both MonitoringObject and PartialMonitoringObject) itself. First, there is a public event available on the object:

 public event EventHandler<MonitoringObjectMembershipChangedEventArgs> OnRelatedEntitiesChanged

This event will get signaled any time any object in the containment hierarchy of the current object is changed. The event includes the Id of the object whose contained members changed, but does NOT include what objects actually changed; a query would need to be performed to get contained objects and compare accordingly.

The object also contains the following properties for quick reference without needing a full Monitoring Object

         public string Name
        public string Path
        public string DisplayName
        public string FullName
        public bool IsManaged
        public DateTime LastModified
        public HealthState HealthState
        public DateTime? StateLastModified
        public bool IsAvailable
        public DateTime? AvailabilityLastModified
        public bool InMaintenanceMode
        public DateTime? MaintenanceModeLastModified
        public ReadOnlyCollection<Guid> MonitoringClassIds
        public Guid LeastDerivedNonAbstractMonitoringClassId

Name is the key value(s) of the monitoring object, while DisplayName is the same as name unless a friendly display name was specified by the discovery source via the DisplayName property defined on the System.Entity class. Path is the concatenated names of the hosts, while FullName is the uniquely identifying full name of the current instance, including name, path and class name. The HealthState of the System.EntityHealth monitor for the object is populated, as is the currently IsAvailable property (which is a function of the health service that is currently monitoring this object being available) and whether or not the object is in maintenance mode. IsManaged is always true and is not used. The MonitoringClassIds are all the non-abstract classes this object is, and the LeastDerivedNonAbstractMonitoringClassId is what it says it is, namely the class that brings in the identity of the object, just with a really long name :-).

There are a ton of methods on this object, that I will leave that for you to browse through. I will go through some as the topics come up with future posts.

Now, how do you get this objects? Well, first, let's go over MonitoringObjectGenericCriteria and MonitoringObjectCriteria. MonitoringObjectGenericCriteria allows you to query by generic properties shared across all monitoring objects. This is the list of properties you can use:

Id
Name
Path
FullName
DisplayName
LastModified
HealthState
StateLastModified
IsAvailable
AvailabilityLastModified
InMaintenanceMode
MaintenanceModeLastModified

MonitoringObjectCriteria uses the same properties and allows you to query for monitoring objects by specific property values, as defined on the class(es) of the objects (such as PrincipalName on Computer), however, you can NOT use non-abstract classes. Note that when querying by MonitoringObjectCriteria, MonitoringObject instances are always returned as we have to make a query on both tables in the database anyway.

What follows is an overview of the methods that are available for retrieving MonitoringObject(s) and PartialMonitoringObject(s). I won't go over all of them as hopefully the docs/Intellisense can help you find what you need.

These are methods that accept criteria as accessible on the ManagementGroup object:

 public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringObjectGenericCriteria criteria)
public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringObjectCriteria criteria)
public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(ICollection<MonitoringObjectCriteria> criteriaCollection)
public ReadOnlyCollection<PartialMonitoringObject> GetPartialMonitoringObjects(MonitoringObjectGenericCriteria criteria)

These are methods that accept criteria as accessible on the monitoring object instance itself. TraversalDepth can be OneLevel or Recursive and determines whether we return immediately contained instances, or all contained instances:

 public ReadOnlyCollection<MonitoringObject> GetRelatedMonitoringObjects(MonitoringObjectGenericCriteria criteria, TraversalDepth traversalDepth)
public ReadOnlyCollection<PartialMonitoringObject> GetRelatedPartialMonitoringObjects(MonitoringObjectGenericCriteria criteria, TraversalDepth traversalDepth)

These methods allow you to specify a class as your criteria, including abstract classes, and are found on ManagementGroup (similar methods are available on MonitoringObject with TraversalDepth as a parameter):

 public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringClass monitoringClass)
public ReadOnlyCollection<PartialMonitoringObject> GetPartialMonitoringObjects(MonitoringClass monitoringClass)

For getting related objects for multiple instances (the UI State View uses these methods), ManagementGroup allows you to get related object to a collection of objects by class, relationship type or MonitoringObjectCriteria (MonitoringObject also has similar methods for a single object):

 public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringClass monitoringClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject

public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringObjectCriteria criteria, TraversalDepth traversalDepth) where T : PartialMonitoringObject

public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringRelationshipClass relationshipClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject

public Dictionary<T, ReadOnlyCollection<PartialMonitoringObject>> GetRelatedPartialMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringClass monitoringClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject

public Dictionary<T, ReadOnlyCollection<PartialMonitoringObject>> GetRelatedPartialMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringRelationshipClass relationshipClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject

This list isn't exhaustive, but it's pretty close. If you have any particular scenarios that you are confused about with regard to these methods and how best to accomplish a particular query, shoot me an email or post a comment. Also note that MonitoringRelationship is another class that has a source MonitoringObject and a target MonitoringObject included in the class itself and provides another convenient way to retrieve monitoring objects, but that is for another post.