I came across a question on the Ops Mgr newsgroups today that I thought was interesting. Someone was trying to change a logical drive free space threshold based on certain criteria. Easy, but the criteria he wanted to do it on was the Volume Label. Much to my surprise, there is no Volume Label property in the Logical Disk class or subclasses in the Windows 2003 Server management pack.
This situation can come up often and for a number of different types of monitoring objects and types of information. Fortunately, it’s relatively easy to expand upon the properties already being collected by object discoveries by creating your own class. Ops Mgr refers to these as “Attributes” in keeping with how MOM 2005 regarded this sort of additional information. In reality, they are actually new classes being created in Ops Mgr that use whatever class you want to extend as their parent class. Using the example above, let’s examine exactly how this is accomplished.
First, let’s create a class in a new MP that is based on the class we want to extend:
<ClassType ID="MyExtendedLogicalDiskClass" Accessibility="Public" Abstract="false" Base="MicrosoftWindowsServer20036062780!Microsoft.Windows.Server.2003.LogicalDisk" Hosted="true" Singleton="false">
Next we’ll add a property to this class. Since this class uses Server 2003 Logical Disk as its base class, it will inherit all of the properties of its parent class as well as its host relationship with Windows Computer. This property will exist in addition to all of that.
<Property ID="MyVolumeNameProperty" Type="string" Key="false" CaseSensitive="false" Length="256" MinLength="0" />
Then we close our class definition.
</ClassType>
<ClassType ID="TestGroup1.Group" Accessibility="Public" Abstract="false" Base="MicrosoftSystemCenterInstanceGroupLibrary6062780!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="true" />
Next, let’s create a group class that we’ll use to differentiate logical drives based on their volume label. We’ll make it an instance group so we can manipulate it in the operator console. The reason we are doing this offline is because post SP1 you seem to no longer be able to see extended classes in the operator console when building dynamic expressions. When you view the members of your group you’ll still see the extended properties you’ve created, but you won’t be able to build criteria on those properties unless you do it offline.
Now, we need to write a discovery that will populate our new extended base class. It needs to create one instance for every instance of its parent class plus add our new property to each of those instances. In this particular example, the volume name of a drive is exposed easily through WMI so we’ll use a WMI based discovery. The WMI portion is easy enough (hey, stop rolling your eyes!) but the syntax required to plug that WMI into a discovery can be a little tricky. First we create a discovery and target it at our parent class using our newly created class and discovery as the discovered types:
<Discovery ID="CustomClassDiscovery" Enabled="true" Target="MicrosoftWindowsServer20036062780!Microsoft.Windows.Server.2003.LogicalDisk" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>PerformanceCollection</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="MyExtendedLogicalDiskClass">
<Property TypeID="MyExtendedLogicalDiskClass" PropertyID="MyVolumeNameProperty" />
</DiscoveryClass>
</DiscoveryTypes>
Next we define the datasource for the discovery.:
<DataSource ID="MyDiscoveryDataSource" TypeID="MicrosoftWindowsLibrary!Microsoft.Windows.Discovery.WMISinglePropertyProvider2">
First we use a standard WMI provider. Next we define the namespace:
<NameSpace>\\$Target/Host/Property[Type="MicrosoftWindowsLibrary!Microsoft.Windows.Computer"]/NetworkName$\Root\CIMv2</NameSpace>
Here’s where it gets a little tricky. A standard WMI namespace would usually look like \\mycomputername\Root\CIMv2. In this particular instance we are targeting the Logical Disk class, which is hosted by Windows Computer. The namespace needs to be written in a context sensitive fashion so that the correct name of the computer will be plugged in at runtime. Since all logical disks are hosted by Windows Computers, we can expose the name of the computer that hosts the disk object we are targeting using the $target/Host expression. If the targeted object is the C: drive on BobsComputer then the namespace will resolve to \\BobsComputer\Root\CIMv2 when that drive is being queried.
<Query>select select * from Win32_LogicalDisk where DeviceID = '$Target/Property[Type="MicrosoftWindowsLibrary!Microsoft.Windows.LogicalDevice"]/DeviceID$'</Query>
<Frequency>30</Frequency>
<ClassID>$MPElement[Name="MyExtendedLogicalDiskClass"]$</ClassID>
<PropertyName>VolumeName</PropertyName>
Next we have a WMI query, the frequency interval (set to 30 seconds for my test lab because I hate waiting), the class ID and the PropertyName. Note the where criteria in the WMI query. Since this is firing against every logical drive on every computer, we need to scope the returned value down to each instance of a logical disk in addition to each computer. This value has to match the WMI Property Name returned by the query.
<InstanceSettings>
<Settings>
<Setting>
<Name>$MPElement[Name="MyExtendedLogicalDiskClass"]/MyVolumeNameProperty$</Name>
<Value>$Data/Property[@Name='VolumeName']$</Value>
</Setting>
The instance settings section is where we define the properties being returned. First, have our custom property that’s being returned from the WMI query.
<Setting>
<Name>$MPElement[Name="MicrosoftWindowsLibrary!Microsoft.Windows.LogicalDevice"]/DeviceID$</Name>
<Value>$Target/Property[Type="MicrosoftWindowsLibrary!Microsoft.Windows.LogicalDevice"]/DeviceID$</Value>
</Setting>
Next, we are defining the DeviceID property. Since our class inherits from Logical Disk, we inherit its Key Property too and as such we need to populate that key property with our discovery. In this instance we are taking the existing key property from our parent class and passing it into our custom class.
<Setting>
<Name>$MPElement[Name="MicrosoftWindowsLibrary!Microsoft.Windows.Computer"]/PrincipalName$</Name>
<Value>$Target/Host/Property[Type="MicrosoftWindowsLibrary!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</Setting>
Even though we’ve already defined our key property for the class, we also have to populate the key property for our hosting class. Windows Computer hosts Logical Disk, meaning that there cannot be a Logical Disk monitoring object in Ops Mgr that isn’t hosted by a Windows Computer. Here we perform the same action, taking the key property already defined in the parent class and passing it right into our custom class.
</Settings>
</InstanceSettings>
</DataSource>
And that’s the end of our Datasource. Next, let’s write a standard discovery for our computer group that populates the group based on our new property:
<Discovery ID="TestGroup1.Group.DiscoveryRule" Enabled="true" Target="TestGroup1.Group" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryRelationship TypeID="MicrosoftSystemCenterInstanceGroupLibrary6062780!Microsoft.SystemCenter.InstanceGroupContainsEntities" />
</DiscoveryTypes>
<DataSource ID="GroupPopulationDataSource" TypeID="SystemCenter!Microsoft.SystemCenter.GroupPopulator">
<RuleId>$MPElement$</RuleId>
<GroupInstanceId>$MPElement[Name="TestGroup1.Group"]$</GroupInstanceId>
<MembershipRules>
<MembershipRule>
<MonitoringClass>$MPElement[Name="MyExtendedLogicalDiskClass"]$</MonitoringClass>
<RelationshipClass>$MPElement[Name="MicrosoftSystemCenterInstanceGroupLibrary6062780!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
<Expression>
<RegExExpression>
<ValueExpression>
<Property>$MPElement[Name="MyExtendedLogicalDiskClass"]/MyVolumeNameProperty$</Property>
</ValueExpression>
<Operator>ContainsSubstring</Operator>
<Pattern>MyVolumeLabel</Pattern>
</RegExExpression>
</Expression>
</MembershipRule>
</MembershipRules>
</DataSource>
</Discovery>
Nothing special there, but now we have a dynamic group discovery that populates off of a value that we have created ourselves. This method will work fine for any kind of property you want to add, although the exact nature of the key properties will vary based on whatever class you use as a parent.
When I go to view my new group, I now see all the properties I’m used to seeing plus my new VolumeName property. One nice thing about this is that it doesn’t break console functionality. The extended class dynamic expression can be safely edited in the Group Properties page, you just can’t add your custom property to an expression that doesn’t already have it because the UI only exposes the properties of the original class, not your custom class.
-Rob