Relationships in OpsMgr 2007, Part 1
Of the two EntityTypes in the OpsMgr 2007 schema, ClassTypes and RelationshipTypes, the latter seems to be a straight-forward concept, but can have interesting ramifications in its application. Because of this, I decided to investigate them more fully, which is the subject of this series. In this first part, I will discuss the declaration of relationship types in a management pack and investigate some of the ramifications and restrictions related to the various base classes. In subsequent parts, I will investigate how relationships are involved in OpsMgr's facilities and some nuances that can help explain some of OpsMgr's less obvious behavior.
Relationships are at the foundation of many OpsMgr facilities:
- Distributed Applications
- Dependency Monitors
As you can see, a majority of the key facilities in OpsMgr rely on relationships.
First, let's discuss the declarative side of a relationship. More accurately, relationship types are declared; instances of that relationship type are discovered. The declaration of a RelationshipType is very simple, as shown in the MP snippets below:
<RelationshipType ID="System.Reference" Accessibility="Public" Abstract="true">
<RelationshipType ID="System.Containment" Accessibility="Public" Abstract="true" Base="System.Reference">
You may or may not recognize these two from the System.Library management pack. The System.Reference relationship type is the base of all relationship types, which explains why it has no Base attribute. System.Containment is another base relationship type, but derives from System.Reference. It is interesting to note that all relationship types must derive from an abstract relationship type, and abstract relationship types can only be declared in System.Library. That essentially limits the lineage of a relationship type to two, three, or four generations through one of three possible parents, respectively:
System.Reference to Your.Derived.Type
System.Reference to System.Containment to Your.Derived.Type
System.Reference to System.Containment to System.Hosting to Your.Derived.Type
There is a fourth RelationshipType declared in System.Library: System.WatchedBy (a.k.a. Perspective). This is not an abstract type and therefore is not available for derivation.
Examining the definitions, we can see that RelationshipType elements follow the schema for declaring an "accessible" MP Element and add two attributes specific to themselves. The standard attributes are ID, Comment (which is not often used), and Accessibility. The two additional attributes for RelationshipType elements are Abstract and Base. I mentioned that Comment is rare. One regular use of this attribute, albeit a bit off topic, is in Rules. Many Rule elements have the GUIDs for the MOM 2005 objects from which they came recorded in the Comment attribute. There are a few other examples, but this attribute is probably one of the least used, from my observations.
For child elements of the RelationshipType element, there are three flavors: Source, Target, and Property. You will find the great majority of relationship types are declared with just Source and Target, which can appear only once. These are at the heart of what a relationship is, exactly.
Both Source and Target specify the ClassTypes that make up the two ends of the RelationshipType. These follow the standard notation of [Alias!]ID, such as System!System.Entity (if we were in another management pack, where we're assuming that the System.Library has been aliased as System) or, in this case, just System.Entity (because this element, the RelationshipType, and that element, the ClassType, are declared in the same management pack). Whatever the relationship represents, it represents it between an object of class Source and an object of class Target. This is fairly straight-forward, but we can talk through a few examples:
- For the System.Containment relationship type, it declares that some System.Entity (Source) can contain some other System.Entity (Target).
- For the System.Hosting relationship type, it declares that some System.Entity (Source) hosts some other System.Entity (Target).
- Taking another example from the AD management pack:
<RelationshipType ID="Microsoft.Windows.Server.2003.AD.DomainControllerRoleHostsNtFrs" Accessibility="Public" Abstract="false" Base="System!System.Hosting">
- This RelationshipType declares that an AD.DomainControllerRole hosts AD.DC.NtFrs. The ID (and DisplayString), Source, and Target of this relationship are all well-chosen and describe the relationship between the types very well.
Before we leave the discussion about the declaration of a RelationshipType, it is worth noting that, while rare, some very good examples of RelationshipTypes with Property declarations exist in common MPs. For example, the AD management pack declares this relationship:
<RelationshipType ID="Microsoft.Windows.Server.AD.Site.contains.Microsoft.Windows.Server.DC.Computer" Accessibility="Public" Abstract="false" Base="System!System.Containment">
<Property ID="IsBridgeheadIP" Type="string" CaseSensitive="false" Length="255"/>
<Property ID="IsBridgeheadSMTP" Type="string" CaseSensitive="false" Length="255"/>
I think this is an excellent example (which also doesn't use the Comment attribute, if you'll notice). In this case, the properties of whether a particular DC is the IP or SMTP replication bridgehead for a particular site is stored in the relationship between the site and its DCs. I think that the tendency would be make these properties of the DC.Computer class itself. While you might be able to pull this off in this example, any many-to-many relationships would need to store relevant properties in the relationship, where they belong. Consider this adapted example:
<RelationshipType ID="Example.WebServer.Hosts.WebSite" ... Base="System!System.Hosting">
<Property ID="IsPreferredServer" Type="string" CaseSensitive="false" Length="1"/>
Clearly, in this case, you cannot mark the web server as being a preferred server, since it could host multiple web sites, for some of which it may not be the preferred server. I would also not be very elegant to have some comma-delimited property of the web site that listed all preferred servers. This is an example of where a RelationshipType declared with Property elements answers some important service modeling needs.
Restrictions and Ramifications
In spite of being relatively straight-forward to declare, relationship types have some interesting restrictions on and ramifications of their definitions:
- For relationships that derive from System.Reference:
- References are generic and can form relationships between just about any combination of classes. Children can reference parents, parents can reference children, they can be reciprocal (class A references class B while class B also references class A), reflexive (class A can reference class A), and many-to-many. In terms of relationship types that cause OpsMgr to perform in some manner, there are far more that derive from System.Containment and System.Hosting than from System.Reference. Many reference relationships are meaningful only in the context of the management pack in which they are declared. That notwithstanding, there are some very important uses of System.Reference relationships intrinsic to OpsMgr. One absolutely ubiquitous example is the Microsoft.SystemCenter.HealthServiceShouldManageEntity relationship. Another genre is the series of relationships formed when using the Distributed Application designer to connect component groups together.
- For relationships that derive from System.Containment:
- Containment relationships are just as freely declared as reference relationships, but some incarnations can have adverse effects on OpsMgr. I have found that certain ViewTypes, especially state and diagram, tend to become very unpredictable if relationships form loops, etc. In short, it has been my experience that containment relationships should be declared to represent as clean a containment model as possible. In other words, it should be a rarity that class A contains class B while class B also contains class A, etc. Although there are not as many restrictions on containment relationships (versus hosting), care should still be taken to model these succinctly.
- For relationships that derive from System.Hosting:
- There are many restrictions on hosting relationships. This is because many things come into play when two classes are related by hosting. First, if the host is deleted, the hosted classes are also deleted, so you have a referential integrity issue. Also, the health service that runs workflows related to the host will also run workflows related to the hosted classes. Finally, and perhaps most importantly, a hosting relationship is baked right into the schema of a class. Hosting relationships are discovered automatically and you cannot even discover an instance of a hosted class without also discovering for that instance the key properties of its host and all of the hosts up the chain. In the database, the underlying database objects for a class will contain the key fields of its hosts. With these types of behind-the-scenes activities, it is clear to see why there are so many restrictions on the declaration of hosting relationships.
- Restrictions on the source ClassType
- The source does not need to be hosted itself, but it can be.
- The source must be non-abstract.
- The source cannot be the same as the target.
- The source can be from any management pack.
- Restrictions on the target ClassType
- The target ClassType definition must be in the same management pack as the RelationshipType definition.
- The target must be a hosted class type or derive from a class type that is hosted.
- The target must not be the target of any other hosting relationship.
- The target can be abstract or non-abstract.
- Restrictions on using the Hosted attribute of a ClassType
- If a class is hosted, it must have a hosting relationship defined for it or derive from a hosted class (which would need its own relationship type)
- Once a ClassType is declared with the Hosted attribute set to true, it must remain consistent on all classes derived from that class.
- Other restrictions:
- A class can be the target of one and only one hosting relationship. This follows the class through its descendants (i.e. a derived class cannot be the target of a hosting relationship different from that of one of its base classes).
- The source and target cannot derive from some common class if that class has any key properties defined. This is slightly different from the previous restrictions in that it is still not allowed even if the common ancestor is not hosted. That is, it is acceptable to have Class B hosting Class C, and have them both derive from Class A if the relationship does not violate any of the aforementioned restrictions; however, if Class A contains key fields, the MP will fail on import. Oddly enough, it passes MPVerify, but when it is imported, the system will try to create Class C with two sets of identical key properties (i.e. columns): its own set of key properties inherited from Class A and the set of key properties from its host, Class B, which are also inherited from Class A. OpsMgr apparently uses a hash to create a "decorated" column name for properties of a given class that is calculated from the class and property names. Since the contributing class name (Class A) and the properties (the keys on Class A) are identical, the MP fails to import, throwing a SQL error related to the attempt to create a table or view with duplicate column names. I've actually run into this problem in a real example, so I hope that this is something that is addressed in a future release; however, this restriction is currently required.
- There can be no circular hosting references, where A hosts B, B hosts C, and then C hosts A.
As you can see, the discussion of what can and cannot be done with hosting relationships is deceptively complex. That's due in part to the fact that there is but a shade of meaning between containment and hosting in the purest sense of the words, but due to what they effect in OpsMgr, there is a great deal more to consider with hosting relationships.
For the most part, if the relationships are kept relatively simple, you will not run into these restrictions. The most common situation that brings you into contact with them is when you are attempting to integrate some existing, robust object model into the OpsMgr model. If you are simply extending the existing concepts around which OpsMgr has already been built (servers, applications, services, etc.), you should have a much easier time than this discussion may lead you to believe. None-the-less, it is interesting to test the limits of what OpsMgr will allow and perhaps even more interesting to see what OpsMgr will allow that perhaps it should not!
In the next post, we will begin to see how the different types of relationships effect OpsMgr behavior.